edit.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. (function (factory) {
  2. if (typeof module === "object" && typeof module.exports === "object") {
  3. var v = factory(require, exports);
  4. if (v !== undefined) module.exports = v;
  5. }
  6. else if (typeof define === "function" && define.amd) {
  7. define(["require", "exports", "./format", "./parser"], factory);
  8. }
  9. })(function (require, exports) {
  10. /*---------------------------------------------------------------------------------------------
  11. * Copyright (c) Microsoft Corporation. All rights reserved.
  12. * Licensed under the MIT License. See License.txt in the project root for license information.
  13. *--------------------------------------------------------------------------------------------*/
  14. 'use strict';
  15. Object.defineProperty(exports, "__esModule", { value: true });
  16. exports.isWS = exports.applyEdit = exports.setProperty = exports.removeProperty = void 0;
  17. var format_1 = require("./format");
  18. var parser_1 = require("./parser");
  19. function removeProperty(text, path, options) {
  20. return setProperty(text, path, void 0, options);
  21. }
  22. exports.removeProperty = removeProperty;
  23. function setProperty(text, originalPath, value, options) {
  24. var _a;
  25. var path = originalPath.slice();
  26. var errors = [];
  27. var root = parser_1.parseTree(text, errors);
  28. var parent = void 0;
  29. var lastSegment = void 0;
  30. while (path.length > 0) {
  31. lastSegment = path.pop();
  32. parent = parser_1.findNodeAtLocation(root, path);
  33. if (parent === void 0 && value !== void 0) {
  34. if (typeof lastSegment === 'string') {
  35. value = (_a = {}, _a[lastSegment] = value, _a);
  36. }
  37. else {
  38. value = [value];
  39. }
  40. }
  41. else {
  42. break;
  43. }
  44. }
  45. if (!parent) {
  46. // empty document
  47. if (value === void 0) { // delete
  48. throw new Error('Can not delete in empty document');
  49. }
  50. return withFormatting(text, { offset: root ? root.offset : 0, length: root ? root.length : 0, content: JSON.stringify(value) }, options);
  51. }
  52. else if (parent.type === 'object' && typeof lastSegment === 'string' && Array.isArray(parent.children)) {
  53. var existing = parser_1.findNodeAtLocation(parent, [lastSegment]);
  54. if (existing !== void 0) {
  55. if (value === void 0) { // delete
  56. if (!existing.parent) {
  57. throw new Error('Malformed AST');
  58. }
  59. var propertyIndex = parent.children.indexOf(existing.parent);
  60. var removeBegin = void 0;
  61. var removeEnd = existing.parent.offset + existing.parent.length;
  62. if (propertyIndex > 0) {
  63. // remove the comma of the previous node
  64. var previous = parent.children[propertyIndex - 1];
  65. removeBegin = previous.offset + previous.length;
  66. }
  67. else {
  68. removeBegin = parent.offset + 1;
  69. if (parent.children.length > 1) {
  70. // remove the comma of the next node
  71. var next = parent.children[1];
  72. removeEnd = next.offset;
  73. }
  74. }
  75. return withFormatting(text, { offset: removeBegin, length: removeEnd - removeBegin, content: '' }, options);
  76. }
  77. else {
  78. // set value of existing property
  79. return withFormatting(text, { offset: existing.offset, length: existing.length, content: JSON.stringify(value) }, options);
  80. }
  81. }
  82. else {
  83. if (value === void 0) { // delete
  84. return []; // property does not exist, nothing to do
  85. }
  86. var newProperty = JSON.stringify(lastSegment) + ": " + JSON.stringify(value);
  87. var index = options.getInsertionIndex ? options.getInsertionIndex(parent.children.map(function (p) { return p.children[0].value; })) : parent.children.length;
  88. var edit = void 0;
  89. if (index > 0) {
  90. var previous = parent.children[index - 1];
  91. edit = { offset: previous.offset + previous.length, length: 0, content: ',' + newProperty };
  92. }
  93. else if (parent.children.length === 0) {
  94. edit = { offset: parent.offset + 1, length: 0, content: newProperty };
  95. }
  96. else {
  97. edit = { offset: parent.offset + 1, length: 0, content: newProperty + ',' };
  98. }
  99. return withFormatting(text, edit, options);
  100. }
  101. }
  102. else if (parent.type === 'array' && typeof lastSegment === 'number' && Array.isArray(parent.children)) {
  103. var insertIndex = lastSegment;
  104. if (insertIndex === -1) {
  105. // Insert
  106. var newProperty = "" + JSON.stringify(value);
  107. var edit = void 0;
  108. if (parent.children.length === 0) {
  109. edit = { offset: parent.offset + 1, length: 0, content: newProperty };
  110. }
  111. else {
  112. var previous = parent.children[parent.children.length - 1];
  113. edit = { offset: previous.offset + previous.length, length: 0, content: ',' + newProperty };
  114. }
  115. return withFormatting(text, edit, options);
  116. }
  117. else if (value === void 0 && parent.children.length >= 0) {
  118. // Removal
  119. var removalIndex = lastSegment;
  120. var toRemove = parent.children[removalIndex];
  121. var edit = void 0;
  122. if (parent.children.length === 1) {
  123. // only item
  124. edit = { offset: parent.offset + 1, length: parent.length - 2, content: '' };
  125. }
  126. else if (parent.children.length - 1 === removalIndex) {
  127. // last item
  128. var previous = parent.children[removalIndex - 1];
  129. var offset = previous.offset + previous.length;
  130. var parentEndOffset = parent.offset + parent.length;
  131. edit = { offset: offset, length: parentEndOffset - 2 - offset, content: '' };
  132. }
  133. else {
  134. edit = { offset: toRemove.offset, length: parent.children[removalIndex + 1].offset - toRemove.offset, content: '' };
  135. }
  136. return withFormatting(text, edit, options);
  137. }
  138. else if (value !== void 0) {
  139. var edit = void 0;
  140. var newProperty = "" + JSON.stringify(value);
  141. if (!options.isArrayInsertion && parent.children.length > lastSegment) {
  142. var toModify = parent.children[lastSegment];
  143. edit = { offset: toModify.offset, length: toModify.length, content: newProperty };
  144. }
  145. else if (parent.children.length === 0 || lastSegment === 0) {
  146. edit = { offset: parent.offset + 1, length: 0, content: parent.children.length === 0 ? newProperty : newProperty + ',' };
  147. }
  148. else {
  149. var index = lastSegment > parent.children.length ? parent.children.length : lastSegment;
  150. var previous = parent.children[index - 1];
  151. edit = { offset: previous.offset + previous.length, length: 0, content: ',' + newProperty };
  152. }
  153. return withFormatting(text, edit, options);
  154. }
  155. else {
  156. throw new Error("Can not " + (value === void 0 ? 'remove' : (options.isArrayInsertion ? 'insert' : 'modify')) + " Array index " + insertIndex + " as length is not sufficient");
  157. }
  158. }
  159. else {
  160. throw new Error("Can not add " + (typeof lastSegment !== 'number' ? 'index' : 'property') + " to parent of type " + parent.type);
  161. }
  162. }
  163. exports.setProperty = setProperty;
  164. function withFormatting(text, edit, options) {
  165. if (!options.formattingOptions) {
  166. return [edit];
  167. }
  168. // apply the edit
  169. var newText = applyEdit(text, edit);
  170. // format the new text
  171. var begin = edit.offset;
  172. var end = edit.offset + edit.content.length;
  173. if (edit.length === 0 || edit.content.length === 0) { // insert or remove
  174. while (begin > 0 && !format_1.isEOL(newText, begin - 1)) {
  175. begin--;
  176. }
  177. while (end < newText.length && !format_1.isEOL(newText, end)) {
  178. end++;
  179. }
  180. }
  181. var edits = format_1.format(newText, { offset: begin, length: end - begin }, options.formattingOptions);
  182. // apply the formatting edits and track the begin and end offsets of the changes
  183. for (var i = edits.length - 1; i >= 0; i--) {
  184. var edit_1 = edits[i];
  185. newText = applyEdit(newText, edit_1);
  186. begin = Math.min(begin, edit_1.offset);
  187. end = Math.max(end, edit_1.offset + edit_1.length);
  188. end += edit_1.content.length - edit_1.length;
  189. }
  190. // create a single edit with all changes
  191. var editLength = text.length - (newText.length - end) - begin;
  192. return [{ offset: begin, length: editLength, content: newText.substring(begin, end) }];
  193. }
  194. function applyEdit(text, edit) {
  195. return text.substring(0, edit.offset) + edit.content + text.substring(edit.offset + edit.length);
  196. }
  197. exports.applyEdit = applyEdit;
  198. function isWS(text, offset) {
  199. return '\r\n \t'.indexOf(text.charAt(offset)) !== -1;
  200. }
  201. exports.isWS = isWS;
  202. });