prettier-sql
Version:
Format whitespace in a SQL query to make it more readable
875 lines (708 loc) • 36.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _Indentation = _interopRequireDefault(require("./Indentation"));
var _InlineBlock = _interopRequireDefault(require("./InlineBlock"));
var _Params = _interopRequireDefault(require("./Params"));
var _utils = require("../utils");
var _token = require("./token");
var _types = require("../types");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
function _toArray(arr) { return _arrayWithHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableRest(); }
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
/** Main formatter class that produces a final output string from list of tokens */
var Formatter = /*#__PURE__*/function () {
/**
* @param {FormatOptions} cfg - config object
* @param {string} cfg.language - the current SQL dialect
* @param {string} cfg.indent - the indentation string, either tabs or a number of spaces
* @param {Boolean} cfg.uppercase - whether to use uppercase keywords
* @param {NewlineMode} cfg.newline - setting to control when to break onto newlines
* @param {Integer} cfg.lineWidth - the maximum line width before breaking
* @param {Integer} cfg.linesBetweenQueries - the number of blank lines between each query
* @param {ParamItems | string[]} cfg.params - placeholder tokens to substitute
*/
function Formatter(cfg) {
_classCallCheck(this, Formatter);
_defineProperty(this, "cfg", void 0);
_defineProperty(this, "newline", void 0);
_defineProperty(this, "currentNewline", void 0);
_defineProperty(this, "lineWidth", void 0);
_defineProperty(this, "indentation", void 0);
_defineProperty(this, "inlineBlock", void 0);
_defineProperty(this, "params", void 0);
_defineProperty(this, "previousReservedToken", void 0);
_defineProperty(this, "withinSelect", void 0);
_defineProperty(this, "tokens", void 0);
_defineProperty(this, "index", void 0);
this.cfg = cfg;
this.cfg.tenSpace = this.cfg.keywordPosition === _types.KeywordMode.tenSpaceLeft || this.cfg.keywordPosition === _types.KeywordMode.tenSpaceRight;
this.newline = cfg.newline;
this.currentNewline = true;
this.lineWidth = cfg.lineWidth;
this.indentation = new _Indentation["default"](this.cfg.indent);
this.inlineBlock = new _InlineBlock["default"](this.lineWidth);
this.params = new _Params["default"](this.cfg.params);
this.previousReservedToken = {};
this.withinSelect = false;
this.tokens = [];
this.index = -1;
}
/**
* SQL Tokenizer for this formatter, provided by subclasses.
*/
_createClass(Formatter, [{
key: "tokenizer",
value: function tokenizer() {
throw new Error('tokenizer() not implemented by subclass');
}
/**
* Reprocess and modify a token based on parsed context.
*
* @param {Token} token - The token to modify
* @return {Token} new token or the original
*/
}, {
key: "tokenOverride",
value: function tokenOverride(token) {
// subclasses can override this to modify tokens during formatting
return token;
}
/**
* Formats whitespace in a SQL string to make it easier to read.
*
* @param {string} query - The SQL query string
*/
}, {
key: "format",
value: function format(query) {
this.tokens = this.tokenizer().tokenize(query);
var formattedQuery = this.getFormattedQueryFromTokens();
var finalQuery = this.postFormat(formattedQuery);
return finalQuery.replace(/^\n*/, '').trimEnd();
}
/**
* Does post-processing on the formatted query.
* @param {string} query - the query string produced from `this.format`
*/
}, {
key: "postFormat",
value: function postFormat(query) {
if (this.cfg.tabulateAlias) {
query = this.formatAliasPositions(query);
}
if (this.cfg.commaPosition !== _types.CommaPosition.after) {
query = this.formatCommaPositions(query);
}
return query;
}
/**
* Handles comma placement - either before, after or tabulated
* @param {string} query - input query string
*/
}, {
key: "formatCommaPositions",
value: function formatCommaPositions(query) {
var _this = this;
// const trailingComma = /,$/;
var lines = query.split('\n');
var newQuery = [];
for (var i = 0; i < lines.length; i++) {
// if line has trailing comma
if (lines[i].match(/.*,$/)) {
(function () {
var commaLines = [lines[i]]; // find all lines in comma-bound clause, + 1
while (lines[i++].match(/.*,$/)) {
commaLines.push(lines[i]);
}
if (_this.cfg.commaPosition === _types.CommaPosition.tabular) {
commaLines = commaLines.map(function (commaLine) {
return commaLine.replace(/,$/, '');
}); // trim all trailing commas
var commaMaxLength = (0, _utils.maxLength)(commaLines); // get longest for alignment
// make all lines the same length by appending spaces before comma
commaLines = commaLines.map(function (commaLine, j) {
return j < commaLines.length - 1 // do not add comma for last item
? commaLine + ' '.repeat(commaMaxLength - commaLine.length) + ',' : commaLine;
});
} else if (_this.cfg.commaPosition === _types.CommaPosition.before) {
var isTabs = _this.cfg.indent.includes('\t'); // loose tab check
commaLines = commaLines.map(function (commaLine) {
return commaLine.replace(/,$/, '');
});
var whitespaceRegex = _this.tokenizer().WHITESPACE_REGEX;
commaLines = commaLines.map(function (commaLine, j) {
if (!j) {
// do not add comma for first item
return commaLine;
}
var precedingWhitespace = commaLine.match(new RegExp('^' + whitespaceRegex + ''));
var trimLastIndent = precedingWhitespace ? precedingWhitespace[1].replace(new RegExp((isTabs ? '\t' : _this.cfg.indent) + '$'), // remove last tab / last indent
'') : '';
return trimLastIndent + // add comma in place of last indent
(isTabs ? ' ' : _this.cfg.indent).replace(/ {2}$/, ', ') + // using 4 width tabs
commaLine.trimStart();
});
}
newQuery = [].concat(_toConsumableArray(newQuery), _toConsumableArray(commaLines));
})();
}
newQuery.push(lines[i]);
}
return newQuery.join('\n');
}
/**
* Handles select alias placement - tabulates if enabled
* @param {string} query - input query string
*/
}, {
key: "formatAliasPositions",
value: function formatAliasPositions(query) {
var lines = query.split('\n');
var newQuery = [];
for (var i = 0; i < lines.length; i++) {
// find SELECT rows with trailing comma, if no comma (only one row) - no-op
if (lines[i].match(/^\s*SELECT/i)) {
var _ret = function () {
var aliasLines = [];
if (lines[i].match(/.*,$/)) {
aliasLines = [lines[i]]; // add select to aliasLines in case of tenSpace formats
} else {
newQuery.push(lines[i]); // add select to new query
if (lines[i].match(/^\s*SELECT\s+.+(?!,$)/i)) {
return "continue";
}
aliasLines.push(lines[++i]);
} // get all lines in SELECT clause
while (lines[i++].match(/.*,$/)) {
aliasLines.push(lines[i]);
}
var splitLines = aliasLines.map(function (line) {
return line.split(/(?<=[^\s]+) (AS )?(?=[^\s]+,?$)/i);
}) // break lines into alias with optional AS, and all preceding text
.map(function (slugs) {
return {
precedingText: slugs[0],
// always first split
alias: slugs.length > 1 ? slugs[slugs.length - 1] : undefined,
// always last in split
as: slugs.length === 3 ? slugs[1] : undefined // 2nd if AS is present, else omitted
};
});
var aliasMaxLength = (0, _utils.maxLength)(splitLines.map(function (_ref) {
var precedingText = _ref.precedingText;
return precedingText.replace(/\s*,\s*$/, '');
}) // get longest of precedingText, trim trailing comma for non-alias columns
); // re-construct line, aligning by inserting space before AS or alias
aliasLines = splitLines.map(function (_ref2) {
var precedingText = _ref2.precedingText,
as = _ref2.as,
alias = _ref2.alias;
return precedingText + (alias ? ' '.repeat(aliasMaxLength - precedingText.length + 1) + (as !== null && as !== void 0 ? as : '') + alias : '');
});
newQuery = [].concat(_toConsumableArray(newQuery), _toConsumableArray(aliasLines));
}();
if (_ret === "continue") continue;
}
newQuery.push(lines[i]);
}
return newQuery.join('\n');
}
/**
* Performs main construction of query from token list, delegates to other methods for formatting based on token criteria
* @return {string} formatted query
*/
}, {
key: "getFormattedQueryFromTokens",
value: function getFormattedQueryFromTokens() {
var formattedQuery = '';
for (this.index = 0; this.index < this.tokens.length; this.index++) {
var token = this.tokenOverride(this.tokens[this.index]); // if token is a Reserved Keyword, Command, Binary Command, Dependent Clause, Logical Operator
if ((0, _token.isReserved)(token)) {
this.previousReservedToken = token;
if (token.type !== _token.TokenType.RESERVED_KEYWORD) {
token = this.tenSpacedToken(token); // convert Reserved Command or Logical Operator to tenSpace format if needed
}
if (token.type === _token.TokenType.RESERVED_COMMAND) {
this.withinSelect = _token.isToken.SELECT(token); // set withinSelect flag if entering a SELECT clause, else reset
}
}
if (token.type === _token.TokenType.LINE_COMMENT) {
formattedQuery = this.formatLineComment(token, formattedQuery);
} else if (token.type === _token.TokenType.BLOCK_COMMENT) {
formattedQuery = this.formatBlockComment(token, formattedQuery);
} else if (token.type === _token.TokenType.RESERVED_COMMAND) {
this.currentNewline = this.checkNewline(this.index);
formattedQuery = this.formatCommand(token, formattedQuery);
} else if (token.type === _token.TokenType.RESERVED_BINARY_COMMAND) {
formattedQuery = this.formatBinaryCommand(token, formattedQuery);
} else if (token.type === _token.TokenType.RESERVED_DEPENDENT_CLAUSE) {
formattedQuery = this.formatDependentClause(token, formattedQuery);
} else if (token.type === _token.TokenType.RESERVED_LOGICAL_OPERATOR) {
formattedQuery = this.formatLogicalOperator(token, formattedQuery);
} else if (token.type === _token.TokenType.RESERVED_KEYWORD) {
formattedQuery = this.formatKeyword(token, formattedQuery);
this.previousReservedToken = token;
} else if (token.type === _token.TokenType.BLOCK_START) {
formattedQuery = this.formatBlockStart(token, formattedQuery);
} else if (token.type === _token.TokenType.BLOCK_END) {
formattedQuery = this.formatBlockEnd(token, formattedQuery);
} else if (token.type === _token.TokenType.PLACEHOLDER) {
formattedQuery = this.formatPlaceholder(token, formattedQuery);
} else if (token.type === _token.TokenType.OPERATOR) {
formattedQuery = this.formatOperator(token, formattedQuery);
} else {
formattedQuery = this.formatWord(token, formattedQuery);
}
}
return formattedQuery.replace(new RegExp(_token.ZWS, 'ugim'), ' '); // replace all ZWS with whitespace for TenSpace formats
}
/**
* Formats word tokens + any potential AS tokens for aliases
* @param {Token} token - current token
* @param {string} query - formatted query so far
*/
}, {
key: "formatWord",
value: function formatWord(token, query) {
var _this$tokenLookAhead, _this$tokenLookAhead2, _this$tokenLookAhead3, _this$tokenLookAhead4;
var prevToken = this.tokenLookBehind();
var nextToken = this.tokenLookAhead();
var asToken = {
type: _token.TokenType.RESERVED_KEYWORD,
value: this.cfg.uppercase ? 'AS' : 'as'
};
var missingTableAlias = // if table alias is missing and alias is always
this.cfg.aliasAs === _types.AliasMode.always && token.type === _token.TokenType.WORD && (prevToken === null || prevToken === void 0 ? void 0 : prevToken.value) === ')';
var missingSelectColumnAlias = // if select column alias is missing and alias is not never
this.cfg.aliasAs !== _types.AliasMode.never && this.withinSelect && token.type === _token.TokenType.WORD && (_token.isToken.END(prevToken) || ((prevToken === null || prevToken === void 0 ? void 0 : prevToken.type) === _token.TokenType.WORD || (prevToken === null || prevToken === void 0 ? void 0 : prevToken.type) === _token.TokenType.NUMBER) && ((nextToken === null || nextToken === void 0 ? void 0 : nextToken.value) === ',' || (0, _token.isCommand)(nextToken))); // bandaid fix until Nearley tree
var missingCastTypeAs = this.cfg.aliasAs === _types.AliasMode.never && // checks for CAST(«expression» [AS] type)
this.withinSelect && _token.isToken.CAST(this.previousReservedToken) && _token.isToken.AS(nextToken) && (((_this$tokenLookAhead = this.tokenLookAhead(2)) === null || _this$tokenLookAhead === void 0 ? void 0 : _this$tokenLookAhead.type) === _token.TokenType.WORD || ((_this$tokenLookAhead2 = this.tokenLookAhead(2)) === null || _this$tokenLookAhead2 === void 0 ? void 0 : _this$tokenLookAhead2.type) === _token.TokenType.RESERVED_KEYWORD) && ((_this$tokenLookAhead3 = this.tokenLookAhead(3)) === null || _this$tokenLookAhead3 === void 0 ? void 0 : _this$tokenLookAhead3.value) === ')';
var isEdgeCaseCTE = // checks for WITH `table` [AS] (
this.cfg.aliasAs === _types.AliasMode.never && _token.isToken.WITH(prevToken) && ((nextToken === null || nextToken === void 0 ? void 0 : nextToken.value) === '(' || _token.isToken.AS(nextToken) && ((_this$tokenLookAhead4 = this.tokenLookAhead(2)) === null || _this$tokenLookAhead4 === void 0 ? void 0 : _this$tokenLookAhead4.value) === '(');
var isEdgeCaseCreateTable = // checks for CREATE TABLE `table` [AS] WITH (
this.cfg.aliasAs === _types.AliasMode.never && (_token.isToken.TABLE(prevToken) || (prevToken === null || prevToken === void 0 ? void 0 : prevToken.value.endsWith('TABLE'))) && (_token.isToken.WITH(nextToken) || _token.isToken.AS(nextToken) && _token.isToken.WITH(this.tokenLookAhead(2)));
var finalQuery = query;
if (missingTableAlias || missingSelectColumnAlias) {
// insert AS before word
finalQuery = this.formatWithSpaces(asToken, finalQuery);
} // insert word
finalQuery = this.formatWithSpaces(token, finalQuery);
if (isEdgeCaseCTE || isEdgeCaseCreateTable || missingCastTypeAs) {
// insert AS after word
finalQuery = this.formatWithSpaces(asToken, finalQuery);
}
return finalQuery;
}
/**
* Checks if a newline should currently be inserted
* @param {number} index - index of current token
* @return {boolean} Whether or not a newline should be inserted
*/
}, {
key: "checkNewline",
value: function checkNewline(index) {
var tail = this.tokens.slice(index + 1); // get all tokens after current token
var nextTokens = tail.slice( // get all tokens between current token and next Reserved Command or query end
0, tail.length ? tail.findIndex(function (_ref3) {
var type = _ref3.type,
value = _ref3.value;
return type === _token.TokenType.RESERVED_COMMAND || type === _token.TokenType.RESERVED_BINARY_COMMAND || value === ';';
}) : undefined // add undefined for EOF
);
if (this.newline === _types.NewlineMode.always || this.withinSelect && nextTokens.some(function (_ref4) {
var type = _ref4.type,
value = _ref4.value;
return type === _token.TokenType.BLOCK_START && value.length > 1;
}) // auto break if SELECT includes CASE statements
) {
return true;
}
if (this.newline === _types.NewlineMode.never) {
return false;
}
var numItems = nextTokens.reduce(function (acc, _ref5) {
var type = _ref5.type,
value = _ref5.value;
if (value === ',' && !acc.inParen) {
return _objectSpread(_objectSpread({}, acc), {}, {
count: acc.count + 1
});
} // count commas between items in clause
if (type === _token.TokenType.BLOCK_START) {
return _objectSpread(_objectSpread({}, acc), {}, {
inParen: true
});
} // don't count commas in functions
if (type === _token.TokenType.BLOCK_END) {
return _objectSpread(_objectSpread({}, acc), {}, {
inParen: false
});
}
return acc;
}, {
count: 1,
inParen: false
} // start with 1 for first word
).count; // calculate length if it were all inline
var inlineWidth = "".concat(this.tokens[index].whitespaceBefore).concat(this.tokens[index].value, " ").concat(nextTokens.map(function (_ref6) {
var value = _ref6.value;
return value === ',' ? value + ' ' : value;
}).join('')).length;
if (this.newline === _types.NewlineMode.lineWidth) {
return inlineWidth > this.lineWidth;
} else if (!Number.isNaN(this.newline)) {
return numItems > this.newline || inlineWidth > this.lineWidth;
}
return true;
}
/** Formats a line comment onto query */
}, {
key: "formatLineComment",
value: function formatLineComment(token, query) {
return this.addNewline(query + this.show(token));
}
/** Formats a block comment onto query */
}, {
key: "formatBlockComment",
value: function formatBlockComment(token, query) {
return this.addNewline(this.addNewline(query) + this.indentComment(token.value));
}
/** Aligns comment to current indentation level */
}, {
key: "indentComment",
value: function indentComment(comment) {
return comment.replace(/\n[\t ]*/g, '\n' + this.indentation.getIndent() + ' ');
}
/**
* Formats a Reserved Command onto query, increasing indentation level where necessary
* @param {Token} token - current token
* @param {string} query - formatted query so far
*/
}, {
key: "formatCommand",
value: function formatCommand(token, query) {
var _this$tokenLookAhead6;
this.indentation.decreaseTopLevel();
query = this.addNewline(query); // indent TenSpace formats, except when preceding a (
if (this.cfg.tenSpace) {
var _this$tokenLookAhead5;
if (((_this$tokenLookAhead5 = this.tokenLookAhead()) === null || _this$tokenLookAhead5 === void 0 ? void 0 : _this$tokenLookAhead5.value) !== '(') {
this.indentation.increaseTopLevel();
} // indent standard format, except when is [FROM] (
} else if (!(((_this$tokenLookAhead6 = this.tokenLookAhead()) === null || _this$tokenLookAhead6 === void 0 ? void 0 : _this$tokenLookAhead6.value) === '(' && _token.isToken.FROM(token))) {
this.indentation.increaseTopLevel();
}
query += this.equalizeWhitespace(this.show(token)); // print token onto query
if (this.currentNewline && !this.cfg.tenSpace) {
query = this.addNewline(query);
} else {
query += ' ';
}
return query;
}
/**
* Formats a Reserved Binary Command onto query, joining neighbouring tokens
* @param {Token} token - current token
* @param {string} query - formatted query so far
*/
}, {
key: "formatBinaryCommand",
value: function formatBinaryCommand(token, query) {
var isJoin = /JOIN/i.test(token.value); // check if token contains JOIN
if (!isJoin || this.cfg.tenSpace) {
// decrease for boolean set operators or in tenSpace modes
this.indentation.decreaseTopLevel();
}
query = this.addNewline(query) + this.equalizeWhitespace(this.show(token));
return isJoin ? query + ' ' : this.addNewline(query);
}
/**
* Formats a Reserved Keyword onto query, skipping AS if disabled
* @param {Token} token - current token
* @param {string} query - formatted query so far
*/
}, {
key: "formatKeyword",
value: function formatKeyword(token, query) {
var _this$tokenLookBehind, _this$tokenLookAhead7;
if (_token.isToken.AS(token) && (this.cfg.aliasAs === _types.AliasMode.never || this.cfg.aliasAs === _types.AliasMode.select && ((_this$tokenLookBehind = this.tokenLookBehind()) === null || _this$tokenLookBehind === void 0 ? void 0 : _this$tokenLookBehind.value) === ')' && // ) [AS] alias but not SELECT (a) [AS] alpha
!this.withinSelect && // skip WITH foo [AS] ( ...
((_this$tokenLookAhead7 = this.tokenLookAhead()) === null || _this$tokenLookAhead7 === void 0 ? void 0 : _this$tokenLookAhead7.value) !== '(')) {
// do not format if skipping AS
return query;
}
return this.formatWithSpaces(token, query);
}
/**
* Formats a Reserved Dependent Clause token onto query, supporting the keyword that precedes it
* @param {Token} token - current token
* @param {string} query - formatted query so far
*/
}, {
key: "formatDependentClause",
value: function formatDependentClause(token, query) {
return this.addNewline(query) + this.equalizeWhitespace(this.show(token)) + ' ';
}
/**
* Formats an Operator onto query, following rules for specific characters
* @param {Token} token - current token
* @param {string} query - formatted query so far
*/
}, {
key: "formatOperator",
value: function formatOperator(token, query) {
var _this$tokenLookBehind2;
// special operator
if (token.value === ',') {
return this.formatComma(token, query);
} else if (token.value === ';') {
return this.formatQuerySeparator(token, query);
} else if (['$', '['].includes(token.value)) {
return this.formatWithSpaces(token, query, 'before');
} else if ([':', ']'].includes(token.value)) {
return this.formatWithSpaces(token, query, 'after');
} else if (['.', '{', '}', '`'].includes(token.value)) {
return this.formatWithoutSpaces(token, query);
} // regular operator
if (this.cfg.denseOperators && ((_this$tokenLookBehind2 = this.tokenLookBehind()) === null || _this$tokenLookBehind2 === void 0 ? void 0 : _this$tokenLookBehind2.type) !== _token.TokenType.RESERVED_COMMAND) {
// do not trim whitespace if SELECT *
return this.formatWithoutSpaces(token, query);
}
return this.formatWithSpaces(token, query);
}
/**
* Formats a Logical Operator onto query, joining boolean conditions
* @param {Token} token - current token
* @param {string} query - formatted query so far
*/
}, {
key: "formatLogicalOperator",
value: function formatLogicalOperator(token, query) {
// ignore AND when BETWEEN x [AND] y
if (_token.isToken.AND(token) && _token.isToken.BETWEEN(this.tokenLookBehind(2))) {
return this.formatWithSpaces(token, query);
}
if (this.cfg.tenSpace) {
this.indentation.decreaseTopLevel();
}
if (this.cfg.breakBeforeBooleanOperator) {
return (this.currentNewline ? this.addNewline(query) : query) + this.equalizeWhitespace(this.show(token)) + ' ';
} else {
query += this.show(token);
return this.currentNewline ? this.addNewline(query) : query;
}
}
/** Replace any sequence of whitespace characters with single space */
}, {
key: "equalizeWhitespace",
value: function equalizeWhitespace(string) {
return string.replace(/[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]+/g, ' ');
}
/**
* Formats a Block Start token (left paren/bracket/brace, CASE) onto query, beginning an Inline Block or increasing indentation where necessary
* @param {Token} token - current token
* @param {string} query - formatted query so far
*/
}, {
key: "formatBlockStart",
value: function formatBlockStart(token, query) {
if (_token.isToken.CASE(token)) {
query = this.formatWithSpaces(token, query);
} else {
var _token$whitespaceBefo, _this$tokenLookBehind3;
// Take out the preceding space unless there was whitespace there in the original query
// or another opening parens or line comment
var preserveWhitespaceFor = [_token.TokenType.BLOCK_START, _token.TokenType.LINE_COMMENT, _token.TokenType.OPERATOR];
if (((_token$whitespaceBefo = token.whitespaceBefore) === null || _token$whitespaceBefo === void 0 ? void 0 : _token$whitespaceBefo.length) === 0 && !preserveWhitespaceFor.includes((_this$tokenLookBehind3 = this.tokenLookBehind()) === null || _this$tokenLookBehind3 === void 0 ? void 0 : _this$tokenLookBehind3.type)) {
query = (0, _utils.trimSpacesEnd)(query);
} else if (!this.cfg.parenOptions.openParenNewline) {
query = query.trimEnd() + ' ';
}
query += this.show(token);
this.inlineBlock.beginIfPossible(this.tokens, this.index);
}
if (!this.inlineBlock.isActive()) {
this.indentation.increaseBlockLevel();
if (!_token.isToken.CASE(token) || this.newline === _types.NewlineMode.always) {
query = this.addNewline(query);
}
}
return query;
}
/**
* Formats a Block End token (right paren/bracket/brace, END) onto query, closing an Inline Block or decreasing indentation where necessary
* @param {Token} token - current token
* @param {string} query - formatted query so far
*/
}, {
key: "formatBlockEnd",
value: function formatBlockEnd(token, query) {
if (this.inlineBlock.isActive()) {
this.inlineBlock.end();
if (_token.isToken.END(token)) {
return this.formatWithSpaces(token, query); // add space before END when closing inline block
}
return this.formatWithSpaces(token, query, 'after'); // do not add space before )
} else {
this.indentation.decreaseBlockLevel();
if (this.cfg.tenSpace) {
query = this.addNewline(query) + this.cfg.indent;
} else if (this.cfg.parenOptions.closeParenNewline) {
query = this.addNewline(query);
} else {
query = query.trimEnd() + ' ';
}
return this.formatWithSpaces(token, query);
}
}
/**
* Formats a Placeholder item onto query, to be replaced with the value of the placeholder
* @param {Token} token - current token
* @param {string} query - formatted query so far
*/
}, {
key: "formatPlaceholder",
value: function formatPlaceholder(token, query) {
return query + this.params.get(token) + ' ';
}
/**
* Formats a comma Operator onto query, ending line unless in an Inline Block
* @param {Token} token - current token
* @param {string} query - formatted query so far
*/
}, {
key: "formatComma",
value: function formatComma(token, query) {
query = (0, _utils.trimSpacesEnd)(query) + this.show(token) + ' ';
if (this.inlineBlock.isActive()) {
return query;
} else if (_token.isToken.LIMIT(this.previousReservedToken)) {
return query;
} else if (this.currentNewline) {
return this.addNewline(query);
} else {
return query;
}
}
/** Simple append of token onto query */
}, {
key: "formatWithoutSpaces",
value: function formatWithoutSpaces(token, query) {
return (0, _utils.trimSpacesEnd)(query) + this.show(token);
}
/**
* Add token onto query with spaces - either before, after, or both
* @param {Token} token - current token
* @param {string} query - formatted query so far
* @param {'before' | 'after' | 'both'} addSpace - where to add spaces around token
* @return {string} token string with specified spaces
*/
}, {
key: "formatWithSpaces",
value: function formatWithSpaces(token, query) {
var addSpace = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'both';
var before = addSpace === 'after' ? (0, _utils.trimSpacesEnd)(query) : query;
var after = addSpace === 'before' ? '' : ' ';
return before + this.show(token) + after;
}
/**
* Format Delimiter token onto query, adding newlines accoring to `this.cfg.linesBetweenQueries`
* @param {Token} token - current token
* @param {string} query - formatted query so far
*/
}, {
key: "formatQuerySeparator",
value: function formatQuerySeparator(token, query) {
this.indentation.resetIndentation();
query = (0, _utils.trimSpacesEnd)(query); // move delimiter to new line if specified
if (this.cfg.semicolonNewline) {
query += '\n';
if (this.cfg.tenSpace) {
query += this.cfg.indent;
}
}
return query + this.show(token) + '\n'.repeat(this.cfg.linesBetweenQueries + 1);
}
/** Converts token to string, uppercasing if enabled */
}, {
key: "show",
value: function show(token) {
if ((0, _token.isReserved)(token) || token.type === _token.TokenType.BLOCK_START || token.type === _token.TokenType.BLOCK_END) {
return this.cfg.uppercase ? token.value.toUpperCase() : token.value.toLowerCase();
} else {
return token.value;
}
}
/** Inserts a newline onto the query */
}, {
key: "addNewline",
value: function addNewline(query) {
query = (0, _utils.trimSpacesEnd)(query);
if (!query.endsWith('\n')) {
query += '\n';
}
return query + this.indentation.getIndent();
}
/** Produces a 10-char wide version of reserved token for TenSpace modes */
}, {
key: "tenSpacedToken",
value: function tenSpacedToken(token) {
var addBuffer = function addBuffer(string) {
var bufferLength = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 9;
return _token.ZWS.repeat(Math.max(bufferLength - string.length, 0));
};
if (this.cfg.tenSpace) {
var bufferItem = token.value; // store which part of keyword receives 10-space buffer
var tail = []; // rest of keyword
if (bufferItem.length >= 10 && bufferItem.includes(' ')) {
// split for long keywords like INNER JOIN or UNION DISTINCT
var _bufferItem$split = bufferItem.split(' ');
var _bufferItem$split2 = _toArray(_bufferItem$split);
bufferItem = _bufferItem$split2[0];
tail = _bufferItem$split2.slice(1);
}
if (this.cfg.keywordPosition === _types.KeywordMode.tenSpaceLeft) {
bufferItem += addBuffer(bufferItem);
} else {
bufferItem = addBuffer(bufferItem) + bufferItem;
}
token.value = bufferItem + [''].concat(_toConsumableArray(tail)).join(' ');
}
return token;
}
/** Fetches nth previous token from the token stream */
}, {
key: "tokenLookBehind",
value: function tokenLookBehind() {
var n = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
return this.tokens[this.index - n];
}
/** Fetches nth next token from the token stream */
}, {
key: "tokenLookAhead",
value: function tokenLookAhead() {
var n = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
return this.tokens[this.index + n];
}
}]);
return Formatter;
}();
exports["default"] = Formatter;
module.exports = exports.default;
//# sourceMappingURL=Formatter.js.map