Skip to content

Commit b905205

Browse files
authored
Merge pull request #35 from mbullington/edit-features
Allow for array modifications, add inPlace formatting option.
2 parents e38baa7 + 0d78fe6 commit b905205

File tree

3 files changed

+107
-24
lines changed

3 files changed

+107
-24
lines changed

src/impl/edit.ts

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export function removeProperty(text: string, path: JSONPath, formattingOptions:
1212
return setProperty(text, path, void 0, formattingOptions);
1313
}
1414

15-
export function setProperty(text: string, originalPath: JSONPath, value: any, formattingOptions: FormattingOptions, getInsertionIndex?: (properties: string[]) => number): Edit[] {
15+
export function setProperty(text: string, originalPath: JSONPath, value: any, formattingOptions: FormattingOptions, getInsertionIndex?: (properties: string[]) => number, isArrayInsertion: boolean = false): Edit[] {
1616
let path = originalPath.slice()
1717
let errors: ParseError[] = [];
1818
let root = parseTree(text, errors);
@@ -96,35 +96,53 @@ export function setProperty(text: string, originalPath: JSONPath, value: any, fo
9696
edit = { offset: previous.offset + previous.length, length: 0, content: ',' + newProperty };
9797
}
9898
return withFormatting(text, edit, formattingOptions);
99-
} else {
100-
if (value === void 0 && parent.children.length >= 0) {
101-
//Removal
102-
let removalIndex = lastSegment;
103-
let toRemove = parent.children[removalIndex];
104-
let edit: Edit;
105-
if (parent.children.length === 1) {
106-
// only item
107-
edit = { offset: parent.offset + 1, length: parent.length - 2, content: '' };
108-
} else if (parent.children.length - 1 === removalIndex) {
109-
// last item
110-
let previous = parent.children[removalIndex - 1];
111-
let offset = previous.offset + previous.length;
112-
let parentEndOffset = parent.offset + parent.length;
113-
edit = { offset, length: parentEndOffset - 2 - offset, content: '' };
114-
} else {
115-
edit = { offset: toRemove.offset, length: parent.children[removalIndex + 1].offset - toRemove.offset, content: '' };
116-
}
117-
return withFormatting(text, edit, formattingOptions);
99+
} else if (value === void 0 && parent.children.length >= 0) {
100+
// Removal
101+
let removalIndex = lastSegment;
102+
let toRemove = parent.children[removalIndex];
103+
let edit: Edit;
104+
if (parent.children.length === 1) {
105+
// only item
106+
edit = { offset: parent.offset + 1, length: parent.length - 2, content: '' };
107+
} else if (parent.children.length - 1 === removalIndex) {
108+
// last item
109+
let previous = parent.children[removalIndex - 1];
110+
let offset = previous.offset + previous.length;
111+
let parentEndOffset = parent.offset + parent.length;
112+
edit = { offset, length: parentEndOffset - 2 - offset, content: '' };
118113
} else {
119-
throw new Error('Array modification not supported yet');
114+
edit = { offset: toRemove.offset, length: parent.children[removalIndex + 1].offset - toRemove.offset, content: '' };
120115
}
116+
return withFormatting(text, edit, formattingOptions);
117+
} else if (value !== void 0) {
118+
let edit: Edit;
119+
const newProperty = `${JSON.stringify(value)}`;
120+
121+
if (!isArrayInsertion && parent.children.length > lastSegment) {
122+
let toModify = parent.children[lastSegment];
123+
124+
edit = { offset: toModify.offset, length: toModify.length, content: newProperty }
125+
} else if (parent.children.length === 0 || lastSegment === 0) {
126+
edit = { offset: parent.offset + 1, length: 0, content: parent.children.length === 0 ? newProperty : newProperty + ',' };
127+
} else {
128+
const index = lastSegment > parent.children.length ? parent.children.length : lastSegment;
129+
const previous = parent.children[index - 1];
130+
edit = { offset: previous.offset + previous.length, length: 0, content: ',' + newProperty };
131+
}
132+
133+
return withFormatting(text, edit, formattingOptions);
134+
} else {
135+
throw new Error(`Can not ${value === void 0 ? 'remove' : (isArrayInsertion ? 'insert' : 'modify')} Array index ${insertIndex} as length is not sufficient`);
121136
}
122137
} else {
123138
throw new Error(`Can not add ${typeof lastSegment !== 'number' ? 'index' : 'property'} to parent of type ${parent.type}`);
124139
}
125140
}
126141

