feat: new editor: jsdroid editor
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
|
||||
package com.stardust.scriptdroid.model.editor;
|
||||
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class EditorColors {
|
||||
|
||||
@SerializedName("editor.background")
|
||||
private String mEditorBackground;
|
||||
@SerializedName("editor.foreground")
|
||||
private String mEditorForeground;
|
||||
@SerializedName("editor.inactiveSelectionBackground")
|
||||
private String mEditorInactiveSelectionBackground;
|
||||
@SerializedName("editorIndentGuide.background")
|
||||
private String mEditorIndentGuideBackground;
|
||||
@SerializedName("editor.selectionHighlightBackground")
|
||||
private String mEditorSelectionHighlightBackground;
|
||||
@SerializedName("imeBar.background")
|
||||
private String mImeBackgroundColor;
|
||||
@SerializedName("imeBar.foreground")
|
||||
private String mImeForegroundColor;
|
||||
|
||||
public String getEditorBackground() {
|
||||
return mEditorBackground;
|
||||
}
|
||||
|
||||
public void setEditorBackground(String editorBackground) {
|
||||
mEditorBackground = editorBackground;
|
||||
}
|
||||
|
||||
public String getEditorForeground() {
|
||||
return mEditorForeground;
|
||||
}
|
||||
|
||||
public void setEditorForeground(String editorForeground) {
|
||||
mEditorForeground = editorForeground;
|
||||
}
|
||||
|
||||
public String getEditorInactiveSelectionBackground() {
|
||||
return mEditorInactiveSelectionBackground;
|
||||
}
|
||||
|
||||
public void setEditorInactiveSelectionBackground(String editorInactiveSelectionBackground) {
|
||||
mEditorInactiveSelectionBackground = editorInactiveSelectionBackground;
|
||||
}
|
||||
|
||||
public String getEditorIndentGuideBackground() {
|
||||
return mEditorIndentGuideBackground;
|
||||
}
|
||||
|
||||
public void setEditorIndentGuideBackground(String editorIndentGuideBackground) {
|
||||
mEditorIndentGuideBackground = editorIndentGuideBackground;
|
||||
}
|
||||
|
||||
public String getEditorSelectionHighlightBackground() {
|
||||
return mEditorSelectionHighlightBackground;
|
||||
}
|
||||
|
||||
public void setEditorSelectionHighlightBackground(String editorSelectionHighlightBackground) {
|
||||
mEditorSelectionHighlightBackground = editorSelectionHighlightBackground;
|
||||
}
|
||||
|
||||
public String getImeBackgroundColor() {
|
||||
return mImeBackgroundColor;
|
||||
}
|
||||
|
||||
public void setImeBackgroundColor(String imeBackgroundColor) {
|
||||
mImeBackgroundColor = imeBackgroundColor;
|
||||
}
|
||||
|
||||
public String getImeForegroundColor() {
|
||||
return mImeForegroundColor;
|
||||
}
|
||||
|
||||
public void setImeForegroundColor(String imeForegroundColor) {
|
||||
mImeForegroundColor = imeForegroundColor;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
|
||||
package com.stardust.scriptdroid.model.editor;
|
||||
|
||||
import android.graphics.Color;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import java.io.Reader;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.mozilla.javascript.Token.*;
|
||||
|
||||
public class EditorTheme {
|
||||
|
||||
private static Gson sGson;
|
||||
|
||||
static {
|
||||
sGson = new GsonBuilder()
|
||||
.registerTypeAdapter(TokenColor.class, new TokenColorDeserializer())
|
||||
.create();
|
||||
}
|
||||
|
||||
@SerializedName("colors")
|
||||
private EditorColors mEditorColors;
|
||||
|
||||
@SerializedName("name")
|
||||
private String mName;
|
||||
|
||||
@SerializedName("$schema")
|
||||
private String mSchema;
|
||||
|
||||
@SerializedName("tokenColors")
|
||||
private List<TokenColor> mTokenColors = Collections.emptyList();
|
||||
|
||||
public static EditorTheme fromJson(String json) {
|
||||
return sGson.fromJson(json, EditorTheme.class);
|
||||
}
|
||||
|
||||
public static EditorTheme fromJson(Reader json) {
|
||||
return sGson.fromJson(json, EditorTheme.class);
|
||||
}
|
||||
|
||||
public EditorColors getEditorColors() {
|
||||
return mEditorColors;
|
||||
}
|
||||
|
||||
public void setEditorColors(EditorColors editorColors) {
|
||||
mEditorColors = editorColors;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
mName = name;
|
||||
}
|
||||
|
||||
public String getSchema() {
|
||||
return mSchema;
|
||||
}
|
||||
|
||||
public void setSchema(String schema) {
|
||||
mSchema = schema;
|
||||
}
|
||||
|
||||
public List<TokenColor> getTokenColors() {
|
||||
return mTokenColors;
|
||||
}
|
||||
|
||||
public void setTokenColors(List<TokenColor> tokenColors) {
|
||||
mTokenColors = tokenColors;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.stardust.scriptdroid.model.editor;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2018/2/21.
|
||||
*/
|
||||
|
||||
public class TokenColor {
|
||||
|
||||
@SerializedName("name")
|
||||
private String mName;
|
||||
|
||||
@SerializedName("scope")
|
||||
private List<String> mScope = Collections.emptyList();
|
||||
|
||||
@SerializedName("settings")
|
||||
private TokenColorSettings mSettings;
|
||||
|
||||
public TokenColor() {
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
mName = name;
|
||||
}
|
||||
|
||||
public List<String> getScope() {
|
||||
return mScope;
|
||||
}
|
||||
|
||||
public void setScope(List<String> scope) {
|
||||
mScope = scope;
|
||||
}
|
||||
|
||||
public TokenColorSettings getSettings() {
|
||||
return mSettings;
|
||||
}
|
||||
|
||||
public void setSettings(TokenColorSettings settings) {
|
||||
mSettings = settings;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.stardust.scriptdroid.model.editor;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
import org.mozilla.javascript.Token;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2018/2/21.
|
||||
*/
|
||||
|
||||
public class TokenColorDeserializer implements JsonDeserializer<TokenColor> {
|
||||
|
||||
@Override
|
||||
public TokenColor deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||
JsonObject object = json.getAsJsonObject();
|
||||
TokenColor tokenColor = new TokenColor();
|
||||
if (object.has("name")) {
|
||||
tokenColor.setName(object.get("name").getAsString());
|
||||
}
|
||||
if (object.has("settings")) {
|
||||
tokenColor.setSettings(context.deserialize(object.get("settings"), TokenColorSettings.class));
|
||||
}
|
||||
if (object.has("scope")) {
|
||||
tokenColor.setScope(deserializeAsList(object, "scope"));
|
||||
}
|
||||
return tokenColor;
|
||||
}
|
||||
|
||||
private List<String> deserializeAsList(JsonObject object, String key) {
|
||||
JsonElement scope = object.get(key);
|
||||
if (scope.isJsonArray()) {
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
for (JsonElement e : scope.getAsJsonArray()) {
|
||||
list.add(e.getAsString());
|
||||
}
|
||||
return list;
|
||||
} else {
|
||||
return Collections.singletonList(scope.getAsString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.stardust.scriptdroid.model.editor;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2018/2/21.
|
||||
*/
|
||||
|
||||
public class TokenColorSettings {
|
||||
|
||||
@SerializedName("foreground")
|
||||
private String mForeground;
|
||||
|
||||
@SerializedName("fontStyle")
|
||||
private String mFontStyle;
|
||||
|
||||
public TokenColorSettings() {
|
||||
}
|
||||
|
||||
public String getForeground() {
|
||||
return mForeground;
|
||||
}
|
||||
|
||||
public void setForeground(String foreground) {
|
||||
mForeground = foreground;
|
||||
}
|
||||
|
||||
public String getFontStyle() {
|
||||
return mFontStyle;
|
||||
}
|
||||
|
||||
public void setFontStyle(String fontStyle) {
|
||||
mFontStyle = fontStyle;
|
||||
}
|
||||
}
|
||||
@@ -1,499 +0,0 @@
|
||||
package com.stardust.scriptdroid.ui.edit;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.RequiresApi;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.ActionMode;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
import android.webkit.ConsoleMessage;
|
||||
import android.webkit.JavascriptInterface;
|
||||
import android.webkit.JsPromptResult;
|
||||
import android.webkit.JsResult;
|
||||
import android.webkit.WebChromeClient;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.stardust.pio.PFiles;
|
||||
import com.stardust.scriptdroid.R;
|
||||
import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder;
|
||||
import com.stardust.util.ClipboardUtil;
|
||||
|
||||
import org.jdeferred.Deferred;
|
||||
import org.jdeferred.impl.DeferredObject;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import io.reactivex.subjects.PublishSubject;
|
||||
import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/9/27.
|
||||
*/
|
||||
|
||||
public class CodeMirrorEditor extends FrameLayout {
|
||||
|
||||
private static final String LOG_TAG = "CodeMirrorEditor";
|
||||
|
||||
|
||||
public interface Callback {
|
||||
|
||||
void onKeyUp(String line, int ch);
|
||||
|
||||
}
|
||||
|
||||
private static String[] sAvailableThemes;
|
||||
private String mTheme = "neo";
|
||||
private FrameLayout mProgressBarContainer;
|
||||
private WebView mWebView;
|
||||
private JavaScriptBridge mJavaScriptBridge = new JavaScriptBridge();
|
||||
private Callback mCallback;
|
||||
private Deferred<Void, Void, Void> mPageFinished = new DeferredObject<>();
|
||||
|
||||
public CodeMirrorEditor(Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public CodeMirrorEditor(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
public CodeMirrorEditor(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init();
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
public CodeMirrorEditor(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
init();
|
||||
}
|
||||
|
||||
public WebView getWebView() {
|
||||
return mWebView;
|
||||
}
|
||||
|
||||
public void setCallback(Callback callback) {
|
||||
mCallback = callback;
|
||||
}
|
||||
|
||||
public void beautifyCode() {
|
||||
evalJavaScript("editor.setValue(js_beautify(editor.getValue(), {indent_size: 2}));");
|
||||
}
|
||||
|
||||
public String[] getAvailableThemes() {
|
||||
if (sAvailableThemes == null) {
|
||||
try {
|
||||
sAvailableThemes = getResources().getAssets().list("editor/codemirror/theme");
|
||||
for (int i = 0; i < sAvailableThemes.length; i++) {
|
||||
sAvailableThemes[i] = PFiles.getNameWithoutExtension(sAvailableThemes[i]);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return sAvailableThemes;
|
||||
}
|
||||
|
||||
|
||||
public String getTheme() {
|
||||
return mTheme;
|
||||
}
|
||||
|
||||
public void setTheme(final String theme) {
|
||||
mPageFinished.promise().done(result -> {
|
||||
evalJavaScript(String.format(Locale.getDefault(),
|
||||
"var e = document.createElement('link');e.setAttribute('rel', 'stylesheet');" +
|
||||
"e.setAttribute('type', 'text/css');e.setAttribute('href', '%s');" +
|
||||
"document.getElementsByTagName('head')[0].appendChild(e);",
|
||||
"codemirror/theme/" + theme + ".css"));
|
||||
evalJavaScript(String.format(Locale.getDefault(), "editor.setOption('theme', '%s');", theme));
|
||||
mTheme = theme;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void init() {
|
||||
setupWebView();
|
||||
setupProgress();
|
||||
mWebView.loadUrl("file:///android_asset/editor/index.html");
|
||||
}
|
||||
|
||||
private void setupProgress() {
|
||||
mProgressBarContainer = new FrameLayout(getContext());
|
||||
mProgressBarContainer.setBackgroundColor(Color.WHITE);
|
||||
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
|
||||
addView(mProgressBarContainer, params);
|
||||
MaterialProgressBar progressBar = new MaterialProgressBar(getContext());
|
||||
int dp50 = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, getResources().getDisplayMetrics());
|
||||
FrameLayout.LayoutParams p = new FrameLayout.LayoutParams(dp50, dp50);
|
||||
p.gravity = Gravity.CENTER;
|
||||
mProgressBarContainer.addView(progressBar, p);
|
||||
}
|
||||
|
||||
private void setupWebView() {
|
||||
mWebView = new WebView(getContext()) {
|
||||
@Override
|
||||
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
|
||||
InputConnection connection = super.onCreateInputConnection(outAttrs);
|
||||
return connection;// == null ? null : new MyInputConnection(connection);
|
||||
}
|
||||
};
|
||||
setupWebSettings();
|
||||
mWebView.addJavascriptInterface(mJavaScriptBridge, "__bridge__");
|
||||
addView(mWebView);
|
||||
}
|
||||
|
||||
|
||||
private void setupWebSettings() {
|
||||
WebSettings settings = mWebView.getSettings();
|
||||
settings.setBuiltInZoomControls(true);
|
||||
//settings.setLoadWithOverviewMode(true);
|
||||
settings.setJavaScriptEnabled(true);
|
||||
settings.setJavaScriptCanOpenWindowsAutomatically(true);
|
||||
settings.setDomStorageEnabled(true);
|
||||
settings.setNeedInitialFocus(true);
|
||||
settings.setDisplayZoomControls(false);
|
||||
mWebView.setWebChromeClient(new MyWebChromeClient());
|
||||
mWebView.setWebViewClient(new MyWebViewClient());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) {
|
||||
return super.startActionModeForChild(originalView, new EditorActionModeCallback(callback, this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback, int type) {
|
||||
return super.startActionModeForChild(originalView, new EditorActionModeCallback(callback, this), type);
|
||||
}
|
||||
|
||||
public void setReadOnly(boolean readOnly) {
|
||||
evalJavaScript(String.format("editor.setOption('readOnly', %b);", readOnly));
|
||||
}
|
||||
|
||||
public void setProgress(boolean onProgress) {
|
||||
mProgressBarContainer.setVisibility(onProgress ? VISIBLE : GONE);
|
||||
}
|
||||
|
||||
public void setText(final String text) {
|
||||
int id = SharedVariables.put(text);
|
||||
mPageFinished.promise().done(result -> evalJavaScript(String.format(Locale.getDefault(),
|
||||
"editor.setValue(__bridge__.getStringFromAndroid(%d));", id)));
|
||||
}
|
||||
|
||||
|
||||
public void insert(String text) {
|
||||
int id = SharedVariables.put(text);
|
||||
mPageFinished.promise().done(result -> evalJavaScript(String.format(Locale.getDefault(),
|
||||
"editor.replaceSelection(__bridge__.getStringFromAndroid(%d));", id)));
|
||||
}
|
||||
|
||||
|
||||
public Observable<String> getText() {
|
||||
return evalString("editor.getValue()");
|
||||
}
|
||||
|
||||
public Observable<String> getLine() {
|
||||
return evalString("editor.getLine(editor.getCursor().line)");
|
||||
}
|
||||
|
||||
public void deleteLine() {
|
||||
evalJavaScript("editor.replaceRange('', {line: editor.getCursor().line, ch: 0}," +
|
||||
" {line: editor.getCursor().line + 1, ch: 0});");
|
||||
}
|
||||
|
||||
public void moveCursor(int dLine, int dCh) {
|
||||
evalJavaScript(String.format(Locale.getDefault(),
|
||||
"editor.setCursor({line: editor.getCursor().line + (%d), ch: editor.getCursor().ch + (%d)});",
|
||||
dLine, dCh));
|
||||
}
|
||||
|
||||
public void copyLine() {
|
||||
getLine()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(s -> {
|
||||
ClipboardUtil.setClip(getContext(), s);
|
||||
Snackbar.make(mWebView, R.string.text_already_copy_to_clip, Snackbar.LENGTH_SHORT).show();
|
||||
});
|
||||
}
|
||||
|
||||
public void cut() {
|
||||
getSelection().observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(s -> {
|
||||
ClipboardUtil.setClip(getContext(), s);
|
||||
evalJavaScript("editor.replaceSelection('');");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public void copy() {
|
||||
getSelection().observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(s -> ClipboardUtil.setClip(getContext(), s));
|
||||
}
|
||||
|
||||
public void paste() {
|
||||
CharSequence clip = ClipboardUtil.getClip(getContext());
|
||||
if (clip == null) {
|
||||
return;
|
||||
}
|
||||
evalJavaScript(String.format("editor.replaceSelection('%s');", clip.toString().replace("'", "\\'")));
|
||||
}
|
||||
|
||||
|
||||
public void selectAll() {
|
||||
evalJavaScript("editor.execCommand('selectAll');");
|
||||
}
|
||||
|
||||
public Observable<Integer> getLineCount() {
|
||||
PublishSubject<Integer> publishSubject = PublishSubject.create();
|
||||
int id = SharedVariables.put(publishSubject);
|
||||
evalJavaScript(String.format(Locale.getDefault(), "__bridge__.setIntFromJs(%d, editor.lineCount())", id));
|
||||
return publishSubject;
|
||||
}
|
||||
|
||||
|
||||
public void undo() {
|
||||
evalJavaScript("editor.undo();");
|
||||
}
|
||||
|
||||
public void redo() {
|
||||
evalJavaScript("editor.redo();");
|
||||
}
|
||||
|
||||
public void jumpTo(int line, int col) {
|
||||
evalJavaScript("editor.setCursor({line: " + line + ", ch: " + col + "});");
|
||||
}
|
||||
|
||||
public void find(String keywords, boolean usingRegex) {
|
||||
setQuery(keywords, usingRegex);
|
||||
findNext();
|
||||
}
|
||||
|
||||
private void setQuery(String keywords, boolean usingRegex) {
|
||||
keywords = keywords.replace("'", "\'");
|
||||
if (usingRegex) {
|
||||
keywords = "/" + keywords + "/";
|
||||
}
|
||||
evalJavaScript("editor.execCommand('clearSearch'); editor.state.search.query = '" +
|
||||
keywords + "';");
|
||||
}
|
||||
|
||||
public void findNext() {
|
||||
evalJavaScript("editor.execCommand('findNext');");
|
||||
}
|
||||
|
||||
public void findPrev() {
|
||||
evalJavaScript("editor.execCommand('findPrev');");
|
||||
}
|
||||
|
||||
public void replaceAll(String keywords, String replacement, boolean usingRegex) {
|
||||
setQuery(keywords, usingRegex);
|
||||
replacement = replacement.replace("'", "\'");
|
||||
evalJavaScript("editor.state.search.text = '" + replacement + "';");
|
||||
evalJavaScript("editor.execCommand('replaceAll');");
|
||||
}
|
||||
|
||||
public void jumpToDef() {
|
||||
evalJavaScript("editor.tern.jumpToDef();");
|
||||
}
|
||||
|
||||
|
||||
public void jumpToLineStart() {
|
||||
evalJavaScript("editor.execCommand('goLineStart');");
|
||||
}
|
||||
|
||||
public void jumpToLineEnd() {
|
||||
evalJavaScript("editor.execCommand('goLineEnd')");
|
||||
}
|
||||
|
||||
public void jumpToStart() {
|
||||
evalJavaScript("editor.execCommand('goDocStart');");
|
||||
}
|
||||
|
||||
public void jumpToEnd() {
|
||||
evalJavaScript("editor.execCommand('goDocEnd');");
|
||||
}
|
||||
|
||||
|
||||
public void showType() {
|
||||
evalJavaScript("editor.tern.showType();");
|
||||
}
|
||||
|
||||
public void rename() {
|
||||
evalJavaScript("editor.tern.rename();");
|
||||
}
|
||||
|
||||
public void selectName() {
|
||||
evalJavaScript("editor.tern.selectName();");
|
||||
}
|
||||
|
||||
|
||||
public void replace(String keywords, String replacement, boolean usingRegex) {
|
||||
setQuery(keywords, usingRegex);
|
||||
replacement = replacement.replace("'", "\'");
|
||||
evalJavaScript("editor.state.search.text = '" + replacement + "';");
|
||||
findNext();
|
||||
}
|
||||
|
||||
public Observable<String> getSelection() {
|
||||
return evalString("editor.getSelection()");
|
||||
}
|
||||
|
||||
private Observable<String> evalString(String expr) {
|
||||
PublishSubject<String> publishSubject = PublishSubject.create();
|
||||
int id = SharedVariables.put(publishSubject);
|
||||
evalJavaScript(String.format(Locale.getDefault(), "__bridge__.setStringFromJs(%d, %s);", id, expr));
|
||||
return publishSubject;
|
||||
}
|
||||
|
||||
public void replaceSelection() {
|
||||
evalJavaScript("editor.execCommand('replace');");
|
||||
}
|
||||
|
||||
private void evalJavaScript(String script) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
mWebView.evaluateJavascript(script, null);
|
||||
} else {
|
||||
mWebView.loadUrl("javascript:" + script);
|
||||
}
|
||||
}
|
||||
|
||||
public void replace(String text, int fromLine, int fromCh, int toLine, int toCh) {
|
||||
String js = "editor.replaceRange('%s', {line: %d, ch: %d}, {line: %d, ch: %d})";
|
||||
evalJavaScript(String.format(Locale.getDefault(), js, text, fromLine, fromCh, toLine, toCh));
|
||||
}
|
||||
|
||||
public class JavaScriptBridge {
|
||||
|
||||
@JavascriptInterface
|
||||
public void setStringFromJs(int id, String text) {
|
||||
PublishSubject<String> publishSubject = SharedVariables.remove(id);
|
||||
if (publishSubject == null)
|
||||
return;
|
||||
publishSubject.onNext(text);
|
||||
publishSubject.onComplete();
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public void setIntFromJs(int id, int i) {
|
||||
PublishSubject<Integer> publishSubject = SharedVariables.remove(id);
|
||||
if (publishSubject == null)
|
||||
return;
|
||||
publishSubject.onNext(i);
|
||||
publishSubject.onComplete();
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public String getStringFromAndroid(int id) {
|
||||
return SharedVariables.remove(id);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public void onKeyUp(String line, int cursor) {
|
||||
if (mCallback == null) {
|
||||
return;
|
||||
}
|
||||
mWebView.post(() -> mCallback.onKeyUp(line, cursor));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class MyWebViewClient extends WebViewClient {
|
||||
|
||||
private final String INIT_SCRIPT = "editor.on('change', function(){__bridge__.onTextChange();});";
|
||||
|
||||
@Override
|
||||
public void onPageFinished(WebView view, String url) {
|
||||
super.onPageFinished(view, url);
|
||||
mPageFinished.resolve(null);
|
||||
evalJavaScript(INIT_SCRIPT);
|
||||
setProgress(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class MyWebChromeClient extends WebChromeClient {
|
||||
|
||||
private final Pattern RENAME = Pattern.compile("New name for ([a-zA-Z]+)");
|
||||
|
||||
@Override
|
||||
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
|
||||
Matcher m = RENAME.matcher(message);
|
||||
if (m.matches() && m.groupCount() >= 1) {
|
||||
showRenamePrompt(m.group(1), defaultValue, result);
|
||||
return true;
|
||||
}
|
||||
return super.onJsPrompt(view, url, message, defaultValue, result);
|
||||
}
|
||||
|
||||
private void showRenamePrompt(String name, String defaultValue, final JsPromptResult result) {
|
||||
new ThemeColorMaterialDialogBuilder(getContext())
|
||||
.title(getResources().getString(R.string.text_rename) + name)
|
||||
.input("", defaultValue, (dialog, input) -> result.confirm(input.toString()))
|
||||
.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
|
||||
new ThemeColorMaterialDialogBuilder(getContext())
|
||||
.title(R.string.text_alert)
|
||||
.content(message)
|
||||
.onPositive((dialog, which) -> result.confirm())
|
||||
.cancelListener(dialog -> result.cancel())
|
||||
.positiveText(R.string.ok)
|
||||
.show();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(WebView view, int newProgress) {
|
||||
setProgress(newProgress != 100);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onConsoleMessage(ConsoleMessage m) {
|
||||
switch (m.messageLevel()) {
|
||||
case LOG:
|
||||
Log.i(LOG_TAG, String.format("%s (%s:%d)", m.message(), m.sourceId(), m.lineNumber()));
|
||||
return true;
|
||||
case TIP:
|
||||
Log.v(LOG_TAG, String.format("%s (%s:%d)", m.message(), m.sourceId(), m.lineNumber()));
|
||||
return true;
|
||||
case DEBUG:
|
||||
Log.d(LOG_TAG, String.format("%s (%s:%d)", m.message(), m.sourceId(), m.lineNumber()));
|
||||
return true;
|
||||
case WARNING:
|
||||
Log.w(LOG_TAG, String.format("%s (%s:%d)", m.message(), m.sourceId(), m.lineNumber()));
|
||||
return true;
|
||||
case ERROR:
|
||||
Log.e(LOG_TAG, String.format("%s (%s:%d)", m.message(), m.sourceId(), m.lineNumber()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package com.stardust.scriptdroid.ui.edit;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.support.v7.view.menu.MenuBuilder;
|
||||
import android.support.v7.view.menu.MenuItemImpl;
|
||||
import android.util.Log;
|
||||
import android.view.ActionMode;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import com.stardust.scriptdroid.R;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/11/2.
|
||||
*/
|
||||
|
||||
public class EditorActionModeCallback implements ActionMode.Callback {
|
||||
|
||||
|
||||
private static final int ID_SELECT_ALL = 34275489;
|
||||
|
||||
private ActionMode.Callback mCallback;
|
||||
private CodeMirrorEditor mEditor;
|
||||
|
||||
public EditorActionModeCallback(ActionMode.Callback callback, CodeMirrorEditor editor) {
|
||||
mCallback = callback;
|
||||
mEditor = editor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
||||
return mCallback.onCreateActionMode(mode, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
|
||||
return mCallback.onPrepareActionMode(mode, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_delete_line:
|
||||
mEditor.deleteLine();
|
||||
return true;
|
||||
case R.id.action_copy_line:
|
||||
mEditor.copyLine();
|
||||
mode.finish();
|
||||
return true;
|
||||
}
|
||||
return mCallback.onActionItemClicked(mode, item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyActionMode(ActionMode mode) {
|
||||
mCallback.onDestroyActionMode(mode);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -5,12 +5,11 @@ import android.support.design.widget.Snackbar;
|
||||
import android.text.InputType;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
import com.stardust.pio.PFiles;
|
||||
import com.stardust.scriptdroid.R;
|
||||
import com.stardust.scriptdroid.autojs.AutoJs;
|
||||
import com.stardust.scriptdroid.ui.build.BuildActivity;
|
||||
import com.stardust.scriptdroid.ui.build.BuildActivity_;
|
||||
import com.stardust.scriptdroid.ui.edit.editor.CodeEditor;
|
||||
import com.stardust.scriptdroid.ui.log.LogActivity_;
|
||||
import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder;
|
||||
import com.stardust.util.ClipboardUtil;
|
||||
@@ -20,7 +19,6 @@ import java.util.Locale;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.annotations.NonNull;
|
||||
import io.reactivex.functions.BiFunction;
|
||||
import io.reactivex.functions.Consumer;
|
||||
|
||||
/**
|
||||
@@ -31,12 +29,12 @@ public class EditorMenu {
|
||||
|
||||
private EditorView mEditorView;
|
||||
private Context mContext;
|
||||
private CodeMirrorEditor mEditor;
|
||||
private CodeEditor mEditor;
|
||||
|
||||
public EditorMenu(EditorView editorView) {
|
||||
mEditorView = editorView;
|
||||
mContext = editorView.getContext();
|
||||
mEditor = editorView.mEditor;
|
||||
mEditor = editorView.getEditor();
|
||||
}
|
||||
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
@@ -69,9 +67,6 @@ public class EditorMenu {
|
||||
case R.id.action_jump_to_line:
|
||||
jumpToLine();
|
||||
return true;
|
||||
case R.id.action_jump_to_def:
|
||||
mEditor.jumpToDef();
|
||||
return true;
|
||||
case R.id.action_jump_to_start:
|
||||
mEditor.jumpToStart();
|
||||
return true;
|
||||
@@ -94,10 +89,10 @@ public class EditorMenu {
|
||||
beautifyCode();
|
||||
return true;
|
||||
case R.id.action_rename:
|
||||
mEditor.rename();
|
||||
//mEditor.rename();
|
||||
return true;
|
||||
case R.id.action_select_variable:
|
||||
mEditor.selectName();
|
||||
//mEditor.selectName();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -110,7 +105,7 @@ public class EditorMenu {
|
||||
showConsole();
|
||||
return true;
|
||||
case R.id.action_show_type:
|
||||
mEditor.showType();
|
||||
// mEditor.showType();
|
||||
return true;
|
||||
case R.id.action_editor_theme:
|
||||
mEditorView.selectEditorTheme();
|
||||
@@ -212,14 +207,11 @@ public class EditorMenu {
|
||||
private void findOrReplace() {
|
||||
mEditor.getSelection()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Consumer<String>() {
|
||||
@Override
|
||||
public void accept(@NonNull String s) throws Exception {
|
||||
.subscribe(s ->
|
||||
new FindOrReplaceDialogBuilder(mContext, mEditorView)
|
||||
.setQueryIfNotEmpty(s)
|
||||
.show();
|
||||
}
|
||||
});
|
||||
.show()
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.support.v4.widget.DrawerLayout;
|
||||
@@ -17,7 +16,6 @@ import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
import com.stardust.autojs.engine.JavaScriptEngine;
|
||||
import com.stardust.autojs.execution.ScriptExecution;
|
||||
import com.stardust.pio.PFiles;
|
||||
@@ -31,10 +29,11 @@ import com.stardust.scriptdroid.model.script.Scripts;
|
||||
import com.stardust.scriptdroid.ui.doc.ManualDialog;
|
||||
import com.stardust.scriptdroid.model.autocomplete.CodeCompletions;
|
||||
import com.stardust.scriptdroid.ui.edit.completion.CodeCompletionBar;
|
||||
import com.stardust.scriptdroid.ui.edit.completion.InputMethodEnhancedBarColors;
|
||||
import com.stardust.scriptdroid.model.autocomplete.Symbols;
|
||||
import com.stardust.scriptdroid.ui.edit.editor.CodeEditor;
|
||||
import com.stardust.scriptdroid.ui.edit.keyboard.FunctionsKeyboardHelper;
|
||||
import com.stardust.scriptdroid.ui.edit.keyboard.FunctionsKeyboardView;
|
||||
import com.stardust.scriptdroid.ui.edit.theme.Theme;
|
||||
import com.stardust.scriptdroid.ui.log.LogActivity_;
|
||||
import com.stardust.scriptdroid.ui.widget.EWebView;
|
||||
import com.stardust.scriptdroid.ui.widget.ToolbarMenuItem;
|
||||
@@ -47,7 +46,6 @@ import org.androidannotations.annotations.EViewGroup;
|
||||
import org.androidannotations.annotations.ViewById;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
@@ -69,7 +67,7 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC
|
||||
public static final String EXTRA_RUN_ENABLED = "Love you with my life...really...17.9.28";
|
||||
|
||||
@ViewById(R.id.editor)
|
||||
CodeMirrorEditor mEditor;
|
||||
CodeEditor mEditor;
|
||||
|
||||
@ViewById(R.id.code_completion_bar)
|
||||
CodeCompletionBar mCodeCompletionBar;
|
||||
@@ -104,9 +102,8 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC
|
||||
private File mFile;
|
||||
private boolean mReadOnly = false;
|
||||
private ScriptExecution mScriptExecution;
|
||||
private boolean mTextChanged = false;
|
||||
private boolean mInitialText = false;
|
||||
private AutoCompletion mAutoCompletion;
|
||||
private Theme mEditorTheme;
|
||||
private FunctionsKeyboardHelper mFunctionsKeyboardHelper;
|
||||
private BroadcastReceiver mOnRunFinishedReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
@@ -209,9 +206,7 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC
|
||||
}
|
||||
|
||||
private void setInitialText(String text) {
|
||||
mInitialText = true;
|
||||
mTextChanged = false;
|
||||
mEditor.setText(text);
|
||||
mEditor.setInitialText(text);
|
||||
}
|
||||
|
||||
|
||||
@@ -222,6 +217,7 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC
|
||||
|
||||
@AfterViews
|
||||
void init() {
|
||||
setTheme(Theme.getDefault(getContext()));
|
||||
setUpEditor();
|
||||
setUpInputMethodEnhancedBar();
|
||||
setUpFunctionsKeyboard();
|
||||
@@ -236,7 +232,7 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC
|
||||
.setContent(mEditor)
|
||||
.setFunctionsTrigger(mShowFunctionsButton)
|
||||
.setFunctionsView(mFunctionsKeyboard)
|
||||
.setEditView(mEditor.getWebView())
|
||||
.setEditView(mEditor.getCodeEditText())
|
||||
.build();
|
||||
mFunctionsKeyboard.setClickCallback(this);
|
||||
}
|
||||
@@ -251,16 +247,9 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC
|
||||
|
||||
|
||||
private void setUpEditor() {
|
||||
setTheme(PreferenceManager.getDefaultSharedPreferences(getContext())
|
||||
.getString(KEY_EDITOR_THEME, mEditor.getTheme()));
|
||||
mEditor.setCallback((line, cursor) -> {
|
||||
if (mInitialText) {
|
||||
mInitialText = false;
|
||||
} else {
|
||||
mTextChanged = true;
|
||||
setMenuItemStatus(R.id.save, true);
|
||||
autoComplete(line, cursor);
|
||||
}
|
||||
mEditor.setCursorChangeCallback((line, cursor) -> {
|
||||
setMenuItemStatus(R.id.save, mEditor.isTextChanged());
|
||||
autoComplete(line, cursor);
|
||||
});
|
||||
|
||||
|
||||
@@ -270,10 +259,11 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC
|
||||
mAutoCompletion.onCursorChange(line, cursor);
|
||||
}
|
||||
|
||||
public void setTheme(String theme) {
|
||||
public void setTheme(Theme theme) {
|
||||
mEditorTheme = theme;
|
||||
mEditor.setTheme(theme);
|
||||
mInputMethodEnhanceBar.setBackgroundColor(InputMethodEnhancedBarColors.getBackgroundColor(theme));
|
||||
int textColor = InputMethodEnhancedBarColors.getTextColor(theme);
|
||||
mInputMethodEnhanceBar.setBackgroundColor(theme.getImeBarBackgroundColor());
|
||||
int textColor = theme.getImeBarForegroundColor();
|
||||
mCodeCompletionBar.setTextColor(textColor);
|
||||
mSymbolBar.setTextColor(textColor);
|
||||
mShowFunctionsButton.setColorFilter(textColor);
|
||||
@@ -321,7 +311,7 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC
|
||||
.doOnNext(s -> PFiles.write(mFile, s))
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnNext(s -> {
|
||||
mTextChanged = false;
|
||||
mEditor.markTextAsSaved();
|
||||
setMenuItemStatus(R.id.save, false);
|
||||
});
|
||||
}
|
||||
@@ -362,7 +352,7 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC
|
||||
}
|
||||
|
||||
public boolean isTextChanged() {
|
||||
return mTextChanged;
|
||||
return mEditor.isTextChanged();
|
||||
}
|
||||
|
||||
public void showConsole() {
|
||||
@@ -381,22 +371,10 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC
|
||||
}
|
||||
|
||||
public void selectEditorTheme() {
|
||||
String[] themes = mEditor.getAvailableThemes();
|
||||
int i = Arrays.asList(themes).indexOf(mEditor.getTheme());
|
||||
new MaterialDialog.Builder(getContext())
|
||||
.title(R.string.text_editor_theme)
|
||||
.items((CharSequence[]) themes)
|
||||
.itemsCallbackSingleChoice(i, (dialog, itemView, which, text) -> {
|
||||
PreferenceManager.getDefaultSharedPreferences(getContext()).edit()
|
||||
.putString(KEY_EDITOR_THEME, text.toString())
|
||||
.apply();
|
||||
setTheme(text.toString());
|
||||
return true;
|
||||
})
|
||||
.show();
|
||||
|
||||
}
|
||||
|
||||
public CodeMirrorEditor getEditor() {
|
||||
public CodeEditor getEditor() {
|
||||
return mEditor;
|
||||
}
|
||||
|
||||
@@ -466,7 +444,7 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC
|
||||
mEditor.insert(m.getName() + "." + p);
|
||||
}
|
||||
if (!property.isVariable()) {
|
||||
mEditor.moveCursor(0, -1);
|
||||
mEditor.moveCursor(-1);
|
||||
}
|
||||
mFunctionsKeyboardHelper.hideFunctionsLayout(true);
|
||||
}
|
||||
|
||||
@@ -1,163 +0,0 @@
|
||||
package com.stardust.scriptdroid.ui.edit;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.RequiresApi;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.inputmethod.CompletionInfo;
|
||||
import android.view.inputmethod.CorrectionInfo;
|
||||
import android.view.inputmethod.ExtractedText;
|
||||
import android.view.inputmethod.ExtractedTextRequest;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
import android.view.inputmethod.InputContentInfo;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/11/4.
|
||||
*/
|
||||
|
||||
public class InputConnectProxy implements InputConnection {
|
||||
|
||||
private InputConnection mInputConnection;
|
||||
|
||||
public InputConnectProxy(InputConnection inputConnection) {
|
||||
mInputConnection = inputConnection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getTextBeforeCursor(int n, int flags) {
|
||||
return mInputConnection.getTextBeforeCursor(n, flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getTextAfterCursor(int n, int flags) {
|
||||
return mInputConnection.getTextAfterCursor(n, flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getSelectedText(int flags) {
|
||||
return mInputConnection.getSelectedText(flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCursorCapsMode(int reqModes) {
|
||||
return mInputConnection.getCursorCapsMode(reqModes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
|
||||
return mInputConnection.getExtractedText(request, flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
|
||||
return mInputConnection.deleteSurroundingText(beforeLength, afterLength);
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
@Override
|
||||
public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
|
||||
return mInputConnection.deleteSurroundingTextInCodePoints(beforeLength, afterLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setComposingText(CharSequence text, int newCursorPosition) {
|
||||
return mInputConnection.setComposingText(text, newCursorPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setComposingRegion(int start, int end) {
|
||||
return mInputConnection.setComposingRegion(start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean finishComposingText() {
|
||||
return mInputConnection.finishComposingText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean commitText(CharSequence text, int newCursorPosition) {
|
||||
return mInputConnection.commitText(text, newCursorPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean commitCompletion(CompletionInfo text) {
|
||||
return mInputConnection.commitCompletion(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean commitCorrection(CorrectionInfo correctionInfo) {
|
||||
return mInputConnection.commitCorrection(correctionInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setSelection(int start, int end) {
|
||||
return mInputConnection.setSelection(start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performEditorAction(int editorAction) {
|
||||
return mInputConnection.performEditorAction(editorAction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performContextMenuAction(int id) {
|
||||
return mInputConnection.performContextMenuAction(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean beginBatchEdit() {
|
||||
return mInputConnection.beginBatchEdit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean endBatchEdit() {
|
||||
return mInputConnection.endBatchEdit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sendKeyEvent(KeyEvent event) {
|
||||
return mInputConnection.sendKeyEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean clearMetaKeyStates(int states) {
|
||||
return mInputConnection.clearMetaKeyStates(states);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean reportFullscreenMode(boolean enabled) {
|
||||
return mInputConnection.reportFullscreenMode(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performPrivateCommand(String action, Bundle data) {
|
||||
return mInputConnection.performPrivateCommand(action, data);
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
@Override
|
||||
public boolean requestCursorUpdates(int cursorUpdateMode) {
|
||||
return mInputConnection.requestCursorUpdates(cursorUpdateMode);
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
@Override
|
||||
public Handler getHandler() {
|
||||
return mInputConnection.getHandler();
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
@Override
|
||||
public void closeConnection() {
|
||||
mInputConnection.closeConnection();
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.N_MR1)
|
||||
@Override
|
||||
public boolean commitContent(@NonNull InputContentInfo inputContentInfo, int flags, @Nullable Bundle opts) {
|
||||
return mInputConnection.commitContent(inputContentInfo, flags, opts);
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package com.stardust.scriptdroid.ui.edit;
|
||||
|
||||
import android.util.SparseArray;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2018/2/4.
|
||||
*/
|
||||
|
||||
public class SharedVariables {
|
||||
|
||||
private static SparseArray<Object> sSharedVariables = new SparseArray<>();
|
||||
private static AtomicInteger sMaxId = new AtomicInteger();
|
||||
|
||||
|
||||
public static int put(Object value) {
|
||||
int id = sMaxId.getAndIncrement();
|
||||
sSharedVariables.put(id, value);
|
||||
return id;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T remove(int id) {
|
||||
Object o = sSharedVariables.get(id);
|
||||
if (o != null)
|
||||
sSharedVariables.remove(id);
|
||||
return (T) o;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
package com.stardust.scriptdroid.ui.edit.completion;
|
||||
|
||||
import android.graphics.Color;
|
||||
|
||||
import com.stardust.util.MapEntries;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/9/28.
|
||||
*/
|
||||
|
||||
public class InputMethodEnhancedBarColors {
|
||||
|
||||
private static final Map<String, Integer> sEditorThemeColors = new MapEntries<String, Integer>()
|
||||
.entry("3024-day", 0xf7f7f7)
|
||||
.entry("3024-night", 0x090300)
|
||||
.entry("abcdef", 0x0f0f0f)
|
||||
.entry("ambiance-mobile", 0xffffff)
|
||||
.entry("ambiance", 0xffffff)
|
||||
.entry("base16-dark", 0x151515)
|
||||
.entry("base16-light", 0xf5f5f5)
|
||||
.entry("bespin", 0x28211c)
|
||||
.entry("dracula", 0x282a36)
|
||||
.entry("duotone-dark", 0x2a2734)
|
||||
.entry("duotone-light", 0xfaf8f5)
|
||||
.entry("eclipse", 0xffffff)
|
||||
.entry("icecoder", 0x1d1d1b)
|
||||
.entry("material", 0x263238)
|
||||
.entry("mbo", 0x2c2c2c)
|
||||
.entry("mdn-like", 0xfefefe)
|
||||
.entry("monokai", 0x272822)
|
||||
.entry("neat", 0xffffff)
|
||||
.entry("neo", 0xffffff)
|
||||
.entry("night", 0x0a001f)
|
||||
.entry("panda-syntax", 0x292a2b)
|
||||
.entry("paraiso-dark", 0x2f1e2e)
|
||||
.entry("paraiso-light", 0xe7e9db)
|
||||
.entry("pastel-on-dark", 0x2c2827)
|
||||
.entry("railscasts", 0x2b2b2b)
|
||||
.entry("rubyblue", 0x112435)
|
||||
.entry("seti", 0x151718)
|
||||
.entry("solarized", 0xffffff)
|
||||
.entry("the-matrix", 0x000000)
|
||||
.entry("tomorrow-night-bright", 0x000000)
|
||||
.entry("tomorrow-night-eighties", 0x000000)
|
||||
.entry("ttcn", 0xffffff)
|
||||
.entry("twilight", 0x141414)
|
||||
.entry("xq-light", 0xffffff)
|
||||
.entry("zenburn", 0x3f3f3f)
|
||||
.map();
|
||||
|
||||
public static int getBackgroundColor(String theme) {
|
||||
int themeColor = sEditorThemeColors.get(theme);
|
||||
if (themeColor == 0xffffff) {
|
||||
return 0x828389;
|
||||
}
|
||||
return themeColor | 0xdd000000;
|
||||
}
|
||||
|
||||
public static int getTextColor(String theme) {
|
||||
int c = sEditorThemeColors.get(theme);
|
||||
if (isBrightColor(c)) {
|
||||
return Color.BLACK;
|
||||
} else {
|
||||
return Color.WHITE;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isBrightColor(int color) {
|
||||
int[] rgb = {Color.red(color), Color.green(color), Color.blue(color)};
|
||||
int brightness = (int) Math.sqrt(rgb[0] * rgb[0] * .241 + rgb[1]
|
||||
* rgb[1] * .691 + rgb[2] * rgb[2] * .068);
|
||||
return brightness >= 200;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
/**
|
||||
* Copyright 2018 WHO<980008027@qq.com>
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* <p>
|
||||
* Modified by project: https://github.com/980008027/JsDroidEditor
|
||||
*/
|
||||
package com.stardust.scriptdroid.ui.edit.editor;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Typeface;
|
||||
import android.support.v7.widget.AppCompatEditText;
|
||||
import android.text.Editable;
|
||||
import android.text.Layout;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
|
||||
import com.stardust.scriptdroid.ui.edit.theme.Theme;
|
||||
|
||||
/**
|
||||
* Created by Administrator on 2018/2/11.
|
||||
*/
|
||||
|
||||
public class CodeEditText extends AppCompatEditText {
|
||||
|
||||
|
||||
private static final String LOG_TAG = "CodeEditText";
|
||||
// 文字范围
|
||||
protected HVScrollView mScrollView;
|
||||
|
||||
private CodeEditor.CursorChangeCallback mCallback;
|
||||
private volatile JavaScriptHighlighter.HighlightTokens mHighlightTokens;
|
||||
private Theme mTheme;
|
||||
|
||||
public CodeEditText(Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public CodeEditText(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
setGravity(Gravity.START);
|
||||
// 设值背景透明
|
||||
setBackgroundColor(Color.TRANSPARENT);
|
||||
// 设置字体颜色
|
||||
setTextColor(Color.TRANSPARENT);
|
||||
// 设置字体
|
||||
setTypeface(Typeface.MONOSPACE);
|
||||
mTheme = Theme.getDefault(getContext());
|
||||
}
|
||||
|
||||
public void setTheme(Theme theme) {
|
||||
mTheme = theme;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
if (mScrollView == null) {
|
||||
mScrollView = (HVScrollView) getParent();
|
||||
}
|
||||
// 根据行号计算左边距padding
|
||||
String max = Integer.toString(getLineCount());
|
||||
float lineNumberSize = getPaint().measureText(max) + 20;
|
||||
if (getPaddingLeft() != lineNumberSize) {
|
||||
setPadding((int) lineNumberSize, 0, 0, 0);
|
||||
invalidate();
|
||||
}
|
||||
super.onDraw(canvas);
|
||||
// 画文字
|
||||
canvas.save();
|
||||
canvas.translate(0, getExtendedPaddingTop());
|
||||
drawText(canvas);
|
||||
canvas.restore();
|
||||
|
||||
}
|
||||
|
||||
// 绘制文本着色
|
||||
private void drawText(Canvas canvas) {
|
||||
JavaScriptHighlighter.HighlightTokens highlightTokens = mHighlightTokens;
|
||||
Layout layout = getLayout();
|
||||
long lineRange = getLineRangeForDraw(layout, canvas);
|
||||
int firstLineForDraw = LayoutHelper.unpackRangeStartFromLong(lineRange);
|
||||
int lastLineForDraw = LayoutHelper.unpackRangeEndFromLong(lineRange);
|
||||
if (firstLineForDraw < 0) {
|
||||
return;
|
||||
}
|
||||
int lineCount = getLineCount();
|
||||
int paddingLeft = getPaddingLeft();
|
||||
Paint paint = getPaint();
|
||||
for (int line = firstLineForDraw; line <= lastLineForDraw && line < lineCount; line++) {
|
||||
int lineBottom = layout.getLineTop(line + 1);
|
||||
int lineBaseline = lineBottom - layout.getLineDescent(line);
|
||||
|
||||
drawLineNumber(canvas, paint, line, lineBaseline);
|
||||
if (highlightTokens == null)
|
||||
continue;
|
||||
|
||||
drawCode(canvas, paint, paddingLeft, line, layout, lineBaseline, highlightTokens);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void drawCode(Canvas canvas, Paint paint, int paddingLeft, int line, Layout layout, int lineBaseline, JavaScriptHighlighter.HighlightTokens highlightTokens) {
|
||||
int lineStart = layout.getLineStart(line);
|
||||
if (lineStart >= mHighlightTokens.getText().length()) {
|
||||
return;
|
||||
}
|
||||
int lineEnd = layout.getLineVisibleEnd(line);
|
||||
int fontCount = 0;
|
||||
int previousColor = mHighlightTokens.getCharColor(lineStart);
|
||||
int previousColorPos = lineStart;
|
||||
for (int i = lineStart; i < lineEnd; i++) {
|
||||
fontCount++;
|
||||
int color = mHighlightTokens.getCharColor(i);
|
||||
if (previousColor != color) {
|
||||
drawText(canvas, paint, paddingLeft, lineBaseline, lineStart, previousColorPos, previousColorPos + fontCount, previousColor);
|
||||
previousColor = color;
|
||||
previousColorPos = i;
|
||||
fontCount = 1;
|
||||
}
|
||||
if (i == lineEnd - 1) {
|
||||
drawText(canvas, paint, paddingLeft, lineBaseline, lineStart, previousColorPos, previousColorPos + fontCount, previousColor);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void drawText(Canvas canvas, Paint paint, int paddingLeft, int lineBaseline, int lineStart, int start, int end, int color) {
|
||||
if (start >= end) {
|
||||
return;
|
||||
}
|
||||
paint.setColor(color);
|
||||
float offsetX = paint.measureText(getText(), lineStart, start);
|
||||
canvas.drawText(getText(), start, end, paddingLeft + offsetX, lineBaseline, paint);
|
||||
}
|
||||
|
||||
private void drawLineNumber(Canvas canvas, Paint paint, int line, int lineBaseline) {
|
||||
String lineNumberText = Integer.toString(line + 1);
|
||||
paint.setColor(mTheme.getLineNumberColor());
|
||||
canvas.drawText(lineNumberText, 0, lineNumberText.length(), 10,
|
||||
lineBaseline, paint);
|
||||
}
|
||||
|
||||
private long getLineRangeForDraw(Layout layout, Canvas canvas) {
|
||||
canvas.save();
|
||||
float clipTop = (mScrollView.getScrollY() == 0) ? 0
|
||||
: getExtendedPaddingTop() + mScrollView.getScrollY()
|
||||
- mScrollView.getPaddingTop();
|
||||
canvas.clipRect(0, clipTop, getWidth(), mScrollView.getScrollY()
|
||||
+ mScrollView.getHeight());
|
||||
long lineRangeForDraw = LayoutHelper.getLineRangeForDraw(layout, canvas);
|
||||
canvas.restore();
|
||||
return lineRangeForDraw;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSelectionChanged(int selStart, int selEnd) {
|
||||
super.onSelectionChanged(selStart, selEnd);
|
||||
if (mCallback == null || selStart != selEnd) {
|
||||
return;
|
||||
}
|
||||
String text = getText().toString();
|
||||
if (text.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
int lineStart = text.lastIndexOf("\n", selStart) + 1;
|
||||
if (lineStart < 0) {
|
||||
lineStart = 0;
|
||||
}
|
||||
if (lineStart > text.length() - 1) {
|
||||
lineStart = text.length() - 1;
|
||||
}
|
||||
int lineEnd = text.indexOf("\n", selStart);
|
||||
if (lineEnd < 0) {
|
||||
lineEnd = text.length();
|
||||
}
|
||||
if (lineEnd < lineStart || lineStart < 0 || lineEnd > text.length())
|
||||
return;
|
||||
String line = text.substring(lineStart, lineEnd);
|
||||
int cursor = selStart - lineStart;
|
||||
mCallback.onCursorChange(line, cursor);
|
||||
}
|
||||
|
||||
public void setCursorChangeCallback(CodeEditor.CursorChangeCallback callback) {
|
||||
mCallback = callback;
|
||||
}
|
||||
|
||||
public void updateHighlightTokens(JavaScriptHighlighter.HighlightTokens highlightTokens) {
|
||||
mHighlightTokens = highlightTokens;
|
||||
Log.d(LOG_TAG, "tokens = " + highlightTokens);
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
package com.stardust.scriptdroid.ui.edit.editor;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import com.stardust.scriptdroid.R;
|
||||
import com.stardust.scriptdroid.ui.edit.theme.Theme;
|
||||
import com.stardust.util.ClipboardUtil;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
|
||||
/**
|
||||
* Copyright 2018 WHO<980008027@qq.com>
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* <p>
|
||||
* Modified by project: https://github.com/980008027/JsDroidEditor
|
||||
*/
|
||||
public class CodeEditor extends HVScrollView {
|
||||
|
||||
|
||||
private CharSequence mReplacement = "";
|
||||
private String mKeywords;
|
||||
private int mFoundIndex = -1;
|
||||
|
||||
public Observable<Integer> getLineCount() {
|
||||
return Observable.just(mCodeEditText.getLayout().getLineCount());
|
||||
}
|
||||
|
||||
public void copyLine() {
|
||||
int line = getLineOfChar(mCodeEditText.getSelectionStart());
|
||||
if (line < 0 || line >= mCodeEditText.getLayout().getLineCount())
|
||||
return;
|
||||
CharSequence lineText = mCodeEditText.getText().subSequence(mCodeEditText.getLayout().getLineStart(line),
|
||||
mCodeEditText.getLayout().getLineEnd(line));
|
||||
ClipboardUtil.setClip(getContext(), lineText);
|
||||
Snackbar.make(this, R.string.text_already_copy_to_clip, Snackbar.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
private int getLineOfChar(int i) {
|
||||
int low = 0;
|
||||
int high = mCodeEditText.getLineCount() - 1;
|
||||
while (low < high) {
|
||||
int mid = (low + high) >>> 1;
|
||||
int midVal = mCodeEditText.getLayout().getLineEnd(mid);
|
||||
|
||||
if (midVal <= i)
|
||||
low = mid + 1;
|
||||
else if (midVal > i)
|
||||
high = mid - 1;
|
||||
}
|
||||
return low;
|
||||
}
|
||||
|
||||
public void deleteLine() {
|
||||
int line = getLineOfChar(mCodeEditText.getSelectionStart());
|
||||
if (line < 0 || line >= mCodeEditText.getLayout().getLineCount())
|
||||
return;
|
||||
mCodeEditText.getText().replace(mCodeEditText.getLayout().getLineStart(line),
|
||||
mCodeEditText.getLayout().getLineEnd(line), "");
|
||||
}
|
||||
|
||||
public void jumpToStart() {
|
||||
mCodeEditText.setSelection(0);
|
||||
}
|
||||
|
||||
public void jumpToEnd() {
|
||||
mCodeEditText.setSelection(mCodeEditText.getText().length());
|
||||
}
|
||||
|
||||
public void jumpToLineStart() {
|
||||
int line = getLineOfChar(mCodeEditText.getSelectionStart());
|
||||
if (line < 0 || line >= mCodeEditText.getLayout().getLineCount())
|
||||
return;
|
||||
mCodeEditText.setSelection(mCodeEditText.getLayout().getLineStart(line));
|
||||
}
|
||||
|
||||
public void jumpToLineEnd() {
|
||||
int line = getLineOfChar(mCodeEditText.getSelectionStart());
|
||||
if (line < 0 || line >= mCodeEditText.getLayout().getLineCount())
|
||||
return;
|
||||
mCodeEditText.setSelection(mCodeEditText.getLayout().getLineEnd(line) - 1);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public interface CursorChangeCallback {
|
||||
|
||||
void onCursorChange(String line, int ch);
|
||||
|
||||
}
|
||||
|
||||
private CodeEditText mCodeEditText;
|
||||
private PreformEdit mPreformEdit;
|
||||
private JavaScriptHighlighter mJavaScriptHighlighter;
|
||||
private Theme mTheme;
|
||||
|
||||
public CodeEditor(Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public CodeEditor(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
private void init() {
|
||||
setFillViewport(true);
|
||||
inflate(getContext(), R.layout.code_editor, this);
|
||||
mCodeEditText = (CodeEditText) findViewById(R.id.code_edit_text);
|
||||
mPreformEdit = new PreformEdit(mCodeEditText);
|
||||
mJavaScriptHighlighter = new JavaScriptHighlighter(mTheme, mCodeEditText);
|
||||
setTheme(Theme.getDefault(getContext()));
|
||||
|
||||
}
|
||||
|
||||
public void setTheme(Theme theme) {
|
||||
mTheme = theme;
|
||||
mJavaScriptHighlighter.setTheme(theme);
|
||||
setBackgroundColor(mTheme.getBackgroundColor());
|
||||
|
||||
}
|
||||
|
||||
|
||||
public boolean isTextChanged() {
|
||||
return mPreformEdit.isTextChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
|
||||
super.onScrollChanged(l, t, oldl, oldt);
|
||||
mCodeEditText.postInvalidate();
|
||||
}
|
||||
|
||||
public CodeEditText getCodeEditText() {
|
||||
return mCodeEditText;
|
||||
}
|
||||
|
||||
public void setInitialText(String text) {
|
||||
mCodeEditText.setText(text);
|
||||
mPreformEdit.setDefaultText(text);
|
||||
}
|
||||
|
||||
public void jumpTo(int line, int col) {
|
||||
mCodeEditText.setSelection(mCodeEditText.getLayout().getLineStart(line) + col);
|
||||
//int top = mCodeEditText.getLayout().getLineTop(line - 1);
|
||||
//post(() -> scrollTo(0, top));
|
||||
|
||||
}
|
||||
|
||||
public void setReadOnly(boolean readOnly) {
|
||||
setEnabled(!readOnly);
|
||||
}
|
||||
|
||||
public void setProgress(boolean progress) {
|
||||
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
mCodeEditText.setText(text);
|
||||
}
|
||||
|
||||
public void setCursorChangeCallback(CursorChangeCallback callback) {
|
||||
mCodeEditText.setCursorChangeCallback(callback);
|
||||
}
|
||||
|
||||
|
||||
public void undo() {
|
||||
mPreformEdit.undo();
|
||||
}
|
||||
|
||||
public void redo() {
|
||||
mPreformEdit.redo();
|
||||
}
|
||||
|
||||
|
||||
public void findNext() {
|
||||
Matcher matcher = Pattern.compile(mKeywords).matcher(mCodeEditText.getText());
|
||||
if (!matcher.find(mFoundIndex + 1)) {
|
||||
return;
|
||||
}
|
||||
mFoundIndex = matcher.start();
|
||||
mCodeEditText.setSelection(mFoundIndex, mFoundIndex + mKeywords.length());
|
||||
}
|
||||
|
||||
public void findPrev() {
|
||||
// FIXME: 2018/2/21 lastIndexOf不支持正则
|
||||
if (mFoundIndex <= 0) {
|
||||
mFoundIndex = mCodeEditText.getText().length();
|
||||
}
|
||||
int index = mCodeEditText.getText().toString().lastIndexOf(mKeywords, mFoundIndex - 1);
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
mFoundIndex = index;
|
||||
mCodeEditText.setSelection(index, index + mKeywords.length());
|
||||
}
|
||||
|
||||
public void replaceSelection() {
|
||||
mCodeEditText.getText().replace(mCodeEditText.getSelectionStart(), mCodeEditText.getSelectionEnd(), mReplacement);
|
||||
}
|
||||
|
||||
public void beautifyCode() {
|
||||
|
||||
}
|
||||
|
||||
public void find(String keywords, boolean usingRegex) {
|
||||
if (usingRegex) {
|
||||
mKeywords = keywords;
|
||||
} else {
|
||||
mKeywords = Pattern.quote(keywords);
|
||||
}
|
||||
mFoundIndex = -1;
|
||||
findNext();
|
||||
}
|
||||
|
||||
public void replace(String keywords, String replacement, boolean usingRegex) {
|
||||
mReplacement = replacement == null ? "" : replacement;
|
||||
find(keywords, usingRegex);
|
||||
}
|
||||
|
||||
public void replaceAll(String keywords, String replacement, boolean usingRegex) {
|
||||
if (!usingRegex) {
|
||||
keywords = Pattern.quote(keywords);
|
||||
}
|
||||
String text = mCodeEditText.getText().toString();
|
||||
text = text.replaceAll(keywords, replacement);
|
||||
setText(text);
|
||||
}
|
||||
|
||||
public void insert(String insertText) {
|
||||
int selection = Math.max(mCodeEditText.getSelectionStart(), 0);
|
||||
mCodeEditText.getText().insert(selection, insertText);
|
||||
}
|
||||
|
||||
public void moveCursor(int dCh) {
|
||||
mCodeEditText.setSelection(mCodeEditText.getSelectionStart() + dCh);
|
||||
}
|
||||
|
||||
public Observable<String> getText() {
|
||||
return Observable.just(mCodeEditText.getText().toString());
|
||||
}
|
||||
|
||||
public Observable<String> getSelection() {
|
||||
int s = mCodeEditText.getSelectionStart();
|
||||
int e = mCodeEditText.getSelectionEnd();
|
||||
if (s == e) {
|
||||
return Observable.just("");
|
||||
}
|
||||
return Observable.just(mCodeEditText.getText().toString().substring(s, e));
|
||||
}
|
||||
|
||||
|
||||
public void markTextAsSaved() {
|
||||
mPreformEdit.clearHistory();
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,110 @@
|
||||
package com.stardust.scriptdroid.ui.edit.editor;
|
||||
|
||||
|
||||
import android.text.Editable;
|
||||
import android.util.Log;
|
||||
|
||||
import com.stardust.autojs.rhino.TokenStream;
|
||||
import com.stardust.pio.UncheckedIOException;
|
||||
import com.stardust.scriptdroid.ui.edit.theme.Theme;
|
||||
import com.stardust.scriptdroid.ui.widget.SimpleTextWatcher;
|
||||
|
||||
import org.mozilla.javascript.Token;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class JavaScriptHighlighter implements SimpleTextWatcher.AfterTextChangedListener {
|
||||
|
||||
|
||||
public static class HighlightTokens {
|
||||
|
||||
private int[] mColors;
|
||||
private String mText;
|
||||
|
||||
public HighlightTokens(String text) {
|
||||
mColors = new int[text.length()];
|
||||
mText = text;
|
||||
}
|
||||
|
||||
|
||||
public int getCharColor(int i) {
|
||||
return mColors[i];
|
||||
}
|
||||
|
||||
|
||||
public void addToken(int tokenStart, int tokenEnd, int color) {
|
||||
for (int i = tokenStart; i < tokenEnd; i++) {
|
||||
mColors[i] = color;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "HighlightTokens{" +
|
||||
"mColors=" + Arrays.toString(mColors) +
|
||||
'}';
|
||||
}
|
||||
|
||||
public int getCharCount() {
|
||||
return mColors.length;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return mText;
|
||||
}
|
||||
}
|
||||
|
||||
private Theme mTheme;
|
||||
private CodeEditText mCodeEditText;
|
||||
private ExecutorService mExecutorService = Executors.newSingleThreadExecutor();
|
||||
private AtomicInteger mRunningHighlighterId = new AtomicInteger();
|
||||
|
||||
public JavaScriptHighlighter(Theme theme, CodeEditText codeEditText) {
|
||||
mTheme = theme;
|
||||
mCodeEditText = codeEditText;
|
||||
codeEditText.addTextChangedListener(new SimpleTextWatcher(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
updateTokens(s.toString());
|
||||
}
|
||||
|
||||
|
||||
public void setTheme(Theme theme) {
|
||||
mTheme = theme;
|
||||
}
|
||||
|
||||
private void updateTokens(String sourceString) {
|
||||
final int id = mRunningHighlighterId.incrementAndGet();
|
||||
mExecutorService.execute(() -> {
|
||||
try {
|
||||
updateTokens(sourceString, id);
|
||||
} catch (IOException neverHappen) {
|
||||
throw new UncheckedIOException(neverHappen);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private void updateTokens(String sourceString, int id) throws IOException {
|
||||
TokenStream ts = new TokenStream(null, sourceString, 0);
|
||||
HighlightTokens highlightTokens = new HighlightTokens(sourceString);
|
||||
int token;
|
||||
while ((token = ts.getToken()) != Token.EOF) {
|
||||
if (mRunningHighlighterId.get() != id)
|
||||
return;
|
||||
int color = mTheme.getColorForToken(token);
|
||||
highlightTokens.addToken(ts.getTokenBeg(), ts.getTokenEnd(), color);
|
||||
}
|
||||
mCodeEditText.updateHighlightTokens(highlightTokens);
|
||||
Log.d("Highlighter", "code = " + sourceString);
|
||||
Log.d("Highlighter", "tokens = " + highlightTokens);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.stardust.scriptdroid.ui.edit.editor;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
import android.text.Layout;
|
||||
import android.text.TextUtils;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2018/2/13.
|
||||
*/
|
||||
|
||||
public class LayoutHelper {
|
||||
|
||||
|
||||
private static final Rect sTempRect = new Rect();
|
||||
|
||||
public static long getLineRangeForDraw(Layout layout, Canvas canvas) {
|
||||
int dtop, dbottom;
|
||||
synchronized (sTempRect) {
|
||||
if (!canvas.getClipBounds(sTempRect)) {
|
||||
// Negative range end used as a special flag
|
||||
return packRangeInLong(0, -1);
|
||||
}
|
||||
|
||||
dtop = sTempRect.top;
|
||||
dbottom = sTempRect.bottom;
|
||||
}
|
||||
|
||||
final int top = Math.max(dtop, 0);
|
||||
final int bottom = Math.min(layout.getLineTop(layout.getLineCount()), dbottom);
|
||||
|
||||
if (top >= bottom) return packRangeInLong(0, -1);
|
||||
return packRangeInLong(layout.getLineForVertical(top), layout.getLineForVertical(bottom));
|
||||
}
|
||||
|
||||
/**
|
||||
* Pack 2 int values into a long, useful as a return value for a range
|
||||
*/
|
||||
public static long packRangeInLong(int start, int end) {
|
||||
return (((long) start) << 32) | end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the start value from a range packed in a long by {@link #packRangeInLong(int, int)}
|
||||
*/
|
||||
public static int unpackRangeStartFromLong(long range) {
|
||||
return (int) (range >>> 32);
|
||||
}
|
||||
|
||||
public static int unpackRangeEndFromLong(long range) {
|
||||
return (int) (range & 0x00000000FFFFFFFFL);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
package com.stardust.scriptdroid.ui.edit.editor;
|
||||
/*
|
||||
* Copyright 2016. SHENQINCI(沈钦赐)<946736079@qq.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.widget.EditText;
|
||||
|
||||
import java.util.Stack;
|
||||
|
||||
/**
|
||||
* 撤销和恢复撤销
|
||||
* Created by 沈钦赐 on 16/6/23.
|
||||
*/
|
||||
public class PreformEdit {
|
||||
//操作序号(一次编辑可能对应多个操作,如替换文字,就是删除+插入)
|
||||
int index;
|
||||
//撤销栈
|
||||
Stack<Action> history = new Stack<>();
|
||||
//恢复栈
|
||||
Stack<Action> historyBack = new Stack<>();
|
||||
|
||||
private Editable editable;
|
||||
private EditText editText;
|
||||
//自动操作标志,防止重复回调,导致无限撤销
|
||||
private boolean flag = false;
|
||||
|
||||
public PreformEdit(@NonNull EditText editText) {
|
||||
CheckNull(editText, "EditText不能为空");
|
||||
this.editable = editText.getText();
|
||||
this.editText = editText;
|
||||
editText.addTextChangedListener(new Watcher());
|
||||
}
|
||||
|
||||
protected void onEditableChanged(Editable s) {
|
||||
|
||||
}
|
||||
|
||||
protected void onTextChanged(Editable s) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 清理记录
|
||||
* Clear history.
|
||||
*/
|
||||
public final void clearHistory() {
|
||||
history.clear();
|
||||
historyBack.clear();
|
||||
}
|
||||
|
||||
public boolean canUndo() {
|
||||
return !history.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤销
|
||||
* Undo.
|
||||
*/
|
||||
public final void undo() {
|
||||
if (history.empty()) return;
|
||||
//锁定操作
|
||||
flag = true;
|
||||
Action action = history.pop();
|
||||
historyBack.push(action);
|
||||
if (action.isAdd) {
|
||||
//撤销添加
|
||||
editable.delete(action.startCursor, action.startCursor + action.actionTarget.length());
|
||||
editText.setSelection(action.startCursor, action.startCursor);
|
||||
} else {
|
||||
//插销删除
|
||||
editable.insert(action.startCursor, action.actionTarget);
|
||||
if (action.endCursor == action.startCursor) {
|
||||
editText.setSelection(action.startCursor + action.actionTarget.length());
|
||||
} else {
|
||||
editText.setSelection(action.startCursor, action.endCursor);
|
||||
}
|
||||
}
|
||||
//释放操作
|
||||
flag = false;
|
||||
//判断是否是下一个动作是否和本动作是同一个操作,直到不同为止
|
||||
if (!history.empty() && history.peek().index == action.index) {
|
||||
undo();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canRedo() {
|
||||
return !historyBack.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复
|
||||
* Redo.
|
||||
*/
|
||||
public final void redo() {
|
||||
if (historyBack.empty()) return;
|
||||
flag = true;
|
||||
Action action = historyBack.pop();
|
||||
history.push(action);
|
||||
if (action.isAdd) {
|
||||
//恢复添加
|
||||
editable.insert(action.startCursor, action.actionTarget);
|
||||
if (action.endCursor == action.startCursor) {
|
||||
editText.setSelection(action.startCursor + action.actionTarget.length());
|
||||
} else {
|
||||
editText.setSelection(action.startCursor, action.endCursor);
|
||||
}
|
||||
} else {
|
||||
//恢复删除
|
||||
editable.delete(action.startCursor, action.startCursor + action.actionTarget.length());
|
||||
editText.setSelection(action.startCursor, action.startCursor);
|
||||
}
|
||||
flag = false;
|
||||
//判断是否是下一个动作是否和本动作是同一个操作
|
||||
if (!historyBack.empty() && historyBack.peek().index == action.index)
|
||||
redo();
|
||||
}
|
||||
|
||||
/**
|
||||
* 首次设置文本
|
||||
* Set default text.
|
||||
*/
|
||||
public final void setDefaultText(CharSequence text) {
|
||||
clearHistory();
|
||||
flag = true;
|
||||
editable.replace(0, editable.length(), text);
|
||||
flag = false;
|
||||
}
|
||||
|
||||
public boolean isTextChanged() {
|
||||
return !history.empty();
|
||||
}
|
||||
|
||||
private class Watcher implements TextWatcher {
|
||||
|
||||
/**
|
||||
* Before text changed.
|
||||
*
|
||||
* @param s the s
|
||||
* @param start the start 起始光标
|
||||
* @param count the endCursor 选择数量
|
||||
* @param after the after 替换增加的文字数
|
||||
*/
|
||||
@Override
|
||||
public final void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
if (flag) return;
|
||||
int end = start + count;
|
||||
if (end > start && end <= s.length()) {
|
||||
CharSequence charSequence = s.subSequence(start, end);
|
||||
//删除了文字
|
||||
if (charSequence.length() > 0) {
|
||||
Action action = new Action(charSequence, start, false);
|
||||
if (count > 1) {
|
||||
//如果一次超过一个字符,说名用户选择了,然后替换或者删除操作
|
||||
action.setSelectCount(count);
|
||||
} else if (count == 1 && count == after) {
|
||||
//一个字符替换
|
||||
action.setSelectCount(count);
|
||||
}
|
||||
//还有一种情况:选择一个字符,然后删除(暂时没有考虑这种情况)
|
||||
|
||||
history.push(action);
|
||||
historyBack.clear();
|
||||
action.setIndex(++index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* On text changed.
|
||||
*
|
||||
* @param s the s
|
||||
* @param start the start 起始光标
|
||||
* @param before the before 选择数量
|
||||
* @param count the endCursor 添加的数量
|
||||
*/
|
||||
@Override
|
||||
public final void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
if (flag) return;
|
||||
int end = start + count;
|
||||
if (end > start) {
|
||||
CharSequence charSequence = s.subSequence(start, end);
|
||||
//添加文字
|
||||
if (charSequence.length() > 0) {
|
||||
Action action = new Action(charSequence, start, true);
|
||||
|
||||
history.push(action);
|
||||
historyBack.clear();
|
||||
if (before > 0) {
|
||||
//文字替换(先删除再增加),删除和增加是同一个操作,所以不需要增加序号
|
||||
action.setIndex(index);
|
||||
} else {
|
||||
action.setIndex(++index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void afterTextChanged(Editable s) {
|
||||
if (flag) return;
|
||||
if (s != editable) {
|
||||
editable = s;
|
||||
onEditableChanged(s);
|
||||
}
|
||||
PreformEdit.this.onTextChanged(s);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class Action {
|
||||
/**
|
||||
* 改变字符.
|
||||
*/
|
||||
CharSequence actionTarget;
|
||||
/**
|
||||
* 光标位置.
|
||||
*/
|
||||
int startCursor;
|
||||
int endCursor;
|
||||
/**
|
||||
* 标志增加操作.
|
||||
*/
|
||||
boolean isAdd;
|
||||
/**
|
||||
* 操作序号.
|
||||
*/
|
||||
int index;
|
||||
|
||||
|
||||
public Action(CharSequence actionTag, int startCursor, boolean add) {
|
||||
this.actionTarget = actionTag;
|
||||
this.startCursor = startCursor;
|
||||
this.endCursor = startCursor;
|
||||
this.isAdd = add;
|
||||
}
|
||||
|
||||
public void setSelectCount(int count) {
|
||||
this.endCursor = endCursor + count;
|
||||
}
|
||||
|
||||
public void setIndex(int index) {
|
||||
this.index = index;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void CheckNull(Object o, String message) {
|
||||
if (o == null) throw new IllegalStateException(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package com.stardust.scriptdroid.ui.edit.theme;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.util.SparseArray;
|
||||
import android.util.SparseIntArray;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.stardust.scriptdroid.model.editor.EditorTheme;
|
||||
import com.stardust.scriptdroid.model.editor.TokenColor;
|
||||
|
||||
import org.mozilla.javascript.Context;
|
||||
import org.mozilla.javascript.Token;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.mozilla.javascript.Token.*;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2018/2/16.
|
||||
*/
|
||||
|
||||
public class Theme {
|
||||
|
||||
|
||||
private int mBackgroundColor;
|
||||
private int mForegroundColor;
|
||||
private int mLineNumberColor;
|
||||
private SparseIntArray mTokenColors = new SparseIntArray();
|
||||
private int mImeBarBackgroundColor;
|
||||
private int mImeBarForegroundColor;
|
||||
|
||||
public Theme(EditorTheme theme) {
|
||||
mBackgroundColor = Color.parseColor(theme.getEditorColors().getEditorBackground());
|
||||
mForegroundColor = Color.parseColor(theme.getEditorColors().getEditorForeground());
|
||||
mLineNumberColor = Color.parseColor(theme.getEditorColors().getEditorIndentGuideBackground());
|
||||
mImeBarBackgroundColor = Color.parseColor(theme.getEditorColors().getImeBackgroundColor());
|
||||
mImeBarForegroundColor = Color.parseColor(theme.getEditorColors().getImeForegroundColor());
|
||||
|
||||
for (TokenColor tokenColor : theme.getTokenColors()) {
|
||||
String foregroundStr = tokenColor.getSettings().getForeground();
|
||||
if (foregroundStr == null)
|
||||
continue;
|
||||
int foreground = Color.parseColor(foregroundStr);
|
||||
for (String scope : tokenColor.getScope()) {
|
||||
setTokenColor(scope, foreground);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setTokenColor(String scope, int foreground) {
|
||||
for (int token : TokenMapping.getTokensForScope(scope)) {
|
||||
mTokenColors.put(token, foreground);
|
||||
}
|
||||
}
|
||||
|
||||
public int getColorForToken(int token) {
|
||||
return mTokenColors.get(token, mForegroundColor);
|
||||
}
|
||||
|
||||
public int getLineNumberColor() {
|
||||
return mLineNumberColor;
|
||||
}
|
||||
|
||||
public static Theme getDefault(android.content.Context context) {
|
||||
return fromAssetsJson(context, "editor/theme/dark_plus.json");
|
||||
}
|
||||
|
||||
public static Theme fromJson(String json) {
|
||||
EditorTheme theme = EditorTheme.fromJson(json);
|
||||
if (theme == null)
|
||||
return null;
|
||||
return new Theme(theme);
|
||||
}
|
||||
|
||||
|
||||
public static Theme fromJson(Reader reader) {
|
||||
EditorTheme theme = EditorTheme.fromJson(reader);
|
||||
if (theme == null)
|
||||
return null;
|
||||
return new Theme(theme);
|
||||
}
|
||||
|
||||
public static Theme fromAssetsJson(android.content.Context context, String assetsPath) {
|
||||
try {
|
||||
return fromJson(new InputStreamReader(context.getAssets().open(assetsPath)));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public int getBackgroundColor() {
|
||||
return mBackgroundColor;
|
||||
}
|
||||
|
||||
public int getImeBarBackgroundColor() {
|
||||
return mImeBarBackgroundColor;
|
||||
}
|
||||
|
||||
public void setImeBarBackgroundColor(int imeBarBackgroundColor) {
|
||||
mImeBarBackgroundColor = imeBarBackgroundColor;
|
||||
}
|
||||
|
||||
public int getImeBarForegroundColor() {
|
||||
return mImeBarForegroundColor;
|
||||
}
|
||||
|
||||
public void setImeBarForegroundColor(int imeBarForegroundColor) {
|
||||
mImeBarForegroundColor = imeBarForegroundColor;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.stardust.scriptdroid.ui.edit.theme;
|
||||
|
||||
|
||||
import org.mozilla.javascript.Token;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2018/2/21.
|
||||
*/
|
||||
|
||||
public class TokenMapping {
|
||||
|
||||
|
||||
private static final List<Integer> KEYWORD = tokenNamesToTypes(Arrays.asList("return", "new", "delete", "typeof", "null", "this", "false", "true", "throw", "in", "instanceof", "yield", "try", "function", "if", "else", "switch", "case", "default", "while", "do", "for", "break", "continue", "var", "with", "catch", "finally", "void", "let", "const", "debugger"));
|
||||
private static final List<Integer> KEYWORD_CONTROL = tokenNamesToTypes(Arrays.asList("if", "else", "switch", "case", "break", "continue", "goto", "return", "try", "catch", "throw", "finally"));
|
||||
private static final List<Integer> KEYWORD_OPERATOR = Collections.emptyList();
|
||||
|
||||
public static List<Integer> getTokensForScope(String scope) {
|
||||
switch (scope) {
|
||||
case "keyword":
|
||||
return KEYWORD;
|
||||
case "keyword.operator":
|
||||
return KEYWORD_OPERATOR;
|
||||
case "keyword.control":
|
||||
return KEYWORD_CONTROL;
|
||||
default:
|
||||
int token = tokenNameToType(scope);
|
||||
if (Token.isValidToken(token)) {
|
||||
return Collections.singletonList(token);
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public static int tokenNameToType(String name) {
|
||||
switch (name) {
|
||||
case "this.self":
|
||||
return Token.THIS;
|
||||
case "keyword.operator.quantifier.regexp":
|
||||
return Token.REGEXP;
|
||||
case "variable":
|
||||
return Token.NAME;
|
||||
case "constant.numeric":
|
||||
return Token.NUMBER;
|
||||
|
||||
}
|
||||
for (int token = Token.ERROR; token < Token.LAST_TOKEN; token++) {
|
||||
if (token == Token.STRICT_SETNAME || token == Token.SETCONSTVAR)
|
||||
continue;
|
||||
if (Token.typeToName(token).equalsIgnoreCase(name)) {
|
||||
return token;
|
||||
}
|
||||
}
|
||||
return -2;
|
||||
}
|
||||
|
||||
private static List<Integer> tokenNamesToTypes(List<String> tokenNames) {
|
||||
List<Integer> types = new ArrayList<>(tokenNames.size());
|
||||
for (String name : tokenNames) {
|
||||
types.add(tokenNameToType(name));
|
||||
}
|
||||
return types;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.stardust.scriptdroid.ui.widget;
|
||||
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2018/2/16.
|
||||
*/
|
||||
|
||||
public class SimpleTextWatcher implements TextWatcher {
|
||||
|
||||
public interface AfterTextChangedListener {
|
||||
void afterTextChanged(Editable s);
|
||||
}
|
||||
|
||||
public interface BeforeTextChangedListener {
|
||||
void beforeTextChanged(CharSequence s, int start, int count, int after);
|
||||
}
|
||||
|
||||
public interface OnTextChangedListener {
|
||||
void onTextChanged(CharSequence s, int start, int before, int count);
|
||||
}
|
||||
|
||||
private BeforeTextChangedListener mBeforeTextChangedListener;
|
||||
private OnTextChangedListener mOnTextChangedListener;
|
||||
private AfterTextChangedListener mAfterTextChangedListener;
|
||||
|
||||
public SimpleTextWatcher(BeforeTextChangedListener beforeTextChangedListener, OnTextChangedListener onTextChangedListener, AfterTextChangedListener afterTextChangedListener) {
|
||||
mBeforeTextChangedListener = beforeTextChangedListener;
|
||||
mOnTextChangedListener = onTextChangedListener;
|
||||
mAfterTextChangedListener = afterTextChangedListener;
|
||||
}
|
||||
|
||||
public SimpleTextWatcher(AfterTextChangedListener afterTextChangedListener) {
|
||||
mAfterTextChangedListener = afterTextChangedListener;
|
||||
}
|
||||
|
||||
public SimpleTextWatcher(BeforeTextChangedListener beforeTextChangedListener) {
|
||||
mBeforeTextChangedListener = beforeTextChangedListener;
|
||||
}
|
||||
|
||||
public SimpleTextWatcher(OnTextChangedListener onTextChangedListener) {
|
||||
mOnTextChangedListener = onTextChangedListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
if (mBeforeTextChangedListener != null) {
|
||||
mBeforeTextChangedListener.beforeTextChanged(s, start, count, after);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
if (mOnTextChangedListener != null) {
|
||||
mOnTextChangedListener.onTextChanged(s, start, before, count);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
if (mAfterTextChangedListener != null) {
|
||||
mAfterTextChangedListener.afterTextChanged(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user