/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ 'use strict'; import { createScanner } from './scanner'; export function format(documentText, range, options) { var initialIndentLevel; var formatText; var formatTextStart; var rangeStart; var rangeEnd; if (range) { rangeStart = range.offset; rangeEnd = rangeStart + range.length; formatTextStart = rangeStart; while (formatTextStart > 0 && !isEOL(documentText, formatTextStart - 1)) { formatTextStart--; } var endOffset = rangeEnd; while (endOffset < documentText.length && !isEOL(documentText, endOffset)) { endOffset++; } formatText = documentText.substring(formatTextStart, endOffset); initialIndentLevel = computeIndentLevel(formatText, options); } else { formatText = documentText; initialIndentLevel = 0; formatTextStart = 0; rangeStart = 0; rangeEnd = documentText.length; } var eol = getEOL(options, documentText); var lineBreak = false; var indentLevel = 0; var indentValue; if (options.insertSpaces) { indentValue = repeat(' ', options.tabSize || 4); } else { indentValue = '\t'; } var scanner = createScanner(formatText, false); var hasError = false; function newLineAndIndent() { return eol + repeat(indentValue, initialIndentLevel + indentLevel); } function scanNext() { var token = scanner.scan(); lineBreak = false; while (token === 15 /* Trivia */ || token === 14 /* LineBreakTrivia */) { lineBreak = lineBreak || (token === 14 /* LineBreakTrivia */); token = scanner.scan(); } hasError = token === 16 /* Unknown */ || scanner.getTokenError() !== 0 /* None */; return token; } var editOperations = []; function addEdit(text, startOffset, endOffset) { if (!hasError && (!range || (startOffset < rangeEnd && endOffset > rangeStart)) && documentText.substring(startOffset, endOffset) !== text) { editOperations.push({ offset: startOffset, length: endOffset - startOffset, content: text }); } } var firstToken = scanNext(); if (firstToken !== 17 /* EOF */) { var firstTokenStart = scanner.getTokenOffset() + formatTextStart; var initialIndent = repeat(indentValue, initialIndentLevel); addEdit(initialIndent, formatTextStart, firstTokenStart); } while (firstToken !== 17 /* EOF */) { var firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart; var secondToken = scanNext(); var replaceContent = ''; var needsLineBreak = false; while (!lineBreak && (secondToken === 12 /* LineCommentTrivia */ || secondToken === 13 /* BlockCommentTrivia */)) { // comments on the same line: keep them on the same line, but ignore them otherwise var commentTokenStart = scanner.getTokenOffset() + formatTextStart; addEdit(' ', firstTokenEnd, commentTokenStart); firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart; needsLineBreak = secondToken === 12 /* LineCommentTrivia */; replaceContent = needsLineBreak ? newLineAndIndent() : ''; secondToken = scanNext(); } if (secondToken === 2 /* CloseBraceToken */) { if (firstToken !== 1 /* OpenBraceToken */) { indentLevel--; replaceContent = newLineAndIndent(); } } else if (secondToken === 4 /* CloseBracketToken */) { if (firstToken !== 3 /* OpenBracketToken */) { indentLevel--; replaceContent = newLineAndIndent(); } } else { switch (firstToken) { case 3 /* OpenBracketToken */: case 1 /* OpenBraceToken */: indentLevel++; replaceContent = newLineAndIndent(); break; case 5 /* CommaToken */: case 12 /* LineCommentTrivia */: replaceContent = newLineAndIndent(); break; case 13 /* BlockCommentTrivia */: if (lineBreak) { replaceContent = newLineAndIndent(); } else if (!needsLineBreak) { // symbol following comment on the same line: keep on same line, separate with ' ' replaceContent = ' '; } break; case 6 /* ColonToken */: if (!needsLineBreak) { replaceContent = ' '; } break; case 10 /* StringLiteral */: if (secondToken === 6 /* ColonToken */) { if (!needsLineBreak) { replaceContent = ''; } break; } // fall through case 7 /* NullKeyword */: case 8 /* TrueKeyword */: case 9 /* FalseKeyword */: case 11 /* NumericLiteral */: case 2 /* CloseBraceToken */: case 4 /* CloseBracketToken */: if (secondToken === 12 /* LineCommentTrivia */ || secondToken === 13 /* BlockCommentTrivia */) { if (!needsLineBreak) { replaceContent = ' '; } } else if (secondToken !== 5 /* CommaToken */ && secondToken !== 17 /* EOF */) { hasError = true; } break; case 16 /* Unknown */: hasError = true; break; } if (lineBreak && (secondToken === 12 /* LineCommentTrivia */ || secondToken === 13 /* BlockCommentTrivia */)) { replaceContent = newLineAndIndent(); } } if (secondToken === 17 /* EOF */) { replaceContent = options.insertFinalNewline ? eol : ''; } var secondTokenStart = scanner.getTokenOffset() + formatTextStart; addEdit(replaceContent, firstTokenEnd, secondTokenStart); firstToken = secondToken; } return editOperations; } function repeat(s, count) { var result = ''; for (var i = 0; i < count; i++) { result += s; } return result; } function computeIndentLevel(content, options) { var i = 0; var nChars = 0; var tabSize = options.tabSize || 4; while (i < content.length) { var ch = content.charAt(i); if (ch === ' ') { nChars++; } else if (ch === '\t') { nChars += tabSize; } else { break; } i++; } return Math.floor(nChars / tabSize); } function getEOL(options, text) { for (var i = 0; i < text.length; i++) { var ch = text.charAt(i); if (ch === '\r') { if (i + 1 < text.length && text.charAt(i + 1) === '\n') { return '\r\n'; } return '\r'; } else if (ch === '\n') { return '\n'; } } return (options && options.eol) || '\n'; } export function isEOL(text, offset) { return '\r\n'.indexOf(text.charAt(offset)) !== -1; }