Download
Getting Started
Members
Projects
Community
Marketplace
Events
Planet Eclipse
Newsletter
Videos
Participate
Report a Bug
Forums
Mailing Lists
Wiki
IRC
How to Contribute
Working Groups
Automotive
Internet of Things
LocationTech
Long-Term Support
PolarSys
Science
OpenMDM
More
Community
Marketplace
Events
Planet Eclipse
Newsletter
Videos
Participate
Report a Bug
Forums
Mailing Lists
Wiki
IRC
How to Contribute
Working Groups
Automotive
Internet of Things
LocationTech
Long-Term Support
PolarSys
Science
OpenMDM
Toggle navigation
Bugzilla – Attachment 262728 Details for
Bug 496437
Provide formatting support in the JS tools
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
Log In
[x]
|
Terms of Use
|
Copyright Agent
Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read
this important communication.
[patch]
Proposed patch
changes (1).patch (text/plain), 202.92 KB, created by
Olivier Thomann
on 2016-06-27 12:34:42 EDT
(
hide
)
Description:
Proposed patch
Filename:
MIME Type:
Creator:
Olivier Thomann
Created:
2016-06-27 12:34:42 EDT
Size:
202.92 KB
patch
obsolete
>diff --git a/bundles/org.eclipse.orion.client.javascript/web/beautifier/beautifier.js b/bundles/org.eclipse.orion.client.javascript/web/beautifier/beautifier.js >new file mode 100644 >index 0000000..ceecdbb >--- /dev/null >+++ b/bundles/org.eclipse.orion.client.javascript/web/beautifier/beautifier.js >@@ -0,0 +1,28 @@ >+/*eslint-env amd, node*/ >+define([ >+ "./lib/beautify-js", >+ "./lib/beautify-css", >+ "./lib/beautify-html" >+], function(js_beautify, css_beautify, html_beautify) { >+ >+ function get_beautify(js_beautify, css_beautify, html_beautify) { >+ // the default is js >+ var beautify = function(src, config) { >+ return js_beautify.js_beautify(src, config); >+ }; >+ >+ // short aliases >+ beautify.js = js_beautify.js_beautify; >+ beautify.css = css_beautify.css_beautify; >+ beautify.html = html_beautify.html_beautify; >+ >+ // legacy aliases >+ beautify.js_beautify = js_beautify.js_beautify; >+ beautify.css_beautify = css_beautify.css_beautify; >+ beautify.html_beautify = html_beautify.html_beautify; >+ >+ return beautify; >+ } >+ >+ return get_beautify(js_beautify, css_beautify, html_beautify); >+}); >\ No newline at end of file >diff --git a/bundles/org.eclipse.orion.client.javascript/web/beautifier/lib/beautify-css.js b/bundles/org.eclipse.orion.client.javascript/web/beautifier/lib/beautify-css.js >new file mode 100644 >index 0000000..394abe8 >--- /dev/null >+++ b/bundles/org.eclipse.orion.client.javascript/web/beautifier/lib/beautify-css.js >@@ -0,0 +1,490 @@ >+/* >+ >+ The MIT License (MIT) >+ >+ Copyright (c) 2007-2013 Einar Lielmanis and contributors. >+ >+ Permission is hereby granted, free of charge, to any person >+ obtaining a copy of this software and associated documentation files >+ (the "Software"), to deal in the Software without restriction, >+ including without limitation the rights to use, copy, modify, merge, >+ publish, distribute, sublicense, and/or sell copies of the Software, >+ and to permit persons to whom the Software is furnished to do so, >+ subject to the following conditions: >+ >+ The above copyright notice and this permission notice shall be >+ included in all copies or substantial portions of the Software. >+ >+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, >+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF >+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND >+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS >+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN >+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN >+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE >+ SOFTWARE. >+ >+ >+ CSS Beautifier >+--------------- >+ >+ Written by Harutyun Amirjanyan, (amirjanyan@gmail.com) >+ >+ Based on code initially developed by: Einar Lielmanis, <einar@jsbeautifier.org> >+ http://jsbeautifier.org/ >+ >+ Usage: >+ css_beautify(source_text); >+ css_beautify(source_text, options); >+ >+ The options are (default in brackets): >+ indent_size (4) - indentation size, >+ indent_char (space) - character to indent with, >+ selector_separator_newline (true) - separate selectors with newline or >+ not (e.g. "a,\nbr" or "a, br") >+ end_with_newline (false) - end with a newline >+ newline_between_rules (true) - add a new line after every css rule >+ space_around_selector_separator (false) - ensure space around selector separators: >+ '>', '+', '~' (e.g. "a>b" -> "a > b") >+ e.g >+ >+ css_beautify(css_source_text, { >+ 'indent_size': 1, >+ 'indent_char': '\t', >+ 'selector_separator': ' ', >+ 'end_with_newline': false, >+ 'newline_between_rules': true, >+ 'space_around_selector_separator': true >+ }); >+*/ >+ >+// http://www.w3.org/TR/CSS21/syndata.html#tokenization >+// http://www.w3.org/TR/css3-syntax/ >+/*eslint-env amd */ >+define([], function() { >+ >+ function css_beautify(source_text, options) { >+ options = options || {}; >+ source_text = source_text || ''; >+ // HACK: newline parsing inconsistent. This brute force normalizes the input. >+ source_text = source_text.replace(/\r\n|[\r\u2028\u2029]/g, '\n'); >+ >+ var indentSize = options.indent_size || 4; >+ var indentCharacter = options.indent_char || ' '; >+ var selectorSeparatorNewline = (options.selector_separator_newline === undefined) ? true : options.selector_separator_newline; >+ var end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline; >+ var newline_between_rules = (options.newline_between_rules === undefined) ? true : options.newline_between_rules; >+ var spaceAroundSelectorSeparator = (options.space_around_selector_separator === undefined) ? false : options.space_around_selector_separator; >+ var eol = options.eol ? options.eol : '\n'; >+ >+ // compatibility >+ if (typeof indentSize === "string") { >+ indentSize = parseInt(indentSize, 10); >+ } >+ >+ if (options.indent_with_tabs) { >+ indentCharacter = '\t'; >+ indentSize = 1; >+ } >+ >+ eol = eol.replace(/\\r/, '\r').replace(/\\n/, '\n'); >+ >+ >+ // tokenizer >+ var whiteRe = /^\s+$/; >+ >+ var pos = -1, >+ ch; >+ var parenLevel = 0; >+ >+ function next() { >+ ch = source_text.charAt(++pos); >+ return ch || ''; >+ } >+ >+ function peek(skipWhitespace) { >+ var result = ''; >+ var prev_pos = pos; >+ if (skipWhitespace) { >+ eatWhitespace(); >+ } >+ result = source_text.charAt(pos + 1) || ''; >+ pos = prev_pos - 1; >+ next(); >+ return result; >+ } >+ >+ function eatString(endChars) { >+ var start = pos; >+ while (next()) { >+ if (ch === "\\") { >+ next(); >+ } else if (endChars.indexOf(ch) !== -1) { >+ break; >+ } else if (ch === "\n") { >+ break; >+ } >+ } >+ return source_text.substring(start, pos + 1); >+ } >+ >+ function peekString(endChar) { >+ var prev_pos = pos; >+ var str = eatString(endChar); >+ pos = prev_pos - 1; >+ next(); >+ return str; >+ } >+ >+ function eatWhitespace() { >+ var result = ''; >+ while (whiteRe.test(peek())) { >+ next(); >+ result += ch; >+ } >+ return result; >+ } >+ >+ function skipWhitespace() { >+ var result = ''; >+ if (ch && whiteRe.test(ch)) { >+ result = ch; >+ } >+ while (whiteRe.test(next())) { >+ result += ch; >+ } >+ return result; >+ } >+ >+ function eatComment(singleLine) { >+ var start = pos; >+ singleLine = peek() === "/"; >+ next(); >+ while (next()) { >+ if (!singleLine && ch === "*" && peek() === "/") { >+ next(); >+ break; >+ } else if (singleLine && ch === "\n") { >+ return source_text.substring(start, pos); >+ } >+ } >+ >+ return source_text.substring(start, pos) + ch; >+ } >+ >+ >+ function lookBack(str) { >+ return source_text.substring(pos - str.length, pos).toLowerCase() === >+ str; >+ } >+ >+ // Nested pseudo-class if we are insideRule >+ // and the next special character found opens >+ // a new block >+ function foundNestedPseudoClass() { >+ var openParen = 0; >+ for (var i = pos + 1; i < source_text.length; i++) { >+ var ch = source_text.charAt(i); >+ if (ch === "{") { >+ return true; >+ } else if (ch === '(') { >+ // pseudoclasses can contain () >+ openParen += 1; >+ } else if (ch === ')') { >+ if (openParen === 0) { >+ return false; >+ } >+ openParen -= 1; >+ } else if (ch === ";" || ch === "}") { >+ return false; >+ } >+ } >+ return false; >+ } >+ >+ // printer >+ var basebaseIndentString = source_text.match(/^[\t ]*/)[0]; >+ var singleIndent = new Array(indentSize + 1).join(indentCharacter); >+ var indentLevel = 0; >+ var nestedLevel = 0; >+ >+ function indent() { >+ indentLevel++; >+ basebaseIndentString += singleIndent; >+ } >+ >+ function outdent() { >+ indentLevel--; >+ basebaseIndentString = basebaseIndentString.slice(0, -indentSize); >+ } >+ >+ var print = {}; >+ print["{"] = function(ch) { >+ print.singleSpace(); >+ output.push(ch); >+ print.newLine(); >+ }; >+ print["}"] = function(ch) { >+ print.newLine(); >+ output.push(ch); >+ print.newLine(); >+ }; >+ >+ print._lastCharWhitespace = function() { >+ return whiteRe.test(output[output.length - 1]); >+ }; >+ >+ print.newLine = function(keepWhitespace) { >+ if (output.length) { >+ if (!keepWhitespace && output[output.length - 1] !== '\n') { >+ print.trim(); >+ } >+ >+ output.push('\n'); >+ >+ if (basebaseIndentString) { >+ output.push(basebaseIndentString); >+ } >+ } >+ }; >+ print.singleSpace = function() { >+ if (output.length && !print._lastCharWhitespace()) { >+ output.push(' '); >+ } >+ }; >+ >+ print.preserveSingleSpace = function() { >+ if (isAfterSpace) { >+ print.singleSpace(); >+ } >+ }; >+ >+ print.trim = function() { >+ while (print._lastCharWhitespace()) { >+ output.pop(); >+ } >+ }; >+ >+ >+ var output = []; >+ /*_____________________--------------------_____________________*/ >+ >+ var insideRule = false; >+ var insidePropertyValue = false; >+ var enteringConditionalGroup = false; >+ var top_ch = ''; >+ var last_top_ch = ''; >+ >+ while (true) { >+ var whitespace = skipWhitespace(); >+ var isAfterSpace = whitespace !== ''; >+ var isAfterNewline = whitespace.indexOf('\n') !== -1; >+ last_top_ch = top_ch; >+ top_ch = ch; >+ >+ if (!ch) { >+ break; >+ } else if (ch === '/' && peek() === '*') { /* css comment */ >+ var header = indentLevel === 0; >+ >+ if (isAfterNewline || header) { >+ print.newLine(); >+ } >+ >+ output.push(eatComment()); >+ print.newLine(); >+ if (header) { >+ print.newLine(true); >+ } >+ } else if (ch === '/' && peek() === '/') { // single line comment >+ if (!isAfterNewline && last_top_ch !== '{') { >+ print.trim(); >+ } >+ print.singleSpace(); >+ output.push(eatComment()); >+ print.newLine(); >+ } else if (ch === '@') { >+ print.preserveSingleSpace(); >+ >+ // deal with less propery mixins @{...} >+ if (peek() === '{') { >+ output.push(eatString('}')); >+ } else { >+ output.push(ch); >+ >+ // strip trailing space, if present, for hash property checks >+ var variableOrRule = peekString(": ,;{}()[]/='\""); >+ >+ if (variableOrRule.match(/[ :]$/)) { >+ // we have a variable or pseudo-class, add it and insert one space before continuing >+ next(); >+ variableOrRule = eatString(": ").replace(/\s$/, ''); >+ output.push(variableOrRule); >+ print.singleSpace(); >+ } >+ >+ variableOrRule = variableOrRule.replace(/\s$/, ''); >+ >+ // might be a nesting at-rule >+ if (variableOrRule in css_beautify.NESTED_AT_RULE) { >+ nestedLevel += 1; >+ if (variableOrRule in css_beautify.CONDITIONAL_GROUP_RULE) { >+ enteringConditionalGroup = true; >+ } >+ } >+ } >+ } else if (ch === '#' && peek() === '{') { >+ print.preserveSingleSpace(); >+ output.push(eatString('}')); >+ } else if (ch === '{') { >+ if (peek(true) === '}') { >+ eatWhitespace(); >+ next(); >+ print.singleSpace(); >+ output.push("{}"); >+ print.newLine(); >+ if (newline_between_rules && indentLevel === 0) { >+ print.newLine(true); >+ } >+ } else { >+ indent(); >+ print["{"](ch); >+ // when entering conditional groups, only rulesets are allowed >+ if (enteringConditionalGroup) { >+ enteringConditionalGroup = false; >+ insideRule = (indentLevel > nestedLevel); >+ } else { >+ // otherwise, declarations are also allowed >+ insideRule = (indentLevel >= nestedLevel); >+ } >+ } >+ } else if (ch === '}') { >+ outdent(); >+ print["}"](ch); >+ insideRule = false; >+ insidePropertyValue = false; >+ if (nestedLevel) { >+ nestedLevel--; >+ } >+ if (newline_between_rules && indentLevel === 0) { >+ print.newLine(true); >+ } >+ } else if (ch === ":") { >+ eatWhitespace(); >+ if ((insideRule || enteringConditionalGroup) && >+ !(lookBack("&") || foundNestedPseudoClass())) { >+ // 'property: value' delimiter >+ // which could be in a conditional group query >+ insidePropertyValue = true; >+ output.push(':'); >+ print.singleSpace(); >+ } else { >+ // sass/less parent reference don't use a space >+ // sass nested pseudo-class don't use a space >+ if (peek() === ":") { >+ // pseudo-element >+ next(); >+ output.push("::"); >+ } else { >+ // pseudo-class >+ output.push(':'); >+ } >+ } >+ } else if (ch === '"' || ch === '\'') { >+ print.preserveSingleSpace(); >+ output.push(eatString(ch)); >+ } else if (ch === ';') { >+ insidePropertyValue = false; >+ output.push(ch); >+ print.newLine(); >+ } else if (ch === '(') { // may be a url >+ if (lookBack("url")) { >+ output.push(ch); >+ eatWhitespace(); >+ if (next()) { >+ if (ch !== ')' && ch !== '"' && ch !== '\'') { >+ output.push(eatString(')')); >+ } else { >+ pos--; >+ } >+ } >+ } else { >+ parenLevel++; >+ print.preserveSingleSpace(); >+ output.push(ch); >+ eatWhitespace(); >+ } >+ } else if (ch === ')') { >+ output.push(ch); >+ parenLevel--; >+ } else if (ch === ',') { >+ output.push(ch); >+ eatWhitespace(); >+ if (selectorSeparatorNewline && !insidePropertyValue && parenLevel < 1) { >+ print.newLine(); >+ } else { >+ print.singleSpace(); >+ } >+ } else if (ch === '>' || ch === '+' || ch === '~') { >+ //handl selector separator spacing >+ if (spaceAroundSelectorSeparator && !insidePropertyValue && parenLevel < 1) { >+ print.singleSpace(); >+ output.push(ch); >+ print.singleSpace(); >+ } else { >+ output.push(ch); >+ } >+ } else if (ch === ']') { >+ output.push(ch); >+ } else if (ch === '[') { >+ print.preserveSingleSpace(); >+ output.push(ch); >+ } else if (ch === '=') { // no whitespace before or after >+ eatWhitespace(); >+ ch = '='; >+ output.push(ch); >+ } else { >+ print.preserveSingleSpace(); >+ output.push(ch); >+ } >+ } >+ >+ >+ var sweetCode = ''; >+ if (basebaseIndentString) { >+ sweetCode += basebaseIndentString; >+ } >+ >+ sweetCode += output.join('').replace(/[\r\n\t ]+$/, ''); >+ >+ // establish end_with_newline >+ if (end_with_newline) { >+ sweetCode += '\n'; >+ } >+ >+ if (eol !== '\n') { >+ sweetCode = sweetCode.replace(/[\n]/g, eol); >+ } >+ >+ return sweetCode; >+ } >+ >+ // https://developer.mozilla.org/en-US/docs/Web/CSS/At-rule >+ css_beautify.NESTED_AT_RULE = { >+ "@page": true, >+ "@font-face": true, >+ "@keyframes": true, >+ // also in CONDITIONAL_GROUP_RULE below >+ "@media": true, >+ "@supports": true, >+ "@document": true >+ }; >+ css_beautify.CONDITIONAL_GROUP_RULE = { >+ "@media": true, >+ "@supports": true, >+ "@document": true >+ }; >+ return { >+ css_beautify: css_beautify >+ }; >+}); >\ No newline at end of file >diff --git a/bundles/org.eclipse.orion.client.javascript/web/javascript/formatting.js b/bundles/org.eclipse.orion.client.javascript/web/javascript/formatting.js >new file mode 100644 >index 0000000..ca5b2a8 >--- /dev/null >+++ b/bundles/org.eclipse.orion.client.javascript/web/javascript/formatting.js >@@ -0,0 +1,194 @@ >+ /******************************************************************************* >+ * @license >+ * Copyright (c) 2016 IBM Corporation and others. >+ * All rights reserved. This program and the accompanying materials are made >+ * available under the terms of the Eclipse Public License v1.0 >+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution >+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). >+ * >+ * Contributors: >+ * IBM Corporation - initial API and implementation >+ *******************************************************************************/ >+/*eslint-env amd, browser*/ >+define([ >+'orion/objects', >+'orion/Deferred', >+'javascript/finder' >+], function(Objects, Deferred) { >+ var config = { >+ // 0:off, 1:warning, 2:error >+ defaults: { >+ "indent_size": 1, >+ "indent_char": " ", >+ "eol": "\n", >+ "indent_level": 0, >+ "indent_with_tabs": false, >+ "preserve_newlines": true, >+ "max_preserve_newlines": 10, >+ "jslint_happy": false, >+ "space_after_anon_function": false, >+ "brace_style": "collapse", >+ "keep_array_indentation": false, >+ "keep_function_indentation": false, >+ "space_before_conditional": true, >+ "break_chained_methods": false, >+ "eval_code": false, >+ "unescape_strings": false, >+ "wrap_line_length": 0, >+ "wrap_attributes": "auto", >+ "wrap_attributes_indent_size": 4, >+ "end_with_newline": false >+ }, >+ >+ setOption: function(ruleId, value, key) { >+ if(Array.isArray(this.rules[ruleId])) { >+ var ruleConfig = this.rules[ruleId]; >+ if (key) { >+ ruleConfig[1] = ruleConfig[1] || {}; >+ ruleConfig[1][key] = value; >+ } else { >+ ruleConfig[0] = value; >+ } >+ } >+ else { >+ this.rules[ruleId] = value; >+ } >+ }, >+ >+ /** >+ * @description Resets the rules to their default values >+ * @function >+ */ >+ setDefaults: function setDefaults() { >+ this.rules = Object.create(null); >+ var keys = Object.keys(this.defaults); >+ for(var i = 0; i < keys.length; i++) { >+ var key = keys[i]; >+ this.rules[key] = this.defaults[key]; >+ } >+ } >+ }; >+ >+ /** >+ * @name javascript.JavaScriptFormatting >+ * @description creates a new instance of the javascript formatter >+ * @constructor >+ * @public >+ * @param {Worker} ternWorker >+ */ >+ function JavaScriptFormatting(ternWorker, jsProject) { >+ this.ternworker = ternWorker; >+ this.project = jsProject; >+ config.setDefaults(); >+ } >+ >+ Objects.mixin(JavaScriptFormatting.prototype, /** @lends javascript.JavaScriptFormatting.prototype*/ { >+ >+ /** >+ * @description Callback from the editor to format the source code >+ * @function >+ * @public >+ * @memberof javascript.JavaScriptFormatting.prototype >+ * @param {orion.edit.EditorContext} editorContext The current editor context >+ * @param {Object} context The current selection context >+ */ >+ execute: function(editorContext, context) { >+ var deferred = new Deferred(); >+ if(this.project) { >+ //TODO make sure we can get the options as set in the formatting preference page Right now only the indent character is customizable >+ // We should expose all existing options - see defaults above >+ this.project.getFormattingOptions().then(function(cfg) { >+ this.format(editorContext, deferred, cfg ? cfg : config.rules); >+ }.bind(this)); >+ } else { >+ this.format(editorContext, deferred, config.rules); >+ } >+ return deferred; >+ }, >+ >+ /** >+ * @description Format the given editor >+ * @function >+ * @private >+ * @param {orion.edit.EditorContext} editorContext The given editor context >+ * @param {Deferred} deferred the given deferred object >+ * @param {Object} configuration the given configuration >+ * @since 6.0 >+ */ >+ format: function(editorContext, deferred, configuration) { >+ return editorContext.getFileMetadata().then(function(meta) { >+ return editorContext.getSelection().then(function(selection) { >+ var start = selection.start; >+ var end = selection.end; >+ var files, request; >+ if (end !== start) { >+ return editorContext.getText(start, end).then(function(text) { >+ files = [{type: 'full', name: meta.location, text: text}]; //$NON-NLS-1$ >+ request = {request: 'beautify', args: {meta: {location: meta.location}, files: files, config: configuration, start: start, end: end, contentType: meta.contentType.id}}; //$NON-NLS-1$ >+ this.ternworker.postMessage( >+ request, >+ function(formatted, err) { >+ if(err) { >+ deferred.reject(); >+ } >+ if(formatted && formatted.text) { >+ deferred.resolve(editorContext.setText(formatted.text, start, end)); >+ } else { >+ deferred.reject(); >+ } >+ }); >+ return deferred; >+ }.bind(this)); >+ } >+ return editorContext.getText().then(function(text) { >+ files = [{type: 'full', name: meta.location, text: text}]; //$NON-NLS-1$ >+ request = {request: 'beautify', args: {meta: {location: meta.location}, files: files, config: configuration, contentType: meta.contentType.id}}; //$NON-NLS-1$ >+ this.ternworker.postMessage( >+ request, >+ function(formatted, err) { >+ if(err) { >+ deferred.reject(); >+ } >+ if(formatted && formatted.text) { >+ deferred.resolve(editorContext.setText(formatted.text)); >+ } else { >+ deferred.reject(); >+ } >+ }); >+ return deferred; >+ }.bind(this)); >+ }.bind(this)); >+ }.bind(this)); >+ }, >+ >+ /** >+ * @description Callback from orion.cm.managedservice >+ * @function >+ * @public >+ * @param {Object} properties The properties that have been changed >+ */ >+ updated: function(properties) { >+ if (!properties) { >+ return; >+ } >+ var oldconfig = properties.pid === 'jsbeautify.config'; >+ var keys = Object.keys(properties); >+ for(var i = 0; i < keys.length; i++) { >+ var key = keys[i]; >+ var ruleId = key; >+ if(oldconfig && config.rules[key] !== config.defaults[key]) { >+ //don't overwrite a new setting with an old one >+ continue; >+ } >+ config.setOption(ruleId, properties[key]); >+ } >+ }, >+ }); >+ >+ >+ JavaScriptFormatting.prototype.constructor = JavaScriptFormatting; >+ >+ return { >+ JavaScriptFormatting: JavaScriptFormatting >+ }; >+}); >\ No newline at end of file >diff --git a/bundles/org.eclipse.orion.client.javascript/web/beautifier/lib/beautify-html.js b/bundles/org.eclipse.orion.client.javascript/web/beautifier/lib/beautify-html.js >new file mode 100644 >index 0000000..0f4fdbe >--- /dev/null >+++ b/bundles/org.eclipse.orion.client.javascript/web/beautifier/lib/beautify-html.js >@@ -0,0 +1,995 @@ >+/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */ >+/* >+ >+ The MIT License (MIT) >+ >+ Copyright (c) 2007-2013 Einar Lielmanis and contributors. >+ >+ Permission is hereby granted, free of charge, to any person >+ obtaining a copy of this software and associated documentation files >+ (the "Software"), to deal in the Software without restriction, >+ including without limitation the rights to use, copy, modify, merge, >+ publish, distribute, sublicense, and/or sell copies of the Software, >+ and to permit persons to whom the Software is furnished to do so, >+ subject to the following conditions: >+ >+ The above copyright notice and this permission notice shall be >+ included in all copies or substantial portions of the Software. >+ >+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, >+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF >+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND >+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS >+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN >+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN >+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE >+ SOFTWARE. >+ >+ >+ Style HTML >+--------------- >+ >+ Written by Nochum Sossonko, (nsossonko@hotmail.com) >+ >+ Based on code initially developed by: Einar Lielmanis, <einar@jsbeautifier.org> >+ http://jsbeautifier.org/ >+ >+ Usage: >+ style_html(html_source); >+ >+ style_html(html_source, options); >+ >+ The options are: >+ indent_inner_html (default false) - indent <head> and <body> sections, >+ indent_size (default 4) - indentation size, >+ indent_char (default space) - character to indent with, >+ wrap_line_length (default 250) - maximum amount of characters per line (0 = disable) >+ brace_style (default "collapse") - "collapse" | "expand" | "end-expand" | "none" >+ put braces on the same line as control statements (default), or put braces on own line (Allman / ANSI style), or just put end braces on own line, or attempt to keep them where they are. >+ unformatted (defaults to inline tags) - list of tags, that shouldn't be reformatted >+ indent_scripts (default normal) - "keep"|"separate"|"normal" >+ preserve_newlines (default true) - whether existing line breaks before elements should be preserved >+ Only works before elements, not inside tags or for text. >+ max_preserve_newlines (default unlimited) - maximum number of line breaks to be preserved in one chunk >+ indent_handlebars (default false) - format and indent {{#foo}} and {{/foo}} >+ end_with_newline (false) - end with a newline >+ extra_liners (default [head,body,/html]) -List of tags that should have an extra newline before them. >+ >+ e.g. >+ >+ style_html(html_source, { >+ 'indent_inner_html': false, >+ 'indent_size': 2, >+ 'indent_char': ' ', >+ 'wrap_line_length': 78, >+ 'brace_style': 'expand', >+ 'preserve_newlines': true, >+ 'max_preserve_newlines': 5, >+ 'indent_handlebars': false, >+ 'extra_liners': ['/html'] >+ }); >+*/ >+/*eslint-env amd */ >+define([ >+ "./beautify-js", >+ "./beautify-css" >+], function(js_beautify, css_beautify) { >+ >+ // function trim(s) { >+ // return s.replace(/^\s+|\s+$/g, ''); >+ // } >+ >+ function ltrim(s) { >+ return s.replace(/^\s+/g, ''); >+ } >+ >+ function rtrim(s) { >+ return s.replace(/\s+$/g, ''); >+ } >+ >+ function style_html(html_source, options, js_beautify, css_beautify) { >+ //Wrapper function to invoke all the necessary constructors and deal with the output. >+ >+ var multi_parser, >+ indent_inner_html, >+ indent_size, >+ indent_character, >+ wrap_line_length, >+ brace_style, >+ unformatted, >+ preserve_newlines, >+ max_preserve_newlines, >+ indent_handlebars, >+ wrap_attributes, >+ wrap_attributes_indent_size, >+ end_with_newline, >+ extra_liners, >+ eol; >+ >+ options = options || {}; >+ >+ // backwards compatibility to 1.3.4 >+ if ((options.wrap_line_length === undefined || parseInt(options.wrap_line_length, 10) === 0) && >+ (options.max_char !== undefined && parseInt(options.max_char, 10) !== 0)) { >+ options.wrap_line_length = options.max_char; >+ } >+ >+ indent_inner_html = (options.indent_inner_html === undefined) ? false : options.indent_inner_html; >+ indent_size = (options.indent_size === undefined) ? 4 : parseInt(options.indent_size, 10); >+ indent_character = (options.indent_char === undefined) ? ' ' : options.indent_char; >+ brace_style = (options.brace_style === undefined) ? 'collapse' : options.brace_style; >+ wrap_line_length = parseInt(options.wrap_line_length, 10) === 0 ? 32786 : parseInt(options.wrap_line_length || 250, 10); >+ unformatted = options.unformatted || [ >+ // https://www.w3.org/TR/html5/dom.html#phrasing-content >+ 'a', 'abbr', 'area', 'audio', 'b', 'bdi', 'bdo', 'br', 'button', 'canvas', 'cite', >+ 'code', 'data', 'datalist', 'del', 'dfn', 'em', 'embed', 'i', 'iframe', 'img', >+ 'input', 'ins', 'kbd', 'keygen', 'label', 'map', 'mark', 'math', 'meter', 'noscript', >+ 'object', 'output', 'progress', 'q', 'ruby', 's', 'samp', /* 'script', */ 'select', 'small', >+ 'span', 'strong', 'sub', 'sup', 'svg', 'template', 'textarea', 'time', 'u', 'var', >+ 'video', 'wbr', 'text', >+ // prexisting - not sure of full effect of removing, leaving in >+ 'acronym', 'address', 'big', 'dt', 'ins', 'small', 'strike', 'tt', >+ 'pre', >+ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' >+ ]; >+ preserve_newlines = (options.preserve_newlines === undefined) ? true : options.preserve_newlines; >+ max_preserve_newlines = preserve_newlines ? >+ (isNaN(parseInt(options.max_preserve_newlines, 10)) ? 32786 : parseInt(options.max_preserve_newlines, 10)) : >+ 0; >+ indent_handlebars = (options.indent_handlebars === undefined) ? false : options.indent_handlebars; >+ wrap_attributes = (options.wrap_attributes === undefined) ? 'auto' : options.wrap_attributes; >+ wrap_attributes_indent_size = (isNaN(parseInt(options.wrap_attributes_indent_size, 10))) ? indent_size : parseInt(options.wrap_attributes_indent_size, 10); >+ end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline; >+ extra_liners = (typeof options.extra_liners === 'object') && options.extra_liners ? >+ options.extra_liners.concat() : (typeof options.extra_liners === 'string') ? >+ options.extra_liners.split(',') : 'head,body,/html'.split(','); >+ eol = options.eol ? options.eol : '\n'; >+ >+ if (options.indent_with_tabs) { >+ indent_character = '\t'; >+ indent_size = 1; >+ } >+ >+ eol = eol.replace(/\\r/, '\r').replace(/\\n/, '\n'); >+ >+ function Parser() { >+ >+ this.pos = 0; //Parser position >+ this.token = ''; >+ this.current_mode = 'CONTENT'; //reflects the current Parser mode: TAG/CONTENT >+ this.tags = { //An object to hold tags, their position, and their parent-tags, initiated with default values >+ parent: 'parent1', >+ parentcount: 1, >+ parent1: '' >+ }; >+ this.tag_type = ''; >+ this.token_text = this.last_token = this.last_text = this.token_type = ''; >+ this.newlines = 0; >+ this.indent_content = indent_inner_html; >+ >+ this.Utils = { //Uilities made available to the various functions >+ whitespace: "\n\r\t ".split(''), >+ >+ single_token: [ >+ // HTLM void elements - aka self-closing tags - aka singletons >+ // https://www.w3.org/html/wg/drafts/html/master/syntax.html#void-elements >+ 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', >+ 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr', >+ // NOTE: Optional tags - are not understood. >+ // https://www.w3.org/TR/html5/syntax.html#optional-tags >+ // The rules for optional tags are too complex for a simple list >+ // Also, the content of these tags should still be indented in many cases. >+ // 'li' is a good exmple. >+ >+ // Doctype and xml elements >+ '!doctype', '?xml', >+ // ?php tag >+ '?php', >+ // other tags that were in this list, keeping just in case >+ 'basefont', 'isindex' >+ ], >+ extra_liners: extra_liners, //for tags that need a line of whitespace before them >+ in_array: function(what, arr) { >+ for (var i = 0; i < arr.length; i++) { >+ if (what === arr[i]) { >+ return true; >+ } >+ } >+ return false; >+ } >+ }; >+ >+ // Return true if the given text is composed entirely of whitespace. >+ this.is_whitespace = function(text) { >+ for (var n = 0; n < text.length; n++) { >+ if (!this.Utils.in_array(text.charAt(n), this.Utils.whitespace)) { >+ return false; >+ } >+ } >+ return true; >+ }; >+ >+ this.traverse_whitespace = function() { >+ var input_char = ''; >+ >+ input_char = this.input.charAt(this.pos); >+ if (this.Utils.in_array(input_char, this.Utils.whitespace)) { >+ this.newlines = 0; >+ while (this.Utils.in_array(input_char, this.Utils.whitespace)) { >+ if (preserve_newlines && input_char === '\n' && this.newlines <= max_preserve_newlines) { >+ this.newlines += 1; >+ } >+ >+ this.pos++; >+ input_char = this.input.charAt(this.pos); >+ } >+ return true; >+ } >+ return false; >+ }; >+ >+ // Append a space to the given content (string array) or, if we are >+ // at the wrap_line_length, append a newline/indentation. >+ // return true if a newline was added, false if a space was added >+ this.space_or_wrap = function(content) { >+ if (this.line_char_count >= this.wrap_line_length) { //insert a line when the wrap_line_length is reached >+ this.print_newline(false, content); >+ this.print_indentation(content); >+ return true; >+ } else { >+ this.line_char_count++; >+ content.push(' '); >+ return false; >+ } >+ }; >+ >+ this.get_content = function() { //function to capture regular content between tags >+ var input_char = '', >+ content = []; >+ >+ while (this.input.charAt(this.pos) !== '<') { >+ if (this.pos >= this.input.length) { >+ return content.length ? content.join('') : ['', 'TK_EOF']; >+ } >+ >+ if (this.traverse_whitespace()) { >+ this.space_or_wrap(content); >+ continue; >+ } >+ >+ if (indent_handlebars) { >+ // Handlebars parsing is complicated. >+ // {{#foo}} and {{/foo}} are formatted tags. >+ // {{something}} should get treated as content, except: >+ // {{else}} specifically behaves like {{#if}} and {{/if}} >+ var peek3 = this.input.substr(this.pos, 3); >+ if (peek3 === '{{#' || peek3 === '{{/') { >+ // These are tags and not content. >+ break; >+ } else if (peek3 === '{{!') { >+ return [this.get_tag(), 'TK_TAG_HANDLEBARS_COMMENT']; >+ } else if (this.input.substr(this.pos, 2) === '{{') { >+ if (this.get_tag(true) === '{{else}}') { >+ break; >+ } >+ } >+ } >+ >+ input_char = this.input.charAt(this.pos); >+ this.pos++; >+ this.line_char_count++; >+ content.push(input_char); //letter at-a-time (or string) inserted to an array >+ } >+ return content.length ? content.join('') : ''; >+ }; >+ >+ this.get_contents_to = function(name) { //get the full content of a script or style to pass to js_beautify >+ if (this.pos === this.input.length) { >+ return ['', 'TK_EOF']; >+ } >+ var content = ''; >+ var reg_match = new RegExp('</' + name + '\\s*>', 'igm'); >+ reg_match.lastIndex = this.pos; >+ var reg_array = reg_match.exec(this.input); >+ var end_script = reg_array ? reg_array.index : this.input.length; //absolute end of script >+ if (this.pos < end_script) { //get everything in between the script tags >+ content = this.input.substring(this.pos, end_script); >+ this.pos = end_script; >+ } >+ return content; >+ }; >+ >+ this.record_tag = function(tag) { //function to record a tag and its parent in this.tags Object >+ if (this.tags[tag + 'count']) { //check for the existence of this tag type >+ this.tags[tag + 'count']++; >+ this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level >+ } else { //otherwise initialize this tag type >+ this.tags[tag + 'count'] = 1; >+ this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level >+ } >+ this.tags[tag + this.tags[tag + 'count'] + 'parent'] = this.tags.parent; //set the parent (i.e. in the case of a div this.tags.div1parent) >+ this.tags.parent = tag + this.tags[tag + 'count']; //and make this the current parent (i.e. in the case of a div 'div1') >+ }; >+ >+ this.retrieve_tag = function(tag) { //function to retrieve the opening tag to the corresponding closer >+ if (this.tags[tag + 'count']) { //if the openener is not in the Object we ignore it >+ var temp_parent = this.tags.parent; //check to see if it's a closable tag. >+ while (temp_parent) { //till we reach '' (the initial value); >+ if (tag + this.tags[tag + 'count'] === temp_parent) { //if this is it use it >+ break; >+ } >+ temp_parent = this.tags[temp_parent + 'parent']; //otherwise keep on climbing up the DOM Tree >+ } >+ if (temp_parent) { //if we caught something >+ this.indent_level = this.tags[tag + this.tags[tag + 'count']]; //set the indent_level accordingly >+ this.tags.parent = this.tags[temp_parent + 'parent']; //and set the current parent >+ } >+ delete this.tags[tag + this.tags[tag + 'count'] + 'parent']; //delete the closed tags parent reference... >+ delete this.tags[tag + this.tags[tag + 'count']]; //...and the tag itself >+ if (this.tags[tag + 'count'] === 1) { >+ delete this.tags[tag + 'count']; >+ } else { >+ this.tags[tag + 'count']--; >+ } >+ } >+ }; >+ >+ this.indent_to_tag = function(tag) { >+ // Match the indentation level to the last use of this tag, but don't remove it. >+ if (!this.tags[tag + 'count']) { >+ return; >+ } >+ var temp_parent = this.tags.parent; >+ while (temp_parent) { >+ if (tag + this.tags[tag + 'count'] === temp_parent) { >+ break; >+ } >+ temp_parent = this.tags[temp_parent + 'parent']; >+ } >+ if (temp_parent) { >+ this.indent_level = this.tags[tag + this.tags[tag + 'count']]; >+ } >+ }; >+ >+ this.get_tag = function(peek) { //function to get a full tag and parse its type >+ var input_char = '', >+ content = [], >+ comment = '', >+ space = false, >+ first_attr = true, >+ tag_start, tag_end, >+ tag_start_char, >+ orig_pos = this.pos, >+ orig_line_char_count = this.line_char_count; >+ >+ peek = peek !== undefined ? peek : false; >+ >+ do { >+ if (this.pos >= this.input.length) { >+ if (peek) { >+ this.pos = orig_pos; >+ this.line_char_count = orig_line_char_count; >+ } >+ return content.length ? content.join('') : ['', 'TK_EOF']; >+ } >+ >+ input_char = this.input.charAt(this.pos); >+ this.pos++; >+ >+ if (this.Utils.in_array(input_char, this.Utils.whitespace)) { //don't want to insert unnecessary space >+ space = true; >+ continue; >+ } >+ >+ if (input_char === "'" || input_char === '"') { >+ input_char += this.get_unformatted(input_char); >+ space = true; >+ >+ } >+ >+ if (input_char === '=') { //no space before = >+ space = false; >+ } >+ >+ if (content.length && content[content.length - 1] !== '=' && input_char !== '>' && space) { >+ //no space after = or before > >+ var wrapped = this.space_or_wrap(content); >+ var indentAttrs = wrapped && input_char !== '/' && wrap_attributes !== 'force'; >+ space = false; >+ if (!first_attr && wrap_attributes === 'force' && input_char !== '/') { >+ this.print_newline(false, content); >+ this.print_indentation(content); >+ indentAttrs = true; >+ } >+ if (indentAttrs) { >+ //indent attributes an auto or forced line-wrap >+ for (var count = 0; count < wrap_attributes_indent_size; count++) { >+ content.push(indent_character); >+ } >+ } >+ for (var i = 0; i < content.length; i++) { >+ if (content[i] === ' ') { >+ first_attr = false; >+ break; >+ } >+ } >+ } >+ >+ if (indent_handlebars && tag_start_char === '<') { >+ // When inside an angle-bracket tag, put spaces around >+ // handlebars not inside of strings. >+ if ((input_char + this.input.charAt(this.pos)) === '{{') { >+ input_char += this.get_unformatted('}}'); >+ if (content.length && content[content.length - 1] !== ' ' && content[content.length - 1] !== '<') { >+ input_char = ' ' + input_char; >+ } >+ space = true; >+ } >+ } >+ >+ if (input_char === '<' && !tag_start_char) { >+ tag_start = this.pos - 1; >+ tag_start_char = '<'; >+ } >+ >+ if (indent_handlebars && !tag_start_char) { >+ if (content.length >= 2 && content[content.length - 1] === '{' && content[content.length - 2] === '{') { >+ if (input_char === '#' || input_char === '/' || input_char === '!') { >+ tag_start = this.pos - 3; >+ } else { >+ tag_start = this.pos - 2; >+ } >+ tag_start_char = '{'; >+ } >+ } >+ >+ this.line_char_count++; >+ content.push(input_char); //inserts character at-a-time (or string) >+ >+ if (content[1] && (content[1] === '!' || content[1] === '?' || content[1] === '%')) { //if we're in a comment, do something special >+ // We treat all comments as literals, even more than preformatted tags >+ // we just look for the appropriate close tag >+ content = [this.get_comment(tag_start)]; >+ break; >+ } >+ >+ if (indent_handlebars && content[1] && content[1] === '{' && content[2] && content[2] === '!') { //if we're in a comment, do something special >+ // We treat all comments as literals, even more than preformatted tags >+ // we just look for the appropriate close tag >+ content = [this.get_comment(tag_start)]; >+ break; >+ } >+ >+ if (indent_handlebars && tag_start_char === '{' && content.length > 2 && content[content.length - 2] === '}' && content[content.length - 1] === '}') { >+ break; >+ } >+ } while (input_char !== '>'); >+ >+ var tag_complete = content.join(''); >+ var tag_index; >+ var tag_offset; >+ >+ if (tag_complete.indexOf(' ') !== -1) { //if there's whitespace, thats where the tag name ends >+ tag_index = tag_complete.indexOf(' '); >+ } else if (tag_complete.charAt(0) === '{') { >+ tag_index = tag_complete.indexOf('}'); >+ } else { //otherwise go with the tag ending >+ tag_index = tag_complete.indexOf('>'); >+ } >+ if (tag_complete.charAt(0) === '<' || !indent_handlebars) { >+ tag_offset = 1; >+ } else { >+ tag_offset = tag_complete.charAt(2) === '#' ? 3 : 2; >+ } >+ var tag_check = tag_complete.substring(tag_offset, tag_index).toLowerCase(); >+ if (tag_complete.charAt(tag_complete.length - 2) === '/' || >+ this.Utils.in_array(tag_check, this.Utils.single_token)) { //if this tag name is a single tag type (either in the list or has a closing /) >+ if (!peek) { >+ this.tag_type = 'SINGLE'; >+ } >+ } else if (indent_handlebars && tag_complete.charAt(0) === '{' && tag_check === 'else') { >+ if (!peek) { >+ this.indent_to_tag('if'); >+ this.tag_type = 'HANDLEBARS_ELSE'; >+ this.indent_content = true; >+ this.traverse_whitespace(); >+ } >+ } else if (this.is_unformatted(tag_check, unformatted)) { // do not reformat the "unformatted" tags >+ comment = this.get_unformatted('</' + tag_check + '>', tag_complete); //...delegate to get_unformatted function >+ content.push(comment); >+ tag_end = this.pos - 1; >+ this.tag_type = 'SINGLE'; >+ } else if (tag_check === 'script' && >+ (tag_complete.search('type') === -1 || >+ (tag_complete.search('type') > -1 && >+ tag_complete.search(/\b(text|application)\/(x-)?(javascript|ecmascript|jscript|livescript|(ld\+)?json)/) > -1))) { >+ if (!peek) { >+ this.record_tag(tag_check); >+ this.tag_type = 'SCRIPT'; >+ } >+ } else if (tag_check === 'style' && >+ (tag_complete.search('type') === -1 || >+ (tag_complete.search('type') > -1 && tag_complete.search('text/css') > -1))) { >+ if (!peek) { >+ this.record_tag(tag_check); >+ this.tag_type = 'STYLE'; >+ } >+ } else if (tag_check.charAt(0) === '!') { //peek for <! comment >+ // for comments content is already correct. >+ if (!peek) { >+ this.tag_type = 'SINGLE'; >+ this.traverse_whitespace(); >+ } >+ } else if (!peek) { >+ if (tag_check.charAt(0) === '/') { //this tag is a double tag so check for tag-ending >+ this.retrieve_tag(tag_check.substring(1)); //remove it and all ancestors >+ this.tag_type = 'END'; >+ } else { //otherwise it's a start-tag >+ this.record_tag(tag_check); //push it on the tag stack >+ if (tag_check.toLowerCase() !== 'html') { >+ this.indent_content = true; >+ } >+ this.tag_type = 'START'; >+ } >+ >+ // Allow preserving of newlines after a start or end tag >+ if (this.traverse_whitespace()) { >+ this.space_or_wrap(content); >+ } >+ >+ if (this.Utils.in_array(tag_check, this.Utils.extra_liners)) { //check if this double needs an extra line >+ this.print_newline(false, this.output); >+ if (this.output.length && this.output[this.output.length - 2] !== '\n') { >+ this.print_newline(true, this.output); >+ } >+ } >+ } >+ >+ if (peek) { >+ this.pos = orig_pos; >+ this.line_char_count = orig_line_char_count; >+ } >+ >+ return content.join(''); //returns fully formatted tag >+ }; >+ >+ this.get_comment = function(start_pos) { //function to return comment content in its entirety >+ // this is will have very poor perf, but will work for now. >+ var comment = '', >+ delimiter = '>', >+ matched = false; >+ >+ this.pos = start_pos; >+ var input_char = this.input.charAt(this.pos); >+ this.pos++; >+ >+ while (this.pos <= this.input.length) { >+ comment += input_char; >+ >+ // only need to check for the delimiter if the last chars match >+ if (comment.charAt(comment.length - 1) === delimiter.charAt(delimiter.length - 1) && >+ comment.indexOf(delimiter) !== -1) { >+ break; >+ } >+ >+ // only need to search for custom delimiter for the first few characters >+ if (!matched && comment.length < 10) { >+ if (comment.indexOf('<![if') === 0) { //peek for <![if conditional comment >+ delimiter = '<![endif]>'; >+ matched = true; >+ } else if (comment.indexOf('<![cdata[') === 0) { //if it's a <[cdata[ comment... >+ delimiter = ']]>'; >+ matched = true; >+ } else if (comment.indexOf('<![') === 0) { // some other ![ comment? ... >+ delimiter = ']>'; >+ matched = true; >+ } else if (comment.indexOf('<!--') === 0) { // <!-- comment ... >+ delimiter = '-->'; >+ matched = true; >+ } else if (comment.indexOf('{{!') === 0) { // {{! handlebars comment >+ delimiter = '}}'; >+ matched = true; >+ } else if (comment.indexOf('<?') === 0) { // {{! handlebars comment >+ delimiter = '?>'; >+ matched = true; >+ } else if (comment.indexOf('<%') === 0) { // {{! handlebars comment >+ delimiter = '%>'; >+ matched = true; >+ } >+ } >+ >+ input_char = this.input.charAt(this.pos); >+ this.pos++; >+ } >+ >+ return comment; >+ }; >+ >+ function tokenMatcher(delimiter) { >+ var token = ''; >+ >+ var add = function(str) { >+ var newToken = token + str.toLowerCase(); >+ token = newToken.length <= delimiter.length ? newToken : newToken.substr(newToken.length - delimiter.length, delimiter.length); >+ }; >+ >+ var doesNotMatch = function() { >+ return token.indexOf(delimiter) === -1; >+ }; >+ >+ return { >+ add: add, >+ doesNotMatch: doesNotMatch >+ }; >+ } >+ >+ this.get_unformatted = function(delimiter, orig_tag) { //function to return unformatted content in its entirety >+ if (orig_tag && orig_tag.toLowerCase().indexOf(delimiter) !== -1) { >+ return ''; >+ } >+ var input_char = ''; >+ var content = ''; >+ var space = true; >+ >+ var delimiterMatcher = tokenMatcher(delimiter); >+ >+ do { >+ >+ if (this.pos >= this.input.length) { >+ return content; >+ } >+ >+ input_char = this.input.charAt(this.pos); >+ this.pos++; >+ >+ if (this.Utils.in_array(input_char, this.Utils.whitespace)) { >+ if (!space) { >+ this.line_char_count--; >+ continue; >+ } >+ if (input_char === '\n' || input_char === '\r') { >+ content += '\n'; >+ /* Don't change tab indention for unformatted blocks. If using code for html editing, this will greatly affect <pre> tags if they are specified in the 'unformatted array' >+ for (var i=0; i<this.indent_level; i++) { >+ content += this.indent_string; >+ } >+ space = false; //...and make sure other indentation is erased >+ */ >+ this.line_char_count = 0; >+ continue; >+ } >+ } >+ content += input_char; >+ delimiterMatcher.add(input_char); >+ this.line_char_count++; >+ space = true; >+ >+ if (indent_handlebars && input_char === '{' && content.length && content.charAt(content.length - 2) === '{') { >+ // Handlebars expressions in strings should also be unformatted. >+ content += this.get_unformatted('}}'); >+ // Don't consider when stopping for delimiters. >+ } >+ } while (delimiterMatcher.doesNotMatch()); >+ >+ return content; >+ }; >+ >+ this.get_token = function() { //initial handler for token-retrieval >+ var token; >+ >+ if (this.last_token === 'TK_TAG_SCRIPT' || this.last_token === 'TK_TAG_STYLE') { //check if we need to format javascript >+ var type = this.last_token.substr(7); >+ token = this.get_contents_to(type); >+ if (typeof token !== 'string') { >+ return token; >+ } >+ return [token, 'TK_' + type]; >+ } >+ if (this.current_mode === 'CONTENT') { >+ token = this.get_content(); >+ if (typeof token !== 'string') { >+ return token; >+ } else { >+ return [token, 'TK_CONTENT']; >+ } >+ } >+ >+ if (this.current_mode === 'TAG') { >+ token = this.get_tag(); >+ if (typeof token !== 'string') { >+ return token; >+ } else { >+ var tag_name_type = 'TK_TAG_' + this.tag_type; >+ return [token, tag_name_type]; >+ } >+ } >+ }; >+ >+ this.get_full_indent = function(level) { >+ level = this.indent_level + level || 0; >+ if (level < 1) { >+ return ''; >+ } >+ >+ return Array(level + 1).join(this.indent_string); >+ }; >+ >+ this.is_unformatted = function(tag_check, unformatted) { >+ //is this an HTML5 block-level link? >+ if (!this.Utils.in_array(tag_check, unformatted)) { >+ return false; >+ } >+ >+ if (tag_check.toLowerCase() !== 'a' || !this.Utils.in_array('a', unformatted)) { >+ return true; >+ } >+ >+ //at this point we have an tag; is its first child something we want to remain >+ //unformatted? >+ var next_tag = this.get_tag(true /* peek. */ ); >+ >+ // test next_tag to see if it is just html tag (no external content) >+ var tag = (next_tag || "").match(/^\s*<\s*\/?([a-z]*)\s*[^>]*>\s*$/); >+ >+ // if next_tag comes back but is not an isolated tag, then >+ // let's treat the 'a' tag as having content >+ // and respect the unformatted option >+ if (!tag || this.Utils.in_array(tag, unformatted)) { >+ return true; >+ } else { >+ return false; >+ } >+ }; >+ >+ this.printer = function(js_source, indent_character, indent_size, wrap_line_length, brace_style) { //handles input/output and some other printing functions >+ >+ this.input = js_source || ''; //gets the input for the Parser >+ >+ // HACK: newline parsing inconsistent. This brute force normalizes the input. >+ this.input = this.input.replace(/\r\n|[\r\u2028\u2029]/g, '\n'); >+ >+ this.output = []; >+ this.indent_character = indent_character; >+ this.indent_string = ''; >+ this.indent_size = indent_size; >+ this.brace_style = brace_style; >+ this.indent_level = 0; >+ this.wrap_line_length = wrap_line_length; >+ this.line_char_count = 0; //count to see if wrap_line_length was exceeded >+ >+ for (var i = 0; i < this.indent_size; i++) { >+ this.indent_string += this.indent_character; >+ } >+ >+ this.print_newline = function(force, arr) { >+ this.line_char_count = 0; >+ if (!arr || !arr.length) { >+ return; >+ } >+ if (force || (arr[arr.length - 1] !== '\n')) { //we might want the extra line >+ if ((arr[arr.length - 1] !== '\n')) { >+ arr[arr.length - 1] = rtrim(arr[arr.length - 1]); >+ } >+ arr.push('\n'); >+ } >+ }; >+ >+ this.print_indentation = function(arr) { >+ for (var i = 0; i < this.indent_level; i++) { >+ arr.push(this.indent_string); >+ this.line_char_count += this.indent_string.length; >+ } >+ }; >+ >+ this.print_token = function(text) { >+ // Avoid printing initial whitespace. >+ if (this.is_whitespace(text) && !this.output.length) { >+ return; >+ } >+ if (text || text !== '') { >+ if (this.output.length && this.output[this.output.length - 1] === '\n') { >+ this.print_indentation(this.output); >+ text = ltrim(text); >+ } >+ } >+ this.print_token_raw(text); >+ }; >+ >+ this.print_token_raw = function(text) { >+ // If we are going to print newlines, truncate trailing >+ // whitespace, as the newlines will represent the space. >+ if (this.newlines > 0) { >+ text = rtrim(text); >+ } >+ >+ if (text && text !== '') { >+ if (text.length > 1 && text.charAt(text.length - 1) === '\n') { >+ // unformatted tags can grab newlines as their last character >+ this.output.push(text.slice(0, -1)); >+ this.print_newline(false, this.output); >+ } else { >+ this.output.push(text); >+ } >+ } >+ >+ for (var n = 0; n < this.newlines; n++) { >+ this.print_newline(n > 0, this.output); >+ } >+ this.newlines = 0; >+ }; >+ >+ this.indent = function() { >+ this.indent_level++; >+ }; >+ >+ this.unindent = function() { >+ if (this.indent_level > 0) { >+ this.indent_level--; >+ } >+ }; >+ }; >+ return this; >+ } >+ >+ /*_____________________--------------------_____________________*/ >+ >+ multi_parser = new Parser(); //wrapping functions Parser >+ multi_parser.printer(html_source, indent_character, indent_size, wrap_line_length, brace_style); //initialize starting values >+ >+ while (true) { >+ var t = multi_parser.get_token(); >+ multi_parser.token_text = t[0]; >+ multi_parser.token_type = t[1]; >+ >+ if (multi_parser.token_type === 'TK_EOF') { >+ break; >+ } >+ >+ switch (multi_parser.token_type) { >+ case 'TK_TAG_START': >+ multi_parser.print_newline(false, multi_parser.output); >+ multi_parser.print_token(multi_parser.token_text); >+ if (multi_parser.indent_content) { >+ multi_parser.indent(); >+ multi_parser.indent_content = false; >+ } >+ multi_parser.current_mode = 'CONTENT'; >+ break; >+ case 'TK_TAG_STYLE': >+ case 'TK_TAG_SCRIPT': >+ multi_parser.print_newline(false, multi_parser.output); >+ multi_parser.print_token(multi_parser.token_text); >+ multi_parser.current_mode = 'CONTENT'; >+ break; >+ case 'TK_TAG_END': >+ //Print new line only if the tag has no content and has child >+ if (multi_parser.last_token === 'TK_CONTENT' && multi_parser.last_text === '') { >+ var tag_name = multi_parser.token_text.match(/\w+/)[0]; >+ var tag_extracted_from_last_output = null; >+ if (multi_parser.output.length) { >+ tag_extracted_from_last_output = multi_parser.output[multi_parser.output.length - 1].match(/(?:<|{{#)\s*(\w+)/); >+ } >+ if (tag_extracted_from_last_output === null || >+ (tag_extracted_from_last_output[1] !== tag_name && !multi_parser.Utils.in_array(tag_extracted_from_last_output[1], unformatted))) { >+ multi_parser.print_newline(false, multi_parser.output); >+ } >+ } >+ multi_parser.print_token(multi_parser.token_text); >+ multi_parser.current_mode = 'CONTENT'; >+ break; >+ case 'TK_TAG_SINGLE': >+ // Don't add a newline before elements that should remain unformatted. >+ var tag_check = multi_parser.token_text.match(/^\s*<([a-z-]+)/i); >+ if (!tag_check || !multi_parser.Utils.in_array(tag_check[1], unformatted)) { >+ multi_parser.print_newline(false, multi_parser.output); >+ } >+ multi_parser.print_token(multi_parser.token_text); >+ multi_parser.current_mode = 'CONTENT'; >+ break; >+ case 'TK_TAG_HANDLEBARS_ELSE': >+ // Don't add a newline if opening {{#if}} tag is on the current line >+ var foundIfOnCurrentLine = false; >+ for (var lastCheckedOutput = multi_parser.output.length - 1; lastCheckedOutput >= 0; lastCheckedOutput--) { >+ if (multi_parser.output[lastCheckedOutput] === '\n') { >+ break; >+ } else { >+ if (multi_parser.output[lastCheckedOutput].match(/{{#if/)) { >+ foundIfOnCurrentLine = true; >+ break; >+ } >+ } >+ } >+ if (!foundIfOnCurrentLine) { >+ multi_parser.print_newline(false, multi_parser.output); >+ } >+ multi_parser.print_token(multi_parser.token_text); >+ if (multi_parser.indent_content) { >+ multi_parser.indent(); >+ multi_parser.indent_content = false; >+ } >+ multi_parser.current_mode = 'CONTENT'; >+ break; >+ case 'TK_TAG_HANDLEBARS_COMMENT': >+ multi_parser.print_token(multi_parser.token_text); >+ multi_parser.current_mode = 'TAG'; >+ break; >+ case 'TK_CONTENT': >+ multi_parser.print_token(multi_parser.token_text); >+ multi_parser.current_mode = 'TAG'; >+ break; >+ case 'TK_STYLE': >+ case 'TK_SCRIPT': >+ if (multi_parser.token_text !== '') { >+ multi_parser.print_newline(false, multi_parser.output); >+ var text = multi_parser.token_text, >+ _beautifier, >+ script_indent_level = 1; >+ if (multi_parser.token_type === 'TK_SCRIPT') { >+ _beautifier = typeof js_beautify === 'function' && js_beautify; >+ } else if (multi_parser.token_type === 'TK_STYLE') { >+ _beautifier = typeof css_beautify === 'function' && css_beautify; >+ } >+ >+ if (options.indent_scripts === "keep") { >+ script_indent_level = 0; >+ } else if (options.indent_scripts === "separate") { >+ script_indent_level = -multi_parser.indent_level; >+ } >+ >+ var indentation = multi_parser.get_full_indent(script_indent_level); >+ if (_beautifier) { >+ >+ // call the Beautifier if avaliable >+ var Child_options = function() { >+ this.eol = '\n'; >+ }; >+ Child_options.prototype = options; >+ var child_options = new Child_options(); >+ text = _beautifier(text.replace(/^\s*/, indentation), child_options); >+ } else { >+ // simply indent the string otherwise >+ var white = text.match(/^\s*/)[0]; >+ var _level = white.match(/[^\n\r]*$/)[0].split(multi_parser.indent_string).length - 1; >+ var reindent = multi_parser.get_full_indent(script_indent_level - _level); >+ text = text.replace(/^\s*/, indentation) >+ .replace(/\r\n|\r|\n/g, '\n' + reindent) >+ .replace(/\s+$/, ''); >+ } >+ if (text) { >+ multi_parser.print_token_raw(text); >+ multi_parser.print_newline(true, multi_parser.output); >+ } >+ } >+ multi_parser.current_mode = 'TAG'; >+ break; >+ default: >+ // We should not be getting here but we don't want to drop input on the floor >+ // Just output the text and move on >+ if (multi_parser.token_text !== '') { >+ multi_parser.print_token(multi_parser.token_text); >+ } >+ break; >+ } >+ multi_parser.last_token = multi_parser.token_type; >+ multi_parser.last_text = multi_parser.token_text; >+ } >+ var sweet_code = multi_parser.output.join('').replace(/[\r\n\t ]+$/, ''); >+ >+ // establish end_with_newline >+ if (end_with_newline) { >+ sweet_code += '\n'; >+ } >+ >+ if (eol !== '\n') { >+ sweet_code = sweet_code.replace(/[\n]/g, eol); >+ } >+ >+ return sweet_code; >+ } >+ >+ return { >+ html_beautify: function(html_source, options) { >+ return style_html(html_source, options, js_beautify.js_beautify, css_beautify.css_beautify); >+ } >+ }; >+}); >\ No newline at end of file >diff --git a/bundles/org.eclipse.orion.client.javascript/web/javascript/plugins/ternWorkerCore.js b/bundles/org.eclipse.orion.client.javascript/web/javascript/plugins/ternWorkerCore.js >index 830b573..bd520d5 100644 >--- a/bundles/org.eclipse.orion.client.javascript/web/javascript/plugins/ternWorkerCore.js >+++ b/bundles/org.eclipse.orion.client.javascript/web/javascript/plugins/ternWorkerCore.js >@@ -508,7 +508,40 @@ function(Tern, defaultOptions, Deferred, Objects, Serialize, Messages, i18nUtil) > } else { > callback(null, {message: Messages['failedToComputeOccurrencesNoServer']}); > } >- } >+ }, >+ /* lint message handler */ >+ 'beautify': function(args, callback) { >+ if(ternserver) { >+ var query = >+ { >+ type: "beautify", //$NON-NLS-1$ >+ file: args.meta.location, >+ args: { >+ config: args.config, >+ start: args.start, >+ end: args.end, >+ contentType: args.contentType >+ } >+ }; >+ ternserver.request( >+ { >+ query: query, >+ files: args.files >+ }, >+ function(error, text) { >+ if(error) { >+ callback({request: 'beautify', error: error.message, message: Messages['failedToFormat']}); //$NON-NLS-1$ >+ } else if(text) { >+ callback({request: 'beautify', text: text}); //$NON-NLS-1$ >+ } else { >+ callback({request: 'beautify', text: ""}); //$NON-NLS-1$ >+ } >+ } >+ ); >+ } else { >+ callback(null, {message: Messages['failedToFormatNoServer']}); >+ } >+ }, > }; > > var ternID = 0; >diff --git a/bundles/org.eclipse.orion.client.javascript/web/javascript/ternPlugins/beautifier.js b/bundles/org.eclipse.orion.client.javascript/web/javascript/ternPlugins/beautifier.js >new file mode 100644 >index 0000000..cf469a7 >--- /dev/null >+++ b/bundles/org.eclipse.orion.client.javascript/web/javascript/ternPlugins/beautifier.js >@@ -0,0 +1,62 @@ >+/******************************************************************************* >+ * @license >+ * Copyright (c) 2016 IBM Corporation and others. >+ * All rights reserved. This program and the accompanying materials are made >+ * available under the terms of the Eclipse Public License v1.0 >+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution >+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). >+ * >+ * Contributors: >+ * IBM Corporation - Allow original requirejs plugin to find files in Orion workspace >+ *******************************************************************************/ >+/* eslint-disable missing-nls */ >+/*eslint-env node, amd*/ >+/*globals tern tern */ >+define([ >+ "tern/lib/tern", >+ "beautifier/beautifier" >+], function(tern, Beautifier) { >+ >+ tern.registerPlugin("beautifier", /* @callback */ function(server, options) { >+ return { >+ //don't need any passes yet >+ }; >+ }); >+ >+ tern.defineQueryType("beautify", { >+ takesFile: true, >+ /** >+ * @callback >+ */ >+ run: function(server, query, file) { >+ return format(query, file); >+ } >+ }); >+ >+ /** >+ * @description Format the code using the right beautifier >+ * @param {Object} query The original Tern query object >+ * @param {Object} file The file object from Tern >+ */ >+ function format(query, file) { >+ // we should format using the right beautifier based on the file extension >+ var args = query.args; >+ var text = file.text; >+ var contentType = ""; >+ if (args) { >+ if (args.start && args.end) { >+ text = file.text.substring(args.start, args.end); >+ } >+ contentType = args.contentType; >+ } >+ switch(contentType) { >+ case "text/html" : >+ return Beautifier.html_beautify(text, query.args.config); >+ case "text/css" : >+ return Beautifier.css_beautify(text, query.args.config); >+ case "application/javascript" : >+ return Beautifier.js_beautify(text, query.args.config); >+ } >+ return text; >+ } >+}); >diff --git a/bundles/org.eclipse.orion.client.javascript/web/beautifier/lib/beautify-js.js b/bundles/org.eclipse.orion.client.javascript/web/beautifier/lib/beautify-js.js >new file mode 100644 >index 0000000..69f76df >--- /dev/null >+++ b/bundles/org.eclipse.orion.client.javascript/web/beautifier/lib/beautify-js.js >@@ -0,0 +1,2254 @@ >+/*eslint-env amd, node*/ >+/* >+ >+ The MIT License (MIT) >+ >+ Copyright (c) 2007-2013 Einar Lielmanis and contributors. >+ >+ Permission is hereby granted, free of charge, to any person >+ obtaining a copy of this software and associated documentation files >+ (the "Software"), to deal in the Software without restriction, >+ including without limitation the rights to use, copy, modify, merge, >+ publish, distribute, sublicense, and/or sell copies of the Software, >+ and to permit persons to whom the Software is furnished to do so, >+ subject to the following conditions: >+ >+ The above copyright notice and this permission notice shall be >+ included in all copies or substantial portions of the Software. >+ >+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, >+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF >+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND >+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS >+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN >+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN >+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE >+ SOFTWARE. >+ >+ JS Beautifier >+--------------- >+ >+ >+ Written by Einar Lielmanis, <einar@jsbeautifier.org> >+ http://jsbeautifier.org/ >+ >+ Originally converted to javascript by Vital, <vital76@gmail.com> >+ "End braces on own line" added by Chris J. Shull, <chrisjshull@gmail.com> >+ Parsing improvements for brace-less statements by Liam Newman <bitwiseman@gmail.com> >+ >+ >+ Usage: >+ js_beautify(js_source_text); >+ js_beautify(js_source_text, options); >+ >+ The options are: >+ indent_size (default 4) - indentation size, >+ indent_char (default space) - character to indent with, >+ preserve_newlines (default true) - whether existing line breaks should be preserved, >+ max_preserve_newlines (default unlimited) - maximum number of line breaks to be preserved in one chunk, >+ >+ jslint_happy (default false) - if true, then jslint-stricter mode is enforced. >+ >+ jslint_happy !jslint_happy >+ --------------------------------- >+ function () function() >+ >+ switch () { switch() { >+ case 1: case 1: >+ break; break; >+ } } >+ >+ space_after_anon_function (default false) - should the space before an anonymous function's parens be added, "function()" vs "function ()", >+ NOTE: This option is overriden by jslint_happy (i.e. if jslint_happy is true, space_after_anon_function is true by design) >+ >+ brace_style (default "collapse") - "collapse-preserve-inline" | "collapse" | "expand" | "end-expand" | "none" >+ put braces on the same line as control statements (default), or put braces on own line (Allman / ANSI style), or just put end braces on own line, or attempt to keep them where they are. >+ >+ space_before_conditional (default true) - should the space before conditional statement be added, "if(true)" vs "if (true)", >+ >+ unescape_strings (default false) - should printable characters in strings encoded in \xNN notation be unescaped, "example" vs "\x65\x78\x61\x6d\x70\x6c\x65" >+ >+ wrap_line_length (default unlimited) - lines should wrap at next opportunity after this number of characters. >+ NOTE: This is not a hard limit. Lines will continue until a point where a newline would >+ be preserved if it were present. >+ >+ end_with_newline (default false) - end output with a newline >+ >+ >+ e.g >+ >+ js_beautify(js_source_text, { >+ 'indent_size': 1, >+ 'indent_char': '\t' >+ }); >+ >+*/ >+define([ >+ "acorn/dist/acorn" >+], function(acorn) { >+ >+ >+ // Object.values polyfill found here: >+ // http://tokenposts.blogspot.com.au/2012/04/javascript-objectkeys-browser.html >+ if (!Object.values) { >+ Object.values = function(o) { >+ if (o !== Object(o)) { >+ throw new TypeError('Object.values called on a non-object'); >+ } >+ var k = [], >+ p; >+ for (p in o) { >+ if (Object.prototype.hasOwnProperty.call(o, p)) { >+ k.push(o[p]); >+ } >+ } >+ return k; >+ }; >+ } >+ >+ >+ function js_beautify(js_source_text, options) { >+ >+ function in_array(what, arr) { >+ for (var i = 0; i < arr.length; i += 1) { >+ if (arr[i] === what) { >+ return true; >+ } >+ } >+ return false; >+ } >+ >+ function trim(s) { >+ return s.replace(/^\s+|\s+$/g, ''); >+ } >+ >+ function ltrim(s) { >+ return s.replace(/^\s+/g, ''); >+ } >+ >+ // function rtrim(s) { >+ // return s.replace(/\s+$/g, ''); >+ // } >+ >+ function sanitizeOperatorPosition(opPosition) { >+ opPosition = opPosition || OPERATOR_POSITION.before_newline; >+ >+ var validPositionValues = Object.values(OPERATOR_POSITION); >+ >+ if (!in_array(opPosition, validPositionValues)) { >+ throw new Error("Invalid Option Value: The option 'operator_position' must be one of the following values\n" + >+ validPositionValues + >+ "\nYou passed in: '" + opPosition + "'"); >+ } >+ >+ return opPosition; >+ } >+ >+ var OPERATOR_POSITION = { >+ before_newline: 'before-newline', >+ after_newline: 'after-newline', >+ preserve_newline: 'preserve-newline', >+ }; >+ >+ var OPERATOR_POSITION_BEFORE_OR_PRESERVE = [OPERATOR_POSITION.before_newline, OPERATOR_POSITION.preserve_newline]; >+ >+ var MODE = { >+ BlockStatement: 'BlockStatement', // 'BLOCK' >+ Statement: 'Statement', // 'STATEMENT' >+ ObjectLiteral: 'ObjectLiteral', // 'OBJECT', >+ ArrayLiteral: 'ArrayLiteral', //'[EXPRESSION]', >+ ForInitializer: 'ForInitializer', //'(FOR-EXPRESSION)', >+ Conditional: 'Conditional', //'(COND-EXPRESSION)', >+ Expression: 'Expression' //'(EXPRESSION)' >+ }; >+ >+ function Beautifier(js_source_text, options) { >+ "use strict"; >+ var output; >+ var tokens = [], >+ token_pos; >+ var Tokenizer; >+ var current_token; >+ var last_type, last_last_text, indent_string; >+ var flags, previous_flags, flag_store; >+ var prefix; >+ >+ var handlers, opt; >+ var baseIndentString = ''; >+ >+ handlers = { >+ 'TK_START_EXPR': handle_start_expr, >+ 'TK_END_EXPR': handle_end_expr, >+ 'TK_START_BLOCK': handle_start_block, >+ 'TK_END_BLOCK': handle_end_block, >+ 'TK_WORD': handle_word, >+ 'TK_RESERVED': handle_word, >+ 'TK_SEMICOLON': handle_semicolon, >+ 'TK_STRING': handle_string, >+ 'TK_EQUALS': handle_equals, >+ 'TK_OPERATOR': handle_operator, >+ 'TK_COMMA': handle_comma, >+ 'TK_BLOCK_COMMENT': handle_block_comment, >+ 'TK_COMMENT': handle_comment, >+ 'TK_DOT': handle_dot, >+ 'TK_UNKNOWN': handle_unknown, >+ 'TK_EOF': handle_eof >+ }; >+ >+ function create_flags(flags_base, mode) { >+ var next_indent_level = 0; >+ if (flags_base) { >+ next_indent_level = flags_base.indentation_level; >+ if (!output.just_added_newline() && >+ flags_base.line_indent_level > next_indent_level) { >+ next_indent_level = flags_base.line_indent_level; >+ } >+ } >+ >+ var next_flags = { >+ mode: mode, >+ parent: flags_base, >+ last_text: flags_base ? flags_base.last_text : '', // last token text >+ last_word: flags_base ? flags_base.last_word : '', // last 'TK_WORD' passed >+ declaration_statement: false, >+ declaration_assignment: false, >+ multiline_frame: false, >+ inline_frame: false, >+ if_block: false, >+ else_block: false, >+ do_block: false, >+ do_while: false, >+ import_block: false, >+ in_case_statement: false, // switch(..){ INSIDE HERE } >+ in_case: false, // we're on the exact line with "case 0:" >+ case_body: false, // the indented case-action block >+ indentation_level: next_indent_level, >+ line_indent_level: flags_base ? flags_base.line_indent_level : next_indent_level, >+ start_line_index: output.get_line_number(), >+ ternary_depth: 0 >+ }; >+ return next_flags; >+ } >+ >+ // Some interpreters have unexpected results with foo = baz || bar; >+ options = options ? options : {}; >+ opt = {}; >+ >+ // compatibility >+ if (options.braces_on_own_line !== undefined) { //graceful handling of deprecated option >+ opt.brace_style = options.braces_on_own_line ? "expand" : "collapse"; >+ } >+ opt.brace_style = options.brace_style ? options.brace_style : (opt.brace_style ? opt.brace_style : "collapse"); >+ >+ // graceful handling of deprecated option >+ if (opt.brace_style === "expand-strict") { >+ opt.brace_style = "expand"; >+ } >+ >+ opt.indent_size = options.indent_size ? parseInt(options.indent_size, 10) : 4; >+ opt.indent_char = options.indent_char ? options.indent_char : ' '; >+ opt.eol = options.eol ? options.eol : 'auto'; >+ opt.preserve_newlines = (options.preserve_newlines === undefined) ? true : options.preserve_newlines; >+ opt.break_chained_methods = (options.break_chained_methods === undefined) ? false : options.break_chained_methods; >+ opt.max_preserve_newlines = (options.max_preserve_newlines === undefined) ? 0 : parseInt(options.max_preserve_newlines, 10); >+ opt.space_in_paren = (options.space_in_paren === undefined) ? false : options.space_in_paren; >+ opt.space_in_empty_paren = (options.space_in_empty_paren === undefined) ? false : options.space_in_empty_paren; >+ opt.jslint_happy = (options.jslint_happy === undefined) ? false : options.jslint_happy; >+ opt.space_after_anon_function = (options.space_after_anon_function === undefined) ? false : options.space_after_anon_function; >+ opt.keep_array_indentation = (options.keep_array_indentation === undefined) ? false : options.keep_array_indentation; >+ opt.space_before_conditional = (options.space_before_conditional === undefined) ? true : options.space_before_conditional; >+ opt.unescape_strings = (options.unescape_strings === undefined) ? false : options.unescape_strings; >+ opt.wrap_line_length = (options.wrap_line_length === undefined) ? 0 : parseInt(options.wrap_line_length, 10); >+ opt.e4x = (options.e4x === undefined) ? false : options.e4x; >+ opt.end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline; >+ opt.comma_first = (options.comma_first === undefined) ? false : options.comma_first; >+ opt.operator_position = sanitizeOperatorPosition(options.operator_position); >+ >+ // For testing of beautify ignore:start directive >+ opt.test_output_raw = (options.test_output_raw === undefined) ? false : options.test_output_raw; >+ >+ // force opt.space_after_anon_function to true if opt.jslint_happy >+ if (opt.jslint_happy) { >+ opt.space_after_anon_function = true; >+ } >+ >+ if (options.indent_with_tabs) { >+ opt.indent_char = '\t'; >+ opt.indent_size = 1; >+ } >+ >+ if (opt.eol === 'auto') { >+ opt.eol = '\n'; >+ if (js_source_text && acorn.lineBreak.test(js_source_text || '')) { >+ opt.eol = js_source_text.match(acorn.lineBreak)[0]; >+ } >+ } >+ >+ opt.eol = opt.eol.replace(/\\r/, '\r').replace(/\\n/, '\n'); >+ >+ //---------------------------------- >+ indent_string = ''; >+ while (opt.indent_size > 0) { >+ indent_string += opt.indent_char; >+ opt.indent_size -= 1; >+ } >+ >+ var preindent_index = 0; >+ if (js_source_text && js_source_text.length) { >+ while ((js_source_text.charAt(preindent_index) === ' ' || >+ js_source_text.charAt(preindent_index) === '\t')) { >+ baseIndentString += js_source_text.charAt(preindent_index); >+ preindent_index += 1; >+ } >+ js_source_text = js_source_text.substring(preindent_index); >+ } >+ >+ last_type = 'TK_START_BLOCK'; // last token type >+ last_last_text = ''; // pre-last token text >+ output = new Output(indent_string, baseIndentString); >+ >+ // If testing the ignore directive, start with output disable set to true >+ output.raw = opt.test_output_raw; >+ >+ >+ // Stack of parsing/formatting states, including MODE. >+ // We tokenize, parse, and output in an almost purely a forward-only stream of token input >+ // and formatted output. This makes the beautifier less accurate than full parsers >+ // but also far more tolerant of syntax errors. >+ // >+ // For example, the default mode is MODE.BlockStatement. If we see a '{' we push a new frame of type >+ // MODE.BlockStatement on the the stack, even though it could be object literal. If we later >+ // encounter a ":", we'll switch to to MODE.ObjectLiteral. If we then see a ";", >+ // most full parsers would die, but the beautifier gracefully falls back to >+ // MODE.BlockStatement and continues on. >+ flag_store = []; >+ set_mode(MODE.BlockStatement); >+ >+ this.beautify = function() { >+ >+ /*jshint onevar:true */ >+ var local_token, sweet_code; >+ Tokenizer = new tokenizer(js_source_text, opt, indent_string); >+ tokens = Tokenizer.tokenize(); >+ token_pos = 0; >+ >+ function get_local_token() { >+ local_token = get_token(); >+ return local_token; >+ } >+ >+ while (get_local_token()) { >+ for (var i = 0; i < local_token.comments_before.length; i++) { >+ // The cleanest handling of inline comments is to treat them as though they aren't there. >+ // Just continue formatting and the behavior should be logical. >+ // Also ignore unknown tokens. Again, this should result in better behavior. >+ handle_token(local_token.comments_before[i]); >+ } >+ handle_token(local_token); >+ >+ last_last_text = flags.last_text; >+ last_type = local_token.type; >+ flags.last_text = local_token.text; >+ >+ token_pos += 1; >+ } >+ >+ sweet_code = output.get_code(); >+ if (opt.end_with_newline) { >+ sweet_code += '\n'; >+ } >+ >+ if (opt.eol !== '\n') { >+ sweet_code = sweet_code.replace(/[\n]/g, opt.eol); >+ } >+ >+ return sweet_code; >+ }; >+ >+ function handle_token(local_token) { >+ var newlines = local_token.newlines; >+ var keep_whitespace = opt.keep_array_indentation && is_array(flags.mode); >+ >+ if (keep_whitespace) { >+ for (var i = 0; i < newlines; i += 1) { >+ print_newline(i > 0); >+ } >+ } else { >+ if (opt.max_preserve_newlines && newlines > opt.max_preserve_newlines) { >+ newlines = opt.max_preserve_newlines; >+ } >+ >+ if (opt.preserve_newlines) { >+ if (local_token.newlines > 1) { >+ print_newline(); >+ for (var j = 1; j < newlines; j += 1) { >+ print_newline(true); >+ } >+ } >+ } >+ } >+ >+ current_token = local_token; >+ handlers[current_token.type](); >+ } >+ >+ // we could use just string.split, but >+ // IE doesn't like returning empty strings >+ function split_linebreaks(s) { >+ //return s.split(/\x0d\x0a|\x0a/); >+ >+ s = s.replace(acorn.allLineBreaks, '\n'); >+ var out = [], >+ idx = s.indexOf("\n"); >+ while (idx !== -1) { >+ out.push(s.substring(0, idx)); >+ s = s.substring(idx + 1); >+ idx = s.indexOf("\n"); >+ } >+ if (s.length) { >+ out.push(s); >+ } >+ return out; >+ } >+ >+ var newline_restricted_tokens = ['break', 'contiue', 'return', 'throw']; >+ >+ function allow_wrap_or_preserved_newline(force_linewrap) { >+ force_linewrap = (force_linewrap === undefined) ? false : force_linewrap; >+ >+ // Never wrap the first token on a line >+ if (output.just_added_newline()) { >+ return; >+ } >+ >+ var shouldPreserveOrForce = (opt.preserve_newlines && current_token.wanted_newline) || force_linewrap; >+ var operatorLogicApplies = in_array(flags.last_text, Tokenizer.positionable_operators) || in_array(current_token.text, Tokenizer.positionable_operators); >+ >+ if (operatorLogicApplies) { >+ var shouldPrintOperatorNewline = ( >+ in_array(flags.last_text, Tokenizer.positionable_operators) && >+ in_array(opt.operator_position, OPERATOR_POSITION_BEFORE_OR_PRESERVE) >+ ) || >+ in_array(current_token.text, Tokenizer.positionable_operators); >+ shouldPreserveOrForce = shouldPreserveOrForce && shouldPrintOperatorNewline; >+ } >+ >+ if (shouldPreserveOrForce) { >+ print_newline(false, true); >+ } else if (opt.wrap_line_length) { >+ if (last_type === 'TK_RESERVED' && in_array(flags.last_text, newline_restricted_tokens)) { >+ // These tokens should never have a newline inserted >+ // between them and the following expression. >+ return; >+ } >+ var proposed_line_length = output.current_line.get_character_count() + current_token.text.length + >+ (output.space_before_token ? 1 : 0); >+ if (proposed_line_length >= opt.wrap_line_length) { >+ print_newline(false, true); >+ } >+ } >+ } >+ >+ function print_newline(force_newline, preserve_statement_flags) { >+ if (!preserve_statement_flags) { >+ if (flags.last_text !== ';' && flags.last_text !== ',' && flags.last_text !== '=' && last_type !== 'TK_OPERATOR') { >+ while (flags.mode === MODE.Statement && !flags.if_block && !flags.do_block) { >+ restore_mode(); >+ } >+ } >+ } >+ >+ if (output.add_new_line(force_newline)) { >+ flags.multiline_frame = true; >+ } >+ } >+ >+ function print_token_line_indentation() { >+ if (output.just_added_newline()) { >+ if (opt.keep_array_indentation && is_array(flags.mode) && current_token.wanted_newline) { >+ output.current_line.push(current_token.whitespace_before); >+ output.space_before_token = false; >+ } else if (output.set_indent(flags.indentation_level)) { >+ flags.line_indent_level = flags.indentation_level; >+ } >+ } >+ } >+ >+ function print_token(printable_token) { >+ if (output.raw) { >+ output.add_raw_token(current_token); >+ return; >+ } >+ >+ if (opt.comma_first && last_type === 'TK_COMMA' && >+ output.just_added_newline()) { >+ if (output.previous_line.last() === ',') { >+ var popped = output.previous_line.pop(); >+ // if the comma was already at the start of the line, >+ // pull back onto that line and reprint the indentation >+ if (output.previous_line.is_empty()) { >+ output.previous_line.push(popped); >+ output.trim(true); >+ output.current_line.pop(); >+ output.trim(); >+ } >+ >+ // add the comma in front of the next token >+ print_token_line_indentation(); >+ output.add_token(','); >+ output.space_before_token = true; >+ } >+ } >+ >+ printable_token = printable_token || current_token.text; >+ print_token_line_indentation(); >+ output.add_token(printable_token); >+ } >+ >+ function indent() { >+ flags.indentation_level += 1; >+ } >+ >+ function deindent() { >+ if (flags.indentation_level > 0 && >+ ((!flags.parent) || flags.indentation_level > flags.parent.indentation_level)) { >+ flags.indentation_level -= 1; >+ >+ } >+ } >+ >+ function set_mode(mode) { >+ if (flags) { >+ flag_store.push(flags); >+ previous_flags = flags; >+ } else { >+ previous_flags = create_flags(null, mode); >+ } >+ >+ flags = create_flags(previous_flags, mode); >+ } >+ >+ function is_array(mode) { >+ return mode === MODE.ArrayLiteral; >+ } >+ >+ function is_expression(mode) { >+ return in_array(mode, [MODE.Expression, MODE.ForInitializer, MODE.Conditional]); >+ } >+ >+ function restore_mode() { >+ if (flag_store.length > 0) { >+ previous_flags = flags; >+ flags = flag_store.pop(); >+ if (previous_flags.mode === MODE.Statement) { >+ output.remove_redundant_indentation(previous_flags); >+ } >+ } >+ } >+ >+ function start_of_object_property() { >+ return flags.parent.mode === MODE.ObjectLiteral && flags.mode === MODE.Statement && ( >+ (flags.last_text === ':' && flags.ternary_depth === 0) || (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set']))); >+ } >+ >+ function start_of_statement() { >+ if ( >+ (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const']) && current_token.type === 'TK_WORD') || >+ (last_type === 'TK_RESERVED' && flags.last_text === 'do') || >+ (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['return', 'throw']) && !current_token.wanted_newline) || >+ (last_type === 'TK_RESERVED' && flags.last_text === 'else' && !(current_token.type === 'TK_RESERVED' && current_token.text === 'if')) || >+ (last_type === 'TK_END_EXPR' && (previous_flags.mode === MODE.ForInitializer || previous_flags.mode === MODE.Conditional)) || >+ (last_type === 'TK_WORD' && flags.mode === MODE.BlockStatement && >+ !flags.in_case && >+ !(current_token.text === '--' || current_token.text === '++') && >+ last_last_text !== 'function' && >+ current_token.type !== 'TK_WORD' && current_token.type !== 'TK_RESERVED') || >+ (flags.mode === MODE.ObjectLiteral && ( >+ (flags.last_text === ':' && flags.ternary_depth === 0) || (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set'])))) >+ ) { >+ >+ set_mode(MODE.Statement); >+ indent(); >+ >+ if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const']) && current_token.type === 'TK_WORD') { >+ flags.declaration_statement = true; >+ } >+ >+ // Issue #276: >+ // If starting a new statement with [if, for, while, do], push to a new line. >+ // if (a) if (b) if(c) d(); else e(); else f(); >+ if (!start_of_object_property()) { >+ allow_wrap_or_preserved_newline( >+ current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['do', 'for', 'if', 'while'])); >+ } >+ >+ return true; >+ } >+ return false; >+ } >+ >+ function all_lines_start_with(lines, c) { >+ for (var i = 0; i < lines.length; i++) { >+ var line = trim(lines[i]); >+ if (line.charAt(0) !== c) { >+ return false; >+ } >+ } >+ return true; >+ } >+ >+ function each_line_matches_indent(lines, indent) { >+ var i = 0, >+ len = lines.length, >+ line; >+ for (; i < len; i++) { >+ line = lines[i]; >+ // allow empty lines to pass through >+ if (line && line.indexOf(indent) !== 0) { >+ return false; >+ } >+ } >+ return true; >+ } >+ >+ function is_special_word(word) { >+ return in_array(word, ['case', 'return', 'do', 'if', 'throw', 'else']); >+ } >+ >+ function get_token(offset) { >+ var index = token_pos + (offset || 0); >+ return (index < 0 || index >= tokens.length) ? null : tokens[index]; >+ } >+ >+ function handle_start_expr() { >+ if (start_of_statement()) { >+ // The conditional starts the statement if appropriate. >+ } >+ >+ var next_mode = MODE.Expression; >+ if (current_token.text === '[') { >+ >+ if (last_type === 'TK_WORD' || flags.last_text === ')') { >+ // this is array index specifier, break immediately >+ // a[x], fn()[x] >+ if (last_type === 'TK_RESERVED' && in_array(flags.last_text, Tokenizer.line_starters)) { >+ output.space_before_token = true; >+ } >+ set_mode(next_mode); >+ print_token(); >+ indent(); >+ if (opt.space_in_paren) { >+ output.space_before_token = true; >+ } >+ return; >+ } >+ >+ next_mode = MODE.ArrayLiteral; >+ if (is_array(flags.mode)) { >+ if (flags.last_text === '[' || >+ (flags.last_text === ',' && (last_last_text === ']' || last_last_text === '}'))) { >+ // ], [ goes to new line >+ // }, [ goes to new line >+ if (!opt.keep_array_indentation) { >+ print_newline(); >+ } >+ } >+ } >+ >+ } else { >+ if (last_type === 'TK_RESERVED' && flags.last_text === 'for') { >+ next_mode = MODE.ForInitializer; >+ } else if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['if', 'while'])) { >+ next_mode = MODE.Conditional; >+ } else { >+ // next_mode = MODE.Expression; >+ } >+ } >+ >+ if (flags.last_text === ';' || last_type === 'TK_START_BLOCK') { >+ print_newline(); >+ } else if (last_type === 'TK_END_EXPR' || last_type === 'TK_START_EXPR' || last_type === 'TK_END_BLOCK' || flags.last_text === '.') { >+ // TODO: Consider whether forcing this is required. Review failing tests when removed. >+ allow_wrap_or_preserved_newline(current_token.wanted_newline); >+ // do nothing on (( and )( and ][ and ]( and .( >+ } else if (!(last_type === 'TK_RESERVED' && current_token.text === '(') && last_type !== 'TK_WORD' && last_type !== 'TK_OPERATOR') { >+ output.space_before_token = true; >+ } else if ((last_type === 'TK_RESERVED' && (flags.last_word === 'function' || flags.last_word === 'typeof')) || >+ (flags.last_text === '*' && last_last_text === 'function')) { >+ // function() vs function () >+ if (opt.space_after_anon_function) { >+ output.space_before_token = true; >+ } >+ } else if (last_type === 'TK_RESERVED' && (in_array(flags.last_text, Tokenizer.line_starters) || flags.last_text === 'catch')) { >+ if (opt.space_before_conditional) { >+ output.space_before_token = true; >+ } >+ } >+ >+ // Should be a space between await and an IIFE >+ if (current_token.text === '(' && last_type === 'TK_RESERVED' && flags.last_word === 'await') { >+ output.space_before_token = true; >+ } >+ >+ // Support of this kind of newline preservation. >+ // a = (b && >+ // (c || d)); >+ if (current_token.text === '(') { >+ if (last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') { >+ if (!start_of_object_property()) { >+ allow_wrap_or_preserved_newline(); >+ } >+ } >+ } >+ >+ // Support preserving wrapped arrow function expressions >+ // a.b('c', >+ // () => d.e >+ // ) >+ if (current_token.text === '(' && last_type !== 'TK_WORD' && last_type !== 'TK_RESERVED') { >+ allow_wrap_or_preserved_newline(); >+ } >+ >+ set_mode(next_mode); >+ print_token(); >+ if (opt.space_in_paren) { >+ output.space_before_token = true; >+ } >+ >+ // In all cases, if we newline while inside an expression it should be indented. >+ indent(); >+ } >+ >+ function handle_end_expr() { >+ // statements inside expressions are not valid syntax, but... >+ // statements must all be closed when their container closes >+ while (flags.mode === MODE.Statement) { >+ restore_mode(); >+ } >+ >+ if (flags.multiline_frame) { >+ allow_wrap_or_preserved_newline(current_token.text === ']' && is_array(flags.mode) && !opt.keep_array_indentation); >+ } >+ >+ if (opt.space_in_paren) { >+ if (last_type === 'TK_START_EXPR' && !opt.space_in_empty_paren) { >+ // () [] no inner space in empty parens like these, ever, ref #320 >+ output.trim(); >+ output.space_before_token = false; >+ } else { >+ output.space_before_token = true; >+ } >+ } >+ if (current_token.text === ']' && opt.keep_array_indentation) { >+ print_token(); >+ restore_mode(); >+ } else { >+ restore_mode(); >+ print_token(); >+ } >+ output.remove_redundant_indentation(previous_flags); >+ >+ // do {} while () // no statement required after >+ if (flags.do_while && previous_flags.mode === MODE.Conditional) { >+ previous_flags.mode = MODE.Expression; >+ flags.do_block = false; >+ flags.do_while = false; >+ >+ } >+ } >+ >+ function handle_start_block() { >+ // Check if this is should be treated as a ObjectLiteral >+ var next_token = get_token(1); >+ var second_token = get_token(2); >+ if (second_token && ( >+ (in_array(second_token.text, [':', ',']) && in_array(next_token.type, ['TK_STRING', 'TK_WORD', 'TK_RESERVED'])) || >+ (in_array(next_token.text, ['get', 'set']) && in_array(second_token.type, ['TK_WORD', 'TK_RESERVED'])) >+ )) { >+ // We don't support TypeScript,but we didn't break it for a very long time. >+ // We'll try to keep not breaking it. >+ if (!in_array(last_last_text, ['class', 'interface'])) { >+ set_mode(MODE.ObjectLiteral); >+ } else { >+ set_mode(MODE.BlockStatement); >+ } >+ } else if (last_type === 'TK_OPERATOR' && flags.last_text === '=>') { >+ // arrow function: (param1, paramN) => { statements } >+ set_mode(MODE.BlockStatement); >+ } else if (in_array(last_type, ['TK_EQUALS', 'TK_START_EXPR', 'TK_COMMA', 'TK_OPERATOR']) || >+ (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['return', 'throw', 'import'])) >+ ) { >+ // Detecting shorthand function syntax is difficult by scanning forward, >+ // so check the surrounding context. >+ // If the block is being returned, imported, passed as arg, >+ // assigned with = or assigned in a nested object, treat as an ObjectLiteral. >+ set_mode(MODE.ObjectLiteral); >+ } else { >+ set_mode(MODE.BlockStatement); >+ } >+ >+ var empty_braces = !next_token.comments_before.length && next_token.text === '}'; >+ var empty_anonymous_function = empty_braces && flags.last_word === 'function' && >+ last_type === 'TK_END_EXPR'; >+ >+ >+ if (opt.brace_style === "expand" || >+ (opt.brace_style === "none" && current_token.wanted_newline)) { >+ if (last_type !== 'TK_OPERATOR' && >+ (empty_anonymous_function || >+ last_type === 'TK_EQUALS' || >+ (last_type === 'TK_RESERVED' && is_special_word(flags.last_text) && flags.last_text !== 'else'))) { >+ output.space_before_token = true; >+ } else { >+ print_newline(false, true); >+ } >+ } else { // collapse >+ if (opt.brace_style === 'collapse-preserve-inline') { >+ // search forward for a newline wanted inside this block >+ var index = 0; >+ var check_token = null; >+ flags.inline_frame = true; >+ do { >+ index += 1; >+ check_token = get_token(index); >+ if (check_token.wanted_newline) { >+ flags.inline_frame = false; >+ break; >+ } >+ } while (check_token.type !== 'TK_EOF' && >+ !(check_token.type === 'TK_END_BLOCK' && check_token.opened === current_token)); >+ } >+ >+ if (is_array(previous_flags.mode) && (last_type === 'TK_START_EXPR' || last_type === 'TK_COMMA')) { >+ // if we're preserving inline, >+ // allow newline between comma and next brace. >+ if (last_type === 'TK_COMMA' || opt.space_in_paren) { >+ output.space_before_token = true; >+ } >+ >+ if (opt.brace_style === 'collapse-preserve-inline' && >+ (last_type === 'TK_COMMA' || (last_type === 'TK_START_EXPR' && flags.inline_frame))) { >+ allow_wrap_or_preserved_newline(); >+ previous_flags.multiline_frame = previous_flags.multiline_frame || flags.multiline_frame; >+ flags.multiline_frame = false; >+ } >+ } else if (last_type !== 'TK_OPERATOR' && last_type !== 'TK_START_EXPR') { >+ if (last_type === 'TK_START_BLOCK') { >+ print_newline(); >+ } else { >+ output.space_before_token = true; >+ } >+ } >+ } >+ print_token(); >+ indent(); >+ } >+ >+ function handle_end_block() { >+ // statements must all be closed when their container closes >+ while (flags.mode === MODE.Statement) { >+ restore_mode(); >+ } >+ var empty_braces = last_type === 'TK_START_BLOCK'; >+ >+ if (opt.brace_style === "expand") { >+ if (!empty_braces) { >+ print_newline(); >+ } >+ } else { >+ // skip {} >+ if (!empty_braces) { >+ if (flags.inline_frame) { >+ output.space_before_token = true; >+ } else if (is_array(flags.mode) && opt.keep_array_indentation) { >+ // we REALLY need a newline here, but newliner would skip that >+ opt.keep_array_indentation = false; >+ print_newline(); >+ opt.keep_array_indentation = true; >+ >+ } else { >+ print_newline(); >+ } >+ } >+ } >+ restore_mode(); >+ print_token(); >+ } >+ >+ function handle_word() { >+ if (current_token.type === 'TK_RESERVED') { >+ if (in_array(current_token.text, ['set', 'get']) && flags.mode !== MODE.ObjectLiteral) { >+ current_token.type = 'TK_WORD'; >+ } else if (in_array(current_token.text, ['as', 'from']) && !flags.import_block) { >+ current_token.type = 'TK_WORD'; >+ } else if (flags.mode === MODE.ObjectLiteral) { >+ var next_token = get_token(1); >+ if (next_token.text === ':') { >+ current_token.type = 'TK_WORD'; >+ } >+ } >+ } >+ >+ if (start_of_statement()) { >+ // The conditional starts the statement if appropriate. >+ } else if (current_token.wanted_newline && !is_expression(flags.mode) && >+ (last_type !== 'TK_OPERATOR' || (flags.last_text === '--' || flags.last_text === '++')) && >+ last_type !== 'TK_EQUALS' && >+ (opt.preserve_newlines || !(last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const', 'set', 'get'])))) { >+ >+ print_newline(); >+ } >+ >+ if (flags.do_block && !flags.do_while) { >+ if (current_token.type === 'TK_RESERVED' && current_token.text === 'while') { >+ // do {} ## while () >+ output.space_before_token = true; >+ print_token(); >+ output.space_before_token = true; >+ flags.do_while = true; >+ return; >+ } else { >+ // do {} should always have while as the next word. >+ // if we don't see the expected while, recover >+ print_newline(); >+ flags.do_block = false; >+ } >+ } >+ >+ // if may be followed by else, or not >+ // Bare/inline ifs are tricky >+ // Need to unwind the modes correctly: if (a) if (b) c(); else d(); else e(); >+ if (flags.if_block) { >+ if (!flags.else_block && (current_token.type === 'TK_RESERVED' && current_token.text === 'else')) { >+ flags.else_block = true; >+ } else { >+ while (flags.mode === MODE.Statement) { >+ restore_mode(); >+ } >+ flags.if_block = false; >+ flags.else_block = false; >+ } >+ } >+ >+ if (current_token.type === 'TK_RESERVED' && (current_token.text === 'case' || (current_token.text === 'default' && flags.in_case_statement))) { >+ print_newline(); >+ if (flags.case_body || opt.jslint_happy) { >+ // switch cases following one another >+ deindent(); >+ flags.case_body = false; >+ } >+ print_token(); >+ flags.in_case = true; >+ flags.in_case_statement = true; >+ return; >+ } >+ >+ if (current_token.type === 'TK_RESERVED' && current_token.text === 'function') { >+ if (in_array(flags.last_text, ['}', ';']) || (output.just_added_newline() && !in_array(flags.last_text, ['[', '{', ':', '=', ',']))) { >+ // make sure there is a nice clean space of at least one blank line >+ // before a new function definition >+ if (!output.just_added_blankline() && !current_token.comments_before.length) { >+ print_newline(); >+ print_newline(true); >+ } >+ } >+ if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD') { >+ if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set', 'new', 'return', 'export', 'async'])) { >+ output.space_before_token = true; >+ } else if (last_type === 'TK_RESERVED' && flags.last_text === 'default' && last_last_text === 'export') { >+ output.space_before_token = true; >+ } else { >+ print_newline(); >+ } >+ } else if (last_type === 'TK_OPERATOR' || flags.last_text === '=') { >+ // foo = function >+ output.space_before_token = true; >+ } else if (!flags.multiline_frame && (is_expression(flags.mode) || is_array(flags.mode))) { >+ // (function >+ } else { >+ print_newline(); >+ } >+ } >+ >+ if (last_type === 'TK_COMMA' || last_type === 'TK_START_EXPR' || last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') { >+ if (!start_of_object_property()) { >+ allow_wrap_or_preserved_newline(); >+ } >+ } >+ >+ if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['function', 'get', 'set'])) { >+ print_token(); >+ flags.last_word = current_token.text; >+ return; >+ } >+ >+ prefix = 'NONE'; >+ >+ if (last_type === 'TK_END_BLOCK') { >+ >+ if (!(current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['else', 'catch', 'finally', 'from']))) { >+ prefix = 'NEWLINE'; >+ } else { >+ if (opt.brace_style === "expand" || >+ opt.brace_style === "end-expand" || >+ (opt.brace_style === "none" && current_token.wanted_newline)) { >+ prefix = 'NEWLINE'; >+ } else { >+ prefix = 'SPACE'; >+ output.space_before_token = true; >+ } >+ } >+ } else if (last_type === 'TK_SEMICOLON' && flags.mode === MODE.BlockStatement) { >+ // TODO: Should this be for STATEMENT as well? >+ prefix = 'NEWLINE'; >+ } else if (last_type === 'TK_SEMICOLON' && is_expression(flags.mode)) { >+ prefix = 'SPACE'; >+ } else if (last_type === 'TK_STRING') { >+ prefix = 'NEWLINE'; >+ } else if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD' || >+ (flags.last_text === '*' && last_last_text === 'function')) { >+ prefix = 'SPACE'; >+ } else if (last_type === 'TK_START_BLOCK') { >+ if (flags.inline_frame) { >+ prefix = 'SPACE'; >+ } else { >+ prefix = 'NEWLINE'; >+ } >+ } else if (last_type === 'TK_END_EXPR') { >+ output.space_before_token = true; >+ prefix = 'NEWLINE'; >+ } >+ >+ if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, Tokenizer.line_starters) && flags.last_text !== ')') { >+ if (flags.last_text === 'else' || flags.last_text === 'export') { >+ prefix = 'SPACE'; >+ } else { >+ prefix = 'NEWLINE'; >+ } >+ >+ } >+ >+ if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['else', 'catch', 'finally'])) { >+ if (!(last_type === 'TK_END_BLOCK' && previous_flags.mode === MODE.BlockStatement) || >+ opt.brace_style === "expand" || >+ opt.brace_style === "end-expand" || >+ (opt.brace_style === "none" && current_token.wanted_newline)) { >+ print_newline(); >+ } else { >+ output.trim(true); >+ var line = output.current_line; >+ // If we trimmed and there's something other than a close block before us >+ // put a newline back in. Handles '} // comment' scenario. >+ if (line.last() !== '}') { >+ print_newline(); >+ } >+ output.space_before_token = true; >+ } >+ } else if (prefix === 'NEWLINE') { >+ if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) { >+ // no newline between 'return nnn' >+ output.space_before_token = true; >+ } else if (last_type !== 'TK_END_EXPR') { >+ if ((last_type !== 'TK_START_EXPR' || !(current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['var', 'let', 'const']))) && flags.last_text !== ':') { >+ // no need to force newline on 'var': for (var x = 0...) >+ if (current_token.type === 'TK_RESERVED' && current_token.text === 'if' && flags.last_text === 'else') { >+ // no newline for } else if { >+ output.space_before_token = true; >+ } else { >+ print_newline(); >+ } >+ } >+ } else if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, Tokenizer.line_starters) && flags.last_text !== ')') { >+ print_newline(); >+ } >+ } else if (flags.multiline_frame && is_array(flags.mode) && flags.last_text === ',' && last_last_text === '}') { >+ print_newline(); // }, in lists get a newline treatment >+ } else if (prefix === 'SPACE') { >+ output.space_before_token = true; >+ } >+ print_token(); >+ flags.last_word = current_token.text; >+ >+ if (current_token.type === 'TK_RESERVED') { >+ if (current_token.text === 'do') { >+ flags.do_block = true; >+ } else if (current_token.text === 'if') { >+ flags.if_block = true; >+ } else if (current_token.text === 'import') { >+ flags.import_block = true; >+ } else if (flags.import_block && current_token.type === 'TK_RESERVED' && current_token.text === 'from') { >+ flags.import_block = false; >+ } >+ } >+ } >+ >+ function handle_semicolon() { >+ if (start_of_statement()) { >+ // The conditional starts the statement if appropriate. >+ // Semicolon can be the start (and end) of a statement >+ output.space_before_token = false; >+ } >+ while (flags.mode === MODE.Statement && !flags.if_block && !flags.do_block) { >+ restore_mode(); >+ } >+ >+ // hacky but effective for the moment >+ if (flags.import_block) { >+ flags.import_block = false; >+ } >+ print_token(); >+ } >+ >+ function handle_string() { >+ if (start_of_statement()) { >+ // The conditional starts the statement if appropriate. >+ // One difference - strings want at least a space before >+ output.space_before_token = true; >+ } else if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD' || flags.inline_frame) { >+ output.space_before_token = true; >+ } else if (last_type === 'TK_COMMA' || last_type === 'TK_START_EXPR' || last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') { >+ if (!start_of_object_property()) { >+ allow_wrap_or_preserved_newline(); >+ } >+ } else { >+ print_newline(); >+ } >+ print_token(); >+ } >+ >+ function handle_equals() { >+ if (start_of_statement()) { >+ // The conditional starts the statement if appropriate. >+ } >+ >+ if (flags.declaration_statement) { >+ // just got an '=' in a var-line, different formatting/line-breaking, etc will now be done >+ flags.declaration_assignment = true; >+ } >+ output.space_before_token = true; >+ print_token(); >+ output.space_before_token = true; >+ } >+ >+ function handle_comma() { >+ print_token(); >+ output.space_before_token = true; >+ if (flags.declaration_statement) { >+ if (is_expression(flags.parent.mode)) { >+ // do not break on comma, for(var a = 1, b = 2) >+ flags.declaration_assignment = false; >+ } >+ >+ if (flags.declaration_assignment) { >+ flags.declaration_assignment = false; >+ print_newline(false, true); >+ } else if (opt.comma_first) { >+ // for comma-first, we want to allow a newline before the comma >+ // to turn into a newline after the comma, which we will fixup later >+ allow_wrap_or_preserved_newline(); >+ } >+ } else if (flags.mode === MODE.ObjectLiteral || >+ (flags.mode === MODE.Statement && flags.parent.mode === MODE.ObjectLiteral)) { >+ if (flags.mode === MODE.Statement) { >+ restore_mode(); >+ } >+ >+ if (!flags.inline_frame) { >+ print_newline(); >+ } >+ } else if (opt.comma_first) { >+ // EXPR or DO_BLOCK >+ // for comma-first, we want to allow a newline before the comma >+ // to turn into a newline after the comma, which we will fixup later >+ allow_wrap_or_preserved_newline(); >+ } >+ } >+ >+ function handle_operator() { >+ if (start_of_statement()) { >+ // The conditional starts the statement if appropriate. >+ } >+ >+ if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) { >+ // "return" had a special handling in TK_WORD. Now we need to return the favor >+ output.space_before_token = true; >+ print_token(); >+ return; >+ } >+ >+ // hack for actionscript's import .*; >+ if (current_token.text === '*' && last_type === 'TK_DOT') { >+ print_token(); >+ return; >+ } >+ >+ if (current_token.text === '::') { >+ // no spaces around exotic namespacing syntax operator >+ print_token(); >+ return; >+ } >+ >+ // Allow line wrapping between operators when operator_position is >+ // set to before or preserve >+ if (last_type === 'TK_OPERATOR' && in_array(opt.operator_position, OPERATOR_POSITION_BEFORE_OR_PRESERVE)) { >+ allow_wrap_or_preserved_newline(); >+ } >+ >+ if (current_token.text === ':' && flags.in_case) { >+ flags.case_body = true; >+ indent(); >+ print_token(); >+ print_newline(); >+ flags.in_case = false; >+ return; >+ } >+ >+ var space_before = true; >+ var space_after = true; >+ var in_ternary = false; >+ var isGeneratorAsterisk = current_token.text === '*' && last_type === 'TK_RESERVED' && flags.last_text === 'function'; >+ var isUnary = in_array(current_token.text, ['-', '+']) && ( >+ in_array(last_type, ['TK_START_BLOCK', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR']) || >+ in_array(flags.last_text, Tokenizer.line_starters) || >+ flags.last_text === ',' >+ ); >+ >+ if (current_token.text === ':') { >+ if (flags.ternary_depth === 0) { >+ // Colon is invalid javascript outside of ternary and object, but do our best to guess what was meant. >+ space_before = false; >+ } else { >+ flags.ternary_depth -= 1; >+ in_ternary = true; >+ } >+ } else if (current_token.text === '?') { >+ flags.ternary_depth += 1; >+ } >+ >+ // let's handle the operator_position option prior to any conflicting logic >+ if (!isUnary && !isGeneratorAsterisk && opt.preserve_newlines && in_array(current_token.text, Tokenizer.positionable_operators)) { >+ var isColon = current_token.text === ':'; >+ var isTernaryColon = (isColon && in_ternary); >+ var isOtherColon = (isColon && !in_ternary); >+ >+ switch (opt.operator_position) { >+ case OPERATOR_POSITION.before_newline: >+ // if the current token is : and it's not a ternary statement then we set space_before to false >+ output.space_before_token = !isOtherColon; >+ >+ print_token(); >+ >+ if (!isColon || isTernaryColon) { >+ allow_wrap_or_preserved_newline(); >+ } >+ >+ output.space_before_token = true; >+ return; >+ >+ case OPERATOR_POSITION.after_newline: >+ // if the current token is anything but colon, or (via deduction) it's a colon and in a ternary statement, >+ // then print a newline. >+ >+ output.space_before_token = true; >+ >+ if (!isColon || isTernaryColon) { >+ if (get_token(1).wanted_newline) { >+ print_newline(false, true); >+ } else { >+ allow_wrap_or_preserved_newline(); >+ } >+ } else { >+ output.space_before_token = false; >+ } >+ >+ print_token(); >+ >+ output.space_before_token = true; >+ return; >+ >+ case OPERATOR_POSITION.preserve_newline: >+ if (!isOtherColon) { >+ allow_wrap_or_preserved_newline(); >+ } >+ >+ // if we just added a newline, or the current token is : and it's not a ternary statement, >+ // then we set space_before to false >+ space_before = !(output.just_added_newline() || isOtherColon); >+ >+ output.space_before_token = space_before; >+ print_token(); >+ output.space_before_token = true; >+ return; >+ } >+ } >+ >+ if (in_array(current_token.text, ['--', '++', '!', '~']) || isUnary) { >+ // unary operators (and binary +/- pretending to be unary) special cases >+ >+ space_before = false; >+ space_after = false; >+ >+ // http://www.ecma-international.org/ecma-262/5.1/#sec-7.9.1 >+ // if there is a newline between -- or ++ and anything else we should preserve it. >+ if (current_token.wanted_newline && (current_token.text === '--' || current_token.text === '++')) { >+ print_newline(false, true); >+ } >+ >+ if (flags.last_text === ';' && is_expression(flags.mode)) { >+ // for (;; ++i) >+ // ^^^ >+ space_before = true; >+ } >+ >+ if (last_type === 'TK_RESERVED') { >+ space_before = true; >+ } else if (last_type === 'TK_END_EXPR') { >+ space_before = !(flags.last_text === ']' && (current_token.text === '--' || current_token.text === '++')); >+ } else if (last_type === 'TK_OPERATOR') { >+ // a++ + ++b; >+ // a - -b >+ space_before = in_array(current_token.text, ['--', '-', '++', '+']) && in_array(flags.last_text, ['--', '-', '++', '+']); >+ // + and - are not unary when preceeded by -- or ++ operator >+ // a-- + b >+ // a * +b >+ // a - -b >+ if (in_array(current_token.text, ['+', '-']) && in_array(flags.last_text, ['--', '++'])) { >+ space_after = true; >+ } >+ } >+ >+ >+ if (((flags.mode === MODE.BlockStatement && !flags.inline_frame) || flags.mode === MODE.Statement) && >+ (flags.last_text === '{' || flags.last_text === ';')) { >+ // { foo; --i } >+ // foo(); --bar; >+ print_newline(); >+ } >+ } else if (isGeneratorAsterisk) { >+ space_before = false; >+ space_after = false; >+ } >+ output.space_before_token = output.space_before_token || space_before; >+ print_token(); >+ output.space_before_token = space_after; >+ } >+ >+ function handle_block_comment() { >+ if (output.raw) { >+ output.add_raw_token(current_token); >+ if (current_token.directives && current_token.directives.preserve === 'end') { >+ // If we're testing the raw output behavior, do not allow a directive to turn it off. >+ output.raw = opt.test_output_raw; >+ } >+ return; >+ } >+ >+ if (current_token.directives) { >+ print_newline(false, true); >+ print_token(); >+ if (current_token.directives.preserve === 'start') { >+ output.raw = true; >+ } >+ print_newline(false, true); >+ return; >+ } >+ >+ // inline block >+ if (!acorn.isNewLine(current_token.text) && !current_token.wanted_newline) { >+ output.space_before_token = true; >+ print_token(); >+ output.space_before_token = true; >+ return; >+ } >+ >+ var lines = split_linebreaks(current_token.text); >+ var j; // iterator for this case >+ var javadoc = false; >+ var starless = false; >+ var lastIndent = current_token.whitespace_before; >+ var lastIndentLength = lastIndent.length; >+ >+ // block comment starts with a new line >+ print_newline(false, true); >+ if (lines.length > 1) { >+ javadoc = all_lines_start_with(lines.slice(1), '*'); >+ starless = each_line_matches_indent(lines.slice(1), lastIndent); >+ } >+ >+ // first line always indented >+ print_token(lines[0]); >+ for (j = 1; j < lines.length; j++) { >+ print_newline(false, true); >+ if (javadoc) { >+ // javadoc: reformat and re-indent >+ print_token(' ' + ltrim(lines[j])); >+ } else if (starless && lines[j].length > lastIndentLength) { >+ // starless: re-indent non-empty content, avoiding trim >+ print_token(lines[j].substring(lastIndentLength)); >+ } else { >+ // normal comments output raw >+ output.add_token(lines[j]); >+ } >+ } >+ >+ // for comments of more than one line, make sure there's a new line after >+ print_newline(false, true); >+ } >+ >+ function handle_comment() { >+ if (current_token.wanted_newline) { >+ print_newline(false, true); >+ } else { >+ output.trim(true); >+ } >+ >+ output.space_before_token = true; >+ print_token(); >+ print_newline(false, true); >+ } >+ >+ function handle_dot() { >+ if (start_of_statement()) { >+ // The conditional starts the statement if appropriate. >+ } >+ >+ if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) { >+ output.space_before_token = true; >+ } else { >+ // allow preserved newlines before dots in general >+ // force newlines on dots after close paren when break_chained - for bar().baz() >+ allow_wrap_or_preserved_newline(flags.last_text === ')' && opt.break_chained_methods); >+ } >+ >+ print_token(); >+ } >+ >+ function handle_unknown() { >+ print_token(); >+ >+ if (current_token.text[current_token.text.length - 1] === '\n') { >+ print_newline(); >+ } >+ } >+ >+ function handle_eof() { >+ // Unwind any open statements >+ while (flags.mode === MODE.Statement) { >+ restore_mode(); >+ } >+ } >+ } >+ >+ >+ function OutputLine(parent) { >+ var _character_count = 0; >+ // use indent_count as a marker for lines that have preserved indentation >+ var _indent_count = -1; >+ >+ var _items = []; >+ var _empty = true; >+ >+ this.set_indent = function(level) { >+ _character_count = parent.baseIndentLength + level * parent.indent_length; >+ _indent_count = level; >+ }; >+ >+ this.get_character_count = function() { >+ return _character_count; >+ }; >+ >+ this.is_empty = function() { >+ return _empty; >+ }; >+ >+ this.last = function() { >+ if (!this._empty) { >+ return _items[_items.length - 1]; >+ } else { >+ return null; >+ } >+ }; >+ >+ this.push = function(input) { >+ _items.push(input); >+ _character_count += input.length; >+ _empty = false; >+ }; >+ >+ this.pop = function() { >+ var item = null; >+ if (!_empty) { >+ item = _items.pop(); >+ _character_count -= item.length; >+ _empty = _items.length === 0; >+ } >+ return item; >+ }; >+ >+ this.remove_indent = function() { >+ if (_indent_count > 0) { >+ _indent_count -= 1; >+ _character_count -= parent.indent_length; >+ } >+ }; >+ >+ this.trim = function() { >+ while (this.last() === ' ') { >+ _items.pop(); >+ _character_count -= 1; >+ } >+ _empty = _items.length === 0; >+ }; >+ >+ this.toString = function() { >+ var result = ''; >+ if (!this._empty) { >+ if (_indent_count >= 0) { >+ result = parent.indent_cache[_indent_count]; >+ } >+ result += _items.join(''); >+ } >+ return result; >+ }; >+ } >+ >+ function Output(indent_string, baseIndentString) { >+ baseIndentString = baseIndentString || ''; >+ this.indent_cache = [baseIndentString]; >+ this.baseIndentLength = baseIndentString.length; >+ this.indent_length = indent_string.length; >+ this.raw = false; >+ >+ var lines = []; >+ this.baseIndentString = baseIndentString; >+ this.indent_string = indent_string; >+ this.previous_line = null; >+ this.current_line = null; >+ this.space_before_token = false; >+ >+ this.add_outputline = function() { >+ this.previous_line = this.current_line; >+ this.current_line = new OutputLine(this); >+ lines.push(this.current_line); >+ }; >+ >+ // initialize >+ this.add_outputline(); >+ >+ >+ this.get_line_number = function() { >+ return lines.length; >+ }; >+ >+ // Using object instead of string to allow for later expansion of info about each line >+ this.add_new_line = function(force_newline) { >+ if (this.get_line_number() === 1 && this.just_added_newline()) { >+ return false; // no newline on start of file >+ } >+ >+ if (force_newline || !this.just_added_newline()) { >+ if (!this.raw) { >+ this.add_outputline(); >+ } >+ return true; >+ } >+ >+ return false; >+ }; >+ >+ this.get_code = function() { >+ var sweet_code = lines.join('\n').replace(/[\r\n\t ]+$/, ''); >+ return sweet_code; >+ }; >+ >+ this.set_indent = function(level) { >+ // Never indent your first output indent at the start of the file >+ if (lines.length > 1) { >+ while (level >= this.indent_cache.length) { >+ this.indent_cache.push(this.indent_cache[this.indent_cache.length - 1] + this.indent_string); >+ } >+ >+ this.current_line.set_indent(level); >+ return true; >+ } >+ this.current_line.set_indent(0); >+ return false; >+ }; >+ >+ this.add_raw_token = function(token) { >+ for (var x = 0; x < token.newlines; x++) { >+ this.add_outputline(); >+ } >+ this.current_line.push(token.whitespace_before); >+ this.current_line.push(token.text); >+ this.space_before_token = false; >+ }; >+ >+ this.add_token = function(printable_token) { >+ this.add_space_before_token(); >+ this.current_line.push(printable_token); >+ }; >+ >+ this.add_space_before_token = function() { >+ if (this.space_before_token && !this.just_added_newline()) { >+ this.current_line.push(' '); >+ } >+ this.space_before_token = false; >+ }; >+ >+ this.remove_redundant_indentation = function(frame) { >+ // This implementation is effective but has some issues: >+ // - can cause line wrap to happen too soon due to indent removal >+ // after wrap points are calculated >+ // These issues are minor compared to ugly indentation. >+ >+ if (frame.multiline_frame || >+ frame.mode === MODE.ForInitializer || >+ frame.mode === MODE.Conditional) { >+ return; >+ } >+ >+ // remove one indent from each line inside this section >+ var index = frame.start_line_index; >+ >+ var output_length = lines.length; >+ while (index < output_length) { >+ lines[index].remove_indent(); >+ index++; >+ } >+ }; >+ >+ this.trim = function(eat_newlines) { >+ eat_newlines = (eat_newlines === undefined) ? false : eat_newlines; >+ >+ this.current_line.trim(indent_string, baseIndentString); >+ >+ while (eat_newlines && lines.length > 1 && >+ this.current_line.is_empty()) { >+ lines.pop(); >+ this.current_line = lines[lines.length - 1]; >+ this.current_line.trim(); >+ } >+ >+ this.previous_line = lines.length > 1 ? lines[lines.length - 2] : null; >+ }; >+ >+ this.just_added_newline = function() { >+ return this.current_line.is_empty(); >+ }; >+ >+ this.just_added_blankline = function() { >+ if (this.just_added_newline()) { >+ if (lines.length === 1) { >+ return true; // start of the file and newline = blank >+ } >+ >+ var line = lines[lines.length - 2]; >+ return line.is_empty(); >+ } >+ return false; >+ }; >+ } >+ >+ >+ var Token = function(type, text, newlines, whitespace_before, parent) { >+ this.type = type; >+ this.text = text; >+ this.comments_before = []; >+ this.newlines = newlines || 0; >+ this.wanted_newline = newlines > 0; >+ this.whitespace_before = whitespace_before || ''; >+ this.parent = parent || null; >+ this.opened = null; >+ this.directives = null; >+ }; >+ >+ function tokenizer(input, opts) { >+ >+ var whitespace = "\n\r\t ".split(''); >+ var digit = /[0-9]/; >+ var digit_bin = /[01]/; >+ var digit_oct = /[01234567]/; >+ var digit_hex = /[0123456789abcdefABCDEF]/; >+ >+ this.positionable_operators = '!= !== % & && * ** + - / : < << <= == === > >= >> >>> ? ^ | ||'.split(' '); >+ var punct = this.positionable_operators.concat( >+ // non-positionable operators - these do not follow operator position settings >+ '! %= &= *= **= ++ += , -- -= /= :: <<= = => >>= >>>= ^= |= ~'.split(' ')); >+ >+ // words which should always start on new line. >+ this.line_starters = 'continue,try,throw,return,var,let,const,if,switch,case,default,for,while,break,function,import,export'.split(','); >+ var reserved_words = this.line_starters.concat(['do', 'in', 'else', 'get', 'set', 'new', 'catch', 'finally', 'typeof', 'yield', 'async', 'await', 'from', 'as']); >+ >+ // /* ... */ comment ends with nearest */ or end of file >+ var block_comment_pattern = /([\s\S]*?)((?:\*\/)|$)/g; >+ >+ // comment ends just before nearest linefeed or end of file >+ var comment_pattern = /([^\n\r\u2028\u2029]*)/g; >+ >+ var directives_block_pattern = /\/\* beautify( \w+[:]\w+)+ \*\//g; >+ var directive_pattern = / (\w+)[:](\w+)/g; >+ var directives_end_ignore_pattern = /([\s\S]*?)((?:\/\*\sbeautify\signore:end\s\*\/)|$)/g; >+ >+ var template_pattern = /((<\?php|<\?=)[\s\S]*?\?>)|(<%[\s\S]*?%>)/g; >+ >+ var n_newlines, whitespace_before_token, in_html_comment, tokens, parser_pos; >+ var input_length; >+ >+ this.tokenize = function() { >+ // cache the source's length. >+ input_length = input.length; >+ parser_pos = 0; >+ in_html_comment = false; >+ tokens = []; >+ >+ var next, last; >+ var token_values; >+ var open = null; >+ var open_stack = []; >+ var comments = []; >+ >+ while (!(last && last.type === 'TK_EOF')) { >+ token_values = tokenize_next(); >+ next = new Token(token_values[1], token_values[0], n_newlines, whitespace_before_token); >+ while (next.type === 'TK_COMMENT' || next.type === 'TK_BLOCK_COMMENT' || next.type === 'TK_UNKNOWN') { >+ if (next.type === 'TK_BLOCK_COMMENT') { >+ next.directives = token_values[2]; >+ } >+ comments.push(next); >+ token_values = tokenize_next(); >+ next = new Token(token_values[1], token_values[0], n_newlines, whitespace_before_token); >+ } >+ >+ if (comments.length) { >+ next.comments_before = comments; >+ comments = []; >+ } >+ >+ if (next.type === 'TK_START_BLOCK' || next.type === 'TK_START_EXPR') { >+ next.parent = last; >+ open_stack.push(open); >+ open = next; >+ } else if ((next.type === 'TK_END_BLOCK' || next.type === 'TK_END_EXPR') && >+ (open && ( >+ (next.text === ']' && open.text === '[') || >+ (next.text === ')' && open.text === '(') || >+ (next.text === '}' && open.text === '{')))) { >+ next.parent = open.parent; >+ next.opened = open; >+ >+ open = open_stack.pop(); >+ } >+ >+ tokens.push(next); >+ last = next; >+ } >+ >+ return tokens; >+ }; >+ >+ function get_directives(text) { >+ if (!text.match(directives_block_pattern)) { >+ return null; >+ } >+ >+ var directives = {}; >+ directive_pattern.lastIndex = 0; >+ var directive_match = directive_pattern.exec(text); >+ >+ while (directive_match) { >+ directives[directive_match[1]] = directive_match[2]; >+ directive_match = directive_pattern.exec(text); >+ } >+ >+ return directives; >+ } >+ >+ function tokenize_next() { >+ var resulting_string; >+ var whitespace_on_this_line = []; >+ >+ n_newlines = 0; >+ whitespace_before_token = ''; >+ >+ if (parser_pos >= input_length) { >+ return ['', 'TK_EOF']; >+ } >+ >+ var last_token; >+ if (tokens.length) { >+ last_token = tokens[tokens.length - 1]; >+ } else { >+ // For the sake of tokenizing we can pretend that there was on open brace to start >+ last_token = new Token('TK_START_BLOCK', '{'); >+ } >+ >+ >+ var c = input.charAt(parser_pos); >+ parser_pos += 1; >+ >+ while (in_array(c, whitespace)) { >+ >+ if (acorn.isNewLine(c)) { >+ if (!(c === '\n' && input.charAt(parser_pos - 2) === '\r')) { >+ n_newlines += 1; >+ whitespace_on_this_line = []; >+ } >+ } else { >+ whitespace_on_this_line.push(c); >+ } >+ >+ if (parser_pos >= input_length) { >+ return ['', 'TK_EOF']; >+ } >+ >+ c = input.charAt(parser_pos); >+ parser_pos += 1; >+ } >+ >+ if (whitespace_on_this_line.length) { >+ whitespace_before_token = whitespace_on_this_line.join(''); >+ } >+ >+ if (digit.test(c) || (c === '.' && digit.test(input.charAt(parser_pos)))) { >+ var allow_decimal = true; >+ var allow_e = true; >+ var local_digit = digit; >+ >+ if (c === '0' && parser_pos < input_length && /[XxOoBb]/.test(input.charAt(parser_pos))) { >+ // switch to hex/oct/bin number, no decimal or e, just hex/oct/bin digits >+ allow_decimal = false; >+ allow_e = false; >+ if (/[Bb]/.test(input.charAt(parser_pos))) { >+ local_digit = digit_bin; >+ } else if (/[Oo]/.test(input.charAt(parser_pos))) { >+ local_digit = digit_oct; >+ } else { >+ local_digit = digit_hex; >+ } >+ c += input.charAt(parser_pos); >+ parser_pos += 1; >+ } else if (c === '.') { >+ // Already have a decimal for this literal, don't allow another >+ allow_decimal = false; >+ } else { >+ // we know this first loop will run. It keeps the logic simpler. >+ c = ''; >+ parser_pos -= 1; >+ } >+ >+ // Add the digits >+ while (parser_pos < input_length && local_digit.test(input.charAt(parser_pos))) { >+ c += input.charAt(parser_pos); >+ parser_pos += 1; >+ >+ if (allow_decimal && parser_pos < input_length && input.charAt(parser_pos) === '.') { >+ c += input.charAt(parser_pos); >+ parser_pos += 1; >+ allow_decimal = false; >+ } else if (allow_e && parser_pos < input_length && /[Ee]/.test(input.charAt(parser_pos))) { >+ c += input.charAt(parser_pos); >+ parser_pos += 1; >+ >+ if (parser_pos < input_length && /[+-]/.test(input.charAt(parser_pos))) { >+ c += input.charAt(parser_pos); >+ parser_pos += 1; >+ } >+ >+ allow_e = false; >+ allow_decimal = false; >+ } >+ } >+ >+ return [c, 'TK_WORD']; >+ } >+ >+ if (acorn.isIdentifierStart(input.charCodeAt(parser_pos - 1))) { >+ if (parser_pos < input_length) { >+ while (acorn.isIdentifierChar(input.charCodeAt(parser_pos))) { >+ c += input.charAt(parser_pos); >+ parser_pos += 1; >+ if (parser_pos === input_length) { >+ break; >+ } >+ } >+ } >+ >+ if (!(last_token.type === 'TK_DOT' || >+ (last_token.type === 'TK_RESERVED' && in_array(last_token.text, ['set', 'get']))) && >+ in_array(c, reserved_words)) { >+ if (c === 'in') { // hack for 'in' operator >+ return [c, 'TK_OPERATOR']; >+ } >+ return [c, 'TK_RESERVED']; >+ } >+ >+ return [c, 'TK_WORD']; >+ } >+ >+ if (c === '(' || c === '[') { >+ return [c, 'TK_START_EXPR']; >+ } >+ >+ if (c === ')' || c === ']') { >+ return [c, 'TK_END_EXPR']; >+ } >+ >+ if (c === '{') { >+ return [c, 'TK_START_BLOCK']; >+ } >+ >+ if (c === '}') { >+ return [c, 'TK_END_BLOCK']; >+ } >+ >+ if (c === ';') { >+ return [c, 'TK_SEMICOLON']; >+ } >+ >+ if (c === '/') { >+ var comment = ''; >+ var comment_match; >+ // peek for comment /* ... */ >+ if (input.charAt(parser_pos) === '*') { >+ parser_pos += 1; >+ block_comment_pattern.lastIndex = parser_pos; >+ comment_match = block_comment_pattern.exec(input); >+ comment = '/*' + comment_match[0]; >+ parser_pos += comment_match[0].length; >+ var directives = get_directives(comment); >+ if (directives && directives.ignore === 'start') { >+ directives_end_ignore_pattern.lastIndex = parser_pos; >+ comment_match = directives_end_ignore_pattern.exec(input); >+ comment += comment_match[0]; >+ parser_pos += comment_match[0].length; >+ } >+ comment = comment.replace(acorn.allLineBreaks, '\n'); >+ return [comment, 'TK_BLOCK_COMMENT', directives]; >+ } >+ // peek for comment // ... >+ if (input.charAt(parser_pos) === '/') { >+ parser_pos += 1; >+ comment_pattern.lastIndex = parser_pos; >+ comment_match = comment_pattern.exec(input); >+ comment = '//' + comment_match[0]; >+ parser_pos += comment_match[0].length; >+ return [comment, 'TK_COMMENT']; >+ } >+ >+ } >+ >+ var startXmlRegExp = /^<([-a-zA-Z:0-9_.]+|{.+?}|!\[CDATA\[[\s\S]*?\]\])(\s+{.+?}|\s+[-a-zA-Z:0-9_.]+|\s+[-a-zA-Z:0-9_.]+\s*=\s*('[^']*'|"[^"]*"|{.+?}))*\s*(\/?)\s*>/; >+ >+ if (c === '`' || c === "'" || c === '"' || // string >+ ( >+ (c === '/') || // regexp >+ (opts.e4x && c === "<" && input.slice(parser_pos - 1).match(startXmlRegExp)) // xml >+ ) && ( // regex and xml can only appear in specific locations during parsing >+ (last_token.type === 'TK_RESERVED' && in_array(last_token.text, ['return', 'case', 'throw', 'else', 'do', 'typeof', 'yield'])) || >+ (last_token.type === 'TK_END_EXPR' && last_token.text === ')' && >+ last_token.parent && last_token.parent.type === 'TK_RESERVED' && in_array(last_token.parent.text, ['if', 'while', 'for'])) || >+ (in_array(last_token.type, ['TK_COMMENT', 'TK_START_EXPR', 'TK_START_BLOCK', >+ 'TK_END_BLOCK', 'TK_OPERATOR', 'TK_EQUALS', 'TK_EOF', 'TK_SEMICOLON', 'TK_COMMA' >+ ])) >+ )) { >+ >+ var sep = c, >+ esc = false, >+ has_char_escapes = false; >+ >+ resulting_string = c; >+ >+ if (sep === '/') { >+ // >+ // handle regexp >+ // >+ var in_char_class = false; >+ while (parser_pos < input_length && >+ ((esc || in_char_class || input.charAt(parser_pos) !== sep) && >+ !acorn.isNewLine(input.charAt(parser_pos)))) { >+ resulting_string += input.charAt(parser_pos); >+ if (!esc) { >+ esc = input.charAt(parser_pos) === '\\'; >+ if (input.charAt(parser_pos) === '[') { >+ in_char_class = true; >+ } else if (input.charAt(parser_pos) === ']') { >+ in_char_class = false; >+ } >+ } else { >+ esc = false; >+ } >+ parser_pos += 1; >+ } >+ } else if (opts.e4x && sep === '<') { >+ // >+ // handle e4x xml literals >+ // >+ >+ var xmlRegExp = /<(\/?)([-a-zA-Z:0-9_.]+|{.+?}|!\[CDATA\[[\s\S]*?\]\])(\s+{.+?}|\s+[-a-zA-Z:0-9_.]+|\s+[-a-zA-Z:0-9_.]+\s*=\s*('[^']*'|"[^"]*"|{.+?}))*\s*(\/?)\s*>/g; >+ var xmlStr = input.slice(parser_pos - 1); >+ var match = xmlRegExp.exec(xmlStr); >+ if (match && match.index === 0) { >+ var rootTag = match[2]; >+ var depth = 0; >+ while (match) { >+ var isEndTag = !!match[1]; >+ var tagName = match[2]; >+ var isSingletonTag = (!!match[match.length - 1]) || (tagName.slice(0, 8) === "![CDATA["); >+ if (tagName === rootTag && !isSingletonTag) { >+ if (isEndTag) { >+ --depth; >+ } else { >+ ++depth; >+ } >+ } >+ if (depth <= 0) { >+ break; >+ } >+ match = xmlRegExp.exec(xmlStr); >+ } >+ var xmlLength = match ? match.index + match[0].length : xmlStr.length; >+ xmlStr = xmlStr.slice(0, xmlLength); >+ parser_pos += xmlLength - 1; >+ xmlStr = xmlStr.replace(acorn.allLineBreaks, '\n'); >+ return [xmlStr, "TK_STRING"]; >+ } >+ } else { >+ // >+ // handle string >+ // >+ var parse_string = function(delimiter, allow_unescaped_newlines, start_sub) { >+ // Template strings can travers lines without escape characters. >+ // Other strings cannot >+ var current_char; >+ while (parser_pos < input_length) { >+ current_char = input.charAt(parser_pos); >+ if (!(esc || (current_char !== delimiter && >+ (allow_unescaped_newlines || !acorn.isNewLine(current_char))))) { >+ break; >+ } >+ >+ // Handle \r\n linebreaks after escapes or in template strings >+ if ((esc || allow_unescaped_newlines) && acorn.isNewLine(current_char)) { >+ if (current_char === '\r' && input.charAt(parser_pos + 1) === '\n') { >+ parser_pos += 1; >+ current_char = input.charAt(parser_pos); >+ } >+ resulting_string += '\n'; >+ } else { >+ resulting_string += current_char; >+ } >+ if (esc) { >+ if (current_char === 'x' || current_char === 'u') { >+ has_char_escapes = true; >+ } >+ esc = false; >+ } else { >+ esc = current_char === '\\'; >+ } >+ >+ parser_pos += 1; >+ >+ if (start_sub && resulting_string.indexOf(start_sub, resulting_string.length - start_sub.length) !== -1) { >+ if (delimiter === '`') { >+ parse_string('}', allow_unescaped_newlines, '`'); >+ } else { >+ parse_string('`', allow_unescaped_newlines, '${'); >+ } >+ } >+ } >+ }; >+ >+ if (sep === '`') { >+ parse_string('`', true, '${'); >+ } else { >+ parse_string(sep); >+ } >+ } >+ >+ if (has_char_escapes && opts.unescape_strings) { >+ resulting_string = unescape_string(resulting_string); >+ } >+ >+ if (parser_pos < input_length && input.charAt(parser_pos) === sep) { >+ resulting_string += sep; >+ parser_pos += 1; >+ >+ if (sep === '/') { >+ // regexps may have modifiers /regexp/MOD , so fetch those, too >+ // Only [gim] are valid, but if the user puts in garbage, do what we can to take it. >+ while (parser_pos < input_length && acorn.isIdentifierStart(input.charCodeAt(parser_pos))) { >+ resulting_string += input.charAt(parser_pos); >+ parser_pos += 1; >+ } >+ } >+ } >+ return [resulting_string, 'TK_STRING']; >+ } >+ >+ if (c === '#') { >+ >+ if (tokens.length === 0 && input.charAt(parser_pos) === '!') { >+ // shebang >+ resulting_string = c; >+ while (parser_pos < input_length && c !== '\n') { >+ c = input.charAt(parser_pos); >+ resulting_string += c; >+ parser_pos += 1; >+ } >+ return [trim(resulting_string) + '\n', 'TK_UNKNOWN']; >+ } >+ >+ >+ >+ // Spidermonkey-specific sharp variables for circular references >+ // https://developer.mozilla.org/En/Sharp_variables_in_JavaScript >+ // http://mxr.mozilla.org/mozilla-central/source/js/src/jsscan.cpp around line 1935 >+ var sharp = '#'; >+ if (parser_pos < input_length && digit.test(input.charAt(parser_pos))) { >+ do { >+ c = input.charAt(parser_pos); >+ sharp += c; >+ parser_pos += 1; >+ } while (parser_pos < input_length && c !== '#' && c !== '='); >+ if (c === '#') { >+ // >+ } else if (input.charAt(parser_pos) === '[' && input.charAt(parser_pos + 1) === ']') { >+ sharp += '[]'; >+ parser_pos += 2; >+ } else if (input.charAt(parser_pos) === '{' && input.charAt(parser_pos + 1) === '}') { >+ sharp += '{}'; >+ parser_pos += 2; >+ } >+ return [sharp, 'TK_WORD']; >+ } >+ } >+ >+ if (c === '<' && (input.charAt(parser_pos) === '?' || input.charAt(parser_pos) === '%')) { >+ template_pattern.lastIndex = parser_pos - 1; >+ var template_match = template_pattern.exec(input); >+ if (template_match) { >+ c = template_match[0]; >+ parser_pos += c.length - 1; >+ c = c.replace(acorn.allLineBreaks, '\n'); >+ return [c, 'TK_STRING']; >+ } >+ } >+ >+ if (c === '<' && input.substring(parser_pos - 1, parser_pos + 3) === '<!--') { >+ parser_pos += 3; >+ c = '<!--'; >+ while (!acorn.isNewLine(input.charAt(parser_pos)) && parser_pos < input_length) { >+ c += input.charAt(parser_pos); >+ parser_pos++; >+ } >+ in_html_comment = true; >+ return [c, 'TK_COMMENT']; >+ } >+ >+ if (c === '-' && in_html_comment && input.substring(parser_pos - 1, parser_pos + 2) === '-->') { >+ in_html_comment = false; >+ parser_pos += 2; >+ return ['-->', 'TK_COMMENT']; >+ } >+ >+ if (c === '.') { >+ return [c, 'TK_DOT']; >+ } >+ >+ if (in_array(c, punct)) { >+ while (parser_pos < input_length && in_array(c + input.charAt(parser_pos), punct)) { >+ c += input.charAt(parser_pos); >+ parser_pos += 1; >+ if (parser_pos >= input_length) { >+ break; >+ } >+ } >+ >+ if (c === ',') { >+ return [c, 'TK_COMMA']; >+ } else if (c === '=') { >+ return [c, 'TK_EQUALS']; >+ } else { >+ return [c, 'TK_OPERATOR']; >+ } >+ } >+ >+ return [c, 'TK_UNKNOWN']; >+ } >+ >+ >+ function unescape_string(s) { >+ var esc = false, >+ out = '', >+ pos = 0, >+ s_hex = '', >+ escaped = 0, >+ c; >+ >+ while (esc || pos < s.length) { >+ >+ c = s.charAt(pos); >+ pos++; >+ >+ if (esc) { >+ esc = false; >+ if (c === 'x') { >+ // simple hex-escape \x24 >+ s_hex = s.substr(pos, 2); >+ pos += 2; >+ } else if (c === 'u') { >+ // unicode-escape, \u2134 >+ s_hex = s.substr(pos, 4); >+ pos += 4; >+ } else { >+ // some common escape, e.g \n >+ out += '\\' + c; >+ continue; >+ } >+ if (!s_hex.match(/^[0123456789abcdefABCDEF]+$/)) { >+ // some weird escaping, bail out, >+ // leaving whole string intact >+ return s; >+ } >+ >+ escaped = parseInt(s_hex, 16); >+ >+ if (escaped >= 0x00 && escaped < 0x20) { >+ // leave 0x00...0x1f escaped >+ if (c === 'x') { >+ out += '\\x' + s_hex; >+ } else { >+ out += '\\u' + s_hex; >+ } >+ continue; >+ } else if (escaped === 0x22 || escaped === 0x27 || escaped === 0x5c) { >+ // single-quote, apostrophe, backslash - escape these >+ out += '\\' + String.fromCharCode(escaped); >+ } else if (c === 'x' && escaped > 0x7e && escaped <= 0xff) { >+ // we bail out on \x7f..\xff, >+ // leaving whole string escaped, >+ // as it's probably completely binary >+ return s; >+ } else { >+ out += String.fromCharCode(escaped); >+ } >+ } else if (c === '\\') { >+ esc = true; >+ } else { >+ out += c; >+ } >+ } >+ return out; >+ } >+ } >+ >+ var beautifier = new Beautifier(js_source_text, options); >+ return beautifier.beautify(); >+ >+ } >+ >+ return { >+ js_beautify: js_beautify >+ }; >+}); >diff --git a/bundles/org.eclipse.orion.client.javascript/web/javascript/nls/root/messages.js b/bundles/org.eclipse.orion.client.javascript/web/javascript/nls/root/messages.js >index fe016b5..54fd7ff 100644 >--- a/bundles/org.eclipse.orion.client.javascript/web/javascript/nls/root/messages.js >+++ b/bundles/org.eclipse.orion.client.javascript/web/javascript/nls/root/messages.js >@@ -281,7 +281,69 @@ define({ > 'astPluginDescription': 'Provides AST (acorn) for Tern', > 'templatesPlugin': 'Orion code templates', > 'templatesPluginDescription': 'Provides a variety of code templates for JavaScript in Orion.', >+ 'formatCommandName' : 'Formatting', >+ 'formatting' : 'Formatting', >+ 'beautifierPluginName' : 'JSBeautify plugin for Tern', >+ 'beautifierPluginDescription' : 'Provides formatting for Tern', > >+ // Common formatting options >+ 'commonFormattingOptions' : 'Common Formatting Options', >+ 'indent_size' : 'Indention size:', >+ 'indent_char' : 'Indentation character:', >+ 'eol' : 'Charater(s) to use as line terminators:', >+ 'end_with_newline' : 'End ouput with newline:', >+ 'indentation_unix' : 'Unix', >+ 'indentation_mac' : 'Mac', >+ 'indentation_windows' : 'Windows', >+ 'identation_space' : 'space', >+ 'indentation_tab' : 'tab', >+ >+ // JS formatting options >+ 'jsFormattingOptions' : 'Formatting Options for Javascript', >+ 'indent_level': 'Initial indentation level:', >+ 'before_newline' : 'Before new line', >+ 'after_newline' : 'After new line', >+ 'preserve_newline' : 'Preserve new line', >+ 'collapse_preserve_inline' : 'Collapse Preserve inline', >+ 'collapse' : 'Collapse', >+ 'expand' : 'Expand', >+ 'end_expand' : 'End expand', >+ 'none' : 'None', >+ 'preserve_new_lines' : 'Preserve line-breaks:', >+ 'max_preserve_new_lines' : 'Number of line-breaks to be preserved in one chunk:', >+ 'space_in_paren' : 'Add padding space within paren:', >+ 'space_in_empty_paren' : 'Add padding space in empty paren:', >+ 'space_after_anon_function' : "Add a space before an anonymous function's parens", >+ 'brace_style' : 'Brace Style:', >+ 'break_chained_methods' : 'Break chained method calls across subsequent lines:', >+ 'keep_array_indentation' : 'Preserve array indentation:', >+ 'space_before_conditional' : 'Space before condition:', >+ 'unescape_strings' : 'Decode printable characters encoded in xNN notation:', >+ 'wrap_line_length' : 'Wrap lines at next opportunity after N characters: (0 for unlimited)', >+ 'e4x' : 'Pass E4X xml literal through untouched:', >+ 'comma_first' : 'Put commas at the beginning of new line instead of end:', >+ 'operator_position' : 'Position for operator:', >+ >+ // CSS formatting options >+ 'cssFormattingOptions' : 'Formatting Options for CSS', >+ 'selector_separator_newline' : 'Add a newline between multiple selectors:', >+ 'newline_between_rules' : 'Add a newline between CSS rules:', >+ 'space_around_selector_separator' : 'Ensure space around selector separators:', >+ >+ // HTML formatting options >+ 'htmlFormattingOptions' : 'Formatting Options for HTML', >+ 'indent_inner_html' : 'Indent <head> and <body> sections:', >+ 'indent_handlebars' : 'Format and indent {{#foo}} and {{/foo}}:', >+ 'wrap_attributes' : 'Wrap attributes to new lines:', >+ 'wrap_attributes_indent_size' : 'Indent wrapped attributes to after N characters:', >+ 'extra_liners' : 'List of tags that should have an extra newline before them (separate with commas):', >+ 'normal' : 'normal', >+ 'keep' : 'keep', >+ 'separate' : 'separate', >+ 'indent_scripts' : 'Indent scripts:', >+ 'auto' : 'auto', >+ 'force' : 'force', >+ > // Other messages > 'unknownError': 'An unknown error occurred.', > 'failedDeleteRequest': 'Failed to delete file from Tern: ${0}', >@@ -318,6 +380,8 @@ define({ > 'serverNotStarted': 'The server has not been started. Request: \'${0}\'', > 'failedToComputeProblems': 'Failed to compute ESLint problems/markers', > 'failedToComputeOutline': 'Failed to compute outline', >+ 'failedToFormat' : 'Failed to format', >+ 'failedToFormatNoServer' : 'Failed to format, server not started', > > //Templates > 'eslintRuleEnableDisable': 'Enable or disable ESLint rule using the ```ruleid:0/1/2``` form.\n\nExample use:\n\n>```/* eslint semi:1, no-console:0, no-redeclare:2 */```', >diff --git a/bundles/org.eclipse.orion.client.javascript/web/javascript/plugins/ternDefaults.js b/bundles/org.eclipse.orion.client.javascript/web/javascript/plugins/ternDefaults.js >index 1e3caa2..208f411 100644 >--- a/bundles/org.eclipse.orion.client.javascript/web/javascript/plugins/ternDefaults.js >+++ b/bundles/org.eclipse.orion.client.javascript/web/javascript/plugins/ternDefaults.js >@@ -43,7 +43,8 @@ define([ > "javascript/ternPlugins/redis", > "javascript/ternPlugins/refs", > "javascript/ternPlugins/templates", >- "javascript/ternPlugins/quickfixes" >+ "javascript/ternPlugins/quickfixes", >+ "javascript/ternPlugins/beautifier" > ], function(Messages, ecma5, ecma6, browser, chai) { > > var defs = [ecma5, ecma6, browser, chai]; >@@ -111,7 +112,12 @@ define([ > "name": Messages["templatesPlugin"], > "description": Messages["templatesPluginDescription"], > "version": "1.0" >- } >+ }, >+ "beautifier": { >+ "name": Messages["beautifierPluginName"], >+ "description": Messages["beautifierPluginDescription"], >+ "version": "1.0" >+ }, > }, > optional: { > "amqp": { >diff --git a/bundles/org.eclipse.orion.client.ui/web/orion/settings/nls/root/messages.js b/bundles/org.eclipse.orion.client.ui/web/orion/settings/nls/root/messages.js >index fb7f7b1..bcd23bc 100644 >--- a/bundles/org.eclipse.orion.client.ui/web/orion/settings/nls/root/messages.js >+++ b/bundles/org.eclipse.orion.client.ui/web/orion/settings/nls/root/messages.js >@@ -1,6 +1,6 @@ > /******************************************************************************* > * @license >- * Copyright (c) 2012 IBM Corporation and others. >+ * Copyright (c) 2012, 2016 IBM Corporation and others. > * All rights reserved. This program and the accompanying materials are made > * available under the terms of the Eclipse Public License v1.0 > * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution >@@ -283,5 +283,5 @@ define({//Default message bundle > "editorTheme selection background": "Selection background", > 'customizeTheme': 'Custom Style...', > 'moreEditorSettings': 'Editor Settings...', >- 'JavascriptSettingWarning' : '${0} Warning: Global settings for ${2} are overriden by the settings defined in: ${1}' >+ 'SettingWarning' : '${0} Warning: Global settings for \'${2}\' are overriden by the settings defined in: ${1}' > }); >diff --git a/bundles/org.eclipse.orion.client.ui/web/orion/settings/ui/PluginSettings.js b/bundles/org.eclipse.orion.client.ui/web/orion/settings/ui/PluginSettings.js >index 53a58ad..1f89c48 100644 >--- a/bundles/org.eclipse.orion.client.ui/web/orion/settings/ui/PluginSettings.js >+++ b/bundles/org.eclipse.orion.client.ui/web/orion/settings/ui/PluginSettings.js >@@ -449,6 +449,16 @@ define([ > }.bind(this)); > }.bind(this)); > } >+ >+ function getFormattingSettings(fileClient, projectPath, callback) { >+ var jsbeautifyrc = projectPath + SettingsList.prototype.JSBEAUTIFYRC; >+ return fileClient.read(jsbeautifyrc, false, false, {readIfExists: true}).then(function(contents) { >+ if (contents !== null) { >+ callback({contents: contents, name: SettingsList.prototype.JSBEAUTIFYRC, location: jsbeautifyrc}); >+ return; >+ } >+ }.bind(this)); >+ } > > SettingsList.prototype = { > /** >@@ -480,6 +490,11 @@ define([ > * The package.json file name > */ > PACKAGE_JSON : 'package.json', >+ /** >+ * The .jsbeautifyrc file name >+ * @see https://github.com/beautify-web/js-beautify/blob/master/README.md >+ */ >+ JSBEAUTIFYRC : '.jsbeautifyrc', > > _makeSection: function(parent, sectionId, title, hasMultipleSections) { > var that = this; >@@ -537,7 +552,7 @@ define([ > if (!this.destroyed) { > var infoText = document.createElement("div"); //$NON-NLS-0$ > infoText.classList.add("setting-info"); //$NON-NLS-0$ >- infoText.textContent = messages.JavascriptSettingWarning; >+ infoText.textContent = messages.SettingWarning; > var icon = document.createElement("span"); //$NON-NLS-0$ > icon.classList.add("core-sprite-warning"); //$NON-NLS-0$ > icon.classList.add("icon-inline"); //$NON-NLS-0$ >@@ -559,7 +574,39 @@ define([ > } > break; > } >- } >+ case "formatting" : { >+ if (pageParams.resource && pageParams.resource.length !== 0) { >+ // resource name starts with /file/ and ends with '/' >+ projectName = pageParams.resource.substring(6, pageParams.resource.length - 1); >+ getFormattingSettings( >+ this.fileClient, >+ pageParams.resource, >+ function(file) { >+ if (!this.destroyed) { >+ var infoText = document.createElement("div"); //$NON-NLS-0$ >+ infoText.classList.add("setting-info"); //$NON-NLS-0$ >+ infoText.textContent = messages.SettingWarning; >+ var icon = document.createElement("span"); //$NON-NLS-0$ >+ icon.classList.add("core-sprite-warning"); //$NON-NLS-0$ >+ icon.classList.add("icon-inline"); //$NON-NLS-0$ >+ icon.classList.add("imageSprite"); //$NON-NLS-0$ >+ var link = document.createElement("a"); //$NON-NLS-0$ >+ link.href = editTemplate.expand({resource: file.location}); >+ link.appendChild(document.createTextNode(file.name)); >+ var projectText = document.createElement("span"); //$NON-NLS-0$ >+ projectText.textContent = projectName; >+ lib.processDOMNodes(infoText, [icon, link, projectText]); >+ if (parent.firstChild) { >+ parent.insertBefore(infoText, parent.firstChild); >+ } else { >+ parent.appendChild(infoText); >+ } >+ return true; >+ } >+ }.bind(this)); >+ } >+ } >+ } > for (var i=0; i<settings.length; i++) { > var setting = settings[i]; > var sectionId = 'settings.section.'; //$NON-NLS-1$ >diff --git a/bundles/org.eclipse.orion.client.javascript/web/javascript/javascriptProject.js b/bundles/org.eclipse.orion.client.javascript/web/javascript/javascriptProject.js >index 163100f..44aa834 100644 >--- a/bundles/org.eclipse.orion.client.javascript/web/javascript/javascriptProject.js >+++ b/bundles/org.eclipse.orion.client.javascript/web/javascript/javascriptProject.js >@@ -20,36 +20,40 @@ define([ > fileName === project.ESLINTRC_JSON || fileName === project.PACKAGE_JSON) { > delete project.map.eslint; > } >+ if (fileName === project.JSBEAUTIFYRC) { >+ delete project.map.formatting; >+ } > }, > /** > * @callback > */ > onModified: function onModified(project, qualifiedName, fileName) { >- this._update(project, fileName); >+ this._update(project, fileName); > }, > /** > * @callback > */ > onDeleted: function onDeleted(project, qualifiedName, fileName) { >- this._update(project, fileName); >+ this._update(project, fileName); > }, > /** > * @callback > */ > onCreated: function onCreated(project, qualifiedName, fileName) { >- this._update(project, fileName); >+ this._update(project, fileName); > }, > /** > * @callback > */ > onMoved: function onMoved(project, qualifiedName, fileName, toQualified, toName) { >- this._update(project, fileName); >+ this._update(project, fileName); > }, > /** > * @callback > */ > onProjectChanged: function onProjectChanged(project, evnt, projectName) { > delete project.map.eslint; >+ delete project.map.formatting; > } > }; > >@@ -114,7 +118,12 @@ define([ > * The node_modules folder name > */ > JavaScriptProject.prototype.NODE_MODULES = 'node_modules'; >- >+ /** >+ * The .jsbeautifyrc file name >+ * @see https://github.com/beautify-web/js-beautify/blob/master/README.md >+ */ >+ JavaScriptProject.prototype.JSBEAUTIFYRC = '.jsbeautifyrc'; >+ > /** > * @description Adds a handler for the given file name to the mapping of handlers > * @function >@@ -154,7 +163,7 @@ define([ > this.ecma = v.ecmaVersion; > } > } catch(err) { >- this.ecma = 6; >+ this.ecma = 6; > } > } > return this.ecma; >@@ -269,17 +278,17 @@ define([ > //TODO support loading YML and YAML files > var vals; > return this.getFile(this.ESLINTRC_JS).then(function(file) { >- vals = readAndMap(this.map, file); >+ vals = readAndMap(this.map, file, "eslint"); > if(vals) { > return vals; > } > return this.getFile(this.ESLINTRC_JSON).then(function(file) { >- vals = readAndMap(this.map, file); >+ vals = readAndMap(this.map, file, "eslint"); > if(vals) { > return vals; > } > return this.getFile(this.ESLINTRC).then(function(file) { >- vals = readAndMap(this.map, file); >+ vals = readAndMap(this.map, file, "eslint"); > if(vals) { > return vals; > } >@@ -298,13 +307,29 @@ define([ > }.bind(this)); > }; > >- function readAndMap(map, file) { >+ /** >+ * @name JavaScriptProject.prototype.getFormattingOptions >+ * @description Returns project-specific formatting options (if any) >+ * @function >+ * @returns {Deferred} A deferred that will resolve to the project-specific formatting options or null >+ * @see https://github.com/beautify-web/js-beautify >+ */ >+ JavaScriptProject.prototype.getFormattingOptions = function getESlintOptions() { >+ if(this.map.formatting) { >+ return new Deferred().resolve(this.map.formatting); >+ } >+ return this.getFile(this.JSBEAUTIFYRC).then(function(file) { >+ return readAndMap(this.map, file, "formatting"); >+ }.bind(this)); >+ }; >+ >+ function readAndMap(map, file, key) { > if(file && file.contents) { > try { > var vals = JSON.parse(file.contents); > if(Object.keys(vals).length > 0) { >- map.eslint = vals; >- return map.eslint; >+ map[key]= vals; >+ return map[key]; > } > } catch(e) { > //fall through and return null >diff --git a/bundles/org.eclipse.orion.client.javascript/web/javascript/plugins/javascriptPlugin.js b/bundles/org.eclipse.orion.client.javascript/web/javascript/plugins/javascriptPlugin.js >index 9d596da..7c64f2f 100644 >--- a/bundles/org.eclipse.orion.client.javascript/web/javascript/plugins/javascriptPlugin.js >+++ b/bundles/org.eclipse.orion.client.javascript/web/javascript/plugins/javascriptPlugin.js >@@ -21,6 +21,7 @@ define([ > 'javascript/scriptResolver', > 'javascript/astManager', > 'javascript/quickFixes', >+'javascript/formatting', > 'javascript/javascriptProject', > 'javascript/contentAssist/ternAssist', > 'javascript/contentAssist/ternProjectAssist', >@@ -45,7 +46,7 @@ define([ > 'i18n!javascript/nls/messages', > 'orion/i18nUtil', > 'orion/URL-shim' >-], function(PluginProvider, mServiceRegistry, Deferred, ScriptResolver, ASTManager, QuickFixes, JavaScriptProject, TernAssist, TernProjectAssist, >+], function(PluginProvider, mServiceRegistry, Deferred, ScriptResolver, ASTManager, QuickFixes, Formatting, JavaScriptProject, TernAssist, TernProjectAssist, > EslintValidator, TernProjectValidator, Occurrences, Hover, Outliner, CUProvider, TernProjectManager, Util, Logger, GenerateDocCommand, OpenDeclCommand, OpenImplCommand, > RenameCommand, RefsCommand, mJS, mJSON, mJSONSchema, mEJS, javascriptMessages, i18nUtil) { > >@@ -69,7 +70,7 @@ define([ > }, {id: "application/json", //$NON-NLS-1$ > "extends": "text/plain", //$NON-NLS-1$ //$NON-NLS-1$ > name: "JSON", //$NON-NLS-1$ >- extension: ["json", "pref", "tern-project", "eslintrc"], //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ >+ extension: ["json", "pref", "tern-project", "eslintrc", "jsbeautifyrc"], //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ > imageClass: "file-sprite-javascript modelDecorationSprite" //$NON-NLS-1$ > }, {id: "application/x-ejs", //$NON-NLS-1$ > "extends": "text/plain", //$NON-NLS-1$ //$NON-NLS-1$ >@@ -680,7 +681,7 @@ define([ > ] > } > ); >- >+ > provider.registerServiceProvider("orion.edit.command", //$NON-NLS-1$ > quickFixComputer, > { >@@ -1496,7 +1497,7 @@ define([ > name: javascriptMessages['prefBestPractices'], > tags: "validation javascript js eslint".split(" "), //$NON-NLS-1$ //$NON-NLS-1$ > category: 'javascript', //$NON-NLS-1$ >- categoryLabel: javascriptMessages['javascriptValidation'], >+ categoryLabel: javascriptMessages['javascriptValidation'], > properties: [ > { > id: "no-eq-null", //$NON-NLS-1$ >@@ -1753,9 +1754,295 @@ define([ > defaultValue: warning, > options: severities > } >-] >+ ] > }] >- }); >+ }); >+ >+ var beautifier = new Formatting.JavaScriptFormatting(ternWorker, jsProject); >+ provider.registerServiceProvider("orion.edit.command", >+ beautifier, >+ { >+ name: javascriptMessages["formatCommandName"], >+ contentType: ["application/javascript", "text/html", "text/css"], >+ id: "format", >+ key : [ 'F', false, true, !Util.isMac, Util.isMac] //$NON-NLS-1$ Alt-Shift-F >+ } >+ ); >+ // register preferences for formatting when modified (updated call) >+ provider.registerService("orion.cm.managedservice", beautifier, {pid: 'jsbeautify.config.common'}); //$NON-NLS-1$ //$NON-NLS-2$ >+ provider.registerService("orion.cm.managedservice", beautifier, {pid: 'jsbeautify.config.js'}); //$NON-NLS-1$ //$NON-NLS-2$ >+ provider.registerService("orion.cm.managedservice", beautifier, {pid: 'jsbeautify.config.html'}); //$NON-NLS-1$ //$NON-NLS-2$ >+ provider.registerService("orion.cm.managedservice", beautifier, {pid: 'jsbeautify.config.css'}); //$NON-NLS-1$ //$NON-NLS-2$ >+ >+ var unix = "\n"; >+ var mac = "\r"; >+ var windows = "\n\r"; >+ var eof_characters = [ >+ {label: javascriptMessages.indentation_unix, value: unix}, >+ {label: javascriptMessages.indentation_mac, value: mac}, >+ {label: javascriptMessages.indentation_windows, value: windows} >+ ]; >+ >+ var space = ' '; >+ var tab= '\t'; >+ var indentation_characters = [ >+ {label: javascriptMessages.identation_space, value: space}, >+ {label: javascriptMessages.indentation_tab, value: tab}, >+ ]; >+ >+ var before_newline = 'before-newline'; >+ var after_newline = 'after-newline'; >+ var preserve_newline = 'preserve-newline'; >+ var operator_positions = [ >+ { label: javascriptMessages.before_newline, value: before_newline}, >+ { label: javascriptMessages.after_newline, value: after_newline}, >+ { label: javascriptMessages.preserve_newline, value: preserve_newline}, >+ ]; >+ >+ var collapse_preserve_inline = 'collapse-preserve-inline'; >+ var collapse = 'collapse'; >+ var expand = 'expand'; >+ var end_expand = 'end-expand'; >+ var none = 'none'; >+ var brace_styles = [ >+ { label: javascriptMessages.collapse_preserve_inline , value: collapse_preserve_inline}, >+ { label: javascriptMessages.collapse , value: collapse}, >+ { label: javascriptMessages.expand , value: expand}, >+ { label: javascriptMessages.end_expand , value: end_expand}, >+ { label: javascriptMessages.none , value: none}, >+ ]; >+ >+ var keep = 'keep'; >+ var separate = 'separate'; >+ var normal = 'normal'; >+ var indent_scripts_values = [ >+ { label: javascriptMessages.normal, value: normal}, >+ { label: javascriptMessages.keep, value: keep}, >+ { label: javascriptMessages.separate, value: separate}, >+ ]; >+ var auto = 'auto'; >+ var force = 'force'; >+ var wrap_attributes_values = [ >+ { label: javascriptMessages.auto, value: auto}, >+ { label: javascriptMessages.force, value: force}, >+ ]; >+ provider.registerServiceProvider("orion.core.setting", {}, { >+ settings: [ >+ { >+ pid: 'jsbeautify.config.common', >+ order: 1, >+ name: javascriptMessages['commonFormattingOptions'], >+ tags: 'beautify javascript js jsbeautify formatting'.split(' '), >+ category: 'formatting', >+ categoryLabel: javascriptMessages['formatting'], >+ properties: [ >+ { >+ id: 'indent_size', //$NON-NLS-1$ >+ name: javascriptMessages['indent_size'], >+ type: 'number', //$NON-NLS-1$ >+ defaultValue: 4 >+ }, >+ { >+ id: 'indent_char', //$NON-NLS-1$ >+ name: javascriptMessages['indent_char'], >+ type: 'string', //$NON-NLS-1$ >+ defaultValue: space, >+ options: indentation_characters >+ }, >+ { >+ id: 'eol', //$NON-NLS-1$ >+ name: javascriptMessages['eol'], >+ type: 'string', //$NON-NLS-1$ >+ defaultValue: unix, >+ options: eof_characters >+ }, >+ { >+ id: 'end_with_newline', //$NON-NLS-1$ >+ name: javascriptMessages['end_with_newline'], >+ type: 'boolean', //$NON-NLS-1$ >+ defaultValue: false >+ }, >+ { >+ id: 'preserve_new_lines', //$NON-NLS-1$ >+ name: javascriptMessages['preserve_new_lines'], >+ type: 'boolean', //$NON-NLS-1$ >+ defaultValue: true >+ }, >+ { >+ id: 'max_preserve_new_lines', //$NON-NLS-1$ >+ name: javascriptMessages['max_preserve_new_lines'], >+ type: 'number', //$NON-NLS-1$ >+ defaultValue: 10 >+ }, >+ { >+ id: 'brace_style', //$NON-NLS-1$ >+ name: javascriptMessages['brace_style'], >+ type: 'boolean', //$NON-NLS-1$ >+ defaultValue: collapse, >+ options: brace_styles >+ }, >+ { >+ id: 'wrap_line_length', //$NON-NLS-1$ >+ name: javascriptMessages['wrap_line_length'], >+ type: 'number', //$NON-NLS-1$ >+ defaultValue: 0 >+ }, >+ ] >+ }, >+ { >+ pid: 'jsbeautify.config.js', >+ order: 2, >+ name: javascriptMessages['jsFormattingOptions'], >+ tags: 'beautify javascript js jsbeautify formatting'.split(' '), >+ category: 'formatting', >+ categoryLabel: javascriptMessages['formatting'], >+ properties: [ >+ { >+ id: 'indent_level', //$NON-NLS-1$ >+ name: javascriptMessages['indent_level'], >+ type: 'number', //$NON-NLS-1$ >+ defaultValue: 0 >+ }, >+ { >+ id: 'space_in_paren', //$NON-NLS-1$ >+ name: javascriptMessages['space_in_paren'], >+ type: 'boolean', //$NON-NLS-1$ >+ defaultValue: false >+ }, >+ { >+ id: 'space_in_empty_paren', //$NON-NLS-1$ >+ name: javascriptMessages['space_in_empty_paren'], >+ type: 'boolean', //$NON-NLS-1$ >+ defaultValue: false >+ }, >+ { >+ id: 'space_after_anon_function', //$NON-NLS-1$ >+ name: javascriptMessages['space_after_anon_function'], >+ type: 'boolean', //$NON-NLS-1$ >+ defaultValue: false >+ }, >+ { >+ id: 'break_chained_methods', //$NON-NLS-1$ >+ name: javascriptMessages['break_chained_methods'], >+ type: 'boolean', //$NON-NLS-1$ >+ defaultValue: false >+ }, >+ { >+ id: 'keep_array_indentation', //$NON-NLS-1$ >+ name: javascriptMessages['keep_array_indentation'], >+ type: 'boolean', //$NON-NLS-1$ >+ defaultValue: false >+ }, >+ { >+ id: 'space_before_conditional', //$NON-NLS-1$ >+ name: javascriptMessages['space_before_conditional'], >+ type: 'boolean', //$NON-NLS-1$ >+ defaultValue: true >+ }, >+ { >+ id: 'unescape_strings', //$NON-NLS-1$ >+ name: javascriptMessages['unescape_strings'], >+ type: 'boolean', //$NON-NLS-1$ >+ defaultValue: false >+ }, >+ { >+ id: 'e4x', //$NON-NLS-1$ >+ name: javascriptMessages['e4x'], >+ type: 'boolean', //$NON-NLS-1$ >+ defaultValue: false >+ }, >+ { >+ id: 'comma_first', //$NON-NLS-1$ >+ name: javascriptMessages['comma_first'], >+ type: 'boolean', //$NON-NLS-1$ >+ defaultValue: false >+ }, >+ { >+ id: 'operator_position', //$NON-NLS-1$ >+ name: javascriptMessages['operator_position'], >+ type: 'string', //$NON-NLS-1$ >+ defaultValue: before_newline, >+ options: operator_positions >+ }, >+ ] >+ }, >+ { >+ pid: 'jsbeautify.config.html', >+ order: 3, >+ name: javascriptMessages['htmlFormattingOptions'], >+ tags: 'beautify javascript js jsbeautify formatting'.split(' '), >+ category: 'formatting', >+ categoryLabel: javascriptMessages['formatting'], >+ properties: [ >+ { >+ id: 'indent_inner_html', //$NON-NLS-1$ >+ name: javascriptMessages['indent_inner_html'], >+ type: 'boolean', //$NON-NLS-1$ >+ defaultValue: false >+ }, >+ { >+ id: 'indent_handlebars', //$NON-NLS-1$ >+ name: javascriptMessages['indent_handlebars'], >+ type: 'boolean', //$NON-NLS-1$ >+ defaultValue: false >+ }, >+ { >+ id: 'wrap_attributes', //$NON-NLS-1$ >+ name: javascriptMessages['wrap_attributes'], >+ type: 'string', //$NON-NLS-1$ >+ defaultValue: 'auto', >+ options: wrap_attributes_values >+ }, >+ { >+ id: 'wrap_attributes_indent_size', //$NON-NLS-1$ >+ name: javascriptMessages['wrap_attributes_indent_size'], >+ type: 'number', //$NON-NLS-1$ >+ defaultValue: 1 >+ }, >+ { >+ id: 'extra_liners', //$NON-NLS-1$ >+ name: javascriptMessages['extra_liners'], >+ type: 'string', //$NON-NLS-1$ >+ defaultValue: 'head,body,html' >+ }, >+ { >+ id: 'indent-scripts', //$NON-NLS-1$ >+ name: javascriptMessages['indent_scripts'], >+ type: 'string', //$NON-NLS-1$ >+ defaultValue: normal, >+ options: indent_scripts_values >+ } >+ ] >+ }, >+ { >+ pid: 'jsbeautify.config.css', >+ order: 4, >+ name: javascriptMessages['cssFormattingOptions'], >+ tags: 'beautify javascript js jsbeautify formatting'.split(' '), >+ category: 'formatting', >+ categoryLabel: javascriptMessages['formatting'], >+ properties: [ >+ { >+ id: 'selector_separator_newline', //$NON-NLS-1$ >+ name: javascriptMessages['selector_separator_newline'], >+ type: 'boolean', //$NON-NLS-1$ >+ defaultValue: true >+ }, >+ { >+ id: 'newline_between_rules', //$NON-NLS-1$ >+ name: javascriptMessages['newline_between_rules'], >+ type: 'boolean', //$NON-NLS-1$ >+ defaultValue: true >+ }, >+ { >+ id: 'space_around_selector_separator', //$NON-NLS-1$ >+ name: javascriptMessages['space_around_selector_separator'], >+ type: 'boolean', //$NON-NLS-1$ >+ defaultValue: false >+ } >+ ] >+ }] >+ }); > > /** > * Register syntax styling for js, json and json schema content
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 496437
:
262728
|
262921
|
262923
|
262926