format.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  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", "./string-intern"], 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. const scanner_1 = require("./scanner");
  18. const string_intern_1 = require("./string-intern");
  19. function format(documentText, range, options) {
  20. let initialIndentLevel;
  21. let formatText;
  22. let formatTextStart;
  23. let rangeStart;
  24. let rangeEnd;
  25. if (range) {
  26. rangeStart = range.offset;
  27. rangeEnd = rangeStart + range.length;
  28. formatTextStart = rangeStart;
  29. while (formatTextStart > 0 && !isEOL(documentText, formatTextStart - 1)) {
  30. formatTextStart--;
  31. }
  32. let endOffset = rangeEnd;
  33. while (endOffset < documentText.length && !isEOL(documentText, endOffset)) {
  34. endOffset++;
  35. }
  36. formatText = documentText.substring(formatTextStart, endOffset);
  37. initialIndentLevel = computeIndentLevel(formatText, options);
  38. }
  39. else {
  40. formatText = documentText;
  41. initialIndentLevel = 0;
  42. formatTextStart = 0;
  43. rangeStart = 0;
  44. rangeEnd = documentText.length;
  45. }
  46. const eol = getEOL(options, documentText);
  47. const eolFastPathSupported = string_intern_1.supportedEols.includes(eol);
  48. let numberLineBreaks = 0;
  49. let indentLevel = 0;
  50. let indentValue;
  51. if (options.insertSpaces) {
  52. indentValue = string_intern_1.cachedSpaces[options.tabSize || 4] ?? repeat(string_intern_1.cachedSpaces[1], options.tabSize || 4);
  53. }
  54. else {
  55. indentValue = '\t';
  56. }
  57. const indentType = indentValue === '\t' ? '\t' : ' ';
  58. let scanner = (0, scanner_1.createScanner)(formatText, false);
  59. let hasError = false;
  60. function newLinesAndIndent() {
  61. if (numberLineBreaks > 1) {
  62. return repeat(eol, numberLineBreaks) + repeat(indentValue, initialIndentLevel + indentLevel);
  63. }
  64. const amountOfSpaces = indentValue.length * (initialIndentLevel + indentLevel);
  65. if (!eolFastPathSupported || amountOfSpaces > string_intern_1.cachedBreakLinesWithSpaces[indentType][eol].length) {
  66. return eol + repeat(indentValue, initialIndentLevel + indentLevel);
  67. }
  68. if (amountOfSpaces <= 0) {
  69. return eol;
  70. }
  71. return string_intern_1.cachedBreakLinesWithSpaces[indentType][eol][amountOfSpaces];
  72. }
  73. function scanNext() {
  74. let token = scanner.scan();
  75. numberLineBreaks = 0;
  76. while (token === 15 /* SyntaxKind.Trivia */ || token === 14 /* SyntaxKind.LineBreakTrivia */) {
  77. if (token === 14 /* SyntaxKind.LineBreakTrivia */ && options.keepLines) {
  78. numberLineBreaks += 1;
  79. }
  80. else if (token === 14 /* SyntaxKind.LineBreakTrivia */) {
  81. numberLineBreaks = 1;
  82. }
  83. token = scanner.scan();
  84. }
  85. hasError = token === 16 /* SyntaxKind.Unknown */ || scanner.getTokenError() !== 0 /* ScanError.None */;
  86. return token;
  87. }
  88. const editOperations = [];
  89. function addEdit(text, startOffset, endOffset) {
  90. if (!hasError && (!range || (startOffset < rangeEnd && endOffset > rangeStart)) && documentText.substring(startOffset, endOffset) !== text) {
  91. editOperations.push({ offset: startOffset, length: endOffset - startOffset, content: text });
  92. }
  93. }
  94. let firstToken = scanNext();
  95. if (options.keepLines && numberLineBreaks > 0) {
  96. addEdit(repeat(eol, numberLineBreaks), 0, 0);
  97. }
  98. if (firstToken !== 17 /* SyntaxKind.EOF */) {
  99. let firstTokenStart = scanner.getTokenOffset() + formatTextStart;
  100. let initialIndent = (indentValue.length * initialIndentLevel < 20) && options.insertSpaces
  101. ? string_intern_1.cachedSpaces[indentValue.length * initialIndentLevel]
  102. : repeat(indentValue, initialIndentLevel);
  103. addEdit(initialIndent, formatTextStart, firstTokenStart);
  104. }
  105. while (firstToken !== 17 /* SyntaxKind.EOF */) {
  106. let firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart;
  107. let secondToken = scanNext();
  108. let replaceContent = '';
  109. let needsLineBreak = false;
  110. while (numberLineBreaks === 0 && (secondToken === 12 /* SyntaxKind.LineCommentTrivia */ || secondToken === 13 /* SyntaxKind.BlockCommentTrivia */)) {
  111. let commentTokenStart = scanner.getTokenOffset() + formatTextStart;
  112. addEdit(string_intern_1.cachedSpaces[1], firstTokenEnd, commentTokenStart);
  113. firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart;
  114. needsLineBreak = secondToken === 12 /* SyntaxKind.LineCommentTrivia */;
  115. replaceContent = needsLineBreak ? newLinesAndIndent() : '';
  116. secondToken = scanNext();
  117. }
  118. if (secondToken === 2 /* SyntaxKind.CloseBraceToken */) {
  119. if (firstToken !== 1 /* SyntaxKind.OpenBraceToken */) {
  120. indentLevel--;
  121. }
  122. ;
  123. if (options.keepLines && numberLineBreaks > 0 || !options.keepLines && firstToken !== 1 /* SyntaxKind.OpenBraceToken */) {
  124. replaceContent = newLinesAndIndent();
  125. }
  126. else if (options.keepLines) {
  127. replaceContent = string_intern_1.cachedSpaces[1];
  128. }
  129. }
  130. else if (secondToken === 4 /* SyntaxKind.CloseBracketToken */) {
  131. if (firstToken !== 3 /* SyntaxKind.OpenBracketToken */) {
  132. indentLevel--;
  133. }
  134. ;
  135. if (options.keepLines && numberLineBreaks > 0 || !options.keepLines && firstToken !== 3 /* SyntaxKind.OpenBracketToken */) {
  136. replaceContent = newLinesAndIndent();
  137. }
  138. else if (options.keepLines) {
  139. replaceContent = string_intern_1.cachedSpaces[1];
  140. }
  141. }
  142. else {
  143. switch (firstToken) {
  144. case 3 /* SyntaxKind.OpenBracketToken */:
  145. case 1 /* SyntaxKind.OpenBraceToken */:
  146. indentLevel++;
  147. if (options.keepLines && numberLineBreaks > 0 || !options.keepLines) {
  148. replaceContent = newLinesAndIndent();
  149. }
  150. else {
  151. replaceContent = string_intern_1.cachedSpaces[1];
  152. }
  153. break;
  154. case 5 /* SyntaxKind.CommaToken */:
  155. if (options.keepLines && numberLineBreaks > 0 || !options.keepLines) {
  156. replaceContent = newLinesAndIndent();
  157. }
  158. else {
  159. replaceContent = string_intern_1.cachedSpaces[1];
  160. }
  161. break;
  162. case 12 /* SyntaxKind.LineCommentTrivia */:
  163. replaceContent = newLinesAndIndent();
  164. break;
  165. case 13 /* SyntaxKind.BlockCommentTrivia */:
  166. if (numberLineBreaks > 0) {
  167. replaceContent = newLinesAndIndent();
  168. }
  169. else if (!needsLineBreak) {
  170. replaceContent = string_intern_1.cachedSpaces[1];
  171. }
  172. break;
  173. case 6 /* SyntaxKind.ColonToken */:
  174. if (options.keepLines && numberLineBreaks > 0) {
  175. replaceContent = newLinesAndIndent();
  176. }
  177. else if (!needsLineBreak) {
  178. replaceContent = string_intern_1.cachedSpaces[1];
  179. }
  180. break;
  181. case 10 /* SyntaxKind.StringLiteral */:
  182. if (options.keepLines && numberLineBreaks > 0) {
  183. replaceContent = newLinesAndIndent();
  184. }
  185. else if (secondToken === 6 /* SyntaxKind.ColonToken */ && !needsLineBreak) {
  186. replaceContent = '';
  187. }
  188. break;
  189. case 7 /* SyntaxKind.NullKeyword */:
  190. case 8 /* SyntaxKind.TrueKeyword */:
  191. case 9 /* SyntaxKind.FalseKeyword */:
  192. case 11 /* SyntaxKind.NumericLiteral */:
  193. case 2 /* SyntaxKind.CloseBraceToken */:
  194. case 4 /* SyntaxKind.CloseBracketToken */:
  195. if (options.keepLines && numberLineBreaks > 0) {
  196. replaceContent = newLinesAndIndent();
  197. }
  198. else {
  199. if ((secondToken === 12 /* SyntaxKind.LineCommentTrivia */ || secondToken === 13 /* SyntaxKind.BlockCommentTrivia */) && !needsLineBreak) {
  200. replaceContent = string_intern_1.cachedSpaces[1];
  201. }
  202. else if (secondToken !== 5 /* SyntaxKind.CommaToken */ && secondToken !== 17 /* SyntaxKind.EOF */) {
  203. hasError = true;
  204. }
  205. }
  206. break;
  207. case 16 /* SyntaxKind.Unknown */:
  208. hasError = true;
  209. break;
  210. }
  211. if (numberLineBreaks > 0 && (secondToken === 12 /* SyntaxKind.LineCommentTrivia */ || secondToken === 13 /* SyntaxKind.BlockCommentTrivia */)) {
  212. replaceContent = newLinesAndIndent();
  213. }
  214. }
  215. if (secondToken === 17 /* SyntaxKind.EOF */) {
  216. if (options.keepLines && numberLineBreaks > 0) {
  217. replaceContent = newLinesAndIndent();
  218. }
  219. else {
  220. replaceContent = options.insertFinalNewline ? eol : '';
  221. }
  222. }
  223. const secondTokenStart = scanner.getTokenOffset() + formatTextStart;
  224. addEdit(replaceContent, firstTokenEnd, secondTokenStart);
  225. firstToken = secondToken;
  226. }
  227. return editOperations;
  228. }
  229. exports.format = format;
  230. function repeat(s, count) {
  231. let result = '';
  232. for (let i = 0; i < count; i++) {
  233. result += s;
  234. }
  235. return result;
  236. }
  237. function computeIndentLevel(content, options) {
  238. let i = 0;
  239. let nChars = 0;
  240. const tabSize = options.tabSize || 4;
  241. while (i < content.length) {
  242. let ch = content.charAt(i);
  243. if (ch === string_intern_1.cachedSpaces[1]) {
  244. nChars++;
  245. }
  246. else if (ch === '\t') {
  247. nChars += tabSize;
  248. }
  249. else {
  250. break;
  251. }
  252. i++;
  253. }
  254. return Math.floor(nChars / tabSize);
  255. }
  256. function getEOL(options, text) {
  257. for (let i = 0; i < text.length; i++) {
  258. const ch = text.charAt(i);
  259. if (ch === '\r') {
  260. if (i + 1 < text.length && text.charAt(i + 1) === '\n') {
  261. return '\r\n';
  262. }
  263. return '\r';
  264. }
  265. else if (ch === '\n') {
  266. return '\n';
  267. }
  268. }
  269. return (options && options.eol) || '\n';
  270. }
  271. function isEOL(text, offset) {
  272. return '\r\n'.indexOf(text.charAt(offset)) !== -1;
  273. }
  274. exports.isEOL = isEOL;
  275. });