blob: e383578475c7ff54b791f0bfbfd83b34e00f6a73 [file] [log] [blame]
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "vscode-languageserver-types", "../beautify/beautify-html", "../utils/strings"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var vscode_languageserver_types_1 = require("vscode-languageserver-types");
var beautify_html_1 = require("../beautify/beautify-html");
var strings_1 = require("../utils/strings");
function format(document, range, options) {
var value = document.getText();
var includesEnd = true;
var initialIndentLevel = 0;
var tabSize = options.tabSize || 4;
if (range) {
var startOffset = document.offsetAt(range.start);
// include all leading whitespace iff at the beginning of the line
var extendedStart = startOffset;
while (extendedStart > 0 && isWhitespace(value, extendedStart - 1)) {
extendedStart--;
}
if (extendedStart === 0 || isEOL(value, extendedStart - 1)) {
startOffset = extendedStart;
}
else {
// else keep at least one whitespace
if (extendedStart < startOffset) {
startOffset = extendedStart + 1;
}
}
// include all following whitespace until the end of the line
var endOffset = document.offsetAt(range.end);
var extendedEnd = endOffset;
while (extendedEnd < value.length && isWhitespace(value, extendedEnd)) {
extendedEnd++;
}
if (extendedEnd === value.length || isEOL(value, extendedEnd)) {
endOffset = extendedEnd;
}
range = vscode_languageserver_types_1.Range.create(document.positionAt(startOffset), document.positionAt(endOffset));
// Do not modify if substring starts in inside an element
// Ending inside an element is fine as it doesn't cause formatting errors
var firstHalf = value.substring(0, startOffset);
if (new RegExp(/.*[<][^>]*$/).test(firstHalf)) {
//return without modification
value = value.substring(startOffset, endOffset);
return [{
range: range,
newText: value
}];
}
includesEnd = endOffset === value.length;
value = value.substring(startOffset, endOffset);
if (startOffset !== 0) {
var startOfLineOffset = document.offsetAt(vscode_languageserver_types_1.Position.create(range.start.line, 0));
initialIndentLevel = computeIndentLevel(document.getText(), startOfLineOffset, options);
}
}
else {
range = vscode_languageserver_types_1.Range.create(vscode_languageserver_types_1.Position.create(0, 0), document.positionAt(value.length));
}
var htmlOptions = {
indent_size: tabSize,
indent_char: options.insertSpaces ? ' ' : '\t',
indent_empty_lines: getFormatOption(options, 'indentEmptyLines', false),
wrap_line_length: getFormatOption(options, 'wrapLineLength', 120),
unformatted: getTagsFormatOption(options, 'unformatted', void 0),
content_unformatted: getTagsFormatOption(options, 'contentUnformatted', void 0),
indent_inner_html: getFormatOption(options, 'indentInnerHtml', false),
preserve_newlines: getFormatOption(options, 'preserveNewLines', true),
max_preserve_newlines: getFormatOption(options, 'maxPreserveNewLines', 32786),
indent_handlebars: getFormatOption(options, 'indentHandlebars', false),
end_with_newline: includesEnd && getFormatOption(options, 'endWithNewline', false),
extra_liners: getTagsFormatOption(options, 'extraLiners', void 0),
wrap_attributes: getFormatOption(options, 'wrapAttributes', 'auto'),
wrap_attributes_indent_size: getFormatOption(options, 'wrapAttributesIndentSize', void 0),
eol: '\n'
};
var result = beautify_html_1.html_beautify(trimLeft(value), htmlOptions);
if (initialIndentLevel > 0) {
var indent = options.insertSpaces ? strings_1.repeat(' ', tabSize * initialIndentLevel) : strings_1.repeat('\t', initialIndentLevel);
result = result.split('\n').join('\n' + indent);
if (range.start.character === 0) {
result = indent + result; // keep the indent
}
}
return [{
range: range,
newText: result
}];
}
exports.format = format;
function trimLeft(str) {
return str.replace(/^\s+/, '');
}
function getFormatOption(options, key, dflt) {
if (options && options.hasOwnProperty(key)) {
var value = options[key];
if (value !== null) {
return value;
}
}
return dflt;
}
function getTagsFormatOption(options, key, dflt) {
var list = getFormatOption(options, key, null);
if (typeof list === 'string') {
if (list.length > 0) {
return list.split(',').map(function (t) { return t.trim().toLowerCase(); });
}
return [];
}
return dflt;
}
function computeIndentLevel(content, offset, options) {
var i = offset;
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(document) {
var text = document.getText();
if (document.lineCount > 1) {
var to = document.offsetAt(vscode_languageserver_types_1.Position.create(1, 0));
var from = to;
while (from > 0 && isEOL(text, from - 1)) {
from--;
}
return text.substr(from, to - from);
}
return '\n';
}
function isEOL(text, offset) {
return '\r\n'.indexOf(text.charAt(offset)) !== -1;
}
function isWhitespace(text, offset) {
return ' \t'.indexOf(text.charAt(offset)) !== -1;
}
});