127142
function withFormatting(text: string, edit: Edit, formattingOptions: FormattingOptions): Edit[] {
143+
if (formattingOptions.inPlace) {
144+
return [{ ...edit }]
145+
}
128146
// apply the edit
129147
let newText = applyEdit(text, edit);
130148

src/main.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,11 @@ export interface FormattingOptions {
322322
* The default 'end of line' character. If not set, '\n' is used as default.
323323
*/
324324
eol?: string;
325+
/**
326+
* If true, changes within {@function format} will not be formatted and their original formatting will be preserved.
327+
* Useful for cutting down on computational time for large files.
328+
*/
329+
inPlace?: boolean;
325330
}
326331

327332
/**
@@ -348,6 +353,11 @@ export interface ModificationOptions {
348353
* Formatting options
349354
*/
350355
formattingOptions: FormattingOptions;
356+
/**
357+
* Default false. If `JSONPath` refers to an index of an array and {@property isArrayInsertion} is `true`, then
358+
* {@function modify} will insert a new item at that location instead of overwriting its contents.
359+
*/
360+
isArrayInsertion?: boolean;
351361
/**
352362
* Optional function to define the insertion index given an existing list of properties.
353363
*/
@@ -370,7 +380,7 @@ export interface ModificationOptions {
370380
* To apply edits to an input, you can use `applyEdits`.
371381
*/
372382
export function modify(text: string, path: JSONPath, value: any, options: ModificationOptions): Edit[] {
373-
return edit.setProperty(text, path, value, options.formattingOptions, options.getInsertionIndex);
383+
return edit.setProperty(text, path, value, options.formattingOptions, options.getInsertionIndex, options.isArrayInsertion);
374384
}
375385

376386
/**

src/test/edit.test.ts

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,13 +121,59 @@ suite('JSON - edits', () => {
121121
assertEdit(content, edits, '{\n "x": "y"\n}');
122122
});
123123

124-
test('insert item to empty array', () => {
124+
test('set item', () => {
125+
let content = '{\n "x": [1, 2, 3],\n "y": 0\n}'
126+
127+
let edits = setProperty(content, ['x', 0], 6, formatterOptions);
128+
assertEdit(content, edits, '{\n "x": [6, 2, 3],\n "y": 0\n}');
129+
130+
edits = setProperty(content, ['x', 1], 5, formatterOptions);
131+
assertEdit(content, edits, '{\n "x": [1, 5, 3],\n "y": 0\n}');
132+
133+
edits = setProperty(content, ['x', 2], 4, formatterOptions);
134+
assertEdit(content, edits, '{\n "x": [1, 2, 4],\n "y": 0\n}');
135+
136+
edits = setProperty(content, ['x', 3], 3, formatterOptions)
137+
assertEdit(content, edits, '{\n "x": [\n 1,\n 2,\n 3,\n 3\n ],\n "y": 0\n}');
138+
});
139+
140+
test('insert item at 0; isArrayInsertion = true', () => {
141+
let content = '[\n 2,\n 3\n]';
142+
let edits = setProperty(content, [0], 1, formatterOptions, undefined, true);
143+
assertEdit(content, edits, '[\n 1,\n 2,\n 3\n]');
144+
});
145+
146+
test('insert item at 0 in empty array', () => {
147+
let content = '[\n]';
148+
let edits = setProperty(content, [0], 1, formatterOptions);
149+
assertEdit(content, edits, '[\n 1\n]');
150+
});
151+
152+
test('insert item at an index; isArrayInsertion = true', () => {
153+
let content = '[\n 1,\n 3\n]';
154+
let edits = setProperty(content, [1], 2, formatterOptions, undefined, true);
155+
assertEdit(content, edits, '[\n 1,\n 2,\n 3\n]');
156+
});
157+
158+
test('insert item at an index in empty array', () => {
159+
let content = '[\n]';
160+
let edits = setProperty(content, [1], 1, formatterOptions);
161+
assertEdit(content, edits, '[\n 1\n]');
162+
});
163+
164+
test('insert item at end index', () => {
165+
let content = '[\n 1,\n 2\n]';
166+
let edits = setProperty(content, [2], 3, formatterOptions);
167+
assertEdit(content, edits, '[\n 1,\n 2,\n 3\n]');
168+
});
169+
170+
test('insert item at end to empty array', () => {
125171
let content = '[\n]';
126172
let edits = setProperty(content, [-1], 'bar', formatterOptions);
127173
assertEdit(content, edits, '[\n "bar"\n]');
128174
});
129175

130-
test('insert item', () => {
176+
test('insert item at end', () => {
131177
let content = '[\n 1,\n 2\n]';
132178
let edits = setProperty(content, [-1], 'bar', formatterOptions);
133179
assertEdit(content, edits, '[\n 1,\n 2,\n "bar"\n]');
@@ -163,4 +209,13 @@ suite('JSON - edits', () => {
163209
assertEdit(content, edits, '// This is a comment\n[\n 1,\n "foo"\n]');
164210
});
165211

212+
test('set property w/ in-place formatting options', () => {
213+
let content = '{\n "x": [1, 2, 3],\n "y": 0\n}'
214+
215+
let edits = setProperty(content, ['x', 0], { a: 1, b: 2 }, formatterOptions);
216+
assertEdit(content, edits, '{\n "x": [{\n "a": 1,\n "b": 2\n }, 2, 3],\n "y": 0\n}');
217+
218+
edits = setProperty(content, ['x', 0], { a: 1, b: 2 }, { ...formatterOptions, inPlace: true });
219+
assertEdit(content, edits, '{\n "x": [{"a":1,"b":2}, 2, 3],\n "y": 0\n}');
220+
});
166221
});

0 commit comments

Comments
 (0)