opt(ui): js beautifier supports formatting xml

This commit is contained in:
hyb1996
2018-09-13 11:07:19 +08:00
parent 23f817f4e3
commit ccf85fe2f5
7 changed files with 121 additions and 36 deletions

View File

@@ -88,6 +88,14 @@
*/ */
(function() { (function() {
var formatXml = require("xml_formatter");
function println(str){
java.lang.System.out.println(str);
}
String.prototype.replaceAll = function(search, replacement) {
var target = this;
return target.split(search).join(replacement);
};
var legacy_beautify_js = var legacy_beautify_js =
/******/ (function(modules) { // webpackBootstrap /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache /******/ // The module cache
@@ -363,6 +371,7 @@ function Beautifier(js_source_text, options) {
'TK_WORD': handle_word, 'TK_WORD': handle_word,
'TK_RESERVED': handle_word, 'TK_RESERVED': handle_word,
'TK_SEMICOLON': handle_semicolon, 'TK_SEMICOLON': handle_semicolon,
'TK_XML': handle_string,
'TK_STRING': handle_string, 'TK_STRING': handle_string,
'TK_EQUALS': handle_equals, 'TK_EQUALS': handle_equals,
'TK_OPERATOR': handle_operator, 'TK_OPERATOR': handle_operator,
@@ -696,6 +705,13 @@ function Beautifier(js_source_text, options) {
printable_token = printable_token || current_token.text; printable_token = printable_token || current_token.text;
print_token_line_indentation(); print_token_line_indentation();
if(current_token.type == "TK_XML" && printable_token){
var indent_count = output.current_line._indent_count;
println("indent_count: " + indent_count);
var indent = output.indent_cache[Math.max(0, indent_count - 1)];
printable_token = formatXml(printable_token, {margin: indent});
current_token.text = printable_token;
}
output.add_token(printable_token); output.add_token(printable_token);
} }
@@ -1877,14 +1893,14 @@ module.exports.mergeOpts = mergeOpts;
function OutputLine(parent) { function OutputLine(parent) {
var _character_count = 0; var _character_count = 0;
// use indent_count as a marker for lines that have preserved indentation // use indent_count as a marker for lines that have preserved indentation
var _indent_count = -1; this._indent_count = -1;
var _items = []; var _items = [];
var _empty = true; var _empty = true;
this.set_indent = function(level) { this.set_indent = function(level) {
_character_count = parent.baseIndentLength + level * parent.indent_length; _character_count = parent.baseIndentLength + level * parent.indent_length;
_indent_count = level; this._indent_count = level;
}; };
this.get_character_count = function() { this.get_character_count = function() {
@@ -1920,8 +1936,8 @@ function OutputLine(parent) {
}; };
this.remove_indent = function() { this.remove_indent = function() {
if (_indent_count > 0) { if (this._indent_count > 0) {
_indent_count -= 1; this._indent_count -= 1;
_character_count -= parent.indent_length; _character_count -= parent.indent_length;
} }
}; };
@@ -1937,8 +1953,8 @@ function OutputLine(parent) {
this.toString = function() { this.toString = function() {
var result = ''; var result = '';
if (!this._empty) { if (!this._empty) {
if (_indent_count >= 0) { if (this._indent_count >= 0) {
result = parent.indent_cache[_indent_count]; result = parent.indent_cache[this._indent_count];
} }
result += _items.join(''); result += _items.join('');
} }
@@ -2567,7 +2583,8 @@ function Tokenizer(input_string, opts) {
xmlStr += input.match(/[\s\S]*/g)[0]; xmlStr += input.match(/[\s\S]*/g)[0];
} }
xmlStr = xmlStr.replace(acorn.allLineBreaks, '\n'); xmlStr = xmlStr.replace(acorn.allLineBreaks, '\n');
return [xmlStr, "TK_STRING"]; n_newlines = 1;
return [xmlStr, "TK_XML"];
} }
} else { } else {
// //

View File

@@ -0,0 +1,43 @@
const stringTimesN = (n, char) => Array(n + 1).join(char)
// Adapted from https://gist.github.com/sente/1083506
function prettifyXml(xmlInput, options) {
options = options || {};
let newlineOption = options.newline || '\n';
let indentOption = options.indent || 4;
let margin = options.margin || '';
const indentString = stringTimesN(indentOption, ' ')
let formatted = '';
const regex = /(>)(<)(\/*)/g;
const xml = xmlInput.replace(regex, '$1' + newlineOption + '$2$3');
let pad = 0;
xml.split(/\r?\n/).forEach(l => {
const line = l.trim();
let indent = 0;
if (line.match(/.+<\/\w[^>]*>$/)) {
indent = 0;
} else if (line.match(/^<\/\w/)) {
// Somehow istanbul doesn't see the else case as covered, although it is. Skip it.
/* istanbul ignore else */
if (pad !== 0) {
pad -= 1
}
} else if (line.match(/^<\w([^>]*[^\/])?>.*$/)) {
indent = 1
} else {
indent = 0
}
const padding = stringTimesN(pad, indentString);
formatted += margin + padding + line + newlineOption // eslint-disable-line prefer-template
pad += indent
})
return formatted.trim()
}
// For non-es2015 usage
module.exports = prettifyXml

View File

@@ -211,6 +211,9 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC
if (content != null) { if (content != null) {
setInitialText(content); setInitialText(content);
} else { } else {
if (path == null) {
return;
}
mFile = new File(path); mFile = new File(path);
if (mName == null) { if (mName == null) {
mName = mFile.getName(); mName = mFile.getName();

View File

@@ -80,7 +80,7 @@ public class CodeEditor extends HVScrollView {
mTextViewRedoUndo = new TextViewUndoRedo(mCodeEditText); mTextViewRedoUndo = new TextViewUndoRedo(mCodeEditText);
mJavaScriptHighlighter = new JavaScriptHighlighter(mTheme, mCodeEditText); mJavaScriptHighlighter = new JavaScriptHighlighter(mTheme, mCodeEditText);
setTheme(Theme.getDefault(getContext())); setTheme(Theme.getDefault(getContext()));
mJsBeautifier = new JsBeautifier(this, "js/beautify.js"); mJsBeautifier = new JsBeautifier(this, "js/js-beautify");
} }

View File

@@ -1,5 +1,6 @@
package com.stardust.autojs.engine; package com.stardust.autojs.engine;
import android.content.res.AssetManager;
import android.net.Uri; import android.net.Uri;
import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.Scriptable;
@@ -7,6 +8,7 @@ import org.mozilla.javascript.commonjs.module.provider.ModuleSource;
import org.mozilla.javascript.commonjs.module.provider.UrlModuleSourceProvider; import org.mozilla.javascript.commonjs.module.provider.UrlModuleSourceProvider;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.net.URI; import java.net.URI;
@@ -21,19 +23,17 @@ import java.util.List;
public class AssetAndUrlModuleSourceProvider extends UrlModuleSourceProvider { public class AssetAndUrlModuleSourceProvider extends UrlModuleSourceProvider {
private static final String MODULES_PATH = "modules";
private android.content.Context mContext; private android.content.Context mContext;
private List<String> mModules; private final URI mBaseURI;
private final URI mBaseURI = URI.create("file:///android_asset/modules"); private final String mAssetDirPath;
private final AssetManager mAssetManager;
public AssetAndUrlModuleSourceProvider(android.content.Context context, List<URI> list) { public AssetAndUrlModuleSourceProvider(android.content.Context context, String assetDirPath, List<URI> list) {
super(list, null); super(list, null);
mContext = context; mContext = context;
try { mAssetDirPath = assetDirPath;
mModules = Arrays.asList(mContext.getAssets().list(MODULES_PATH)); mBaseURI = URI.create("file:///android_asset/" + assetDirPath);
} catch (IOException e) { mAssetManager = mContext.getAssets();
throw new RuntimeException(e);
}
} }
@Override @Override
@@ -42,10 +42,11 @@ public class AssetAndUrlModuleSourceProvider extends UrlModuleSourceProvider {
if (!moduleIdWithExtension.endsWith(".js")) { if (!moduleIdWithExtension.endsWith(".js")) {
moduleIdWithExtension += ".js"; moduleIdWithExtension += ".js";
} }
if (mModules.contains(moduleIdWithExtension)) { try {
return new ModuleSource(new InputStreamReader(mContext.getAssets().open(MODULES_PATH + "/" + moduleIdWithExtension)), null, return new ModuleSource(new InputStreamReader(mAssetManager.open(mAssetDirPath + "/" + moduleIdWithExtension)), null,
URI.create(moduleIdWithExtension), mBaseURI, validator); new URI(mBaseURI.toString() + "/" + moduleIdWithExtension), mBaseURI, validator);
} } catch (FileNotFoundException e) {
return super.loadFromPrivilegedLocations(moduleId, validator); return super.loadFromPrivilegedLocations(moduleId, validator);
} }
} }
}

View File

@@ -49,6 +49,7 @@ public class RhinoJavaScriptEngine extends JavaScriptEngine {
private static final String LOG_TAG = "RhinoJavaScriptEngine"; private static final String LOG_TAG = "RhinoJavaScriptEngine";
private static final String MODULES_PATH = "modules";
private static int contextCount = 0; private static int contextCount = 0;
private static StringScriptSource sInitScript; private static StringScriptSource sInitScript;
private static final ConcurrentHashMap<Context, RhinoJavaScriptEngine> sContextEngineMap = new ConcurrentHashMap<>(); private static final ConcurrentHashMap<Context, RhinoJavaScriptEngine> sContextEngineMap = new ConcurrentHashMap<>();
@@ -129,7 +130,7 @@ public class RhinoJavaScriptEngine extends JavaScriptEngine {
} }
void initRequireBuilder(Context context, Scriptable scope) { void initRequireBuilder(Context context, Scriptable scope) {
AssetAndUrlModuleSourceProvider provider = new AssetAndUrlModuleSourceProvider(mAndroidContext, AssetAndUrlModuleSourceProvider provider = new AssetAndUrlModuleSourceProvider(mAndroidContext, MODULES_PATH,
Collections.singletonList(new File("/").toURI())); Collections.singletonList(new File("/").toURI()));
new RequireBuilder() new RequireBuilder()
.setModuleScriptProvider(new SoftCachingModuleScriptProvider(provider)) .setModuleScriptProvider(new SoftCachingModuleScriptProvider(provider))

View File

@@ -4,14 +4,20 @@ import android.content.Context;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
import com.stardust.autojs.engine.AssetAndUrlModuleSourceProvider;
import com.stardust.pio.PFiles; import com.stardust.pio.PFiles;
import com.stardust.pio.UncheckedIOException; import com.stardust.pio.UncheckedIOException;
import org.mozilla.javascript.Function; import org.mozilla.javascript.Function;
import org.mozilla.javascript.ImporterTopLevel;
import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.commonjs.module.RequireBuilder;
import org.mozilla.javascript.commonjs.module.provider.SoftCachingModuleScriptProvider;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Collections;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@@ -21,6 +27,7 @@ import java.util.concurrent.Executors;
public class JsBeautifier { public class JsBeautifier {
public interface Callback { public interface Callback {
void onSuccess(String beautifiedCode); void onSuccess(String beautifiedCode);
@@ -34,12 +41,14 @@ public class JsBeautifier {
private org.mozilla.javascript.Context mScriptContext; private org.mozilla.javascript.Context mScriptContext;
private Scriptable mScriptable; private Scriptable mScriptable;
private final String mBeautifyJsPath; private final String mBeautifyJsPath;
private final String mBeautifyJsDir;
private View mView; private View mView;
public JsBeautifier(View view, String beautifyJsPath) { public JsBeautifier(View view, String beautifyJsDirPath) {
mContext = view.getContext(); mContext = view.getContext();
mView = view; mView = view;
mBeautifyJsPath = beautifyJsPath; mBeautifyJsDir = beautifyJsDirPath;
mBeautifyJsPath = PFiles.join(beautifyJsDirPath, "beautify.js");
} }
public void beautify(final String code, final Callback callback) { public void beautify(final String code, final Callback callback) {
@@ -65,11 +74,24 @@ public class JsBeautifier {
} }
private void enterContext() { private void enterContext() {
if (mScriptContext == null) { if (mScriptContext != null) {
return;
}
mScriptContext = org.mozilla.javascript.Context.enter(); mScriptContext = org.mozilla.javascript.Context.enter();
mScriptContext.setLanguageVersion(org.mozilla.javascript.Context.VERSION_1_8); mScriptContext.setLanguageVersion(org.mozilla.javascript.Context.VERSION_1_8);
mScriptContext.setOptimizationLevel(-1); mScriptContext.setOptimizationLevel(-1);
if (mScriptable == null) {
ImporterTopLevel importerTopLevel = new ImporterTopLevel();
importerTopLevel.initStandardObjects(mScriptContext, false);
mScriptable = importerTopLevel;
} }
AssetAndUrlModuleSourceProvider provider = new AssetAndUrlModuleSourceProvider(mContext, mBeautifyJsDir,
Collections.singletonList(new File("/").toURI()));
new RequireBuilder()
.setModuleScriptProvider(new SoftCachingModuleScriptProvider(provider))
.setSandboxed(false)
.createRequire(mScriptContext, mScriptable)
.install(mScriptable);
} }
public void prepare() { public void prepare() {
@@ -93,8 +115,6 @@ public class JsBeautifier {
try { try {
enterContext(); enterContext();
InputStream is = mContext.getAssets().open(mBeautifyJsPath); InputStream is = mContext.getAssets().open(mBeautifyJsPath);
if (mScriptable == null)
mScriptable = mScriptContext.initSafeStandardObjects();
mJsBeautifyFunction = (Function) mScriptContext.evaluateString(mScriptable, PFiles.read(is), "<js_beautify>", 1, null); mJsBeautifyFunction = (Function) mScriptContext.evaluateString(mScriptable, PFiles.read(is), "<js_beautify>", 1, null);
} catch (IOException e) { } catch (IOException e) {
exitContext(); exitContext();