format.js 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  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", "./scanner"], 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.isEOL = exports.format = void 0;
  17. var scanner_1 = require("./scanner");
  18. function format(documentText, range, options) {
  19. var initialIndentLevel;
  20. var formatText;
  21. var formatTextStart;
  22. var rangeStart;
  23. var rangeEnd;
  24. if (range) {
  25. rangeStart = range.offset;
  26. rangeEnd = rangeStart + range.length;
  27. formatTextStart = rangeStart;
  28. while (formatTextStart > 0 && !isEOL(documentText, formatTextStart - 1)) {
  29. formatTextStart--;
  30. }
  31. var endOffset = rangeEnd;
  32. while (endOffset < documentText.length && !isEOL(documentText, endOffset)) {
  33. endOffset++;
  34. }
  35. formatText = documentText.substring(formatTextStart, endOffset);
  36. initialIndentLevel = computeIndentLevel(formatText, options);
  37. }
  38. else {
  39. formatText = documentText;
  40. initialIndentLevel = 0;
  41. formatTextStart = 0;
  42. rangeStart = 0;
  43. rangeEnd = documentText.length;
  44. }
  45. var eol = getEOL(options, documentText);
  46. var lineBreak = false;
  47. var indentLevel = 0;
  48. var indentValue;
  49. if (options.insertSpaces) {
  50. indentValue = repeat(' ', options.tabSize || 4);
  51. }
  52. else {
  53. indentValue = '\t';
  54. }
  55. var scanner = scanner_1.createScanner(formatText, false);
  56. var hasError = false;
  57. function newLineAndIndent() {
  58. return eol + repeat(indentValue, initialIndentLevel + indentLevel);
  59. }
  60. function scanNext() {
  61. var token = scanner.scan();
  62. lineBreak = false;
  63. while (token === 15 /* Trivia */ || token === 14 /* LineBreakTrivia */) {
  64. lineBreak = lineBreak || (token === 14 /* LineBreakTrivia */);
  65. token = scanner.scan();
  66. }
  67. hasError = token === 16 /* Unknown */ || scanner.getTokenError() !== 0 /* None */;
  68. return token;
  69. }
  70. var editOperations = [];
  71. function addEdit(text, startOffset, endOffset) {
  72. if (!hasError && (!range || (startOffset < rangeEnd && endOffset > rangeStart)) && documentText.substring(startOffset, endOffset) !== text) {
  73. editOperations.push({ offset: startOffset, length: endOffset - startOffset, content: text });
  74. }
  75. }
  76. var firstToken = scanNext();
  77. if (firstToken !== 17 /* EOF */) {
  78. var firstTokenStart = scanner.getTokenOffset() + formatTextStart;
  79. var initialIndent = repeat(indentValue, initialIndentLevel);
  80. addEdit(initialIndent, formatTextStart, firstTokenStart);
  81. }
  82. while (firstToken !== 17 /* EOF */) {
  83. var firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart;
  84. var secondToken = scanNext();
  85. var replaceContent = '';
  86. var needsLineBreak = false;
  87. while (!lineBreak && (secondToken === 12 /* LineCommentTrivia */ || secondToken === 13 /* BlockCommentTrivia */)) {
  88. // comments on the same line: keep them on the same line, but ignore them otherwise
  89. var commentTokenStart = scanner.getTokenOffset() + formatTextStart;
  90. addEdit(' ', firstTokenEnd, commentTokenStart);
  91. firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart;
  92. needsLineBreak = secondToken === 12 /* LineCommentTrivia */;
  93. replaceContent = needsLineBreak ? newLineAndIndent() : '';
  94. secondToken = scanNext();
  95. }
  96. if (secondToken === 2 /* CloseBraceToken */) {
  97. if (firstToken !== 1 /* OpenBraceToken */) {
  98. indentLevel--;
  99. replaceContent = newLineAndIndent();
  100. }
  101. }
  102. else if (secondToken === 4 /* CloseBracketToken */) {
  103. if (firstToken !== 3 /* OpenBracketToken */) {
  104. indentLevel--;
  105. replaceContent = newLineAndIndent();
  106. }
  107. }
  108. else {
  109. switch (firstToken) {
  110. case 3 /* OpenBracketToken */:
  111. case 1 /* OpenBraceToken */:
  112. indentLevel++;
  113. replaceContent = newLineAndIndent();
  114. break;
  115. case 5 /* CommaToken */:
  116. case 12 /* LineCommentTrivia */:
  117. replaceContent = newLineAndIndent();
  118. break;
  119. case 13 /* BlockCommentTrivia */:
  120. if (lineBreak) {
  121. replaceContent = newLineAndIndent();
  122. }
  123. else if (!needsLineBreak) {
  124. // symbol following comment on the same line: keep on same line, separate with ' '
  125. replaceContent = ' ';
  126. }
  127. break;
  128. case 6 /* ColonToken */:
  129. if (!needsLineBreak) {
  130. replaceContent = ' ';
  131. }
  132. break;
  133. case 10 /* StringLiteral */:
  134. if (secondToken === 6 /* ColonToken */) {
  135. if (!needsLineBreak) {
  136. replaceContent = '';
  137. }
  138. break;
  139. }
  140. // fall through
  141. case 7 /* NullKeyword */:
  142. case 8 /* TrueKeyword */:
  143. case 9 /* FalseKeyword */:
  144. case 11 /* NumericLiteral */:
  145. case 2 /* CloseBraceToken */:
  146. case 4 /* CloseBracketToken */:
  147. if (secondToken === 12 /* LineCommentTrivia */ || secondToken === 13 /* BlockCommentTrivia */) {
  148. if (!needsLineBreak) {
  149. replaceContent = ' ';
  150. }
  151. }
  152. else if (secondToken !== 5 /* CommaToken */ && secondToken !== 17 /* EOF */) {
  153. hasError = true;
  154. }
  155. break;
  156. case 16 /* Unknown */:
  157. hasError = true;
  158. break;
  159. }
  160. if (lineBreak && (secondToken === 12 /* LineCommentTrivia */ || secondToken === 13 /* BlockCommentTrivia */)) {
  161. replaceContent = newLineAndIndent();
  162. }
  163. }
  164. if (secondToken === 17 /* EOF */) {
  165. replaceContent = options.insertFinalNewline ? eol : '';
  166. }
  167. var secondTokenStart = scanner.getTokenOffset() + formatTextStart;
  168. addEdit(replaceContent, firstTokenEnd, secondTokenStart);
  169. firstToken = secondToken;
  170. }
  171. return editOperations;
  172. }
  173. exports.format = format;
  174. function repeat(s, count) {
  175. var result = '';
  176. for (var i = 0; i < count; i++) {
  177. result += s;
  178. }
  179. return result;
  180. }
  181. function computeIndentLevel(content, options) {
  182. var i = 0;
  183. var nChars = 0;
  184. var tabSize = options.tabSize || 4;
  185. while (i < content.length) {
  186. var ch = content.charAt(i);
  187. if (ch === ' ') {
  188. nChars++;
  189. }
  190. else if (ch === '\t') {
  191. nChars += tabSize;
  192. }
  193. else {
  194. break;
  195. }
  196. i++;
  197. }
  198. return Math.floor(nChars / tabSize);
  199. }
  200. function getEOL(options, text) {
  201. for (var i = 0; i < text.length; i++) {
  202. var ch = text.charAt(i);
  203. if (ch === '\r') {
  204. if (i + 1 < text.length && text.charAt(i + 1) === '\n') {
  205. return '\r\n';
  206. }
  207. return '\r';
  208. }
  209. else if (ch === '\n') {
  210. return '\n';
  211. }
  212. }
  213. return (options && options.eol) || '\n';
  214. }
  215. function isEOL(text, offset) {
  216. return '\r\n'.indexOf(text.charAt(offset)) !== -1;
  217. }
  218. exports.isEOL = isEOL;
  219. });