stringifyCollection.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. 'use strict';
  2. var Collection = require('../nodes/Collection.js');
  3. var identity = require('../nodes/identity.js');
  4. var stringify = require('./stringify.js');
  5. var stringifyComment = require('./stringifyComment.js');
  6. function stringifyCollection(collection, ctx, options) {
  7. const flow = ctx.inFlow ?? collection.flow;
  8. const stringify = flow ? stringifyFlowCollection : stringifyBlockCollection;
  9. return stringify(collection, ctx, options);
  10. }
  11. function stringifyBlockCollection({ comment, items }, ctx, { blockItemPrefix, flowChars, itemIndent, onChompKeep, onComment }) {
  12. const { indent, options: { commentString } } = ctx;
  13. const itemCtx = Object.assign({}, ctx, { indent: itemIndent, type: null });
  14. let chompKeep = false; // flag for the preceding node's status
  15. const lines = [];
  16. for (let i = 0; i < items.length; ++i) {
  17. const item = items[i];
  18. let comment = null;
  19. if (identity.isNode(item)) {
  20. if (!chompKeep && item.spaceBefore)
  21. lines.push('');
  22. addCommentBefore(ctx, lines, item.commentBefore, chompKeep);
  23. if (item.comment)
  24. comment = item.comment;
  25. }
  26. else if (identity.isPair(item)) {
  27. const ik = identity.isNode(item.key) ? item.key : null;
  28. if (ik) {
  29. if (!chompKeep && ik.spaceBefore)
  30. lines.push('');
  31. addCommentBefore(ctx, lines, ik.commentBefore, chompKeep);
  32. }
  33. }
  34. chompKeep = false;
  35. let str = stringify.stringify(item, itemCtx, () => (comment = null), () => (chompKeep = true));
  36. if (comment)
  37. str += stringifyComment.lineComment(str, itemIndent, commentString(comment));
  38. if (chompKeep && comment)
  39. chompKeep = false;
  40. lines.push(blockItemPrefix + str);
  41. }
  42. let str;
  43. if (lines.length === 0) {
  44. str = flowChars.start + flowChars.end;
  45. }
  46. else {
  47. str = lines[0];
  48. for (let i = 1; i < lines.length; ++i) {
  49. const line = lines[i];
  50. str += line ? `\n${indent}${line}` : '\n';
  51. }
  52. }
  53. if (comment) {
  54. str += '\n' + stringifyComment.indentComment(commentString(comment), indent);
  55. if (onComment)
  56. onComment();
  57. }
  58. else if (chompKeep && onChompKeep)
  59. onChompKeep();
  60. return str;
  61. }
  62. function stringifyFlowCollection({ comment, items }, ctx, { flowChars, itemIndent, onComment }) {
  63. const { indent, indentStep, flowCollectionPadding: fcPadding, options: { commentString } } = ctx;
  64. itemIndent += indentStep;
  65. const itemCtx = Object.assign({}, ctx, {
  66. indent: itemIndent,
  67. inFlow: true,
  68. type: null
  69. });
  70. let reqNewline = false;
  71. let linesAtValue = 0;
  72. const lines = [];
  73. for (let i = 0; i < items.length; ++i) {
  74. const item = items[i];
  75. let comment = null;
  76. if (identity.isNode(item)) {
  77. if (item.spaceBefore)
  78. lines.push('');
  79. addCommentBefore(ctx, lines, item.commentBefore, false);
  80. if (item.comment)
  81. comment = item.comment;
  82. }
  83. else if (identity.isPair(item)) {
  84. const ik = identity.isNode(item.key) ? item.key : null;
  85. if (ik) {
  86. if (ik.spaceBefore)
  87. lines.push('');
  88. addCommentBefore(ctx, lines, ik.commentBefore, false);
  89. if (ik.comment)
  90. reqNewline = true;
  91. }
  92. const iv = identity.isNode(item.value) ? item.value : null;
  93. if (iv) {
  94. if (iv.comment)
  95. comment = iv.comment;
  96. if (iv.commentBefore)
  97. reqNewline = true;
  98. }
  99. else if (item.value == null && ik && ik.comment) {
  100. comment = ik.comment;
  101. }
  102. }
  103. if (comment)
  104. reqNewline = true;
  105. let str = stringify.stringify(item, itemCtx, () => (comment = null));
  106. if (i < items.length - 1)
  107. str += ',';
  108. if (comment)
  109. str += stringifyComment.lineComment(str, itemIndent, commentString(comment));
  110. if (!reqNewline && (lines.length > linesAtValue || str.includes('\n')))
  111. reqNewline = true;
  112. lines.push(str);
  113. linesAtValue = lines.length;
  114. }
  115. let str;
  116. const { start, end } = flowChars;
  117. if (lines.length === 0) {
  118. str = start + end;
  119. }
  120. else {
  121. if (!reqNewline) {
  122. const len = lines.reduce((sum, line) => sum + line.length + 2, 2);
  123. reqNewline = len > Collection.Collection.maxFlowStringSingleLineLength;
  124. }
  125. if (reqNewline) {
  126. str = start;
  127. for (const line of lines)
  128. str += line ? `\n${indentStep}${indent}${line}` : '\n';
  129. str += `\n${indent}${end}`;
  130. }
  131. else {
  132. str = `${start}${fcPadding}${lines.join(' ')}${fcPadding}${end}`;
  133. }
  134. }
  135. if (comment) {
  136. str += stringifyComment.lineComment(str, indent, commentString(comment));
  137. if (onComment)
  138. onComment();
  139. }
  140. return str;
  141. }
  142. function addCommentBefore({ indent, options: { commentString } }, lines, comment, chompKeep) {
  143. if (comment && chompKeep)
  144. comment = comment.replace(/^\n+/, '');
  145. if (comment) {
  146. const ic = stringifyComment.indentComment(commentString(comment), indent);
  147. lines.push(ic.trimStart()); // Avoid double indent on first line
  148. }
  149. }
  150. exports.stringifyCollection = stringifyCollection;