├── .github
└── workflows
│ └── build-debug.yml
├── .gitignore
├── LICENSE.md
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── mrikso
│ │ └── texteditor
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── mrikso
│ │ │ └── texteditor
│ │ │ ├── MainActivity.java
│ │ │ └── TextEditor.java
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ └── activity_main.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── mrikso
│ └── texteditor
│ └── ExampleUnitTest.java
├── build.gradle
├── codeeditor
├── .gitignore
├── build.gradle
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── mrikso
│ │ └── codeeditor
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── mrikso
│ │ │ └── codeeditor
│ │ │ ├── common
│ │ │ ├── OnCaretScrollListener.java
│ │ │ ├── OnKeyShortcutListener.java
│ │ │ ├── OnRowChangedListener.java
│ │ │ ├── OnSelectionChangedListener.java
│ │ │ └── OnTextChangeListener.java
│ │ │ ├── lang
│ │ │ ├── Language.java
│ │ │ ├── LanguageC.java
│ │ │ ├── LanguageCFamily.java
│ │ │ ├── LanguageCpp.java
│ │ │ ├── LanguageCsharp.java
│ │ │ ├── LanguageJava.java
│ │ │ ├── LanguageJavascript.java
│ │ │ ├── LanguageLua.java
│ │ │ ├── LanguageNonProg.java
│ │ │ ├── LanguageObjectiveC.java
│ │ │ ├── LanguagePHP.java
│ │ │ ├── LanguagePython.java
│ │ │ ├── LanguageRuby.java
│ │ │ ├── LanguageSmali.java
│ │ │ └── xml
│ │ │ │ └── XMLLanguage.java
│ │ │ ├── util
│ │ │ ├── DLog.java
│ │ │ ├── Document.java
│ │ │ ├── DocumentProvider.java
│ │ │ ├── FindThread.java
│ │ │ ├── Flag.java
│ │ │ ├── HelperUtils.java
│ │ │ ├── Lexer.java
│ │ │ ├── LinearSearchStrategy.java
│ │ │ ├── Pair.java
│ │ │ ├── ProgressObserver.java
│ │ │ ├── ProgressSource.java
│ │ │ ├── SearchStrategy.java
│ │ │ ├── TextBuffer.java
│ │ │ ├── TextBufferCache.java
│ │ │ ├── TextWarriorException.java
│ │ │ └── UndoStack.java
│ │ │ └── view
│ │ │ ├── ClipboardPanel.java
│ │ │ ├── ColorScheme.java
│ │ │ ├── ColorSchemeLight.java
│ │ │ ├── FreeScrollingTextField.java
│ │ │ ├── KeysInterpreter.java
│ │ │ ├── TextFieldController.java
│ │ │ ├── TextFieldInputConnection.java
│ │ │ ├── TouchNavigationMethod.java
│ │ │ ├── YoyoNavigationMethod.java
│ │ │ └── autocomplete
│ │ │ ├── AutoCompletePanel.java
│ │ │ ├── AutoPanelAdapter.java
│ │ │ └── ListItem.java
│ └── res
│ │ ├── drawable
│ │ └── icon_method.png
│ │ └── layout
│ │ └── auto_panel_item.xml
│ └── test
│ └── java
│ └── com
│ └── mrikso
│ └── codeeditor
│ └── ExampleUnitTest.java
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.github/workflows/build-debug.yml:
--------------------------------------------------------------------------------
1 | name: Android CI
2 |
3 | on:
4 | # push:
5 | pull_request:
6 | workflow_dispatch:
7 |
8 | jobs:
9 | build:
10 | name: Build debug apk
11 | runs-on: ubuntu-latest
12 | steps:
13 | - name: Cancel previous runs
14 | uses: styfle/cancel-workflow-action@0.5.0
15 | with:
16 | access_token: ${{ github.token }}
17 |
18 | - uses: actions/checkout@v2
19 |
20 | - name: set up JDK 11
21 | uses: actions/setup-java@v2
22 | with:
23 | java-version: '11'
24 | distribution: 'adopt'
25 | cache: gradle
26 |
27 | - name: Grant execute permission for gradlew
28 | run: chmod +x gradlew
29 |
30 | - name: Build debug apk
31 | uses: eskatos/gradle-command-action@v1
32 | with:
33 | arguments: assembleDebug
34 | distributions-cache-enabled: true
35 | dependencies-cache-enabled: true
36 | configuration-cache-enabled: true
37 |
38 | - name: Upload debug apk
39 | uses: actions/upload-artifact@v2
40 | if: ${{ !github.head_ref }}
41 | with:
42 | name: apk-debug
43 | path: app/build/outputs/apk/debug
44 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CodeEditor
2 | Code editor for android, Based TextWarrior View.
3 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 29
5 | buildToolsVersion "29.0.2"
6 |
7 | defaultConfig {
8 | applicationId "com.mrikso.texteditor"
9 | minSdkVersion 21
10 | targetSdkVersion 29
11 | versionCode 1
12 | versionName "1.0"
13 |
14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
15 | }
16 |
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 | compileOptions {
24 | sourceCompatibility = 1.8
25 | targetCompatibility = 1.8
26 | }
27 | }
28 |
29 | dependencies {
30 | implementation fileTree(dir: 'libs', include: ['*.jar'])
31 | // implementation project(':lib-n-ide')
32 | implementation 'androidx.appcompat:appcompat:1.1.0'
33 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
34 | testImplementation 'junit:junit:4.12'
35 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
36 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
37 | implementation project(path: ':codeeditor')
38 | }
39 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/mrikso/texteditor/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.mrikso.texteditor;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.test.platform.app.InstrumentationRegistry;
6 | import androidx.test.ext.junit.runners.AndroidJUnit4;
7 |
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 |
11 | import static org.junit.Assert.*;
12 |
13 | /**
14 | * Instrumented test, which will execute on an Android device.
15 | *
16 | * @see Testing documentation
17 | */
18 | @RunWith(AndroidJUnit4.class)
19 | public class ExampleInstrumentedTest {
20 | @Test
21 | public void useAppContext() {
22 | // Context of the app under test.
23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
24 |
25 | assertEquals("com.mrikso.texteditor", appContext.getPackageName());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mrikso/texteditor/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.mrikso.texteditor;
2 |
3 | import androidx.annotation.NonNull;
4 | import androidx.appcompat.app.AppCompatActivity;
5 |
6 | import android.os.Bundle;
7 |
8 | //import com.mrikso.codeeditor.TextEditor;
9 |
10 |
11 | public class MainActivity extends AppCompatActivity {
12 |
13 | @Override
14 | protected void onCreate(Bundle savedInstanceState) {
15 | super.onCreate(savedInstanceState);
16 | setContentView(R.layout.activity_main);
17 | TextEditor textEditor = findViewById(R.id.codeEditor);
18 | textEditor.setText("lol");
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mrikso/texteditor/TextEditor.java:
--------------------------------------------------------------------------------
1 | package com.mrikso.texteditor;
2 |
3 | import android.content.Context;
4 | import android.graphics.Color;
5 | import android.graphics.Typeface;
6 | import android.util.AttributeSet;
7 | import android.util.DisplayMetrics;
8 | import android.util.TypedValue;
9 | import android.view.KeyEvent;
10 | import android.widget.Toast;
11 |
12 | import com.mrikso.codeeditor.lang.Language;
13 | import com.mrikso.codeeditor.lang.LanguageJava;
14 | import com.mrikso.codeeditor.util.Document;
15 | import com.mrikso.codeeditor.util.DocumentProvider;
16 | import com.mrikso.codeeditor.util.Lexer;
17 | import com.mrikso.codeeditor.view.ColorScheme;
18 | import com.mrikso.codeeditor.view.FreeScrollingTextField;
19 | import com.mrikso.codeeditor.view.YoyoNavigationMethod;
20 | import com.mrikso.codeeditor.view.autocomplete.AutoCompletePanel;
21 |
22 | import java.io.File;
23 |
24 |
25 | public class TextEditor extends FreeScrollingTextField {
26 | private Document _inputtingDoc;
27 | private boolean _isWordWrap;
28 | private Context mContext;
29 | private String _lastSelectFile;
30 | private int _index;
31 | private Toast toast;
32 | /*
33 | private Handler handler = new Handler() {
34 | @Override
35 | public void handleMessage(Message msg) {
36 | switch (msg.what) {
37 |
38 | case ReadThread.MSG_READ_OK:
39 | setText(msg.obj.toString());
40 | break;
41 | case ReadThread.MSG_READ_FAIL:
42 | showToast("打开失败");
43 | break;
44 | case WriteThread.MSG_WRITE_OK:
45 | showToast("保存成功");
46 | break;
47 | case WriteThread.MSG_WRITE_FAIL:
48 | showToast("保存失败");
49 | break;
50 | }
51 | }
52 | };
53 | /*/
54 | public TextEditor(Context context) {
55 | super(context);
56 | mContext = context;
57 | init();
58 | }
59 |
60 | public TextEditor(Context context, AttributeSet attributeSet) {
61 | super(context, attributeSet);
62 | mContext = context;
63 | init();
64 | }
65 |
66 | private void init() {
67 | setVerticalScrollBarEnabled(true);
68 | setTypeface(Typeface.MONOSPACE);
69 | DisplayMetrics dm = mContext.getResources().getDisplayMetrics();
70 | //设置字体大小
71 | float size = TypedValue.applyDimension(2, BASE_TEXT_SIZE_PIXELS, dm);
72 | setTextSize((int) size);
73 | setShowLineNumbers(true);
74 | //setAutoCompete(true);
75 | setHighlightCurrentRow(true);
76 | setWordWrap(true);
77 | setAutoComplete(true);
78 | setAutoIndent(true);
79 | setUseGboard(true);
80 | setAutoIndentWidth(2);
81 | setLanguage(LanguageJava.getInstance());
82 | setNavigationMethod(new YoyoNavigationMethod(this));
83 | int textColor = Color.BLACK;// 默认文字颜色
84 | int selectionText = Color.argb(255, 0, 120, 215);//选择文字颜色
85 | setTextColor(textColor);
86 | setTextHighlightColor(selectionText);
87 | }
88 |
89 | @Override
90 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
91 | // TODO: Implement this method
92 | super.onLayout(changed, left, top, right, bottom);
93 | if (_index != 0 && right > 0) {
94 | moveCaret(_index);
95 | _index = 0;
96 | }
97 | }
98 |
99 | public void setKeywordColor(int color) {
100 | getColorScheme().setColor(ColorScheme.Colorable.KEYWORD, color);
101 | }
102 |
103 | public void setBaseWordColor(int color) {
104 | getColorScheme().setColor(ColorScheme.Colorable.NAME, color);
105 | }
106 |
107 | public void setStringColor(int color) {
108 | getColorScheme().setColor(ColorScheme.Colorable.STRING, color);
109 | }
110 |
111 | public void setCommentColor(int color) {
112 | getColorScheme().setColor(ColorScheme.Colorable.COMMENT, color);
113 | }
114 |
115 | public void setBackgroundColor(int color) {
116 | getColorScheme().setColor(ColorScheme.Colorable.BACKGROUND, color);
117 | }
118 |
119 | public void setTextColor(int color) {
120 | getColorScheme().setColor(ColorScheme.Colorable.FOREGROUND, color);
121 | }
122 |
123 | public void setTextHighlightColor(int color) {
124 | getColorScheme().setColor(ColorScheme.Colorable.SELECTION_BACKGROUND, color);
125 | }
126 |
127 | public void setLanguage(Language language){
128 | AutoCompletePanel.setLanguage(language);
129 | Lexer.setLanguage(language);
130 | }
131 |
132 | public String getSelectedText() {
133 | // TODO: Implement this method
134 | return hDoc.subSequence(getSelectionStart(), getSelectionEnd() - getSelectionStart()).toString();
135 | }
136 |
137 | public void gotoLine(int line) {
138 | if (line > hDoc.getRowCount()) {
139 | line = hDoc.getRowCount();
140 |
141 | }
142 | int i = getText().getLineOffset(line - 1);
143 | setSelection(i);
144 | }
145 |
146 | @Override
147 | public boolean onKeyShortcut(int keyCode, KeyEvent event) {
148 | final int filteredMetaState = event.getMetaState() & ~KeyEvent.META_CTRL_MASK;
149 | if (KeyEvent.metaStateHasNoModifiers(filteredMetaState)) {
150 | switch (keyCode) {
151 | case KeyEvent.KEYCODE_A:
152 | selectAll();
153 | return true;
154 | case KeyEvent.KEYCODE_X:
155 | cut();
156 | return true;
157 | case KeyEvent.KEYCODE_C:
158 | copy();
159 | return true;
160 | case KeyEvent.KEYCODE_V:
161 | paste();
162 | return true;
163 | }
164 | }
165 | return super.onKeyShortcut(keyCode, event);
166 | }
167 |
168 | @Override
169 | public void setWordWrap(boolean enable) {
170 | // TODO: Implement this method
171 | _isWordWrap = enable;
172 | super.setWordWrap(enable);
173 | }
174 |
175 | public DocumentProvider getText() {
176 | return createDocumentProvider();
177 | }
178 |
179 | public void setText(CharSequence c) {
180 | Document doc = new Document(this);
181 | doc.setWordWrap(_isWordWrap);
182 | doc.setText(c);
183 | setDocumentProvider(new DocumentProvider(doc));
184 | }
185 |
186 | public File getOpenedFile() {
187 | if (_lastSelectFile != null)
188 | return new File(_lastSelectFile);
189 |
190 | return null;
191 | }
192 |
193 | public void setOpenedFile(String file) {
194 | _lastSelectFile = file;
195 | }
196 |
197 | public void insert(int idx, String text) {
198 | selectText(false);
199 | moveCaret(idx);
200 | paste(text);
201 | }
202 |
203 | public void replaceAll(CharSequence c) {
204 | replaceText(0, getLength() - 1, c.toString());
205 | }
206 |
207 | public void setSelection(int index) {
208 | selectText(false);
209 | if (!hasLayout())
210 | moveCaret(index);
211 | else
212 | _index = index;
213 | }
214 |
215 | public void undo() {
216 |
217 | DocumentProvider doc = createDocumentProvider();
218 | int newPosition = doc.undo();
219 |
220 | if (newPosition >= 0) {
221 | //TODO editor.setEdited(false);
222 | // if reached original condition of file
223 | setEdited(true);
224 | respan();
225 | selectText(false);
226 | moveCaret(newPosition);
227 | invalidate();
228 | }
229 |
230 | }
231 |
232 | public void redo() {
233 |
234 | DocumentProvider doc = createDocumentProvider();
235 | int newPosition = doc.redo();
236 |
237 | if (newPosition >= 0) {
238 | setEdited(true);
239 |
240 | respan();
241 | selectText(false);
242 | moveCaret(newPosition);
243 | invalidate();
244 | }
245 |
246 | }
247 | /*
248 | public void open(String filename) {
249 | _lastSelectFile = filename;
250 |
251 | File inputFile = new File(filename);
252 | _inputtingDoc = new Document(this);
253 | _inputtingDoc.setWordWrap(this.isWordWrap());
254 | ReadThread readThread = new ReadThread(inputFile.getAbsolutePath(), handler);
255 | readThread.start();
256 | }
257 |
258 | /**
259 | * 保存文件
260 | * * @param file
261 | */
262 | /*
263 | public void save(String file) {
264 | WriteThread writeThread = new WriteThread(getText().toString(), file, handler);
265 | writeThread.start();
266 | }
267 |
268 |
269 | */
270 | private void showToast(CharSequence text) {
271 | if (toast == null) {
272 | toast = Toast.makeText(mContext, text, Toast.LENGTH_SHORT);
273 | } else {
274 | toast.setText(text);
275 | }
276 | toast.show();
277 | }
278 | }
279 |
280 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MrIkso/CodeEditor/a807a1459eae6a94b84c5687584677ae9eac8013/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MrIkso/CodeEditor/a807a1459eae6a94b84c5687584677ae9eac8013/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MrIkso/CodeEditor/a807a1459eae6a94b84c5687584677ae9eac8013/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MrIkso/CodeEditor/a807a1459eae6a94b84c5687584677ae9eac8013/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MrIkso/CodeEditor/a807a1459eae6a94b84c5687584677ae9eac8013/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MrIkso/CodeEditor/a807a1459eae6a94b84c5687584677ae9eac8013/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MrIkso/CodeEditor/a807a1459eae6a94b84c5687584677ae9eac8013/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MrIkso/CodeEditor/a807a1459eae6a94b84c5687584677ae9eac8013/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MrIkso/CodeEditor/a807a1459eae6a94b84c5687584677ae9eac8013/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MrIkso/CodeEditor/a807a1459eae6a94b84c5687584677ae9eac8013/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #6200EE
4 | #3700B3
5 | #03DAC5
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Text Editor
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/mrikso/texteditor/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.mrikso.texteditor;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 |
5 | repositories {
6 | google()
7 | jcenter()
8 |
9 | }
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:3.6.3'
12 |
13 |
14 | // NOTE: Do not place your application dependencies here; they belong
15 | // in the individual module build.gradle files
16 | }
17 | }
18 |
19 | allprojects {
20 | repositories {
21 | google()
22 | jcenter()
23 |
24 | }
25 | }
26 |
27 | task clean(type: Delete) {
28 | delete rootProject.buildDir
29 | }
30 |
--------------------------------------------------------------------------------
/codeeditor/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/codeeditor/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 29
5 | buildToolsVersion "29.0.2"
6 |
7 | defaultConfig {
8 | minSdkVersion 21
9 | targetSdkVersion 29
10 | versionCode 1
11 | versionName "1.0"
12 |
13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
14 | consumerProguardFiles 'consumer-rules.pro'
15 | }
16 |
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 | compileOptions {
24 | sourceCompatibility = 1.8
25 | targetCompatibility = 1.8
26 | }
27 |
28 | }
29 |
30 | dependencies {
31 | implementation fileTree(dir: 'libs', include: ['*.jar'])
32 |
33 | implementation 'androidx.appcompat:appcompat:1.1.0'
34 | implementation 'commons-io:commons-io:2.6'
35 | // implementation 'org.apache.commons:commons-lang3:3.7
36 | testImplementation 'junit:junit:4.12'
37 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
38 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
39 | }
40 |
--------------------------------------------------------------------------------
/codeeditor/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MrIkso/CodeEditor/a807a1459eae6a94b84c5687584677ae9eac8013/codeeditor/consumer-rules.pro
--------------------------------------------------------------------------------
/codeeditor/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/codeeditor/src/androidTest/java/com/mrikso/codeeditor/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.mrikso.codeeditor;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.test.platform.app.InstrumentationRegistry;
6 | import androidx.test.ext.junit.runners.AndroidJUnit4;
7 |
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 |
11 | import static org.junit.Assert.*;
12 |
13 | /**
14 | * Instrumented test, which will execute on an Android device.
15 | *
16 | * @see Testing documentation
17 | */
18 | @RunWith(AndroidJUnit4.class)
19 | public class ExampleInstrumentedTest {
20 | @Test
21 | public void useAppContext() {
22 | // Context of the app under test.
23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
24 |
25 | assertEquals("com.mrikso.codeeditor.test", appContext.getPackageName());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/codeeditor/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/common/OnCaretScrollListener.java:
--------------------------------------------------------------------------------
1 | package com.mrikso.codeeditor.common;
2 |
3 | public interface OnCaretScrollListener {
4 | void updateCaret(int caretIndex);
5 | }
6 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/common/OnKeyShortcutListener.java:
--------------------------------------------------------------------------------
1 | package com.mrikso.codeeditor.common;
2 |
3 | import android.view.KeyEvent;
4 |
5 | public interface OnKeyShortcutListener {
6 | boolean onKeyShortcut(int keyCode,KeyEvent event);
7 | }
8 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/common/OnRowChangedListener.java:
--------------------------------------------------------------------------------
1 | package com.mrikso.codeeditor.common;
2 |
3 | public interface OnRowChangedListener {
4 | void onRowChanged(int newRowIndex);
5 | }
6 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/common/OnSelectionChangedListener.java:
--------------------------------------------------------------------------------
1 | package com.mrikso.codeeditor.common;
2 |
3 | public interface OnSelectionChangedListener {
4 | void onSelectionChanged(boolean active,int selStart, int selEnd);
5 | }
6 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/common/OnTextChangeListener.java:
--------------------------------------------------------------------------------
1 | package com.mrikso.codeeditor.common;
2 |
3 | public interface OnTextChangeListener {
4 | void onNewLine(String s, int caretPosition, int pos);
5 |
6 | void onDel(CharSequence text, int cursorPosition, int delCount);
7 |
8 | void onAdd(CharSequence text, int cursorPosition, int addCount);
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/lang/Language.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 Tah Wei Hoon.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License Version 2.0,
5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html
6 | *
7 | * This software is provided "as is". Use at your own risk.
8 | */
9 | package com.mrikso.codeeditor.lang;
10 |
11 | import com.mrikso.codeeditor.util.Lexer;
12 |
13 | import java.util.HashMap;
14 | import java.util.*;
15 |
16 | /**
17 | * Base class for programming language syntax.
18 | * By default, C-like symbols and operators are included, but not keywords.
19 | */
20 | public abstract class Language
21 | {
22 | public final static char EOF = '\uFFFF';
23 | public final static char NULL_CHAR = '\u0000';
24 | public final static char NEWLINE = '\n';
25 | public final static char BACKSPACE = '\b';
26 | public final static char TAB = '\t';
27 | public final static String GLYPH_NEWLINE = "\u21b5";
28 | public final static String GLYPH_SPACE = "\u00b7";
29 | public final static String GLYPH_TAB = "\u00bb";
30 |
31 |
32 | private final static char[] BASIC_C_OPERATORS = {
33 | '(', ')', '{', '}', '.', ',', ';', '=', '+', '-',
34 | '/', '*', '&', '!', '|', ':', '[', ']', '<', '>',
35 | '?', '~', '%', '^'
36 | };
37 |
38 |
39 | protected HashMap _keywordsMap = new HashMap(0);
40 | protected HashMap _namesMap = new HashMap(0);
41 | protected HashMap _basesMap = new HashMap(0);
42 | protected HashMap _usersMap = new HashMap(0);//userWord是那种用户自己定义的标识符
43 | protected HashMap _operatorsMap = generateOperators(BASIC_C_OPERATORS);
44 |
45 | private ArrayList _userCache = new ArrayList();
46 | private String[] _userWords=new String[0];
47 | private String[] _keyword;
48 | private String[] _name = new String[0];
49 |
50 | public void updateUserWord()
51 | {
52 | // TODO: Implement this method
53 | String[] uw = new String[_userCache.size()];
54 | _userWords = _userCache.toArray(uw);
55 | }
56 |
57 | public String[] getUserWord()
58 | {
59 | return _userWords;
60 | }
61 |
62 | public String[] getNames()
63 | {
64 | return _name;
65 | }
66 |
67 | public String[] getBasePackage(String name)
68 | {
69 | return _basesMap.get(name);
70 | }
71 |
72 | public String[] getKeywords()
73 | {
74 | return _keyword;
75 | }
76 |
77 | public void setKeywords(String[] keywords)
78 | {
79 | _keyword = new String[keywords.length];
80 | for(int i=0;i(keywords.length);
85 | for (int i = 0; i < keywords.length; ++i)
86 | {
87 | _keywordsMap.put(keywords[i], Lexer.KEYWORD);
88 | }
89 | }
90 |
91 | public void setNames(String[] names)
92 | {
93 | _name = names;
94 | ArrayList buf=new ArrayList();
95 | _namesMap = new HashMap(names.length);
96 | for (int i = 0; i < names.length; ++i)
97 | {
98 | if(!buf.contains(names[i]))
99 | buf.add(names[i]);
100 | _namesMap.put(names[i], Lexer.NAME);
101 | }
102 | _name=new String[buf.size()];
103 | buf.toArray(_name);
104 | }
105 |
106 | public void addBasePackage(String name, String[] names)
107 | {
108 | _basesMap.put(name, names);
109 | }
110 |
111 | public void clearUserWord()
112 | {
113 | _userCache.clear();
114 | _usersMap.clear();
115 | }
116 |
117 | public void addUserWord(String name)
118 | {
119 | if(!_userCache.contains(name) && !_namesMap.containsKey(name))
120 | _userCache.add(name);
121 | _usersMap.put(name, Lexer.NAME);
122 | }
123 |
124 | protected void setOperators(char[] operators)
125 | {
126 | _operatorsMap = generateOperators(operators);
127 | }
128 |
129 | private HashMap generateOperators(char[] operators)
130 | {
131 | HashMap operatorsMap = new HashMap(operators.length);
132 | for (int i = 0; i < operators.length; ++i)
133 | {
134 | operatorsMap.put(operators[i], Lexer.OPERATOR);
135 | }
136 | return operatorsMap;
137 | }
138 |
139 | public final boolean isOperator(char c)
140 | {
141 | return _operatorsMap.containsKey(c);
142 | }
143 |
144 | public final boolean isKeyword(String s)
145 | {
146 | return _keywordsMap.containsKey(s);
147 | }
148 |
149 | public final boolean isName(String s)
150 | {
151 | return _namesMap.containsKey(s);
152 | }
153 |
154 | public final boolean isBasePackage(String s)
155 | {
156 | return _basesMap.containsKey(s);
157 | }
158 |
159 | public final boolean isBaseWord(String p, String s)
160 | {
161 | String[] pkg= _basesMap.get(p);
162 | for (String n:pkg)
163 | {
164 | if (n.equals(s))
165 | return true;
166 | }
167 | return false;
168 | }
169 |
170 | public final boolean isUserWord(String s)
171 | {
172 | return _usersMap.containsKey(s);
173 | }
174 |
175 | private boolean contains(String[] a, String s)
176 | {
177 | for (String n:a)
178 | {
179 | if (n.equals(s))
180 | return true;
181 | }
182 | return false;
183 | }
184 |
185 | private boolean contains(ArrayList a, String s)
186 | {
187 | for (String n:a)
188 | {
189 | if (n.equals(s))
190 | return true;
191 | }
192 | return false;
193 | }
194 |
195 | /**
196 | * 空白符
197 | * @param c
198 | * @return
199 | */
200 | public boolean isWhitespace(char c)
201 | {
202 | return (c == ' ' || c == '\n' || c == '\t' ||
203 | c == '\r' || c == '\f' || c == EOF);
204 | }
205 |
206 | /**
207 | * 点运算符
208 | * @param c
209 | * @return
210 | */
211 | public boolean isSentenceTerminator(char c)
212 | {
213 | return (c == '.');
214 | }
215 |
216 | /**
217 | * 斜杠
218 | * @param c
219 | * @return
220 | */
221 | public boolean isEscapeChar(char c)
222 | {
223 | return (c == '\\');
224 | }
225 |
226 | /**
227 | * Derived classes that do not do represent C-like programming languages
228 | * should return false; otherwise return true
229 | */
230 | public boolean isProgLang()
231 | {
232 | return true;
233 | }
234 |
235 | /**
236 | * Whether the word after c is a token
237 | */
238 | public boolean isWordStart(char c)
239 | {
240 | return false;
241 | }
242 |
243 | /**
244 | * Whether cSc is a token, where S is a sequence of characters that are on the same line
245 | * 字符串引号
246 | */
247 | public boolean isDelimiterA(char c)
248 | {
249 | return (c == '"');
250 | }
251 |
252 | /**
253 | * Same concept as isDelimiterA(char), but Language and its subclasses can
254 | * specify a second type of symbol to use here
255 | * 单个字符引号
256 | */
257 | public boolean isDelimiterB(char c)
258 | {
259 | return (c == '\'');
260 | }
261 |
262 | /**
263 | * Whether cL is a token, where L is a sequence of characters until the end of the line
264 | * 宏定义
265 | */
266 | public boolean isLineAStart(char c)
267 | {
268 | return (c == '#');
269 | }
270 |
271 | /**
272 | * Same concept as isLineAStart(char), but Language and its subclasses can
273 | * specify a second type of symbol to use here
274 | */
275 | public boolean isLineBStart(char c)
276 | {
277 | return false;
278 | }
279 |
280 | /**
281 | * Whether c0c1L is a token, where L is a sequence of characters until the end of the line
282 | * 单行注释
283 | */
284 | public boolean isLineStart(char c0, char c1)
285 | {
286 | return (c0 == '/' && c1 == '/');
287 | }
288 |
289 | /**
290 | * Whether c0c1 signifies the start of a multi-line token
291 | * 多行注释开始
292 | */
293 | public boolean isMultilineStartDelimiter(char c0, char c1)
294 | {
295 | return (c0 == '/' && c1 == '*');
296 | }
297 |
298 | /**
299 | * Whether c0c1 signifies the end of a multi-line token
300 | * 多行注释结束
301 | */
302 | public boolean isMultilineEndDelimiter(char c0, char c1)
303 | {
304 | return (c0 == '*' && c1 == '/');
305 | }
306 | }
307 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/lang/LanguageC.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 Tah Wei Hoon.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License Version 2.0,
5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html
6 | *
7 | * This software is provided "as is". Use at your own risk.
8 | */
9 | package com.mrikso.codeeditor.lang;
10 |
11 | import java.util.ArrayList;
12 | import java.util.Arrays;
13 |
14 | /**
15 | * Singleton class containing the symbols and operators of the C language
16 | */
17 | public class LanguageC extends Language{
18 | private static Language _theOne = null;
19 |
20 | private final static String[] keywords = {
21 | "char", "double", "float", "int", "long", "short", "void",
22 | "auto", "const", "extern", "register", "static", "volatile",
23 | "signed", "unsigned", "sizeof", "typedef",
24 | "enum", "struct", "union",
25 | "break", "case", "continue", "default", "do", "else", "for",
26 | "goto", "if", "return", "switch", "while",
27 | "define","include","ifdef","endif","ifndef","error","elif","line","pragma","undef"
28 | };
29 | private final static String[] functions={
30 | "abort","abs","acos","asctime","asin","assert","atan","atan2","atexit","atof","atoi","atol"
31 | ,"bsearch","calloc","ceil","clearerr","clock","cos","cosh","ctime","difftime","div"
32 | ,"exit","exp","fabs","fclose","feof","ferror","fflush","fgetc","fgetpos","fgets","floor"
33 | ,"fmod","fopen","fprintf","fputc","fputs","fread","free","freopen","frexp","fscanf","fseek","fsetpos","ftell","fwrite"
34 | ,"getc","getchar","getenv","gets","gmtime","isalnum","isalpha","iscntrl","isdigit","isgraph","islower","isprint","ispunct","isspace","isupper","isxdigit","labs","ldexp","ldiv","localtime","log","log10","longjmp"
35 | ,"main","malloc","memchr","memcmp","memcpy","memmove","memset","mktime","modf","perror","pow","printf"
36 | ,"putc","putchar","puts","qsort","raise","rand","realloc","remove","rename","rewind"
37 | ,"scanf","setbuf","setjmp","setvbuf","signal","sin","sinh","sprintf","sqrt","srand","sscanf","strcat","strchr","strcmp","strcoll","strcpy","strcspn","strerror","strftime","strlen","strncat","strncmp","strncpy","strpbrk","strrchr","strspn","strstr","strtod","strtok","strtol","strtoul","strxfrm","system"
38 | ,"tan","tanh","time","tmpfile","tmpnam","tolower","toupper","ungetc","va_arg","vprintf","vfprintf"
39 | ,"__LINE__","__FILE__","__DATE__","__TIME__","_cplusplus","__STDC__"
40 |
41 | };
42 | private final static String[] header={
43 | "math.h","stdio.h","stdlib.h","string.h","time.h","errno.h","ctype.h","local.h"
44 | };
45 | private final static char[] BASIC_C_OPERATORS = {
46 | '(', ')', '{', '}', '.', ',', ';', '=', '+', '-',
47 | '/', '*', '&', '!', '|', ':', '[', ']', '<', '>',
48 | '?', '~', '%', '^'
49 | };
50 | public static Language getInstance(){
51 | if(_theOne == null){
52 | _theOne = new LanguageC();
53 | }
54 | return _theOne;
55 | }
56 |
57 | private LanguageC(){
58 | String[] diyWord= new String[header.length+functions.length];
59 | System.arraycopy(functions,0,diyWord,0,functions.length);
60 | System.arraycopy(header,0,diyWord,functions.length,header.length);
61 | setKeywords(keywords);
62 | setNames(diyWord);
63 | setOperators(BASIC_C_OPERATORS);
64 |
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/lang/LanguageCFamily.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011 Tah Wei Hoon.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License Version 2.0,
5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html
6 | *
7 | * This software is provided "as is". Use at your own risk.
8 | */
9 | package com.mrikso.codeeditor.lang;
10 |
11 | import com.mrikso.codeeditor.util.Lexer;
12 |
13 | import java.util.HashMap;
14 |
15 | /**
16 | * Singleton class containing C-like symbols and operators but no keywords
17 | */
18 | public abstract class LanguageCFamily
19 | {
20 | public final static char EOF = '\uFFFF';
21 | public final static char NULL_CHAR = '\u0000';
22 | public final static char NEWLINE = '\n';
23 | public final static char BACKSPACE = '\b';
24 | public final static char TAB = '\t';
25 |
26 | protected HashMap _keywords;
27 | protected HashMap _operators;
28 |
29 | private final static char[] basic_c_operators = {
30 | '(', ')', '{', '}', '.', ',', ';', '=', '+', '-',
31 | '/', '*', '&', '!', '|', ':', '[', ']', '<', '>',
32 | '?', '~', '%', '^'
33 | };
34 |
35 | { replaceOperators(basic_c_operators); }
36 |
37 |
38 | protected void registerKeywords(String[] keywords)
39 | {
40 | _keywords = new HashMap(keywords.length);
41 | for (int i = 0; i < keywords.length; ++i)
42 | {
43 | _keywords.put(keywords[i], Lexer.KEYWORD);
44 | }
45 | }
46 |
47 | protected void replaceOperators(char[] operators)
48 | {
49 | _operators = new HashMap<>(operators.length);
50 | for (int i = 0; i < operators.length; ++i)
51 | {
52 | _operators.put(operators[i], Lexer.OPERATOR);
53 | }
54 | }
55 |
56 | public final boolean isOperator(char c)
57 | {
58 | return _operators.containsKey(c);
59 | }
60 |
61 | public final boolean isKeyword(String s)
62 | {
63 | return _keywords.containsKey(s);
64 | }
65 |
66 | public boolean isWhitespace(char c)
67 | {
68 | return (c == ' ' || c == '\n' || c == '\t' ||
69 | c == '\r' || c == '\f' || c == EOF);
70 | }
71 |
72 | public boolean isSentenceTerminator(char c)
73 | {
74 | return (c == '.');
75 | }
76 |
77 | public boolean isEscapeChar(char c)
78 | {
79 | return (c == '\\');
80 | }
81 |
82 | /**
83 | * Derived classes that do not do represent C-like programming languages
84 | * should return false; otherwise return true
85 | */
86 | public boolean isProgLang()
87 | {
88 | return true;
89 | }
90 |
91 | /**
92 | * Whether the word after c is a token
93 | */
94 | public boolean isWordStart(char c)
95 | {
96 | return false;
97 | }
98 |
99 | /**
100 | * Whether cSc is a token, where S is a sequence of characters that are on the same line
101 | */
102 | public boolean isDelimiterA(char c)
103 | {
104 | return (c == '"');
105 | }
106 |
107 | /**
108 | * Same concept as isDelimiterA(char), but Language and its subclasses can
109 | * specify a second type of symbol to use here
110 | */
111 | public boolean isDelimiterB(char c)
112 | {
113 | return (c == '\'');
114 | }
115 |
116 | /**
117 | * Whether cL is a token, where L is a sequence of characters until the end of the line
118 | */
119 | public boolean isLineAStart(char c)
120 | {
121 | return (c == '#');
122 | }
123 |
124 | /**
125 | * Same concept as isLineAStart(char), but Language and its subclasses can
126 | * specify a second type of symbol to use here
127 | */
128 | public boolean isLineBStart(char c)
129 | {
130 | return false;
131 | }
132 |
133 | /**
134 | * Whether c0c1L is a token, where L is a sequence of characters until the end of the line
135 | */
136 | public boolean isLineStart(char c0, char c1)
137 | {
138 | return (c0 == '/' && c1 == '/');
139 | }
140 |
141 | /**
142 | * Whether c0c1 signifies the start of a multi-line token
143 | */
144 | public boolean isMultilineStartDelimiter(char c0, char c1)
145 | {
146 | return (c0 == '/' && c1 == '*');
147 | }
148 |
149 | /**
150 | * Whether c0c1 signifies the end of a multi-line token
151 | */
152 | public boolean isMultilineEndDelimiter(char c0, char c1)
153 | {
154 | return (c0 == '*' && c1 == '/');
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/lang/LanguageCpp.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011 Tah Wei Hoon.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License Version 2.0,
5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html
6 | *
7 | * This software is provided "as is". Use at your own risk.
8 | */
9 | package com.mrikso.codeeditor.lang;
10 |
11 | public class LanguageCpp extends Language{
12 | private static Language _theOne = null;
13 |
14 | private final static String[] keywords = {
15 | "bool", "char", "double", "float", "int", "long", "short", "void", "wchar_t",
16 | "auto", "const", "extern", "mutable", "register", "static", "volatile",
17 | "signed", "unsigned", "true", "false",
18 | "new", "delete", "sizeof", "typedef", "typeid", "typename",
19 | "const_cast", "dynamic_cast", "reinterpret_cast", "static_cast",
20 | "class", "enum", "explicit", "operator", "struct", "template", "union", "virtual",
21 | "private", "protected", "public", "friend", "this",
22 | "break", "case", "catch", "continue", "default", "do", "else", "for",
23 | "goto", "if", "return", "switch", "throw", "try", "while",
24 | "export", "namespace", "using", "asm", "inline","restrict"
25 | };
26 | private final static String[] cFunctions={
27 | "abort()","abs(int num):int","acos(double arg):double","asctime(const struct tm *ptr):char *","asin(double arg):double","assert(int exp)","atan(double arg):double","atan2(double y,double x):double","atexit(void (*func)(void)):int","atof(const char *str):double","atoi(const char *str):int","atol(const char *str):long"
28 | ,"bsearch(const void *key,const void *buf,size_t num,size_t size,int (*compare)(const void *,const void *))","calloc(size_t num, size_t size)","ceil(double num):double","clearerr(FILE *stream)","clock():clock_t","cos(double arg):double","cosh(double arg):double","ctime(const time_t *time):char *","difftime(time_t time2,time_t time1):double","div(int numerator,int denominator):div_t"
29 | ,"exit(int exit_code)","exp(double arg):double","fabs(double arg):double","fclose(FILE *stream):int","feof(FILE *stream):int","ferror(FILE *stream):int","fflush(FILE *stream):int","fgetc(FILE *stream):int","fgetpos(FILE *stream,fpos_t *position):int","fgets(char *str, int num,FILE *stream):char *","floor(double arg):double"
30 | ,"fmod(double x,double y):double","fopen(const char *fname, const char *mode):FILE *","fprintf(FILE *stream,const char *format,...):int","fputc(int ch,FILE *stream):int","fputs(const char *str,FILE *stream):int","fread(void *buffer, size_t size, size_t num, FILE *stream):int","free(void *ptr)","freopen(const char *fname,const char *mode,FILE *stream):FILE *","frexp(double num,int *exp):double","fscanf(FILE *stream,const char *format,...):double","fseek(FILE *stream,long offset,int origin):int","fsetpos(FILE *stream,const fpos_t *position):int","ftell(FILE *stream):long","fwrite(const void *buffer,size_t size,size_t count,FILE *stream):int"
31 | ,"getc(FILE *stream):int","getchar():int","getenv(const char *name):char *","gets(char *str):char *","gmtime(const time_t *time):struct tm *","isalnum(int ch):int","isalpha(int ch):int","iscntrl(int ch):int","isdigit(int ch):int","isgraph(int ch):int","islower(int ch):int","isprint(int ch):int","ispunct(int ch):int","isspace(int ch):int","isupper(int ch):int","isxdigit(int ch):int","labs(long num):long","ldexp(double num,int exp):double","ldiv(long numerator,long denominator):ldiv_t","localtime(const time_t *time):struct tm *","log(double num):double","log10(double num):double","longjmp(jmp_buf envbuf,int status)"
32 | ,"malloc(size_t size):void *","memchr(const void *buffer,int ch,size_t count):void *","memcmp(const void *buffer1,const void *buffer2,size_t count):int","memcpy(void *to,const void *from,size_t count):void *","memmove(void *to,const void *from,size_t count):void *","memset(void *buffer,int ch,size_t count):void *","mktime(struct tm *time):time_t","modf(double num,double *i):double","perror(const char *str)","pow(double base,double exp):double","printf(const char *format,...):int"
33 | ,"putc(int ch,FILE *stream):int","putchar(int ch):int","puts(char *str):int","qsort(void *buf,size_t num,size_t size,int (*compare)(const void *,const void *))","raise(int signal):int","rand():int","realloc(void *ptr,size_t size):void *","remove(const char *fname):int","rename(const char *oldfname,const char *newfname):int","rewind(FILE *stream)"
34 | ,"scanf(const char *format,...):int","setbuf(FILE *stream,char *buffer)","setjmp(jmp_buf envbuf):int","setvbuf(FILE *stream,char *buffer,int mode,size_t size):int","signal(int signal,void (*func)(int))","sin(double arg):double","sinh(double arg):double","sprintf(char *buffer,const char *format,...):int","sqrt(double num):double","srand(unsigned seed)","sscanf(const char *buffer,const char *format,...):int","strcat(char *str1,const char *str2):char *","strchr(const char *str, int ch):char *","strcmp(const char *str1,const char *str2):int","strcoll(const char *str1,const char *str2):int","strcpy(char *to,const char *from):char *","strcspn(const char *str1,const char *str2):size_t","strerror(int num):char *","strftime(char *str,size_t maxsize,const char *fmt,struct tm *time):size_t","strlen(char *str):size_t","strncat(char *str1,const char *str2,size_t count):char *","strncmp(const char *str1,const char *str2,size_t count):int","strncpy(char *to,const char *from,size_t count):char *","strpbrk(const char *str1,const char *str2):char *","strrchr(const char *str,int ch):char *","strspn(const char *str1, const char *str2):size_t","strstr(const char *str1, const char *str2):char *","strtod(const char *start,char **end):double","strtok(char *str1,const char *str2):char *","strtol(const char *start,char **end,int base):long","strtoul(const char *start,char **end,int base):unsigned long","strxfrm(char *str1,const char *str2,size_t num):size_t","system(const char *command):int"
35 | ,"tan(double arg):double","tanh(double arg):double","time(time_t *time):time_t","tmpfile():FILE *","tmpnam(char *name):char *","tolower(int ch):int","toupper(int ch):int","ungetc(int ch, FILE *stream):int","va_arg(va_list arg_ptr,type):type","vprintf(char *format,va_list arg_ptr):int","vfprintf(FILE *stream,const char *format,va_list arg_ptr):int","vsprintf(char *buffer,char *format,va_list arg_ptr):int"
36 |
37 | };
38 |
39 | private final static String[] preDefineField={
40 | "__LINE__","__FILE__","__DATE__","__TIME__","__cplusplus","__STDC__","__func__","__VA_ARGS__","__attribute__"
41 | };
42 | private final static String[] cppNamespace ={
43 | "std"
44 | };
45 |
46 | private final static String[] cppClasses={
47 | //io
48 | "fstream","ifstream","ofstream","cout","cin","cerr","endl"
49 | //模板库
50 | ,"bitset","string","list","deque","map","multimap","multiset","set","priority_queue","queue","stack","vector"
51 | //c++11 模板库
52 | ,"array","forward_list","unordered_map","unordered_set"
53 | };
54 |
55 | private final static String[] cppFunctions={
56 | "any()","append()","assign()","at()","back()","bad()","begin()","c_str()","capacity()","clear()","compare()","copy()","count()","data()","empty()","end()","eof()","equal_range()","erase()","fail()","fill()","find()","find_first_not_of()","find_first_of()","find_last_not_of()","find_last_of()","flags()","flip()","flush()","front()","fstream"
57 | ,"gcount()","get()","get_allocator()","getline()","good()","ignore()","insert()","iterator()","key_comp()","length()","lower_bound()","max_size()","merge()","none()","open()","peek()","pop()","pop_back()","pop_front()","precision()","push()","push_back()","push_front()","put()","putback"
58 | ,"rbegin()","rdstate()","read()","remove()","remove_if()","rend()","replace()","reserve()","reset()","resize()","reverse()","rfind()","seekg()","seekp()","set()","setf()","size()","sort()","splice()","substr()","swap()","sync_with_stdio()","tellg()","tellp()","test()","to_string()","to_ulong()","top"
59 | ,"unique()","unsetf()","upper_bound()","value_comp()","width()","write()"
60 | };
61 |
62 | private final static String[] extraWord={
63 | "define","include","ifdef","endif","ifndef","error","elif","line","pragma","undef","main"
64 | };
65 | private final static char[] BASIC_C_OPERATORS = {
66 | '(', ')', '{', '}', '.', ',', ';', '=', '+', '-',
67 | '/', '*', '&', '!', '|', ':', '[', ']', '<', '>',
68 | '?', '~', '%', '^'
69 | };
70 | public static Language getInstance(){
71 | if(_theOne == null){
72 | _theOne = new LanguageCpp();
73 | }
74 | return _theOne;
75 | }
76 | public void addNames(String[] names) {
77 | String[] old=this.getNames();
78 | String[] news=new String[old.length + names.length];
79 | System.arraycopy(old, 0, news, 0, old.length);
80 | System.arraycopy(names, 0, news, old.length, names.length);
81 | this.setNames(news);
82 |
83 | }
84 | private LanguageCpp(){
85 | setOperators(BASIC_C_OPERATORS);
86 | setKeywords(keywords);
87 | setNames(cFunctions);//先setName才能addName
88 | addNames(preDefineField);
89 | addNames(cppNamespace);
90 | addNames(cppClasses);
91 | addNames(cppFunctions);
92 | addNames(extraWord);
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/lang/LanguageCsharp.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011 Tah Wei Hoon.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License Version 2.0,
5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html
6 | *
7 | * This software is provided "as is". Use at your own risk.
8 | */
9 | package com.mrikso.codeeditor.lang;
10 |
11 | /**
12 | * Singleton class containing the symbols and operators of the C# language
13 | */
14 | public class LanguageCsharp extends LanguageCFamily {
15 | private static LanguageCFamily _theOne = null;
16 |
17 | private final static String[] keywords = {
18 | "abstract", "as", "base", "bool", "break", "byte", "case", "catch",
19 | "char", "checked", "class", "const", "continue", "decimal", "default",
20 | "delegate", "do", "double", "else", "enum", "event", "explicit",
21 | "extern", "false", "finally", "fixed", "float", "for", "foreach",
22 | "goto", "if", "implicit", "in", "int", "interface", "internal", "is",
23 | "lock", "long", "namespace", "new", "null", "object", "operator",
24 | "out", "override", "params", "private", "protected", "public",
25 | "readonly", "ref", "return", "sbyte", "sealed", "short", "sizeof",
26 | "stackalloc", "static", "string", "struct", "switch", "this", "throw",
27 | "true", "try", "typeof", "uint", "ulong", "unchecked", "unsafe",
28 | "ushort", "using", "virtual", "void", "volatile", "while",
29 | "dynamic", "get", "set", "add", "remove", "global", "value", "var",
30 | "yield", "alias", "partial",
31 | "from", "where", "join", "on", "equals", "into", "let", "orderby",
32 | "ascending", "descending", "select", "group", "by"
33 | };
34 |
35 | public static LanguageCFamily getCharacterEncodings(){
36 | if(_theOne == null){
37 | _theOne = new LanguageCsharp();
38 | }
39 | return _theOne;
40 | }
41 |
42 | private LanguageCsharp(){
43 | super.registerKeywords(keywords);
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/lang/LanguageJava.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011 Tah Wei Hoon.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License Version 2.0,
5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html
6 | *
7 | * This software is provided "as is". Use at your own risk.
8 | */
9 | package com.mrikso.codeeditor.lang;
10 |
11 | /**
12 | * Singleton class containing the symbols and operators of the Java language
13 | */
14 | public class LanguageJava extends Language{
15 | private static Language _theOne = null;
16 |
17 | private final static String[] keywords = {
18 | "void", "boolean", "byte", "char", "short", "int", "long", "float", "double", "strictfp",
19 | "import", "package", "new", "class", "interface", "extends", "implements", "enum",
20 | "public", "private", "protected", "static", "abstract", "final", "native", "volatile",
21 | "assert", "try", "throw", "throws", "catch", "finally", "instanceof", "super", "this",
22 | "if", "else", "for", "do", "while", "switch", "case", "default",
23 | "continue", "break", "return", "synchronized", "transient",
24 | "true", "false", "null", "import", "package"
25 | };
26 |
27 |
28 | public static Language getInstance(){
29 | if(_theOne == null){
30 | _theOne = new LanguageJava();
31 | }
32 | return _theOne;
33 | }
34 |
35 | private LanguageJava(){
36 | setKeywords(keywords);
37 | }
38 |
39 | /**
40 | * Java has no preprocessors. Override base class implementation
41 | */
42 | public boolean isLineAStart(char c){
43 | return false;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/lang/LanguageJavascript.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011 Tah Wei Hoon.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License Version 2.0,
5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html
6 | *
7 | * This software is provided "as is". Use at your own risk.
8 | */
9 | package com.mrikso.codeeditor.lang;
10 |
11 | /**
12 | * Singleton class containing the symbols and operators of the Javascript language
13 | */
14 | public class LanguageJavascript extends LanguageCFamily {
15 | private static LanguageCFamily _theOne = null;
16 |
17 | private final static String[] keywords = {
18 | "abstract", "boolean", "break", "byte", "case", "catch", "char",
19 | "class", "const", "continue", "debugger", "default", "delete", "do",
20 | "double", "else", "enum", "export", "extends", "false", "final",
21 | "finally", "float", "for", "function", "goto", "if", "implements",
22 | "import", "in", "instanceof", "int", "interface", "long", "native",
23 | "new", "null", "package", "private", "protected", "public", "return",
24 | "short", "static", "super", "switch", "synchronized", "this", "throw",
25 | "throws", "transient", "true", "try", "typeof", "var", "void",
26 | "volatile", "while", "with"
27 | };
28 |
29 | public static LanguageCFamily getCharacterEncodings(){
30 | if(_theOne == null){
31 | _theOne = new LanguageJavascript();
32 | }
33 | return _theOne;
34 | }
35 |
36 | private LanguageJavascript(){
37 | super.registerKeywords(keywords);
38 | }
39 |
40 | public boolean isLineAStart(char c){
41 | return false;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/lang/LanguageLua.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 Tah Wei Hoon.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License Version 2.0,
5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html
6 | *
7 | * This software is provided "as is". Use at your own risk.
8 | */
9 | package com.mrikso.codeeditor.lang;
10 |
11 | /**
12 | * Singleton class containing the symbols and operators of the Javascript language
13 | */
14 | public class LanguageLua extends Language {
15 | private static Language _theOne = null;
16 |
17 | //private final static String functionTarget = "_ENV|_G|_VERSION|assert|collectgarbage|coroutine|create|isyieldable|resume|running|status|wrap|yield|debug|gethook|getinfo|getlocal|getmetatable|getregistry|getupvalue|getuservalue|sethook|setlocal|setmetatable|setupvalue|setuservalue|traceback|upvalueid|upvaluejoin|dofile|error|getfenv|getmetatable|io|close|flush|input|lines|open|output|popen|read|stderr|stdin|stdout|tmpfile|type|write|ipairs|load|loadfile|loadstring|luajava|bindClass|clear|coding|createArray|createProxy|instanceof|loadLib|loaded|luapath|new|newInstance|package|math|abs|acos|asin|atan|atan2|ceil|cos|cosh|deg|exp|floor|fmod|frexp|huge|ldexp|log|log10|max|maxinteger|min|mininteger|modf|pi|pow|rad|random|randomseed|sin|sinh|sqrt|tan|tanh|tointeger|type|ult|module|next|os|clock|date|difftime|execute|exit|getenv|remove|rename|setlocale|time|tmpname|package|config|cpath|loaded|loaders|loadlib|path|preload|searchers|searchpath|seeall|pairs|pcall|print|rawequal|rawget|rawlen|rawset|require|select|setfenv|setmetatable|string|byte|char|dump|find|format|gfind|gmatch|gsub|len|lower|match|pack|packsize|rep|reverse|sub|unpack|upper|table|concat|foreach|foreachi|insert|maxn|move|pack|remove|sort|unpack|tonumber|tostring|type|unpack|char|charpattern|utf8|codepoint|codes|len|offset|xpcall";
18 | //private final static String functionTarget = "_ENV|_G|_VERSION|assert|collectgarbage|coroutine.create|coroutine.isyieldable|coroutine.resume|coroutine.running|coroutine.status|coroutine.wrap|coroutine.yield|debug.debug|debug.gethook|debug.getinfo|debug.getlocal|debug.getmetatable|debug.getregistry|debug.getupvalue|debug.getuservalue|debug.sethook|debug.setlocal|debug.setmetatable|debug.setupvalue|debug.setuservalue|debug.traceback|debug.upvalueid|debug.upvaluejoin|dofile|error|getfenv|getmetatable|io.close|io.flush|io.input|io.lines|io.open|io.output|io.popen|io.read|io.stderr|io.stdin|io.stdout|io.tmpfile|io.type|io.write|ipairs|load|loadfile|loadstring|luajava.bindClass|luajava.clear|luajava.coding|luajava.createArray|luajava.createProxy|luajava.instanceof|luajava.loadLib|luajava.loaded|luajava.luapath|luajava.new|luajava.newInstance|luajava.package|math.abs|math.acos|math.asin|math.atan|math.atan2|math.ceil|math.cos|math.cosh|math.deg|math.exp|math.floor|math.fmod|math.frexp|math.huge|math.ldexp|math.log|math.log10|math.max|math.maxinteger|math.min|math.mininteger|math.modf|math.pi|math.pow|math.rad|math.random|math.randomseed|math.sin|math.sinh|math.sqrt|math.tan|math.tanh|math.tointeger|math.type|math.ult|module|next|os.clock|os.date|os.difftime|os.execute|os.exit|os.getenv|os.remove|os.rename|os.setlocale|os.time|os.tmpname|package.config|package.cpath|package.loaded|package.loaders|package.loadlib|package.path|package.preload|package.searchers|package.searchpath|package.seeall|pairs|pcall|print|rawequal|rawget|rawlen|rawset|require|select|setfenv|setmetatable|string.byte|string.char|string.dump|string.find|string.format|string.gfind|string.gmatch|string.gsub|string.len|string.lower|string.match|string.pack|string.packsize|string.rep|string.reverse|string.sub|string.unpack|string.upper|table.concat|table.foreach|table.foreachi|table.insert|table.maxn|table.move|table.pack|table.remove|table.sort|table.unpack|tonumber|tostring|type|unpack|utf8.char|utf8.charpattern|utf8.codepoint|utf8.codes|utf8.len|utf8.offset|xpcall";
19 |
20 | private final static String keywordTarget ="and|break|do|else|elseif|end|false|for|function|goto|if|in|local|nil|not|or|repeat|return|then|true|until|while";
21 | private final static String globalTarget="__add|__band|__bnot|__bor|__bxor|__call|__concat|__div|__eq|__idiv|__index|__le|__len|__lt|__mod|__mul|__newindex|__pow|__shl|__shr|__sub|__unm|_ENV|_G|assert|collectgarbage|dofile|error|findtable|getmetatable|ipairs|load|loadfile|loadstring|module|next|pairs|pcall|print|rawequal|rawget|rawlen|rawset|require|select|self|setmetatable|tointeger|tonumber|tostring|type|unpack|xpcall";
22 |
23 | private final static String packageName="coroutine|debug|io|luajava|math|os|package|string|table|utf8";
24 | private final static String package_coroutine = "create|isyieldable|resume|running|status|wrap|yield";
25 | private final static String package_debug = "debug|gethook|getinfo|getlocal|getmetatable|getregistry|getupvalue|getuservalue|sethook|setlocal|setmetatable|setupvalue|setuservalue|traceback|upvalueid|upvaluejoin";
26 | private final static String package_io = "close|flush|input|lines|open|output|popen|read|stderr|stdin|stdout|tmpfile|type|write";
27 | private final static String package_luajava = "astable|bindClass|clear|coding|createArray|createProxy|instanceof|loadLib|loaded|luapath|new|newInstance|package|tostring";
28 | private final static String package_math = "abs|acos|asin|atan|atan2|ceil|cos|cosh|deg|exp|floor|fmod|frexp|huge|ldexp|log|log10|max|maxinteger|min|mininteger|modf|pi|pow|rad|random|randomseed|sin|sinh|sqrt|tan|tanh|tointeger|type|ult";
29 | private final static String package_os = "clock|date|difftime|execute|exit|getenv|remove|rename|setlocale|time|tmpname";
30 | private final static String package_package = "config|cpath|loaded|loaders|loadlib|path|preload|searchers|searchpath|seeall";
31 | private final static String package_string = "byte|char|dump|find|format|gfind|gmatch|gsub|len|lower|match|pack|packsize|rep|reverse|sub|unpack|upper";
32 | private final static String package_table = "concat|foreach|foreachi|insert|maxn|move|pack|remove|sort|unpack";
33 | private final static String package_utf8 = "char|charpattern|codepoint|codes|len|offset";
34 | private final static String extFunctionTarget="activity|call|compile|dump|each|enum|import|loadbitmap|loadlayout|loadmenu|set|task|thread|timer";
35 | private final static String functionTarget = globalTarget+"|"+extFunctionTarget+"|"+packageName;;
36 | private final static String[] keywords = keywordTarget.split("\\|");
37 |
38 | private final static String[] names = functionTarget.split("\\|");
39 |
40 | private final static char[] LUA_OPERATORS = {
41 | '(', ')', '{', '}', ',', ';', '=', '+', '-',
42 | '/', '*', '&', '!', '|', ':', '[', ']', '<', '>',
43 | '?', '~', '%', '^'
44 | };
45 | public static Language getInstance(){
46 | if(_theOne == null){
47 | _theOne = new LanguageLua();
48 | }
49 | return _theOne;
50 | }
51 |
52 | private LanguageLua(){
53 | super.setOperators(LUA_OPERATORS);
54 | super.setKeywords(keywords);
55 | super.setNames(names);
56 | super.addBasePackage("io",package_io.split("\\|"));
57 | super.addBasePackage("string",package_string.split("\\|"));
58 | super.addBasePackage("luajava",package_luajava.split("\\|"));
59 | super.addBasePackage("os",package_os.split("\\|"));
60 | super.addBasePackage("table",package_table.split("\\|"));
61 | super.addBasePackage("math",package_math.split("\\|"));
62 | super.addBasePackage("utf8",package_utf8.split("\\|"));
63 | super.addBasePackage("coroutine",package_coroutine.split("\\|"));
64 | super.addBasePackage("package",package_package.split("\\|"));
65 | super.addBasePackage("debug",package_debug.split("\\|"));
66 | }
67 |
68 | /**
69 | * Whether the word after c is a token
70 | */
71 | public boolean isWordStart2(char c){
72 | return (c=='.');
73 | }
74 |
75 | public boolean isLineAStart(char c){
76 | return false;
77 | }
78 |
79 | /**
80 | * Whether c0c1L is a token, where L is a sequence of characters until the end of the line
81 | */
82 | public boolean isLineStart(char c0, char c1){
83 | return (c0 == '-' && c1 == '-');
84 | }
85 |
86 | /**
87 | * Whether c0c1 signifies the start of a multi-line token
88 | */
89 | public boolean isMultilineStartDelimiter(char c0, char c1){
90 | return (c0 == '[' && c1 == '[');
91 | }
92 |
93 | /**
94 | * Whether c0c1 signifies the end of a multi-line token
95 | */
96 | public boolean isMultilineEndDelimiter(char c0, char c1){
97 | return (c0 == ']' && c1 == ']');
98 | }
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/lang/LanguageNonProg.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 Tah Wei Hoon.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License Version 2.0,
5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html
6 | *
7 | * This software is provided "as is". Use at your own risk.
8 | */
9 | package com.mrikso.codeeditor.lang;
10 |
11 | /**
12 | * Singleton class that represents a non-programming language without keywords,
13 | * operators etc.
14 | */
15 | public class LanguageNonProg extends Language{
16 | private static Language _theOne = null;
17 |
18 | private final static String[] keywords = {};
19 |
20 | private final static char[] operators = {};
21 |
22 |
23 | public static Language getInstance(){
24 | if(_theOne == null){
25 | _theOne = new LanguageNonProg();
26 | }
27 | return _theOne;
28 | }
29 |
30 | private LanguageNonProg(){
31 | super.setKeywords(keywords);
32 | super.setOperators(operators);
33 | }
34 |
35 | @Override
36 | public boolean isProgLang(){
37 | return false;
38 | }
39 |
40 | @Override
41 | public boolean isEscapeChar(char c){
42 | return false;
43 | }
44 |
45 | @Override
46 | public boolean isDelimiterA(char c){
47 | return false;
48 | }
49 |
50 | @Override
51 | public boolean isDelimiterB(char c){
52 | return false;
53 | }
54 |
55 | @Override
56 | public boolean isLineAStart(char c){
57 | return false;
58 | }
59 |
60 | @Override
61 | public boolean isLineStart(char c0, char c1){
62 | return false;
63 | }
64 |
65 | @Override
66 | public boolean isMultilineStartDelimiter(char c0, char c1){
67 | return false;
68 | }
69 |
70 | @Override
71 | public boolean isMultilineEndDelimiter(char c0, char c1){
72 | return false;
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/lang/LanguageObjectiveC.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011 Tah Wei Hoon.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License Version 2.0,
5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html
6 | *
7 | * This software is provided "as is". Use at your own risk.
8 | */
9 | package com.mrikso.codeeditor.lang;
10 |
11 | /**
12 | * Singleton class containing the symbols and operators of the Objective-C language
13 | */
14 | public class LanguageObjectiveC extends LanguageCFamily{
15 | private static LanguageCFamily _theOne = null;
16 |
17 | private final static String[] keywords = {
18 | "char", "double", "float", "int", "long", "short", "void",
19 | "auto", "const", "extern", "register", "static", "volatile",
20 | "signed", "unsigned", "sizeof", "typedef",
21 | "enum", "struct", "union",
22 | "break", "case", "continue", "default", "do", "else", "for",
23 | "goto", "if", "return", "switch", "while",
24 | "@class", "@implementation", "@interface", "@protocol", "@property",
25 | "@private", "@protected", "@public", "@optional", "@required",
26 | "@defs", "@dynamic", "@encode", "@synchronized", "@selector", "@synthesize",
27 | "@try", "@catch", "@throw", "@finally", "@end",
28 | "id", "self", "super", "nil", "Nil", "NULL", "SEL", "BOOL", "YES", "NO",
29 | "in", "out", "inout", "bycopy", "byref", "oneway",
30 | "getter", "setter", "readwrite", "readonly", "assign", "retain", "copy", "nonatomic"
31 | };
32 |
33 | public static LanguageCFamily getCharacterEncodings(){
34 | if(_theOne == null){
35 | _theOne = new LanguageObjectiveC();
36 | }
37 | return _theOne;
38 | }
39 |
40 | private LanguageObjectiveC(){
41 | super.registerKeywords(keywords);
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/lang/LanguagePHP.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011 Tah Wei Hoon.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License Version 2.0,
5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html
6 | *
7 | * This software is provided "as is". Use at your own risk.
8 | */
9 | package com.mrikso.codeeditor.lang;
10 |
11 | /**
12 | * Singleton class containing the symbols and operators of the PHP language
13 | */
14 | public class LanguagePHP extends LanguageCFamily {
15 | private static LanguageCFamily _theOne = null;
16 |
17 | private final static String[] keywords = {
18 | "abstract", "and", "array", "as", "break", "case", "catch", "class",
19 | "clone", "const", "continue", "declare", "default", "do", "else",
20 | "elseif", "enddeclare", "endfor", "endforeach", "endif", "endswitch",
21 | "endwhile", "extends", "final", "for", "foreach", "function", "global",
22 | "goto", "if", "implements", "interface", "instanceof", "namespace",
23 | "new", "or", "private", "protected", "public", "static", "switch",
24 | "throw", "try", "use", "var", "while", "xor",
25 | "die", "echo", "empty", "exit", "eval", "include", "include_once",
26 | "isset", "list", "require", "require_once", "return", "print", "unset",
27 | "self", "static", "parent", "true", "TRUE", "false", "FALSE", "null", "NULL"
28 | };
29 |
30 | private final static char[] operators = {
31 | '(', ')', '{', '}', '.', ',', ';', '=', '+', '-',
32 | '/', '*', '&', '!', '|', ':', '[', ']', '<', '>',
33 | '?', '~', '%', '^', '`', '@'
34 | };
35 |
36 |
37 | public static LanguageCFamily getCharacterEncodings(){
38 | if(_theOne == null){
39 | _theOne = new LanguagePHP();
40 | }
41 | return _theOne;
42 | }
43 |
44 | private LanguagePHP(){
45 | super.registerKeywords(keywords);
46 | super.replaceOperators(operators);
47 | }
48 |
49 | @Override
50 | public boolean isLineAStart(char c){
51 | return false;
52 | }
53 |
54 | @Override
55 | public boolean isWordStart(char c){
56 | return (c == '$');
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/lang/LanguagePython.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011 Tah Wei Hoon.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License Version 2.0,
5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html
6 | *
7 | * This software is provided "as is". Use at your own risk.
8 | */
9 | package com.mrikso.codeeditor.lang;
10 |
11 | /**
12 | * Singleton class containing the symbols and operators of the Python language
13 | */
14 | public class LanguagePython extends LanguageCFamily{
15 | private static LanguageCFamily _theOne = null;
16 |
17 | private final static String[] keywords = {
18 | "and", "assert", "break", "class", "continue", "def", "del",
19 | "elif", "else", "except", "exec", "finally", "for", "from",
20 | "global", "if", "import", "in", "is", "lambda", "not", "or",
21 | "pass", "print", "raise", "return", "try", "while", "with",
22 | "yield", "True", "False", "None"
23 | };
24 |
25 | private final static char[] operators = {
26 | '(', ')', '{', '}', '.', ',', ';', '=', '+', '-',
27 | '/', '*', '&', '!', '|', ':', '[', ']', '<', '>',
28 | '~', '%', '^'
29 | }; // no ternary operator ? :
30 |
31 |
32 | @Override
33 | public boolean isWordStart(char c){
34 | return (c == '@');
35 | }
36 |
37 | @Override
38 | public boolean isLineAStart(char c){
39 | return false;
40 | }
41 |
42 | @Override
43 | public boolean isLineBStart(char c){
44 | return (c == '#');
45 | }
46 |
47 | @Override
48 | public boolean isLineStart(char c0, char c1){
49 | return false;
50 | }
51 |
52 | @Override
53 | public boolean isMultilineStartDelimiter(char c0, char c1){
54 | return false;
55 | }
56 |
57 | public static LanguageCFamily getCharacterEncodings(){
58 | if(_theOne == null){
59 | _theOne = new LanguagePython();
60 | }
61 | return _theOne;
62 | }
63 |
64 | private LanguagePython(){
65 | super.registerKeywords(keywords);
66 | super.replaceOperators(operators);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/lang/LanguageRuby.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011 Tah Wei Hoon.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License Version 2.0,
5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html
6 | *
7 | * This software is provided "as is". Use at your own risk.
8 | */
9 | package com.mrikso.codeeditor.lang;
10 |
11 | /**
12 | * Singleton class containing the symbols and operators of the Ruby language
13 | */
14 | public class LanguageRuby extends LanguageCFamily {
15 | private static LanguageCFamily _theOne = null;
16 |
17 | private final static String[] keywords = {
18 | "alias", "and", "BEGIN", "begin", "break", "case", "catch", "class", "def",
19 | "defined?", "do", "else", "elsif", "END", "end", "ensure", "false",
20 | "for", "if", "in", "module", "next", "nil", "not", "or", "public",
21 | "private", "protected", "raise", "redo", "rescue", "retry", "return", "self",
22 | "super", "then", "throw", "true", "undef", "unless", "until", "when", "while",
23 | "yield", "self", "nil", "true", "false", "TRUE", "FALSE", "NIL"
24 | };
25 |
26 |
27 | @Override
28 | public boolean isWordStart(char c){
29 | return (c == '$');
30 | }
31 |
32 | @Override
33 | public boolean isLineAStart(char c){
34 | return false;
35 | }
36 |
37 | @Override
38 | public boolean isLineBStart(char c){
39 | return (c == '#');
40 | }
41 |
42 | @Override
43 | public boolean isLineStart(char c0, char c1){
44 | return false;
45 | }
46 |
47 | @Override
48 | public boolean isMultilineStartDelimiter(char c0, char c1){
49 | return false;
50 | }
51 |
52 | public static LanguageCFamily getCharacterEncodings(){
53 | if(_theOne == null){
54 | _theOne = new LanguageRuby();
55 | }
56 | return _theOne;
57 | }
58 |
59 | private LanguageRuby(){
60 | super.registerKeywords(keywords);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/lang/LanguageSmali.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011 Tah Wei Hoon.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License Version 2.0,
5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html
6 | *
7 | * This software is provided "as is". Use at your own risk.
8 | */
9 | package com.mrikso.codeeditor.lang;
10 |
11 | /**
12 | * Singleton class containing the symbols and operators of the Smali language
13 | */
14 | public class LanguageSmali extends Language {
15 | private final static String[] keywords = {
16 | "goto",
17 | "return-void",
18 | "nop",
19 | "const/4",
20 | "move-result",
21 | "move-result-wide",
22 | "move-result-object",
23 | "move-exception",
24 | "return",
25 | "return-wide",
26 | "return-object",
27 | "monitor-enter",
28 | "monitor-exit",
29 | "throw",
30 | "move",
31 | "move-wide",
32 | "move-object",
33 | "array-length",
34 | "neg-int",
35 | "not-int",
36 | "neg-long",
37 | "not-long",
38 | "neg-float",
39 | "neg-double",
40 | "int-to-long",
41 | "int-to-float",
42 | "int-to-double",
43 | "long-to-int",
44 | "long-to-float",
45 | "long-to-double",
46 | "float-to-int",
47 | "float-to-long",
48 | "float-to-double",
49 | "double-to-int",
50 | "double-to-long",
51 | "double-to-float",
52 | "int-to-byte",
53 | "int-to-char",
54 | "int-to-short",
55 | "add-int/2addr",
56 | "sub-int/2addr",
57 | "mul-int/2addr",
58 | "div-int/2addr",
59 | "rem-int/2addr",
60 | "and-int/2addr",
61 | "or-int/2addr",
62 | "xor-int/2addr",
63 | "shl-int/2addr",
64 | "shr-int/2addr",
65 | "ushr-int/2addr",
66 | "add-long/2addr",
67 | "sub-long/2addr",
68 | "mul-long/2addr",
69 | "div-long/2addr",
70 | "rem-long/2addr",
71 | "and-long/2addr",
72 | "or-long/2addr",
73 | "xor-long/2addr",
74 | "shl-long/2addr",
75 | "shr-long/2addr",
76 | "ushr-long/2addr",
77 | "add-float/2addr",
78 | "sub-float/2addr",
79 | "mul-float/2addr",
80 | "div-float/2addr",
81 | "rem-float/2addr",
82 | "add-double/2addr",
83 | "sub-double/2addr",
84 | "mul-double/2addr",
85 | "div-double/2addr",
86 | "rem-double/2addr",
87 | "goto/16",
88 | "sget",
89 | "sget-wide",
90 | "sget-object",
91 | "sget-boolean",
92 | "sget-byte",
93 | "sget-char",
94 | "sget-short",
95 | "sput",
96 | "sput-wide",
97 | "sput-object",
98 | "sput-boolean",
99 | "sput-byte",
100 | "sput-char",
101 | "sput-short",
102 | "const-string",
103 | "check-cast",
104 | "new-instance",
105 | "const-class",
106 | "const/high16",
107 | "const-wide/high16",
108 | "const/16",
109 | "const-wide/16",
110 | "if-eqz",
111 | "if-nez",
112 | "if-ltz",
113 | "if-gez",
114 | "if-gtz",
115 | "if-lez",
116 | "add-int/lit8",
117 | "rsub-int/lit8",
118 | "mul-int/lit8",
119 | "div-int/lit8",
120 | "rem-int/lit8",
121 | "and-int/lit8",
122 | "or-int/lit8",
123 | "xor-int/lit8",
124 | "shl-int/lit8",
125 | "shr-int/lit8",
126 | "ushr-int/lit8",
127 | "iget",
128 | "iget-wide",
129 | "iget-object",
130 | "iget-boolean",
131 | "iget-byte",
132 | "iget-char",
133 | "iget-short",
134 | "iput",
135 | "iput-wide",
136 | "iput-object",
137 | "iput-boolean",
138 | "iput-byte",
139 | "iput-char",
140 | "iput-short",
141 | "instance-of",
142 | "new-array",
143 | "iget-quick",
144 | "iget-wide-quick",
145 | "iget-object-quick",
146 | "iput-quick",
147 | "iput-wide-quick",
148 | "iput-object-quick",
149 | "rsub-int",
150 | "add-int/lit16",
151 | "mul-int/lit16",
152 | "div-int/lit16",
153 | "rem-int/lit16",
154 | "and-int/lit16",
155 | "or-int/lit16",
156 | "xor-int/lit16",
157 | "if-eq",
158 | "if-ne",
159 | "if-lt",
160 | "if-ge",
161 | "if-gt",
162 | "if-le",
163 | "move/from16",
164 | "move-wide/from16",
165 | "move-object/from16",
166 | "cmpl-float",
167 | "cmpg-float",
168 | "cmpl-double",
169 | "cmpg-double",
170 | "cmp-long",
171 | "aget",
172 | "aget-wide",
173 | "aget-object",
174 | "aget-boolean",
175 | "aget-byte",
176 | "aget-char",
177 | "aget-short",
178 | "aput",
179 | "aput-wide",
180 | "aput-object",
181 | "aput-boolean",
182 | "aput-byte",
183 | "aput-char",
184 | "aput-short",
185 | "add-int",
186 | "sub-int",
187 | "mul-int",
188 | "div-int",
189 | "rem-int",
190 | "and-int",
191 | "or-int",
192 | "xor-int",
193 | "shl-int",
194 | "shr-int",
195 | "ushr-int",
196 | "add-long",
197 | "sub-long",
198 | "mul-long",
199 | "div-long",
200 | "rem-long",
201 | "and-long",
202 | "or-long",
203 | "xor-long",
204 | "shl-long",
205 | "shr-long",
206 | "ushr-long",
207 | "add-float",
208 | "sub-float",
209 | "mul-float",
210 | "div-float",
211 | "rem-float",
212 | "add-double",
213 | "sub-double",
214 | "mul-double",
215 | "div-double",
216 | "rem-double",
217 | "goto/32",
218 | "const-string/jumbo",
219 | "const",
220 | "const-wide/32",
221 | "fill-array-data",
222 | "packed-switch",
223 | "sparse-switch",
224 | "move/16",
225 | "move-wide/16",
226 | "move-object/16",
227 | "invoke-virtual",
228 | "invoke-super",
229 | "invoke-direct",
230 | "invoke-static",
231 | "invoke-interface",
232 | "filled-new-array",
233 | "invoke-direct-empty",
234 | "execute-inline",
235 | "invoke-virtual-quick",
236 | "invoke-super-quick",
237 | "invoke-virtual/range",
238 | "invoke-super/range",
239 | "invoke-direct/range",
240 | "invoke-static/range",
241 | "invoke-interface/range",
242 | "filled-new-array/range",
243 | "invoke-virtual-quick/range",
244 | "invoke-super-quick/range",
245 | "const-wide"
246 | };
247 | private final static String[] clalifcators = {
248 | "public", "static", "private", "final", "return", "void"
249 | }; //
250 |
251 | private final static char[] operators = {
252 | '(', ')', '{', '}', '[', ']', '<', '>'
253 | }; //
254 | private final static String[] mProKeyWord = {
255 | ".class",
256 | ".super",
257 | ".implements",
258 | ".field",
259 | ".subannotation",
260 | ".annotation",
261 | ".enum",
262 | ".method",
263 | ".registers",
264 | ".locals",
265 | ".array-data",
266 | ".packed-switch",
267 | ".sparse-switch",
268 | ".catch",
269 | ".catchall",
270 | ".parameter",
271 | ".prologue",
272 | ".epilogue",
273 | ".source",
274 | ".end",
275 | ".restart"
276 | };
277 | private static Language _theOne = null;
278 |
279 | public void addNames(String[] names) {
280 | String[] old=getNames();
281 | //if(old.length != 0) {
282 | String[] news = new String[old.length + names.length];
283 | System.arraycopy(old, 0, news, 0, old.length);
284 | System.arraycopy(names, 0, news, old.length, names.length);
285 | setNames(news);
286 | //}
287 | //else {
288 | //setNames(names);
289 | //}
290 | }
291 |
292 | private LanguageSmali() {
293 | setOperators(operators);
294 | setKeywords(keywords);
295 | addNames(mProKeyWord);
296 | addNames(clalifcators);
297 |
298 | }
299 |
300 | public static Language getInstance() {
301 | if (_theOne == null) {
302 | _theOne = new LanguageSmali();
303 | }
304 | return _theOne;
305 | }
306 |
307 | @Override
308 | public boolean isLineAStart(char c) {
309 | return false;
310 | }
311 |
312 | @Override
313 | public boolean isLineBStart(char c) {
314 | return (c == '#');
315 | }
316 |
317 | @Override
318 | public boolean isWordStart(char c){
319 | return (c == '.');
320 | }
321 |
322 | @Override
323 | public boolean isLineStart(char c0, char c1) {
324 | return false;
325 | }
326 |
327 | @Override
328 | public boolean isMultilineStartDelimiter(char c0, char c1) {
329 | return false;
330 | }
331 |
332 | }
333 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/lang/xml/XMLLanguage.java:
--------------------------------------------------------------------------------
1 | package com.mrikso.codeeditor.lang.xml;
2 |
3 | import com.mrikso.codeeditor.lang.Language;
4 |
5 |
6 | /**
7 | * Singleton class containing the symbols and operators of the Javascript language
8 | */
9 | public class XMLLanguage extends Language {
10 |
11 | private static XMLLanguage _theOne;
12 |
13 | public static Language getInstance() {
14 | if (_theOne == null) {
15 | _theOne = new XMLLanguage();
16 | }
17 | return _theOne;
18 | }
19 |
20 | private XMLLanguage() {
21 | }
22 |
23 | /**
24 | * Whether the word after c is a token
25 | */
26 |
27 | public boolean isWordStart2(char c) {
28 | return (c == '.');
29 | }
30 |
31 | public boolean isLineAStart(char c) {
32 | return false;
33 | }
34 |
35 | /**
36 | * Whether c0c1 signifies the start of a multi-line token
37 | */
38 | public boolean isMultilineStartDelimiter(char c0, char c1, char c2, char c3) {
39 | return (c0 == '<' && c1 == '!' && c2 == '-' && c3 == '-');
40 | }
41 |
42 | /**
43 | * Whether c0c1 signifies the end of a multi-line token
44 | */
45 | public boolean isMultilineEndDelimiter(char c0, char c1, char c2) {
46 | return (c0 == '-' && c1 == '-' && c2 == '>');
47 | }
48 | public boolean isMultilineStartDelimiter(char c0, char c1) {
49 | return (c0 == '<' && c1 == '!');
50 | }
51 |
52 | /**
53 | * Whether c0c1 signifies the end of a multi-line token
54 | */
55 | public boolean isMultilineEndDelimiter(char c0, char c1) {
56 | return (c0 == '-' && c1 == '>');
57 | }
58 | }
59 |
60 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/util/DLog.java:
--------------------------------------------------------------------------------
1 | package com.mrikso.codeeditor.util;
2 |
3 | import java.util.Arrays;
4 |
5 |
6 | public class DLog {
7 | private static final String TAG = "CodeEditor";
8 | public static boolean DEBUG = true;
9 |
10 | public static int v(String tag, String msg) {
11 | if (!DEBUG)
12 | return 0;
13 | return android.util.Log.v(tag, msg);
14 | }
15 |
16 | public static int v(String tag, String msg, Throwable tr) {
17 | if (!DEBUG)
18 | return 0;
19 | return android.util.Log.v(tag, msg, tr);
20 | }
21 |
22 | public static int d(String msg) {
23 | return d(TAG, msg);
24 | }
25 |
26 | public static int d(String tag, String msg) {
27 | if (!DEBUG)
28 | return 0;
29 | return android.util.Log.d(tag, msg);
30 | }
31 |
32 | public static int d(String tag, String msg, Throwable tr) {
33 | if (!DEBUG)
34 | return 0;
35 | return android.util.Log.d(tag, msg, tr);
36 | }
37 |
38 | public static int d(String format, Object... args) {
39 | return d(TAG, String.format(format, args));
40 | }
41 |
42 | public static int d(Throwable t) {
43 | return d(TAG, t.getMessage(), t);
44 | }
45 |
46 | public static int i(String tag, String msg) {
47 | if (!DEBUG)
48 | return 0;
49 | return android.util.Log.i(tag, msg);
50 | }
51 |
52 | public static int i(String tag, String msg, Throwable tr) {
53 | if (!DEBUG)
54 | return 0;
55 | return android.util.Log.i(tag, msg, tr);
56 | }
57 |
58 | public static int w(String msg) {
59 | return android.util.Log.w(TAG, msg);
60 | }
61 |
62 | public static int w(String tag, String msg) {
63 | return android.util.Log.w(tag, msg);
64 | }
65 |
66 | public static int w(String tag, String msg, Throwable tr) {
67 | return android.util.Log.w(tag, msg, tr);
68 | }
69 |
70 | public static int w(String tag, Throwable tr) {
71 | return android.util.Log.w(tag, tr);
72 | }
73 |
74 | public static int e(String tag, String msg) {
75 | return logError(tag, msg, null);
76 | }
77 |
78 | public static int e(String tag, String msg, Throwable tr) {
79 | return logError(tag, msg, tr);
80 | }
81 |
82 | public static int e(String msg) {
83 | return e(TAG, msg);
84 | }
85 |
86 | public static int e(String msg, Throwable t) {
87 | return e(TAG, msg, t);
88 | }
89 |
90 | public static int e(String format, Object... args) {
91 | return e(TAG, String.format(format, args));
92 | }
93 |
94 | public static int e(Throwable t) {
95 | if (t == null)
96 | return 0;
97 | return logError(TAG, t.getMessage(), t);
98 | }
99 |
100 | private static int logError(String tag, String msg, Throwable t) {
101 | return android.util.Log.e(tag, msg, t);
102 | }
103 |
104 | public static void log(Object... params) {
105 | if (DLog.DEBUG) DLog.d(TAG, "log: " + Arrays.toString(params));
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/util/Document.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 Tah Wei Hoon.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License Version 2.0,
5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html
6 | *
7 | * This software is provided "as is". Use at your own risk.
8 | */
9 | package com.mrikso.codeeditor.util;
10 |
11 | import com.mrikso.codeeditor.lang.Language;
12 |
13 | import java.util.ArrayList;
14 |
15 | /**
16 | * A decorator of TextBuffer that adds word-wrap capabilities.
17 | *
18 | * Positions for word wrap row breaks are stored here.
19 | * Word-wrap is enabled by default.
20 | */
21 | public class Document extends TextBuffer
22 | {
23 |
24 | private boolean _isWordWrap = false;
25 |
26 | /** Contains info related to printing of characters, display size and so on */
27 | private TextFieldMetrics _metrics;
28 |
29 | /** A table containing the character offset of every row in the document.
30 | * Values are valid only in word-wrap mode */
31 | private ArrayList _rowTable;
32 |
33 | public Document(TextFieldMetrics metrics)
34 | {
35 | super();
36 | _metrics = metrics;
37 | resetRowTable();
38 | }
39 |
40 | public void setText(CharSequence text)
41 | {
42 | int lineCount=1;
43 | int len=text.length();
44 | char[] ca=new char[TextBuffer.memoryNeeded(len)];
45 | for (int i=0;i < len;i++)
46 | {
47 | ca[i] = text.charAt(i);
48 | if (text.charAt(i) == '\n')
49 | lineCount++;
50 | }
51 | setBuffer(ca, len, lineCount);
52 | }
53 |
54 | private void resetRowTable()
55 | {
56 | ArrayList rowTable = new ArrayList();
57 | rowTable.add(0); //every document contains at least 1 row
58 | _rowTable = rowTable;
59 | }
60 |
61 | public void setMetrics(TextFieldMetrics metrics)
62 | {
63 | _metrics = metrics;
64 | }
65 |
66 | /**
67 | * Enable/disable word wrap. If enabled, the document is immediately
68 | * analyzed for word wrap breakpoints, which might take an arbitrarily long time.
69 | */
70 | public void setWordWrap(boolean enable)
71 | {
72 | if (enable && !_isWordWrap)
73 | {
74 | _isWordWrap = true;
75 | analyzeWordWrap();
76 | }
77 | else if (!enable && _isWordWrap)
78 | {
79 | _isWordWrap = false;
80 | analyzeWordWrap();
81 | }
82 | }
83 |
84 | public boolean isWordWrap()
85 | {
86 | return _isWordWrap;
87 | }
88 |
89 |
90 | @Override
91 | public synchronized void delete(int charOffset, int totalChars, long timestamp, boolean undoable)
92 | {
93 | super.delete(charOffset, totalChars, timestamp, undoable);
94 |
95 | int startRow = findRowNumber(charOffset);
96 | int analyzeEnd = findNextLineFrom(charOffset);
97 | updateWordWrapAfterEdit(startRow, analyzeEnd, -totalChars);
98 | }
99 |
100 | @Override
101 | public synchronized void insert(char[] c, int charOffset, long timestamp, boolean undoable)
102 | {
103 | super.insert(c, charOffset, timestamp, undoable);
104 |
105 | int startRow = findRowNumber(charOffset);
106 | int analyzeEnd = findNextLineFrom(charOffset + c.length);
107 | updateWordWrapAfterEdit(startRow, analyzeEnd, c.length);
108 | }
109 |
110 | @Override
111 | /**
112 | * Moves _gapStartIndex by displacement units. Note that displacement can be
113 | * negative and will move _gapStartIndex to the left.
114 | *
115 | * Only UndoStack should use this method to carry out a simple undo/redo
116 | * of insertions/deletions. No error checking is done.
117 | */
118 | synchronized void shiftGapStart(int displacement)
119 | {
120 | super.shiftGapStart(displacement);
121 |
122 | if (displacement != 0)
123 | {
124 | int startOffset = (displacement > 0)
125 | ? _gapStartIndex - displacement
126 | : _gapStartIndex;
127 | int startRow = findRowNumber(startOffset);
128 | int analyzeEnd = findNextLineFrom(_gapStartIndex);
129 | updateWordWrapAfterEdit(startRow, analyzeEnd, displacement);
130 | }
131 | }
132 |
133 | //No error checking is done on parameters.
134 | private int findNextLineFrom(int charOffset)
135 | {
136 | int lineEnd = logicalToRealIndex(charOffset);
137 |
138 | while (lineEnd < _contents.length)
139 | {
140 | // skip the gap
141 | if (lineEnd == _gapStartIndex)
142 | {
143 | lineEnd = _gapEndIndex;
144 | }
145 |
146 | if (_contents[lineEnd] == Language.NEWLINE ||
147 | _contents[lineEnd] == Language.EOF)
148 | {
149 | break;
150 | }
151 |
152 | ++lineEnd;
153 | }
154 |
155 | return realToLogicalIndex(lineEnd) + 1;
156 | }
157 |
158 | private void updateWordWrapAfterEdit(int startRow, int analyzeEnd, int delta)
159 | {
160 | if (startRow > 0)
161 | {
162 | // if the first word becomes shorter or an inserted space breaks it
163 | // up, it may fit the previous line, so analyse that line too
164 | --startRow;
165 | }
166 | int analyzeStart = _rowTable.get(startRow);
167 |
168 | //changes only affect the rows after startRow
169 | removeRowMetadata(startRow + 1, analyzeEnd - delta);
170 | adjustOffsetOfRowsFrom(startRow + 1, delta);
171 | analyzeWordWrap(startRow + 1, analyzeStart, analyzeEnd);
172 | }
173 |
174 | /**
175 | * Removes row offset info from fromRow to the row that endOffset is on,
176 | * inclusive.
177 | *
178 | * No error checking is done on parameters.
179 | */
180 | private void removeRowMetadata(int fromRow, int endOffset)
181 | {
182 | while (fromRow < _rowTable.size() &&
183 | _rowTable.get(fromRow) <= endOffset)
184 | {
185 | _rowTable.remove(fromRow);
186 | }
187 | }
188 |
189 | private void adjustOffsetOfRowsFrom(int fromRow, int offset)
190 | {
191 | for (int i = fromRow; i < _rowTable.size(); ++i)
192 | {
193 | _rowTable.set(i, _rowTable.get(i) + offset);
194 | }
195 | }
196 |
197 | public void analyzeWordWrap()
198 | {
199 |
200 | resetRowTable();
201 |
202 | if (_isWordWrap&&!hasMinimumWidthForWordWrap())
203 | {
204 | if (_metrics.getRowWidth() > 0)
205 | {
206 | TextWarriorException.fail("Text field has non-zero width but still too small for word wrap");
207 | }
208 | // _metrics.getRowWidth() might legitmately be zero when the text field has not been layout yet
209 | return;
210 | }
211 |
212 | analyzeWordWrap(1, 0, getTextLength());
213 | }
214 |
215 | private boolean hasMinimumWidthForWordWrap()
216 | {
217 | final int maxWidth = _metrics.getRowWidth();
218 | //assume the widest char is 2ems wide
219 | return (maxWidth >= 2 * _metrics.getAdvance('M'));
220 | }
221 |
222 | //No error checking is done on parameters.
223 | //A word consists of a sequence of 0 or more non-whitespace characters followed by
224 | //exactly one whitespace character. Note that EOF is considered whitespace.
225 | private void analyzeWordWrap(int rowIndex, int startOffset, int endOffset)
226 | {
227 | if (!_isWordWrap)
228 | {
229 | int offset = logicalToRealIndex(startOffset);
230 | int end = logicalToRealIndex(endOffset);
231 | ArrayList rowTable = new ArrayList();
232 |
233 | while (offset < end)
234 | {
235 | // skip the gap
236 | if (offset == _gapStartIndex)
237 | {
238 | offset = _gapEndIndex;
239 | }
240 | char c = _contents[offset];
241 | if (c == Language.NEWLINE)
242 | {
243 | //start a new row
244 | rowTable.add(realToLogicalIndex(offset) + 1);
245 | }
246 | ++offset;
247 |
248 | }
249 | _rowTable.addAll(rowIndex, rowTable);
250 | return;
251 | }
252 | if (!hasMinimumWidthForWordWrap())
253 | {
254 | TextWarriorException.fail("Not enough space to do word wrap");
255 | return;
256 | }
257 |
258 | ArrayList rowTable = new ArrayList();
259 | int offset = logicalToRealIndex(startOffset);
260 | int end = logicalToRealIndex(endOffset);
261 | int potentialBreakPoint = startOffset;
262 | int wordExtent = 0;
263 | final int maxWidth = _metrics.getRowWidth();
264 | int remainingWidth = maxWidth;
265 |
266 | while (offset < end)
267 | {
268 | // skip the gap
269 | if (offset == _gapStartIndex)
270 | {
271 | offset = _gapEndIndex;
272 | }
273 |
274 | char c = _contents[offset];
275 | wordExtent += _metrics.getAdvance(c);
276 |
277 | boolean isWhitespace = (c == ' ' || c == Language.TAB
278 | || c == Language.NEWLINE || c == Language.EOF);
279 |
280 | if (isWhitespace)
281 | {
282 | //full word obtained
283 | if (wordExtent <= remainingWidth)
284 | {
285 | remainingWidth -= wordExtent;
286 | }
287 | else if (wordExtent > maxWidth)
288 | {
289 | //handle a word too long to fit on one row
290 | int current = logicalToRealIndex(potentialBreakPoint);
291 | remainingWidth = maxWidth;
292 |
293 | //start the word on a new row, if it isn't already
294 | if (potentialBreakPoint != startOffset && (rowTable.isEmpty() ||
295 | potentialBreakPoint != rowTable.get(rowTable.size() - 1)))
296 | {
297 | rowTable.add(potentialBreakPoint);
298 | }
299 |
300 | while (current <= offset)
301 | {
302 | // skip the gap
303 | if (current == _gapStartIndex)
304 | {
305 | current = _gapEndIndex;
306 | }
307 |
308 | int advance = _metrics.getAdvance(_contents[current]);
309 | if (advance > remainingWidth)
310 | {
311 | rowTable.add(realToLogicalIndex(current));
312 | remainingWidth = maxWidth - advance;
313 | }
314 | else
315 | {
316 | remainingWidth -= advance;
317 | }
318 |
319 | ++current;
320 | }
321 | }
322 | else
323 | {
324 | //invariant: potentialBreakPoint != startOffset
325 | //put the word on a new row
326 | rowTable.add(potentialBreakPoint);
327 | remainingWidth = maxWidth - wordExtent;
328 | }
329 |
330 | wordExtent = 0;
331 | potentialBreakPoint = realToLogicalIndex(offset) + 1;
332 | }
333 |
334 | if (c == Language.NEWLINE)
335 | {
336 | //start a new row
337 | rowTable.add(potentialBreakPoint);
338 | remainingWidth = maxWidth;
339 | }
340 |
341 | ++offset;
342 | }
343 |
344 | //merge with existing row table
345 | _rowTable.addAll(rowIndex, rowTable);
346 | }
347 |
348 | public String getRow(int rowNumber)
349 | {
350 |
351 | int rowSize = getRowSize(rowNumber);
352 | if (rowSize == 0)
353 | {
354 | return new String();
355 | }
356 |
357 | int startIndex = _rowTable.get(rowNumber);
358 | return subSequence(startIndex, rowSize).toString();
359 | }
360 |
361 | public int getRowSize(int rowNumber)
362 | {
363 |
364 | if (isInvalidRow(rowNumber))
365 | {
366 | return 0;
367 | }
368 |
369 | if (rowNumber != (_rowTable.size() - 1))
370 | {
371 | return _rowTable.get(rowNumber + 1) - _rowTable.get(rowNumber);
372 | }
373 | else
374 | {
375 | //last row
376 | return getTextLength() - _rowTable.get(rowNumber);
377 | }
378 | }
379 |
380 | public int getRowCount()
381 | {
382 |
383 | return _rowTable.size();
384 | }
385 |
386 | public int getRowOffset(int rowNumber)
387 | {
388 |
389 |
390 | if (isInvalidRow(rowNumber))
391 | {
392 | return -1;
393 | }
394 |
395 | return _rowTable.get(rowNumber);
396 | }
397 |
398 | /**
399 | * Get the row number that charOffset is on
400 | *
401 | * @return The row number that charOffset is on, or -1 if charOffset is invalid
402 | */
403 | public int findRowNumber(int charOffset)
404 | {
405 |
406 | if (!isValid(charOffset))
407 | {
408 | return -1;
409 | }
410 |
411 | //binary search of _rowTable
412 | int right = _rowTable.size() - 1;
413 | int left = 0;
414 | while (right >= left)
415 | {
416 | int mid = (left + right) / 2;
417 | int nextLineOffset = ((mid + 1) < _rowTable.size()) ? _rowTable.get(mid + 1) : getTextLength();
418 | if (charOffset >= _rowTable.get(mid) && charOffset < nextLineOffset)
419 | {
420 | return mid;
421 | }
422 |
423 | if (charOffset >= nextLineOffset)
424 | {
425 | left = mid + 1;
426 | }
427 | else
428 | {
429 | right = mid - 1;
430 | }
431 | }
432 |
433 | //should not be here
434 | return -1;
435 | }
436 |
437 |
438 | protected boolean isInvalidRow(int rowNumber)
439 | {
440 | return rowNumber < 0 || rowNumber >= _rowTable.size();
441 | }
442 |
443 |
444 |
445 | public static interface TextFieldMetrics
446 | {
447 | /**
448 | * Returns printed width of c.
449 | *
450 | * @param c Character to measure
451 | * @return Advance of character, in pixels
452 | */
453 | public int getAdvance(char c);
454 |
455 | /**
456 | * Returns the maximum width available for a row of text to be layout. This
457 | * should not be larger than the width of the text field.
458 | *
459 | * @return Maximum width of a row, in pixels
460 | */
461 | public int getRowWidth();
462 | }
463 | }
464 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/util/DocumentProvider.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 Tah Wei Hoon.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License Version 2.0,
5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html
6 | *
7 | * This software is provided "as is". Use at your own risk.
8 | */
9 | package com.mrikso.codeeditor.util;
10 |
11 | import com.mrikso.codeeditor.lang.Language;
12 |
13 | import java.util.List;
14 |
15 | /**
16 | * Iterator class to access characters of the underlying text buffer.
17 | *
18 | * The usage procedure is as follows:
19 | * 1. Call seekChar(offset) to mark the position to start iterating
20 | * 2. Call hasNext() to see if there are any more char
21 | * 3. Call next() to get the next char
22 | *
23 | * If there is more than 1 DocumentProvider pointing to the same Document,
24 | * changes made by one DocumentProvider will not cause other DocumentProviders
25 | * to be notified. Implement a publish/subscribe interface if required.
26 | */
27 | public class DocumentProvider implements CharSequence
28 | {
29 |
30 | /** Current position in the text. Range [ 0, _theText.getTextLength() ) */
31 | private int _currIndex;
32 | private final Document _theText;
33 |
34 | public DocumentProvider(Document.TextFieldMetrics metrics){
35 | _currIndex = 0;
36 | _theText = new Document(metrics);
37 | }
38 |
39 | public DocumentProvider(Document doc){
40 | _currIndex = 0;
41 | _theText = doc;
42 | }
43 |
44 | public DocumentProvider(DocumentProvider rhs){
45 | _currIndex = 0;
46 | _theText = rhs._theText;
47 | }
48 |
49 | @Override
50 | public int length()
51 | {
52 | // TODO: Implement this method
53 | return _theText.length();
54 | }
55 | /**
56 | * Get a substring of up to maxChars length, starting from charOffset
57 | */
58 | public CharSequence subSequence(int charOffset, int maxChars){
59 | return _theText.subSequence(charOffset, maxChars);
60 | }
61 |
62 | public char charAt(int charOffset){
63 | if(_theText.isValid(charOffset)){
64 | return _theText.charAt(charOffset);
65 | }
66 | else{
67 | return Language.NULL_CHAR;
68 | }
69 | }
70 |
71 | public String getRow(int rowNumber){
72 | return _theText.getRow(rowNumber);
73 | }
74 |
75 | /**
76 | * Get the row number that charOffset is on
77 | */
78 | public int findRowNumber(int charOffset){
79 | return _theText.findRowNumber(charOffset);
80 | }
81 |
82 | /**
83 | * Get the line number that charOffset is on. The difference between a line
84 | * and a row is that a line can be word-wrapped into many rows.
85 | */
86 | public int findLineNumber(int charOffset){
87 | return _theText.findLineNumber(charOffset);
88 | }
89 |
90 | /**
91 | * Get the offset of the first character on rowNumber
92 | */
93 | public int getRowOffset(int rowNumber){
94 | return _theText.getRowOffset(rowNumber);
95 | }
96 |
97 |
98 | /**
99 | * Get the offset of the first character on lineNumber. The difference
100 | * between a line and a row is that a line can be word-wrapped into many rows.
101 | */
102 | public int getLineOffset(int lineNumber){
103 | return _theText.getLineOffset(lineNumber);
104 | }
105 |
106 | /**
107 | * Sets the iterator to point at startingChar.
108 | *
109 | * If startingChar is invalid, hasNext() will return false, and _currIndex
110 | * will be set to -1.
111 | *
112 | * @return startingChar, or -1 if startingChar does not exist
113 | */
114 | public int seekChar(int startingChar){
115 | if(_theText.isValid(startingChar)){
116 | _currIndex = startingChar;
117 | }
118 | else{
119 | _currIndex = -1;
120 | }
121 | return _currIndex;
122 | }
123 |
124 | public boolean hasNext(){
125 | return (_currIndex >= 0 &&
126 | _currIndex < _theText.getTextLength());
127 | }
128 |
129 | /**
130 | * Returns the next character and moves the iterator forward.
131 | *
132 | * Does not do bounds-checking. It is the responsibility of the caller
133 | * to check hasNext() first.
134 | *
135 | * @return Next character
136 | */
137 | public char next(){
138 | char nextChar = _theText.charAt(_currIndex);
139 | ++_currIndex;
140 | return nextChar;
141 | }
142 |
143 | /**
144 | * Inserts c into the document, shifting existing characters from
145 | * insertionPoint (inclusive) to the right
146 | *
147 | * If insertionPoint is invalid, nothing happens.
148 | */
149 | public void insertBefore(char c, int insertionPoint, long timestamp){
150 | if(!_theText.isValid(insertionPoint)){
151 | return;
152 | }
153 |
154 | char[] a = new char[1];
155 | a[0] = c;
156 | _theText.insert(a, insertionPoint, timestamp, true);
157 | }
158 |
159 | /**
160 | * Inserts characters of cArray into the document, shifting existing
161 | * characters from insertionPoint (inclusive) to the right
162 | *
163 | * If insertionPoint is invalid, nothing happens.
164 | */
165 | public void insertBefore(char[] cArray, int insertionPoint, long timestamp){
166 | if(!_theText.isValid(insertionPoint) || cArray.length == 0){
167 | return;
168 | }
169 |
170 | _theText.insert(cArray, insertionPoint, timestamp, true);
171 | }
172 |
173 | public void insert(int i, CharSequence s)
174 | {
175 | _theText.insert(new char[]{s.charAt(0)},i,System.nanoTime(),true);
176 | }
177 | /**
178 | * Deletes the character at deletionPoint index.
179 | * If deletionPoint is invalid, nothing happens.
180 | */
181 | public void deleteAt(int deletionPoint, long timestamp){
182 | if(!_theText.isValid(deletionPoint)){
183 | return;
184 | }
185 | _theText.delete(deletionPoint, 1, timestamp, true);
186 | }
187 |
188 |
189 | /**
190 | * Deletes up to maxChars number of characters starting from deletionPoint
191 | * If deletionPoint is invalid, or maxChars is not positive, nothing happens.
192 | */
193 | public void deleteAt(int deletionPoint, int maxChars, long time){
194 | if(!_theText.isValid(deletionPoint) || maxChars <= 0){
195 | return;
196 | }
197 | int totalChars = Math.min(maxChars, _theText.getTextLength() - deletionPoint);
198 | _theText.delete(deletionPoint, totalChars, time, true);
199 | }
200 |
201 | /**
202 | * Returns true if the underlying text buffer is in batch edit mode
203 | */
204 | public boolean isBatchEdit(){
205 | return _theText.isBatchEdit();
206 | }
207 |
208 | /**
209 | * Signals the beginning of a series of insert/delete operations that can be
210 | * undone/redone as a single unit
211 | */
212 | public void beginBatchEdit(){
213 | _theText.beginBatchEdit();
214 | }
215 |
216 | /**
217 | * Signals the end of a series of insert/delete operations that can be
218 | * undone/redone as a single unit
219 | */
220 | public void endBatchEdit(){
221 | _theText.endBatchEdit();
222 | }
223 |
224 | /**
225 | * Returns the number of rows in the document
226 | */
227 | public int getRowCount(){
228 | return _theText.getRowCount();
229 | }
230 |
231 | /**
232 | * Returns the number of characters in the row specified by rowNumber
233 | */
234 | public int getRowSize(int rowNumber){
235 | return _theText.getRowSize(rowNumber);
236 | }
237 |
238 | /**
239 | * Returns the number of characters in the document, including the terminal
240 | * End-Of-File character
241 | */
242 | public int docLength(){
243 | return _theText.getTextLength();
244 | }
245 |
246 | //TODO make thread-safe
247 | /**
248 | * Removes spans from the document.
249 | * Beware: Not thread-safe! Another thread may be modifying the same spans
250 | * returned from getSpans()
251 | */
252 | public void clearSpans(){
253 | _theText.clearSpans();
254 | }
255 |
256 | /**
257 | * Beware: Not thread-safe!
258 | */
259 | public List getSpans(){
260 | return _theText.getSpans();
261 | }
262 |
263 | /**
264 | * Sets the spans to use in the document.
265 | * Spans are continuous sequences of characters that have the same format
266 | * like color, font, etc.
267 | *
268 | * @param spans A collection of Pairs, where Pair.first is the start
269 | * position of the token, and Pair.second is the type of the token.
270 | */
271 | public void setSpans(List spans){
272 | _theText.setSpans(spans);
273 | }
274 |
275 | public void setMetrics(Document.TextFieldMetrics metrics){
276 | _theText.setMetrics(metrics);
277 | }
278 |
279 | /**
280 | * Enable/disable word wrap for the document. If enabled, the document is
281 | * immediately analyzed for word wrap breakpoints, which might take an
282 | * arbitrarily long time.
283 | */
284 | public void setWordWrap(boolean enable){
285 | _theText.setWordWrap(enable);
286 | }
287 |
288 | public boolean isWordWrap(){
289 | return _theText.isWordWrap();
290 | }
291 |
292 | /**
293 | * Analyze the document for word wrap break points. Does nothing if word
294 | * wrap is disabled for the document.
295 | */
296 | public void analyzeWordWrap(){
297 | _theText.analyzeWordWrap();
298 | }
299 |
300 | public boolean canUndo() {
301 | return _theText.canUndo();
302 | }
303 |
304 | public boolean canRedo() {
305 | return _theText.canRedo();
306 | }
307 |
308 | public int undo() {
309 | return _theText.undo();
310 | }
311 |
312 | public int redo() {
313 | return _theText.redo();
314 | }
315 |
316 | @Override
317 | public String toString()
318 | {
319 | // TODO: Implement this method
320 | return _theText.toString();
321 | }
322 |
323 |
324 | }
325 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/util/FindThread.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011 Tah Wei Hoon.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License Version 2.0,
5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html
6 | *
7 | * This software is provided "as is". Use at your own risk.
8 | */
9 | package com.mrikso.codeeditor.util;
10 |
11 | import java.util.Vector;
12 |
13 |
14 | /**
15 | * Worker thread to carry our find and replaceAll operations.
16 | * The find thread should not be reused after it has completed. Create a new one
17 | * for another operation.
18 | */
19 | public class FindThread extends Thread implements ProgressSource {
20 | /**
21 | * Reported progress will be scaled from 0 to MAX_PROGRESS
22 | */
23 | private final static int MAX_PROGRESS = 100;
24 | final private SearchStrategy FINDER = new LinearSearchStrategy();
25 | protected int _requestCode;
26 | protected DocumentProvider _src;
27 | protected Vector _progressObservers = new Vector();
28 | protected String _searchText;
29 | protected String _replacementText;
30 | protected int _start;
31 | protected boolean _isCaseSensitive;
32 | protected boolean _isWholeWord;
33 | protected boolean _isDone = false;
34 | protected FindResults _results;
35 | private int _docSize = 0; // size, in chars, of the document to search
36 |
37 | private FindThread(int requestCode, DocumentProvider src, String searchText, int start,
38 | boolean isCaseSensitive, boolean isWholeWord) {
39 | _requestCode = requestCode;
40 | _src = src;
41 | _start = start;
42 | _searchText = searchText;
43 | _isCaseSensitive = isCaseSensitive;
44 | _isWholeWord = isWholeWord;
45 | _docSize = src.docLength();
46 | }
47 |
48 | private FindThread(int requestCode, DocumentProvider src, String searchText, String replacementText, int start,
49 | boolean isCaseSensitive, boolean isWholeWord) {
50 | _requestCode = requestCode;
51 | _src = src;
52 | _start = start;
53 | _searchText = searchText;
54 | _replacementText = replacementText;
55 | _isCaseSensitive = isCaseSensitive;
56 | _isWholeWord = isWholeWord;
57 | _docSize = src.docLength();
58 | }
59 |
60 | static public FindThread createFindThread(DocumentProvider src, String searchText, int start, boolean isForwardSearch,
61 | boolean isCaseSensitive, boolean isWholeWord) {
62 |
63 | int requestCode = (isForwardSearch) ? ProgressSource.FIND : ProgressSource.FIND_BACKWARDS;
64 |
65 | return new FindThread(requestCode, src, searchText, start, isCaseSensitive, isWholeWord);
66 | }
67 |
68 | static public FindThread createReplaceAllThread(DocumentProvider src, String searchText, String replacementText, int start,
69 | boolean isCaseSensitive, boolean isWholeWord) {
70 | return new FindThread(ProgressSource.REPLACE_ALL, src, searchText, replacementText, start, isCaseSensitive, isWholeWord);
71 | }
72 |
73 | public void run() {
74 | _isDone = false;
75 | _results = new FindResults(_searchText.length());
76 |
77 | switch (_requestCode) {
78 | case ProgressSource.FIND:
79 | _results.foundOffset = FINDER.wrappedFind(_src, _searchText, _start, _isCaseSensitive, _isWholeWord);
80 | notifyComplete(_results);
81 | break;
82 | case ProgressSource.FIND_BACKWARDS:
83 | _results.foundOffset = FINDER.wrappedFindBackwards(_src, _searchText, _start,
84 | _isCaseSensitive, _isWholeWord);
85 | notifyComplete(_results);
86 | break;
87 | case ProgressSource.REPLACE_ALL:
88 | Pair replaceResult = FINDER.replaceAll(_src, _searchText, _replacementText, _start,
89 | _isCaseSensitive, _isWholeWord);
90 | _results.replacementCount = replaceResult.getFirst();
91 | _results.newStartPosition = replaceResult.getSecond();
92 | notifyComplete(_results);
93 | break;
94 | default:
95 | TextWarriorException.assertVerbose(false, "Invalid request code for FindThread");
96 | break;
97 | }
98 | }
99 |
100 | @Override
101 | public final int getMin() {
102 | return 0;
103 | }
104 |
105 | @Override
106 | public final int getMax() {
107 | return MAX_PROGRESS;
108 | }
109 |
110 | @Override
111 | public final int getCurrent() {
112 | double progressProportion = (_docSize == 0) ? 0 : (double) FINDER.getProgress() / (double) _docSize;
113 | return (int) (progressProportion * MAX_PROGRESS);
114 | }
115 |
116 | @Override
117 | public final void forceStop() {
118 | //TODO implement
119 | }
120 |
121 | @Override
122 | public final boolean isDone() {
123 | return _isDone;
124 | }
125 |
126 | @Override
127 | synchronized public final void registerObserver(ProgressObserver po) {
128 | _progressObservers.addElement(po);
129 | }
130 |
131 | @Override
132 | synchronized public final void removeObservers() {
133 | _progressObservers.clear();
134 | }
135 |
136 | synchronized protected void notifyComplete(Object result) {
137 | _isDone = true;
138 | for (ProgressObserver po : _progressObservers) {
139 | po.onComplete(_requestCode, result);
140 | }
141 | }
142 |
143 | public final int getRequestCode() {
144 | return _requestCode;
145 | }
146 |
147 | public final FindResults getResults() {
148 | return _results;
149 | }
150 |
151 | public static class FindResults {
152 | public int foundOffset = -1;
153 | public int replacementCount = 0;
154 | public int newStartPosition = 0;
155 | public int searchTextLength = 0; //for convenience
156 |
157 | public FindResults(int searchLength) {
158 | searchTextLength = searchLength;
159 | }
160 | }
161 |
162 | }
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/util/Flag.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 Tah Wei Hoon.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License Version 2.0,
5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html
6 | *
7 | * This software is provided "as is". Use at your own risk.
8 | */
9 | package com.mrikso.codeeditor.util;
10 |
11 | public class Flag {
12 | private boolean state = false;
13 |
14 | synchronized public final void set(){
15 | state = true;
16 | }
17 |
18 | synchronized public final void clear(){
19 | state = false;
20 | }
21 |
22 | synchronized public final boolean isSet(){
23 | return state;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/util/HelperUtils.java:
--------------------------------------------------------------------------------
1 | package com.mrikso.codeeditor.util;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.Bitmap;
6 | import android.graphics.Canvas;
7 | import android.graphics.drawable.Drawable;
8 | import android.util.TypedValue;
9 |
10 | import androidx.core.content.ContextCompat;
11 |
12 | import com.mrikso.codeeditor.R;
13 |
14 | public class HelperUtils {
15 |
16 | //get AccentColor
17 | public static int fetchAccentColor(Context context) {
18 | TypedArray typedArray = context.obtainStyledAttributes(new TypedValue().data, new int[]{R.attr.colorAccent});
19 | int color = typedArray.getColor(0, 0);
20 | typedArray.recycle();
21 | return color;
22 | }
23 |
24 | public static float getDpi(Context context) {
25 | return context.getResources().getDisplayMetrics().density;
26 | }
27 |
28 | // create bitmap from vector drawable
29 | public static Bitmap getBitmap(Context context, int res) {
30 | Bitmap bitmap = null;
31 | Drawable vectorDrawable = ContextCompat.getDrawable(context, res);
32 | if (vectorDrawable != null) {
33 | vectorDrawable.setAlpha(210);
34 | vectorDrawable.setTint(fetchAccentColor(context));
35 | bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(), vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
36 | Canvas canvas = new Canvas(bitmap);
37 | vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
38 | vectorDrawable.draw(canvas);
39 | return bitmap;
40 | }
41 | return bitmap;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/util/LinearSearchStrategy.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 Tah Wei Hoon.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License Version 2.0,
5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html
6 | *
7 | * This software is provided "as is". Use at your own risk.
8 | */
9 | package com.mrikso.codeeditor.util;
10 |
11 |
12 | import com.mrikso.codeeditor.lang.Language;
13 |
14 | public class LinearSearchStrategy implements SearchStrategy {
15 | private int _unitsDone = 0;
16 |
17 | @Override
18 | // only applicable to replaceAll operation
19 | public int getProgress() {
20 | return _unitsDone;
21 | }
22 |
23 | @Override
24 | public int wrappedFind(DocumentProvider src, String target, int start,
25 | boolean isCaseSensitive, boolean isWholeWord) {
26 |
27 | // search towards end of doc first...
28 | int foundOffset = find(src, target, start, src.docLength(),
29 | isCaseSensitive, isWholeWord);
30 | // ...then from beginning of doc
31 | if (foundOffset < 0) {
32 | foundOffset = find(src, target, 0, start,
33 | isCaseSensitive, isWholeWord);
34 | }
35 |
36 | return foundOffset;
37 | }
38 |
39 | @Override
40 | public int find(DocumentProvider src, String target, int start, int end,
41 | boolean isCaseSensitive, boolean isWholeWord) {
42 | if (target.length() == 0) {
43 | return -1;
44 | }
45 | if (start < 0) {
46 | TextWarriorException.fail("TextBuffer.find: Invalid start position");
47 | start = 0;
48 | }
49 | if (end > src.docLength()) {
50 | TextWarriorException.fail("TextBuffer.find: Invalid end position");
51 | end = src.docLength();
52 | }
53 |
54 | end = Math.min(end, src.docLength() - target.length() + 1);
55 | int offset = start;
56 | while (offset < end) {
57 | if (equals(src, target, offset, isCaseSensitive) &&
58 | (!isWholeWord || isSandwichedByWhitespace(src, offset, target.length()))) {
59 | break;
60 | }
61 |
62 | ++offset;
63 | ++_unitsDone;
64 | }
65 |
66 | if (offset < end) {
67 | return offset;
68 | } else {
69 | return -1;
70 | }
71 | }
72 |
73 | @Override
74 | public int wrappedFindBackwards(DocumentProvider src, String target, int start,
75 | boolean isCaseSensitive, boolean isWholeWord) {
76 |
77 | // search towards beginning of doc first...
78 | int foundOffset = findBackwards(src, target, start, -1,
79 | isCaseSensitive, isWholeWord);
80 | // ...then from end of doc
81 | if (foundOffset < 0) {
82 | foundOffset = findBackwards(src, target, src.docLength() - 1, start,
83 | isCaseSensitive, isWholeWord);
84 | }
85 |
86 | return foundOffset;
87 | }
88 |
89 |
90 | @Override
91 | public int findBackwards(DocumentProvider src, String target, int start, int end,
92 | boolean isCaseSensitive, boolean isWholeWord) {
93 | if (target.length() == 0) {
94 | return -1;
95 | }
96 | if (start >= src.docLength()) {
97 | TextWarriorException.fail("Invalid start position given to TextBuffer.find");
98 | start = src.docLength() - 1;
99 | }
100 | if (end < -1) {
101 | TextWarriorException.fail("Invalid end position given to TextBuffer.find");
102 | end = -1;
103 | }
104 | int offset = Math.min(start, src.docLength() - target.length());
105 | while (offset > end) {
106 | if (equals(src, target, offset, isCaseSensitive) &&
107 | (!isWholeWord || isSandwichedByWhitespace(src, offset, target.length()))) {
108 | break;
109 | }
110 |
111 | --offset;
112 | }
113 |
114 | if (offset > end) {
115 | return offset;
116 | } else {
117 | return -1;
118 | }
119 | }
120 |
121 | @Override
122 | public Pair replaceAll(DocumentProvider src, String searchText,
123 | String replacementText, int mark,
124 | boolean isCaseSensitive, boolean isWholeWord) {
125 | int replacementCount = 0;
126 | int anchor = mark;
127 | _unitsDone = 0;
128 |
129 | final char[] replacement = replacementText.toCharArray();
130 | int foundIndex = find(src, searchText, 0, src.docLength(),
131 | isCaseSensitive, isWholeWord);
132 | long timestamp = System.nanoTime();
133 |
134 | src.beginBatchEdit();
135 | while (foundIndex != -1) {
136 | src.deleteAt(foundIndex, searchText.length(), timestamp);
137 | src.insertBefore(replacement, foundIndex, timestamp);
138 | if (foundIndex < anchor) {
139 | // adjust anchor because of differences in doc length
140 | // after word replacement
141 | anchor += replacementText.length() - searchText.length();
142 | }
143 | ++replacementCount;
144 | _unitsDone += searchText.length(); //skip replaced chars
145 | foundIndex = find(
146 | src,
147 | searchText,
148 | foundIndex + replacementText.length(),
149 | src.docLength(),
150 | isCaseSensitive,
151 | isWholeWord);
152 | }
153 | src.endBatchEdit();
154 |
155 | return new Pair(replacementCount, Math.max(anchor, 0));
156 | }
157 |
158 |
159 | protected boolean equals(DocumentProvider src, String target,
160 | int srcOffset, boolean isCaseSensitive) {
161 | if ((src.docLength() - srcOffset) < target.length()) {
162 | //compared range in src must at least be as long as target
163 | return false;
164 | }
165 |
166 | int i;
167 | for (i = 0; i < target.length(); ++i) {
168 | if (isCaseSensitive &&
169 | target.charAt(i) != src.charAt(i + srcOffset)) {
170 | return false;
171 | }
172 | // for case-insensitive search, compare both strings in lower case
173 | if (!isCaseSensitive &&
174 | Character.toLowerCase(target.charAt(i)) !=
175 | Character.toLowerCase(src.charAt(i + srcOffset))) {
176 | return false;
177 | }
178 |
179 | }
180 |
181 | return true;
182 | }
183 |
184 | /**
185 | * Checks if a word starting at startPosition with size length is bounded
186 | * by whitespace.
187 | */
188 | protected boolean isSandwichedByWhitespace(DocumentProvider src,
189 | int start, int length) {
190 | Language charSet = Lexer.getLanguage();
191 | boolean startWithWhitespace = (start == 0) || charSet.isWhitespace(src.charAt(start - 1));
192 |
193 | int end = start + length;
194 | boolean endWithWhitespace = (end == src.docLength()) || charSet.isWhitespace(src.charAt(end));
195 |
196 | return (startWithWhitespace && endWithWhitespace);
197 | }
198 |
199 | }
200 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/util/Pair.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 Tah Wei Hoon.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License Version 2.0,
5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html
6 | *
7 | * This software is provided "as is". Use at your own risk.
8 | */
9 | package com.mrikso.codeeditor.util;
10 |
11 | public final class Pair {
12 | public int first;
13 | public int second;
14 |
15 | public Pair(int x, int y){
16 | first = x;
17 | second = y;
18 | }
19 |
20 | public final void setFirst(int value){
21 | first = value;
22 | }
23 |
24 | public final void setSecond(int value){
25 | second = value;
26 | }
27 |
28 | public final int getFirst() {
29 | return first;
30 | }
31 |
32 | public final int getSecond() {
33 | return second;
34 | }
35 |
36 | @Override
37 | public String toString() {
38 | return "("+first+","+second+")";
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/util/ProgressObserver.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011 Tah Wei Hoon.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License Version 2.0,
5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html
6 | *
7 | * This software is provided "as is". Use at your own risk.
8 | */
9 | package com.mrikso.codeeditor.util;
10 |
11 | /**
12 | * Can be registered with a {@link ProgressSource} of interest, which will cause
13 | * progress updates to be sent to the ProgressObserver.
14 | */
15 | public interface ProgressObserver {
16 | public void onComplete(int requestCode, Object result);
17 | public void onError(int requestCode, int errorCode, String message);
18 | public void onCancel(int requestCode);
19 | }
20 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/util/ProgressSource.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011 Tah Wei Hoon.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License Version 2.0,
5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html
6 | *
7 | * This software is provided "as is". Use at your own risk.
8 | */
9 | package com.mrikso.codeeditor.util;
10 |
11 | /**
12 | * Represents tasks that carry out long computations
13 | */
14 | public interface ProgressSource {
15 | /** Minimum progress value */
16 | public abstract int getMin();
17 | /** Maximum progress value */
18 | public abstract int getMax();
19 | /** Current progress value */
20 | public abstract int getCurrent();
21 |
22 | /** Whether computation is done */
23 | public abstract boolean isDone();
24 | /** Aborts computation */
25 | public abstract void forceStop();
26 | /** Registers observers that will be informed of changes to the progress state */
27 | public abstract void registerObserver(ProgressObserver obsv);
28 | /** Removes all attached observers */
29 | public abstract void removeObservers();
30 |
31 | /* Nature of computation tasks */
32 | static final public int NONE = 0;
33 | static final public int READ = 1;
34 | static final public int WRITE = 2;
35 | static final public int FIND = 4;
36 | static final public int FIND_BACKWARDS = 8;
37 | static final public int REPLACE_ALL = 16;
38 | static final public int ANALYZE_TEXT = 32;
39 |
40 | /* Error codes */
41 | static final public int ERROR_UNKNOWN = 0;
42 | static final public int ERROR_OUT_OF_MEMORY = 1;
43 | static final public int ERROR_INDEX_OUT_OF_RANGE = 2;
44 | }
45 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/util/SearchStrategy.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 Tah Wei Hoon.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License Version 2.0,
5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html
6 | *
7 | * This software is provided "as is". Use at your own risk.
8 | */
9 | package com.mrikso.codeeditor.util;
10 |
11 | public interface SearchStrategy {
12 | /**
13 | * Searches for target, starting from start (inclusive),
14 | * and stopping at end (exclusive).
15 | *
16 | * @return charOffset of found string; -1 if not found
17 | */
18 | public int find(DocumentProvider src, String target, int start, int end,
19 | boolean isCaseSensitive, boolean isWholeWord);
20 |
21 | /**
22 | * Searches for target, starting from start (inclusive),
23 | * wrapping around to the beginning of document and
24 | * stopping at start (exclusive).
25 | *
26 | * @return charOffset of found string; -1 if not found
27 | */
28 | public int wrappedFind(DocumentProvider src, String target, int start,
29 | boolean isCaseSensitive, boolean isWholeWord);
30 |
31 | /**
32 | * Searches backwards from startCharOffset (inclusive),
33 | * and stopping at end (exclusive).
34 | *
35 | * @return charOffset of found string; -1 if not found
36 | */
37 | public int findBackwards(DocumentProvider src, String target, int start, int end,
38 | boolean isCaseSensitive, boolean isWholeWord);
39 |
40 | /**
41 | * Searches backwards from start (inclusive), wrapping around to
42 | * the end of document and stopping at start (exclusive).
43 | *
44 | * @return charOffset of found string; -1 if not found
45 | */
46 | public int wrappedFindBackwards(DocumentProvider src, String target, int start,
47 | boolean isCaseSensitive, boolean isWholeWord);
48 |
49 | /**
50 | * Replace all matches of searchText in src with replacementText.
51 | *
52 | * @param mark Optional. A position in src that can be tracked for changes.
53 | * After replacements are made, the position may be shifted because of
54 | * insertion/deletion of text before it. The new position of mark is
55 | * returned in Pair.second. If mark is an invalid position, Pair.second
56 | * is undefined.
57 | *
58 | * @return Pair.first is the number of replacements made.
59 | * Pair.second is new position of mark after replacements are made.
60 | */
61 | public Pair replaceAll(DocumentProvider src, String searchText,
62 | String replacementText, int mark,
63 | boolean isCaseSensitive, boolean isWholeWord);
64 |
65 |
66 | /**
67 | * The number of characters that have been examined by the current find
68 | * operation. This method is not synchronized, and the value returned
69 | * may be outdated.
70 | *
71 | * @return The number of characters searched so far
72 | */
73 | public int getProgress();
74 | }
75 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/util/TextBufferCache.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 Tah Wei Hoon.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License Version 2.0,
5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html
6 | *
7 | * This software is provided "as is". Use at your own risk.
8 | */
9 | package com.mrikso.codeeditor.util;
10 |
11 | /**
12 | * A LRU cache that stores the last seek line and its corresponding index so
13 | * that future lookups can start from the cached position instead of
14 | * the beginning of the file
15 | *
16 | * _cache.Pair.First = line index
17 | * _cache.Pair.Second = character offset of first character in that line
18 | *
19 | * TextBufferCache always has one valid entry (0,0) signifying that in line 0,
20 | * the first character is at offset 0. This is true even for an "empty" file,
21 | * which is not really empty because TextBuffer inserts a EOF character in it.
22 | *
23 | * Therefore, _cache[0] is always occupied by the entry (0,0). It is not affected
24 | * by invalidateCache, cache miss, etc. operations
25 | */
26 | public class TextBufferCache {
27 | private static final int CACHE_SIZE = 4; // minimum = 1
28 | private Pair[] _cache = new Pair[CACHE_SIZE];
29 |
30 | public TextBufferCache(){
31 | _cache[0] = new Pair(0, 0); // invariant lineIndex and charOffset relation
32 | for (int i = 1; i < CACHE_SIZE; ++i){
33 | _cache[i] = new Pair(-1, -1);
34 | // -1 line index is used implicitly in calculations in getNearestMatch
35 | }
36 | }
37 |
38 | //TODO consider extracting common parts with getNearestCharOffset(int)
39 | public Pair getNearestLine(int lineIndex){
40 | int nearestMatch = 0;
41 | int nearestDistance = Integer.MAX_VALUE;
42 | for(int i = 0; i < CACHE_SIZE; ++i){
43 | int distance = Math.abs(lineIndex - _cache[i].getFirst());
44 | if (distance < nearestDistance){
45 | nearestDistance = distance;
46 | nearestMatch = i;
47 | }
48 | }
49 |
50 | Pair nearestEntry = _cache[nearestMatch];
51 | makeHead(nearestMatch);
52 | return nearestEntry;
53 | }
54 |
55 | public Pair getNearestCharOffset(int charOffset){
56 | int nearestMatch = 0;
57 | int nearestDistance = Integer.MAX_VALUE;
58 | for(int i = 0; i < CACHE_SIZE; ++i){
59 | int distance = Math.abs(charOffset - _cache[i].getSecond());
60 | if (distance < nearestDistance){
61 | nearestDistance = distance;
62 | nearestMatch = i;
63 | }
64 | }
65 |
66 | Pair nearestEntry = _cache[nearestMatch];
67 | makeHead(nearestMatch);
68 | return nearestEntry;
69 | }
70 |
71 | /**
72 | * Place _cache[newHead] at the top of the list
73 | */
74 | private void makeHead(int newHead){
75 | if(newHead == 0){
76 | return;
77 | }
78 |
79 | Pair temp = _cache[newHead];
80 | for(int i = newHead; i > 1; --i){
81 | _cache[i] = _cache[i-1];
82 | }
83 | _cache[1] = temp; // _cache[0] is always occupied by (0,0)
84 | }
85 |
86 | public void updateEntry(int lineIndex, int charOffset){
87 | if(lineIndex <= 0){
88 | // lineIndex 0 always has 0 charOffset; ignore. Also ignore negative lineIndex
89 | return;
90 | }
91 |
92 | if(!replaceEntry(lineIndex, charOffset)){
93 | insertEntry(lineIndex, charOffset);
94 | }
95 | }
96 |
97 | private boolean replaceEntry(int lineIndex, int charOffset){
98 | for (int i = 1; i < CACHE_SIZE; ++i){
99 | if(_cache[i].getFirst() == lineIndex){
100 | _cache[i].setSecond(charOffset);
101 | return true;
102 | }
103 | }
104 | return false;
105 | }
106 |
107 | private void insertEntry(int lineIndex, int charOffset){
108 | makeHead(CACHE_SIZE-1); // rotate right list of entries
109 | // replace head (most recently used entry) with new entry
110 | _cache[1] = new Pair(lineIndex, charOffset);
111 | }
112 |
113 | /**
114 | * Invalidate all cache entries that have char offset >= fromCharOffset
115 | */
116 | final protected void invalidateCache(int fromCharOffset){
117 | for (int i = 1; i < CACHE_SIZE; ++i){
118 | if(_cache[i].getSecond() >= fromCharOffset){
119 | _cache[i] = new Pair(-1, -1);
120 | }
121 | }
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/util/TextWarriorException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 Tah Wei Hoon.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License Version 2.0,
5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html
6 | *
7 | * This software is provided "as is". Use at your own risk.
8 | */
9 | package com.mrikso.codeeditor.util;
10 |
11 | import android.util.Log;
12 |
13 | public class TextWarriorException extends Exception {
14 | private static final boolean NDEBUG = false; // set to true to suppress assertions
15 |
16 | public TextWarriorException(String msg) {
17 | super(msg);
18 | }
19 |
20 | static public void fail(final String details) {
21 | assertVerbose(false, details);
22 | }
23 |
24 | @SuppressWarnings("all") //suppress dead code warning when NDEBUG == true
25 | static public void assertVerbose(boolean condition, final String details) {
26 | if (NDEBUG) {
27 | return;
28 | }
29 |
30 | if (!condition) {
31 | /* For Android, a Context has to be passed into this method
32 | * to display the error message on the device screen */
33 | System.err.print("TextWarrior assertion failed: ");
34 | // System.err.println(details);
35 | Log.i("codeeditor", details);
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/util/UndoStack.java:
--------------------------------------------------------------------------------
1 |
2 | package com.mrikso.codeeditor.util;
3 |
4 | import java.util.LinkedList;
5 |
6 | /**
7 | * Implements undo/redo for insertion and deletion events of TextBuffer
8 | *
9 | * This class is tightly coupled to the implementation of TextBuffer, in
10 | * particular the inner workings of the gap data structure to optimize
11 | * undo/redo efficiency
12 | *
13 | * When text is inserted/deleted...
14 | * 1. Before text is inserted/deleted, TextBuffer calls captureInsert()/captureDelete()
15 | * 2. If the insertion/deletion is a continuation of the previous edit,
16 | * the incoming edit is merged with the top entry of the undo stack.
17 | * For 2 edits to be considered continuous, they must be the same type,
18 | * (insert or delete), occur within a pre-defined time interval of MERGE_TIME,
19 | * and the later edit must start off where the caret would have been after
20 | * the earlier edit.
21 | * 3. If the incoming edit is not continuous with the previous one, a new entry
22 | * for it is pushed on the stack
23 | *
24 | * Batch mode:
25 | * A client application can specify consecutive insert/delete operations to
26 | * undo/redo as a group. Edits made between a call to beginBatchEdit()
27 | * and a closing endBatchEdit() call are grouped as a unit.
28 | *
29 | * Undo/redo:
30 | * Undo/redo commands merely move the stack pointer and do not delete or insert
31 | * entries. Only when a new edit is made will the entries after the stack
32 | * pointer be deleted.
33 | *
34 | * Optimizaton notes:
35 | * Edited characters are copied lazily. When a new entry is pushed on the undo
36 | * stack, only the starting position and length of the inserted/deleted segment
37 | * is recorded. When another entry is pushed or when the entry is first undone,
38 | * the affected characters are then copied over. This optimization exploits the
39 | * non-destructive nature of continuous edits in TextBuffer -- deleted characters
40 | * can be retrieved from the gap and inserted characters are trivially available.
41 | * For undo/redo of the topmost entry, only the gap boundaries of TextBuffer
42 | * need to be moved.
43 | */
44 | public class UndoStack {
45 | private TextBuffer _buf;
46 | private LinkedList _stack = new LinkedList();
47 | private boolean _isBatchEdit = false;
48 | /** for grouping batch operations */
49 | private int _groupId = 0;
50 | /** where new entries should go */
51 | private int _top = 0;
52 | /** timestamp for the previous edit operation */
53 | long _lastEditTime = -1;
54 |
55 | public UndoStack(TextBuffer buf){
56 | _buf = buf;
57 | }
58 |
59 | /**
60 | * Undo the previous insert/delete operation
61 | *
62 | * @return The suggested position of the caret after the undo, or -1 if
63 | * there is nothing to undo
64 | */
65 | public int undo(){
66 | if(canUndo()){
67 | Command lastUndone = _stack.get(_top-1);
68 | int group = lastUndone._group;
69 | do{
70 | Command c = _stack.get(_top-1);
71 | if(c._group != group){
72 | break;
73 | }
74 |
75 | lastUndone = c;
76 | c.undo();
77 | --_top;
78 | }
79 | while(canUndo());
80 |
81 | return lastUndone.findUndoPosition();
82 | }
83 |
84 | return -1;
85 | }
86 |
87 | /**
88 | * Redo the previous insert/delete operation
89 | *
90 | * @return The suggested position of the caret after the redo, or -1 if
91 | * there is nothing to redo
92 | */
93 | public int redo(){
94 | if(canRedo()){
95 | Command lastRedone = _stack.get(_top);
96 | int group = lastRedone._group;
97 | do{
98 | Command c = _stack.get(_top);
99 | if(c._group != group){
100 | break;
101 | }
102 |
103 | lastRedone = c;
104 | c.redo();
105 | ++_top;
106 | }
107 | while(canRedo());
108 |
109 | return lastRedone.findRedoPosition();
110 | }
111 |
112 | return -1;
113 | }
114 |
115 | //TODO extract common parts of captureInsert and captureDelete
116 | /**
117 | * Records an insert operation. Should be called before the insertion is
118 | * actually done.
119 | */
120 | public void captureInsert(int start, int length, long time){
121 | boolean mergeSuccess = false;
122 |
123 | if(canUndo()){
124 | Command c = _stack.get(_top - 1);
125 |
126 | if(c instanceof InsertCommand
127 | && c.merge(start, length, time)){
128 | mergeSuccess = true;
129 | }
130 | else{
131 | c.recordData();
132 | }
133 | }
134 |
135 | if(!mergeSuccess){
136 | push(new InsertCommand(start, length, _groupId));
137 |
138 | if(!_isBatchEdit){
139 | _groupId++;
140 | }
141 | }
142 |
143 | _lastEditTime = time;
144 | }
145 |
146 | /**
147 | * Records a delete operation. Should be called before the deletion is
148 | * actually done.
149 | */
150 | public void captureDelete(int start, int length, long time){
151 | boolean mergeSuccess = false;
152 |
153 | if(canUndo()){
154 | Command c = _stack.get(_top - 1);
155 |
156 | if(c instanceof DeleteCommand
157 | && c.merge(start, length, time)){
158 | mergeSuccess = true;
159 | }
160 | else{
161 | c.recordData();
162 | }
163 | }
164 |
165 | if(!mergeSuccess){
166 | push(new DeleteCommand(start, length, _groupId));
167 |
168 | if(!_isBatchEdit){
169 | _groupId++;
170 | }
171 | }
172 |
173 | _lastEditTime = time;
174 | }
175 |
176 | private void push(Command c){
177 | trimStack();
178 | ++_top;
179 | _stack.add(c);
180 | }
181 |
182 | private void trimStack(){
183 | while(_stack.size() > _top){
184 | _stack.removeLast();
185 | }
186 | }
187 |
188 | public final boolean canUndo(){
189 | return _top > 0;
190 | }
191 |
192 | public final boolean canRedo(){
193 | return _top < _stack.size();
194 | }
195 |
196 | public boolean isBatchEdit(){
197 | return _isBatchEdit;
198 | }
199 |
200 | public void beginBatchEdit(){
201 | _isBatchEdit = true;
202 | }
203 |
204 | public void endBatchEdit(){
205 | _isBatchEdit = false;
206 | _groupId++;
207 | }
208 |
209 |
210 |
211 | private abstract class Command{
212 | public final static long MERGE_TIME = 1000000000; //750ms in nanoseconds
213 | /** Start position of the edit */
214 | public int _start;
215 | /** Length of the affected segment */
216 | public int _length;
217 | /** Contents of the affected segment */
218 | public String _data;
219 | /** Group ID. Commands of the same group are undone/redone as a unit */
220 | public int _group;
221 |
222 | public abstract void undo();
223 | public abstract void redo();
224 | /** Populates _data with the affected text */
225 | public abstract void recordData();
226 | public abstract int findUndoPosition();
227 | public abstract int findRedoPosition();
228 |
229 | /**
230 | * Attempts to merge in an edit. This will only be successful if the new
231 | * edit is continuous. See {@link UndoStack} for the requirements
232 | * of a continuous edit.
233 | *
234 | * @param start Start position of the new edit
235 | * @param length Length of the newly edited segment
236 | * @param time Timestamp when the new edit was made. There are no
237 | * restrictions on the units used, as long as it is consistently used
238 | * in the whole program
239 | *
240 | * @return Whether the merge was successful
241 | */
242 | public abstract boolean merge(int start, int length, long time);
243 | }
244 |
245 | private class InsertCommand extends Command{
246 | /**
247 | * Corresponds to an insertion of text of size length just before
248 | * start position.
249 | */
250 | public InsertCommand(int start, int length, int groupNumber){
251 | _start = start;
252 | _length = length;
253 | _group = groupNumber;
254 | }
255 |
256 | @Override
257 | public boolean merge(int newStart, int length, long time) {
258 | if(_lastEditTime < 0){
259 | return false;
260 | }
261 |
262 | if((time - _lastEditTime) < MERGE_TIME
263 | && newStart == _start + _length){
264 | _length += length;
265 | trimStack();
266 | return true;
267 | }
268 |
269 | return false;
270 | }
271 |
272 | @Override
273 | public void recordData() {
274 | //TODO handle memory allocation failure
275 | _data = _buf.subSequence(_start, _length).toString();
276 | }
277 |
278 | @Override
279 | public void undo() {
280 | if(_data == null){
281 | recordData();
282 | _buf.shiftGapStart(-_length);
283 | }
284 | else{
285 | //dummy timestamp of 0
286 | _buf.delete(_start, _length, 0 ,false);
287 | }
288 | }
289 |
290 | @Override
291 | public void redo() {
292 | //dummy timestamp of 0
293 | _buf.insert(_data.toCharArray(), _start, 0, false);
294 | }
295 |
296 | @Override
297 | public int findRedoPosition() {
298 | return _start + _length;
299 | }
300 |
301 | @Override
302 | public int findUndoPosition() {
303 | return _start;
304 | }
305 | }
306 |
307 |
308 | private class DeleteCommand extends Command{
309 | /**
310 | * Corresponds to an deletion of text of size length starting from
311 | * start position, inclusive.
312 | */
313 | public DeleteCommand(int start, int length, int seqNumber){
314 | _start = start;
315 | _length = length;
316 | _group = seqNumber;
317 | }
318 |
319 | @Override
320 | public boolean merge(int newStart, int length, long time) {
321 | if(_lastEditTime < 0){
322 | return false;
323 | }
324 |
325 | if((time - _lastEditTime) < MERGE_TIME
326 | && newStart == _start - _length - length + 1){
327 | _start = newStart;
328 | _length += length;
329 | trimStack();
330 | return true;
331 | }
332 |
333 | return false;
334 | }
335 |
336 | @Override
337 | public void recordData() {
338 | //TODO handle memory allocation failure
339 | _data = new String(_buf.gapSubSequence(_length));
340 | }
341 |
342 | @Override
343 | public void undo() {
344 | if(_data == null){
345 | recordData();
346 | _buf.shiftGapStart(_length);
347 | }
348 | else{
349 | //dummy timestamp of 0
350 | _buf.insert(_data.toCharArray(), _start, 0, false);
351 | }
352 | }
353 |
354 | @Override
355 | public void redo() {
356 | //dummy timestamp of 0
357 | _buf.delete(_start, _length, 0, false);
358 | }
359 |
360 | @Override
361 | public int findRedoPosition() {
362 | return _start;
363 | }
364 |
365 | @Override
366 | public int findUndoPosition() {
367 | return _start + _length;
368 | }
369 | }// end inner class
370 | }
371 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/view/ClipboardPanel.java:
--------------------------------------------------------------------------------
1 | package com.mrikso.codeeditor.view;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.Rect;
6 | import android.os.Build;
7 | import android.view.ActionMode;
8 | import android.view.Menu;
9 | import android.view.MenuItem;
10 | import android.view.View;
11 |
12 | import androidx.annotation.RequiresApi;
13 |
14 | import com.mrikso.codeeditor.R;
15 |
16 | public class ClipboardPanel {
17 | protected FreeScrollingTextField _textField;
18 | private Context _context;
19 |
20 | private ActionMode _clipboardActionMode;
21 | private ActionMode.Callback2 _clipboardActionModeCallback2;
22 | private Rect caret;
23 |
24 | public ClipboardPanel(FreeScrollingTextField textField) {
25 | _textField = textField;
26 | _context = textField.getContext();
27 | }
28 |
29 | public Context getContext() {
30 | return _context;
31 | }
32 |
33 | public void show() {
34 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
35 | initData();
36 | startClipboardActionNew();
37 | } else {
38 | startClipboardAction();
39 | }
40 |
41 | }
42 |
43 | public void hide() {
44 | stopClipboardAction();
45 | }
46 |
47 | public void startClipboardAction() {
48 | // TODO: Implement this method
49 | if (_clipboardActionMode == null)
50 | _textField.startActionMode(new ActionMode.Callback() {
51 |
52 | @Override
53 | public boolean onCreateActionMode(ActionMode mode, Menu menu) {
54 | // TODO: Implement this method
55 | _clipboardActionMode = mode;
56 | mode.setTitle(android.R.string.selectTextMode);
57 | TypedArray array = _context.getTheme().obtainStyledAttributes(new int[]{
58 | android.R.attr.actionModeSelectAllDrawable,
59 | android.R.attr.actionModeCutDrawable,
60 | android.R.attr.actionModeCopyDrawable,
61 | android.R.attr.actionModePasteDrawable,
62 | });
63 | menu.add(0, 0, 0, _context.getString(android.R.string.selectAll))
64 | .setShowAsActionFlags(2)
65 | .setAlphabeticShortcut('a')
66 | .setIcon(array.getDrawable(0));
67 |
68 | menu.add(0, 1, 0, _context.getString(android.R.string.cut))
69 | .setShowAsActionFlags(2)
70 | .setAlphabeticShortcut('x')
71 | .setIcon(array.getDrawable(1));
72 |
73 | menu.add(0, 2, 0, _context.getString(android.R.string.copy))
74 | .setShowAsActionFlags(2)
75 | .setAlphabeticShortcut('c')
76 | .setIcon(array.getDrawable(2));
77 |
78 | menu.add(0, 3, 0, _context.getString(android.R.string.paste))
79 | .setShowAsActionFlags(2)
80 | .setAlphabeticShortcut('v')
81 | .setIcon(array.getDrawable(3));
82 | array.recycle();
83 | return true;
84 | }
85 |
86 | @Override
87 | public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
88 | // TODO: Implement this method
89 | return false;
90 | }
91 |
92 | @Override
93 | public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
94 | // TODO: Implement this method
95 | switch (item.getItemId()) {
96 | case 0:
97 | _textField.selectAll();
98 | break;
99 | case 1:
100 | _textField.cut();
101 | mode.finish();
102 | break;
103 | case 2:
104 | _textField.copy();
105 | mode.finish();
106 | break;
107 | case 3:
108 | _textField.paste();
109 | mode.finish();
110 | }
111 | return false;
112 | }
113 |
114 | @Override
115 | public void onDestroyActionMode(ActionMode p1) {
116 | // TODO: Implement this method
117 | _textField.selectText(false);
118 | _clipboardActionMode = null;
119 | }
120 | });
121 |
122 | }
123 |
124 | @RequiresApi(api = Build.VERSION_CODES.M)
125 | public void startClipboardActionNew() {
126 | // TODO: Implement this method
127 | if (_clipboardActionMode == null)
128 | _textField.startActionMode(_clipboardActionModeCallback2, ActionMode.TYPE_FLOATING);
129 | }
130 |
131 | @RequiresApi(api = Build.VERSION_CODES.M)
132 | private void initData(){
133 | _clipboardActionModeCallback2 = new ActionMode.Callback2() {
134 | @Override
135 | public boolean onCreateActionMode(ActionMode mode, Menu menu) {
136 | // TODO: Implement this method
137 | _clipboardActionMode = mode;
138 | menu.add(0, 0, 0, _context.getString(android.R.string.selectAll))
139 | .setShowAsActionFlags(2)
140 | .setAlphabeticShortcut('a');
141 |
142 | menu.add(0, 1, 0, _context.getString(android.R.string.cut))
143 | .setShowAsActionFlags(2)
144 | .setAlphabeticShortcut('x');
145 | menu.add(0, 2, 0, _context.getString(android.R.string.copy))
146 | .setShowAsActionFlags(2)
147 | .setAlphabeticShortcut('c');
148 | menu.add(0, 3, 0, _context.getString(android.R.string.paste))
149 | .setShowAsActionFlags(2)
150 | .setAlphabeticShortcut('v');
151 | menu.add(0, 4, 0, "Delete")
152 | .setShowAsActionFlags(2)
153 | .setAlphabeticShortcut('d');
154 | return true;
155 | }
156 |
157 | @Override
158 | public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
159 | // TODO: Implement this method
160 | return false;
161 | }
162 |
163 | @Override
164 | public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
165 | // TODO: Implement this method
166 | switch (item.getItemId()) {
167 | case 0:
168 | _textField.selectAll();
169 | break;
170 | case 1:
171 | _textField.cut();
172 | mode.finish();
173 | break;
174 | case 2:
175 | _textField.copy();
176 | mode.finish();
177 | break;
178 | case 3:
179 | _textField.paste();
180 | mode.finish();
181 | case 4:
182 | _textField.delete();
183 | mode.finish();
184 | }
185 | return false;
186 |
187 |
188 | }
189 |
190 | @Override
191 | public void onDestroyActionMode(ActionMode p1) {
192 | // TODO: Implement this method
193 | _textField.selectText(false);
194 | _clipboardActionMode = null;
195 | caret = null;
196 | }
197 |
198 | @Override
199 | public void onGetContentRect(ActionMode mode, View view, Rect outRect){
200 | caret = _textField.getBoundingBox(_textField.getCaretPosition());
201 | int x = outRect.left + _textField.getPaddingLeft();
202 | int y = outRect.bottom + _textField.getPaddingTop();
203 | outRect = caret;
204 | super.onGetContentRect(mode, view, outRect);
205 | }
206 | };
207 |
208 | }
209 |
210 | public void stopClipboardAction() {
211 | if (_clipboardActionMode != null) {
212 | //_clipboardActionModeCallback2.onDestroyActionMode(_clipboardActionMode);
213 | _clipboardActionMode.finish();
214 | _clipboardActionMode = null;
215 |
216 | }
217 | }
218 |
219 | }
220 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/view/ColorScheme.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 Tah Wei Hoon.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License Version 2.0,
5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html
6 | *
7 | * This software is provided "as is". Use at your own risk.
8 | */
9 |
10 | package com.mrikso.codeeditor.view;
11 |
12 | import com.mrikso.codeeditor.util.Lexer;
13 | import com.mrikso.codeeditor.util.TextWarriorException;
14 |
15 | import java.util.HashMap;
16 |
17 | public abstract class ColorScheme {
18 | public enum Colorable {
19 | FOREGROUND, BACKGROUND, SELECTION_FOREGROUND, SELECTION_BACKGROUND,
20 | CARET_FOREGROUND, CARET_BACKGROUND, CARET_DISABLED, LINE_HIGHLIGHT,
21 | NON_PRINTING_GLYPH, COMMENT, KEYWORD, NAME, NUMBER, STRING,
22 | SECONDARY
23 | }
24 |
25 | protected HashMap _colors = generateDefaultColors();
26 |
27 | public void setColor(Colorable colorable, int color) {
28 | _colors.put(colorable, color);
29 | }
30 |
31 | public int getColor(Colorable colorable) {
32 | Integer color = _colors.get(colorable);
33 | if (color == null) {
34 | TextWarriorException.fail("Color not specified for " + colorable);
35 | return 0;
36 | }
37 | return color;
38 | }
39 |
40 | // Currently, color scheme is tightly coupled with semantics of the token types
41 | public int getTokenColor(int tokenType) {
42 | Colorable element;
43 | switch (tokenType) {
44 | case Lexer.NORMAL:
45 | element = Colorable.FOREGROUND;
46 | break;
47 | case Lexer.KEYWORD:
48 | element = Colorable.KEYWORD;
49 | break;
50 | case Lexer.NAME:
51 | element = Colorable.NAME;
52 | break;
53 | case Lexer.DOUBLE_SYMBOL_LINE: //fall-through
54 | case Lexer.DOUBLE_SYMBOL_DELIMITED_MULTILINE:
55 | element = Colorable.COMMENT;
56 | break;
57 | case Lexer.SINGLE_SYMBOL_DELIMITED_A: //fall-through
58 | case Lexer.SINGLE_SYMBOL_DELIMITED_B:
59 | element = Colorable.STRING;
60 | break;
61 | case Lexer.NUMBER:
62 | element = Colorable.NUMBER;
63 | break;
64 | case Lexer.SINGLE_SYMBOL_LINE_A: //fall-through
65 | case Lexer.SINGLE_SYMBOL_WORD:
66 | case Lexer.OPERATOR:
67 | element = Colorable.SECONDARY;
68 | break;
69 | case Lexer.SINGLE_SYMBOL_LINE_B: //类型
70 | element = Colorable.NAME;
71 | break;
72 | default:
73 | TextWarriorException.fail("Invalid token type");
74 | element = Colorable.FOREGROUND;
75 | break;
76 | }
77 | return getColor(element);
78 | }
79 |
80 | /**
81 | * Whether this color scheme uses a dark background, like black or dark grey.
82 | */
83 | public abstract boolean isDark();
84 |
85 | private HashMap generateDefaultColors() {
86 | // High-contrast, black-on-white color scheme
87 | HashMap colors = new HashMap(Colorable.values().length);
88 | colors.put(Colorable.FOREGROUND, BLACK);//前景色
89 | colors.put(Colorable.BACKGROUND, WHITE);
90 | colors.put(Colorable.SELECTION_FOREGROUND, WHITE);//选择文本的前景色
91 | colors.put(Colorable.SELECTION_BACKGROUND, 0xFF97C024);//选择文本的背景色
92 | colors.put(Colorable.CARET_FOREGROUND, WHITE);
93 | colors.put(Colorable.CARET_BACKGROUND, LIGHT_BLUE2);
94 | colors.put(Colorable.CARET_DISABLED, GREY);
95 | colors.put(Colorable.LINE_HIGHLIGHT, 0x20888888);
96 |
97 | colors.put(Colorable.NON_PRINTING_GLYPH, 0xff2b91af);//行号
98 | colors.put(Colorable.COMMENT, OLIVE_GREEN); //注释
99 | colors.put(Colorable.KEYWORD, BLUE); //关键字
100 | colors.put(Colorable.NAME, GREY); // Eclipse default color
101 | colors.put(Colorable.NUMBER, PINK); // 数字
102 | colors.put(Colorable.STRING, DARK_RED); //字符串
103 | colors.put(Colorable.SECONDARY, 0xff6f008a);//宏定义
104 | return colors;
105 | }
106 |
107 | // In ARGB format: 0xAARRGGBB
108 | private static final int BLACK = 0xFF000000;
109 | private static final int BLUE = 0xFF0000FF;
110 | private static final int DARK_RED = 0xFFA31515;
111 | private static final int PINK = 0xFFD040DD;
112 | private static final int GREY = 0xFF808080;
113 | private static final int OLIVE_GREEN = 0xFF3F7F5F;
114 | private static final int WHITE = 0xFFFFFFE0;
115 | private static final int LIGHT_BLUE2 = 0xFF40B0FF;
116 | }
117 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/view/ColorSchemeLight.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 Tah Wei Hoon.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License Version 2.0,
5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html
6 | *
7 | * This software is provided "as is". Use at your own risk.
8 | */
9 |
10 | package com.mrikso.codeeditor.view;
11 |
12 |
13 | /**
14 | * Off-black on off-white background color scheme
15 | */
16 | public class ColorSchemeLight extends ColorScheme {
17 |
18 | public ColorSchemeLight(){
19 | setColor(Colorable.FOREGROUND, OFF_BLACK);
20 | setColor(Colorable.BACKGROUND, OFF_WHITE);
21 | setColor(Colorable.SELECTION_FOREGROUND, OFF_WHITE);
22 | setColor(Colorable.CARET_FOREGROUND, OFF_WHITE);
23 | }
24 |
25 | private static final int OFF_WHITE = 0xFFF0F0ED;
26 | private static final int OFF_BLACK = 0xFF333333;
27 |
28 | @Override
29 | public boolean isDark() {
30 | return false;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/view/KeysInterpreter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 Tah Wei Hoon.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License Version 2.0,
5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html
6 | *
7 | * This software is provided "as is". Use at your own risk.
8 | */
9 | package com.mrikso.codeeditor.view;
10 |
11 | import android.view.KeyEvent;
12 |
13 | import com.mrikso.codeeditor.lang.Language;
14 |
15 | public class KeysInterpreter {
16 | public static boolean isSwitchPanel(KeyEvent event) {
17 | return (event.isShiftPressed() &&
18 | (event.getKeyCode() == KeyEvent.KEYCODE_ENTER));
19 | }
20 |
21 | /**
22 | * Maps shortcut keys and Android keycodes to printable characters.
23 | * Note that whitespace is considered printable.
24 | *
25 | * @param event The KeyEvent to interpret
26 | * @return The printable character the event represents,
27 | * or Language.NULL_CHAR if the event does not represent a printable char
28 | */
29 | public static char keyEventToPrintableChar(KeyEvent event) {
30 | char c = Language.NULL_CHAR;
31 |
32 | // convert tab, backspace, newline and space keycodes to standard ASCII values
33 | if (isNewline(event)) {
34 | c = Language.NEWLINE;
35 | } else if (isBackspace(event)) {
36 | c = Language.BACKSPACE;
37 | }
38 | // This should be before the check for isSpace() because the
39 | // shortcut for TAB uses the SPACE key.
40 | else if (isTab(event)) {
41 | c = Language.TAB;
42 | } else if (isSpace(event)) {
43 | c = ' ';
44 | } else if (event.isPrintingKey()) {
45 | c = (char) event.getUnicodeChar(event.getMetaState());
46 | }
47 |
48 | return c;
49 | }
50 |
51 | private static boolean isTab(KeyEvent event) {
52 | return (event.isShiftPressed() &&
53 | (event.getKeyCode() == KeyEvent.KEYCODE_SPACE)) ||
54 | (event.getKeyCode() == KeyEvent.KEYCODE_TAB);
55 | }
56 |
57 | private static boolean isBackspace(KeyEvent event) {
58 | return (event.getKeyCode() == KeyEvent.KEYCODE_DEL);
59 | }
60 |
61 | private static boolean isNewline(KeyEvent event) {
62 | return (event.getKeyCode() == KeyEvent.KEYCODE_ENTER);
63 | }
64 |
65 | private static boolean isSpace(KeyEvent event) {
66 | return (event.getKeyCode() == KeyEvent.KEYCODE_SPACE);
67 | }
68 |
69 | public static boolean isNavigationKey(KeyEvent event) {
70 | int keyCode = event.getKeyCode();
71 | return keyCode == KeyEvent.KEYCODE_DPAD_DOWN ||
72 | keyCode == KeyEvent.KEYCODE_DPAD_UP ||
73 | keyCode == KeyEvent.KEYCODE_DPAD_RIGHT ||
74 | keyCode == KeyEvent.KEYCODE_DPAD_LEFT;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/view/TextFieldInputConnection.java:
--------------------------------------------------------------------------------
1 | package com.mrikso.codeeditor.view;
2 |
3 | import android.text.InputType;
4 | import android.view.KeyEvent;
5 | import android.view.inputmethod.BaseInputConnection;
6 |
7 | import com.mrikso.codeeditor.lang.Language;
8 | import com.mrikso.codeeditor.util.DLog;
9 | import com.mrikso.codeeditor.util.Lexer;
10 |
11 | import static com.mrikso.codeeditor.util.DLog.log;
12 |
13 | //*********************************************************************
14 | //************************** InputConnection **************************
15 | //*********************************************************************
16 | /*
17 | * Does not provide ExtractedText related methods
18 | */
19 | public class TextFieldInputConnection extends BaseInputConnection {
20 | private boolean isComposing = false;
21 | private int composingCharCount = 0;
22 | private int mCaretPosition = -1;
23 | private FreeScrollingTextField textField;
24 |
25 | public TextFieldInputConnection(FreeScrollingTextField v ) {
26 | super(v, true);
27 | textField = v;
28 | }
29 |
30 | public void resetComposingState() {
31 | composingCharCount = 0;
32 | isComposing = false;
33 | textField.hDoc.endBatchEdit();
34 | }
35 |
36 | @Override
37 | public boolean performContextMenuAction(int id) {
38 | switch (id) {
39 | case android.R.id.copy:
40 | textField.copy();
41 | break;
42 | case android.R.id.cut:
43 | textField.cut();
44 | break;
45 | case android.R.id.paste:
46 | textField.paste();
47 | break;
48 | case android.R.id.startSelectingText:
49 | case android.R.id.stopSelectingText:
50 | case android.R.id.selectAll:
51 | textField.selectAll();
52 | break;
53 | }
54 |
55 | return false;
56 | }
57 |
58 | @Override
59 | public boolean sendKeyEvent(KeyEvent event) {
60 | switch (event.getKeyCode()) {
61 | case KeyEvent.KEYCODE_SHIFT_LEFT:
62 | if (textField.isSelectText())
63 | textField.selectText(false);
64 | else
65 | textField.selectText(true);
66 | break;
67 | case KeyEvent.KEYCODE_DPAD_LEFT:
68 | textField.moveCaretLeft();
69 | break;
70 | case KeyEvent.KEYCODE_DPAD_UP:
71 | textField.moveCaretUp();
72 | break;
73 | case KeyEvent.KEYCODE_DPAD_RIGHT:
74 | textField.moveCaretRight();
75 | break;
76 | case KeyEvent.KEYCODE_DPAD_DOWN:
77 | textField.moveCaretDown();
78 | break;
79 | case KeyEvent.KEYCODE_MOVE_HOME:
80 | textField.moveCaret(0);
81 | break;
82 | case KeyEvent.KEYCODE_MOVE_END:
83 | textField.moveCaret(textField.hDoc.length() - 1);
84 | break;
85 | case KeyEvent.KEYCODE_ENTER:
86 | case KeyEvent.KEYCODE_NUMPAD_ENTER:
87 | if (textField.mAutoCompletePanel.isShow()) {
88 | textField.mAutoCompletePanel.selectFirst();
89 | } else {
90 | return super.sendKeyEvent(event);
91 | }
92 | break;
93 | default:
94 | return super.sendKeyEvent(event);
95 | }
96 | return true;
97 | }
98 |
99 | /**
100 | * Only true when the InputConnection has not been used by the IME yet.
101 | * Can be programatically cleared by resetComposingState()
102 | */
103 | public boolean isComposingStarted() {
104 | return isComposing;
105 | }
106 |
107 | /**
108 | * 输入法传递过来的字符串
109 | *
110 | * @param text
111 | * @param newCursorPosition
112 | * @return
113 | */
114 | @Override
115 | public boolean setComposingText(CharSequence text, int newCursorPosition) {
116 | isComposing = true;
117 | if(!textField.hDoc.isBatchEdit()) {
118 | textField.hDoc.beginBatchEdit();
119 | }
120 | if (textField.mFieldController._isInSelectionMode) {
121 | textField.mFieldController.selectionDelete();
122 | composingCharCount = 0;
123 | }
124 | else {
125 | textField.mFieldController.replaceComposingText(
126 | textField.getCaretPosition() - composingCharCount,
127 | composingCharCount,
128 | text.toString());
129 | composingCharCount = text.length();
130 | }
131 | //TODO reduce invalidate calls
132 | if(newCursorPosition > 1) {
133 | textField.mFieldController.moveCaret(mCaretPosition + newCursorPosition - 1);
134 | } else if(newCursorPosition <= 0) {
135 | textField.mFieldController.moveCaret(mCaretPosition - text.length() - newCursorPosition);
136 | }
137 | // log("setComposingText:"+text+","+newCursorPosition);
138 | return true;
139 | }
140 |
141 | @Override
142 | public boolean commitText(CharSequence text, int newCursorPosition) {
143 | log("commitText:"+text+","+newCursorPosition+","+composingCharCount);
144 | /*
145 | mFieldController.replaceComposingText(
146 | getCaretPosition() - composingCharCount,
147 | composingCharCount,
148 | text.toString());
149 | composingCharCount = 0;
150 | hDoc.endBatchEdit();
151 | //TODO reduce invalidate calls
152 | if (newCursorPosition > 1) {
153 | mFieldController.moveCaret(mCaretPosition + newCursorPosition - 1);
154 | }
155 | else if(newCursorPosition==1){
156 | mFieldController.moveCaret(mCaretPosition + newCursorPosition);
157 | }
158 | // else if (newCursorPosition <= 0) {
159 | // mFieldController.moveCaret(mCaretPosition - text.length() - newCursorPosition);
160 | //}
161 | isComposing = false;
162 | return true;
163 |
164 | */
165 | return setComposingText(text, newCursorPosition) && finishComposingText();
166 | }
167 |
168 |
169 | @Override
170 | public boolean deleteSurroundingText(int leftLength, int rightLength) {
171 | if (composingCharCount != 0) {
172 | DLog.d("codeeditor","Warning: Implmentation of InputConnection.deleteSurroundingText" +
173 | " will not skip composing text");
174 | }
175 |
176 | textField.mFieldController.deleteAroundComposingText(leftLength, rightLength);
177 | return true;
178 | }
179 |
180 | @Override
181 | public boolean finishComposingText() {
182 | resetComposingState();
183 | return true;
184 | }
185 |
186 | @Override
187 | public int getCursorCapsMode(int reqModes) {
188 | int capsMode = 0;
189 |
190 | // Ignore InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS; not used in TextWarrior
191 |
192 | if ((reqModes & InputType.TYPE_TEXT_FLAG_CAP_WORDS)
193 | == InputType.TYPE_TEXT_FLAG_CAP_WORDS) {
194 | int prevChar = mCaretPosition - 1;
195 | if (prevChar < 0 || Lexer.getLanguage().isWhitespace(textField.hDoc.charAt(prevChar))) {
196 | capsMode |= InputType.TYPE_TEXT_FLAG_CAP_WORDS;
197 |
198 | //set CAP_SENTENCES if client is interested in it
199 | if ((reqModes & InputType.TYPE_TEXT_FLAG_CAP_SENTENCES)
200 | == InputType.TYPE_TEXT_FLAG_CAP_SENTENCES) {
201 | capsMode |= InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
202 | }
203 | }
204 | }
205 |
206 | // Strangely, Android soft keyboard does not set TYPE_TEXT_FLAG_CAP_SENTENCES
207 | // in reqModes even if it is interested in doing auto-capitalization.
208 | // Android bug? Therefore, we assume TYPE_TEXT_FLAG_CAP_SENTENCES
209 | // is always set to be on the safe side.
210 | else {
211 | Language lang = Lexer.getLanguage();
212 |
213 | int prevChar = mCaretPosition - 1;
214 | int whitespaceCount = 0;
215 | boolean capsOn = true;
216 |
217 | // Turn on caps mode only for the first char of a sentence.
218 | // A fresh line is also considered to start a new sentence.
219 | // The position immediately after a period is considered lower-case.
220 | // Examples: "abc.com" but "abc. Com"
221 | while (prevChar >= 0) {
222 | char c = textField.hDoc.charAt(prevChar);
223 | if (c == Language.NEWLINE) {
224 | break;
225 | }
226 |
227 | if (!lang.isWhitespace(c)) {
228 | if (whitespaceCount == 0 || !lang.isSentenceTerminator(c)) {
229 | capsOn = false;
230 | }
231 | break;
232 | }
233 |
234 | ++whitespaceCount;
235 | --prevChar;
236 | }
237 |
238 | if (capsOn) {
239 | capsMode |= InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
240 | }
241 | }
242 |
243 | return capsMode;
244 | }
245 |
246 | @Override
247 | public CharSequence getTextAfterCursor(int maxLen, int flags) {
248 | return textField.mFieldController.getTextAfterCursor(maxLen); //ignore flags
249 | }
250 |
251 | @Override
252 | public CharSequence getTextBeforeCursor(int maxLen, int flags) {
253 | return textField.mFieldController.getTextBeforeCursor(maxLen); //ignore flags
254 | }
255 |
256 | @Override
257 | public boolean setComposingRegion(int start, int end){
258 | isComposing = true;
259 | mCaretPosition = start;
260 | composingCharCount = end - start;
261 | return true;
262 |
263 | }
264 | @Override
265 | public boolean setSelection(int start, int end) {
266 | log("setSelection:"+start+","+end);
267 |
268 | if (start == end) {
269 | if (start == 0) {
270 | //适配搜狗输入法
271 | if (textField.getCaretPosition() > 0) {
272 | textField.mFieldController.moveCaret(textField.getCaretPosition() - 1);
273 | }
274 | } else {
275 | textField.mFieldController.moveCaret(start);
276 | }
277 | } else {
278 | textField.mFieldController.setSelectionRange(start, end - start, false, true);
279 | }
280 | return true;
281 | }
282 | }// end inner class
283 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/view/autocomplete/AutoCompletePanel.java:
--------------------------------------------------------------------------------
1 | package com.mrikso.codeeditor.view.autocomplete;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.drawable.Drawable;
6 | import android.graphics.drawable.GradientDrawable;
7 | import android.view.View;
8 | import android.widget.AdapterView;
9 | import android.widget.AdapterView.OnItemClickListener;
10 | import android.widget.Filter;
11 | import android.widget.ListPopupWindow;
12 | import android.widget.TextView;
13 |
14 | import com.mrikso.codeeditor.R;
15 | import com.mrikso.codeeditor.lang.Language;
16 | import com.mrikso.codeeditor.lang.LanguageNonProg;
17 | import com.mrikso.codeeditor.view.FreeScrollingTextField;
18 |
19 |
20 | public class AutoCompletePanel {
21 |
22 | public static Language _globalLanguage = LanguageNonProg.getInstance();
23 | public CharSequence _constraint;
24 | private FreeScrollingTextField _textField;
25 | private Context _context;
26 | private ListPopupWindow _autoCompletePanel;
27 | private AutoPanelAdapter _adapter;
28 | private Filter _filter;
29 | private int _verticalOffset;
30 | private int _height;
31 | private int _horizontal;
32 | private int _backgroundColor;
33 | private GradientDrawable gd;
34 | public int _textColor;
35 | private boolean isShow = false;
36 |
37 | public AutoCompletePanel(FreeScrollingTextField textField) {
38 | _textField = textField;
39 | _context = textField.getContext();
40 | initAutoCompletePanel();
41 |
42 | }
43 |
44 | synchronized public static Language getLanguage() {
45 | return _globalLanguage;
46 | }
47 |
48 | synchronized public static void setLanguage(Language lang) {
49 | _globalLanguage = lang;
50 | }
51 |
52 | public void setTextColor(int color) {
53 | _textColor = color;
54 | gd.setStroke(1, color);
55 | _autoCompletePanel.setBackgroundDrawable(gd);
56 | }
57 |
58 | public void setBackgroundColor(int color) {
59 | _backgroundColor = color;
60 | gd.setColor(color);
61 | _autoCompletePanel.setBackgroundDrawable(gd);
62 | }
63 |
64 | public void setBackground(Drawable color) {
65 | _autoCompletePanel.setBackgroundDrawable(color);
66 | }
67 |
68 | @SuppressWarnings("ResourceType")
69 | private void initAutoCompletePanel() {
70 | _autoCompletePanel = new ListPopupWindow(_context);
71 | _autoCompletePanel.setAnchorView(_textField);
72 | _adapter = new AutoPanelAdapter(_context, this, _textField);
73 | _autoCompletePanel.setAdapter(_adapter);
74 | _filter = _adapter.getFilter();
75 | //_autoCompletePanel.setContentWidth(ListPopupWindow.WRAP_CONTENT);
76 | setHeight(300);
77 |
78 | TypedArray array = _context.getTheme().obtainStyledAttributes(new int[]{
79 | android.R.attr.colorBackground,
80 | android.R.attr.textColorPrimary,
81 | });
82 | int backgroundColor = array.getColor(0, 0xFF00FF);
83 | int textColor = array.getColor(1, 0xFF00FF);
84 | array.recycle();
85 | gd = new GradientDrawable();
86 | gd.setColor(backgroundColor);
87 | gd.setCornerRadius(4);
88 | gd.setStroke(1, textColor);
89 | setTextColor(textColor);
90 | _autoCompletePanel.setBackgroundDrawable(gd);
91 | _autoCompletePanel.setOnItemClickListener(new OnItemClickListener() {
92 | @Override
93 | public void onItemClick(AdapterView> p1, View p2, int p3, long p4) {
94 | select(p3);
95 | }
96 | });
97 | }
98 |
99 | public void selectFirst() {
100 | select(0);
101 | }
102 |
103 | public void select(int pos) {
104 | View view = _adapter.getView(pos, null, null);
105 | TextView textView = view.findViewById(R.id.auto_panel_text);
106 | String text = textView.getText().toString();
107 | String commitText = null;
108 | boolean isFunc = text.contains("(");
109 | if (isFunc) {
110 | commitText = text.substring(0, text.indexOf('(')) + "()";
111 | } else {
112 | commitText = text;
113 | }
114 | _textField.replaceText(_textField.getCaretPosition() - _constraint.length(), _constraint.length(), commitText);
115 | _adapter.abort();
116 | dismiss();
117 | if (isFunc) {
118 | _textField.moveCaretLeft();
119 | }
120 |
121 | }
122 |
123 | public void setWidth(int width) {
124 | _autoCompletePanel.setWidth(width);
125 | }
126 |
127 | public void setHeight(int height) {
128 | if (_height != height) {
129 | _height = height;
130 | _autoCompletePanel.setHeight(height);
131 | }
132 | }
133 |
134 | public void setHorizontalOffset(int horizontal) {
135 | horizontal = Math.min(horizontal, _textField.getWidth() / 2);
136 | if (_horizontal != horizontal) {
137 | _horizontal = horizontal;
138 | _autoCompletePanel.setHorizontalOffset(horizontal);
139 | }
140 | }
141 |
142 | void setVerticalOffset(int verticalOffset) {
143 | //verticalOffset=Math.min(verticalOffset,_textField.getWidth()/2);
144 | int max = 0 - _autoCompletePanel.getHeight();
145 | if (verticalOffset > max) {
146 | _textField.scrollBy(0, verticalOffset - max);
147 | verticalOffset = max;
148 | }
149 | if (_verticalOffset != verticalOffset) {
150 | _verticalOffset = verticalOffset;
151 | _autoCompletePanel.setVerticalOffset(verticalOffset);
152 | }
153 | }
154 |
155 | public void update(CharSequence constraint) {
156 | _adapter.restart();
157 | _filter.filter(constraint);
158 | }
159 |
160 | public void show() {
161 | if (!_autoCompletePanel.isShowing())
162 | _autoCompletePanel.show();
163 | _autoCompletePanel.getListView().setFadingEdgeLength(0);
164 | isShow = true;
165 | }
166 |
167 | public void dismiss() {
168 | if (_autoCompletePanel.isShowing()) {
169 | isShow = false;
170 | _autoCompletePanel.dismiss();
171 | }
172 |
173 | }
174 |
175 | public boolean isShow() {
176 | return _autoCompletePanel.isShowing();
177 | }
178 |
179 | }
180 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/view/autocomplete/AutoPanelAdapter.java:
--------------------------------------------------------------------------------
1 | package com.mrikso.codeeditor.view.autocomplete;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.BitmapFactory;
6 | import android.graphics.Color;
7 | import android.text.SpannableString;
8 | import android.text.Spanned;
9 | import android.text.style.ForegroundColorSpan;
10 | import android.util.DisplayMetrics;
11 | import android.util.TypedValue;
12 | import android.view.LayoutInflater;
13 | import android.view.View;
14 | import android.view.ViewGroup;
15 | import android.widget.BaseAdapter;
16 | import android.widget.Filter;
17 | import android.widget.Filterable;
18 | import android.widget.ImageView;
19 | import android.widget.TextView;
20 |
21 | import com.mrikso.codeeditor.R;
22 | import com.mrikso.codeeditor.util.Flag;
23 | import com.mrikso.codeeditor.view.FreeScrollingTextField;
24 |
25 | import java.util.ArrayList;
26 | import java.util.List;
27 |
28 | import static com.mrikso.codeeditor.util.DLog.log;
29 |
30 | /**
31 | * Adapter定义
32 | */
33 | public class AutoPanelAdapter extends BaseAdapter implements Filterable {
34 |
35 | private final int PADDING = 20;
36 | private int _h;
37 | private Flag _abort;
38 | private DisplayMetrics dm;
39 | private List listItems;
40 | private Bitmap bitmap;
41 | private Context _context;
42 | private AutoCompletePanel mAutoComplete;
43 | private FreeScrollingTextField mTextFiled;
44 |
45 | public AutoPanelAdapter(Context context, AutoCompletePanel panel, FreeScrollingTextField textField) {
46 | _context = context;
47 | mAutoComplete = panel;
48 | mTextFiled = textField;
49 | _abort = new Flag();
50 | listItems = new ArrayList<>();
51 | dm = context.getResources().getDisplayMetrics();
52 | bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.icon_method);
53 | }
54 |
55 | public void abort() {
56 | _abort.set();
57 | }
58 |
59 | @Override
60 | public int getCount() {
61 | return listItems.size();
62 | }
63 |
64 | @Override
65 | public ListItem getItem(int i) {
66 | return listItems.get(i);
67 | }
68 |
69 | @Override
70 | public long getItemId(int i) {
71 | return i;
72 | }
73 |
74 | @Override
75 | public View getView(int i, View view, ViewGroup viewGroup) {
76 | View tempView = null;
77 | if (view == null) {
78 | View rootView = LayoutInflater.from(_context).inflate(R.layout.auto_panel_item, null);
79 | tempView = rootView;
80 | } else {
81 | tempView = view;
82 | }
83 | TextView textView = tempView.findViewById(R.id.auto_panel_text);
84 | ImageView imageView = tempView.findViewById(R.id.auto_panel_icon);
85 | String text = getItem(i).getText();
86 | SpannableString spannableString = null;
87 | ForegroundColorSpan foregroundColorSpan = null;
88 | // Setting setting = Setting.getInstance(_context);
89 | log(text);
90 | if (text.contains("(")) {
91 | //函数
92 | ForegroundColorSpan argsForegroundColorSpan = null;
93 | spannableString = new SpannableString(text);
94 | // if(setting.isDarkMode()) {
95 | // foregroundColorSpan = new ForegroundColorSpan(Color.WHITE);
96 | // argsForegroundColorSpan = new ForegroundColorSpan(Color.parseColor("#9C9C9C"));
97 | // }
98 | // else{
99 | foregroundColorSpan = new ForegroundColorSpan(Color.BLACK);
100 | argsForegroundColorSpan = new ForegroundColorSpan(Color.GRAY);
101 | // }
102 | spannableString.setSpan(foregroundColorSpan, 0, text.indexOf('('), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
103 | spannableString.setSpan(argsForegroundColorSpan, text.indexOf('('), text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
104 | } else if (text.contains("[keyword]")) {
105 | //关键字
106 | // if(setting.isDarkMode()) {
107 | // foregroundColorSpan = new ForegroundColorSpan(Color.YELLOW);
108 | // }
109 | // else{
110 | foregroundColorSpan = new ForegroundColorSpan(mAutoComplete._textColor);
111 | // }
112 | int idx = text.indexOf("[keyword]");
113 | text = text.substring(0, idx);
114 | spannableString = new SpannableString(text);
115 | spannableString.setSpan(foregroundColorSpan, 0, text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
116 | } else {
117 | //其他
118 | spannableString = new SpannableString(text);
119 | // if(setting.isDarkMode()) {
120 | // foregroundColorSpan = new ForegroundColorSpan(Color.WHITE);
121 | // }
122 | // else{
123 | foregroundColorSpan = new ForegroundColorSpan(mAutoComplete._textColor);
124 | // }
125 | spannableString.setSpan(foregroundColorSpan, 0, text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
126 | }
127 | textView.setText(spannableString);
128 | imageView.setImageBitmap(getItem(i).getBitmap());
129 | return tempView;
130 | }
131 |
132 | public void restart() {
133 | _abort.clear();
134 | }
135 |
136 | /**
137 | * 计算列表高
138 | *
139 | * @return
140 | */
141 | public int getItemHeight() {
142 | if (_h != 0)
143 | return _h;
144 | LayoutInflater inflater = LayoutInflater.from(_context);
145 | View rootView = inflater.inflate(R.layout.auto_panel_item, null);
146 | rootView.measure(0, 0);
147 | _h = rootView.getMeasuredHeight();
148 |
149 | return _h;
150 | }
151 |
152 | /**
153 | * 实现自动完成的过滤算法
154 | */
155 | @Override
156 | public Filter getFilter() {
157 | Filter filter = new Filter() {
158 | /**
159 | * 本方法在后台线程执行,定义过滤算法
160 | */
161 | @Override
162 | protected FilterResults performFiltering(CharSequence constraint) {
163 | // 此处实现过滤
164 | // 过滤后利用FilterResults将过滤结果返回
165 | ArrayList buf = new ArrayList();
166 | String input = String.valueOf(constraint).toLowerCase();
167 |
168 | String[] keywords = AutoCompletePanel._globalLanguage.getUserWord();
169 | for (String k : keywords) {
170 | if (k.toLowerCase().startsWith(input))
171 | buf.add(k);
172 | }
173 | keywords = AutoCompletePanel._globalLanguage.getKeywords();
174 | for (String k : keywords) {
175 | if (k.indexOf(input) == 0)
176 | buf.add(k);
177 | }
178 | keywords = AutoCompletePanel._globalLanguage.getNames();
179 | for (String k : keywords) {
180 | if (k.toLowerCase().startsWith(input))
181 | buf.add(k);
182 | }
183 | mAutoComplete._constraint = input;
184 | FilterResults filterResults = new FilterResults();
185 | filterResults.values = buf; // results是上面的过滤结果
186 | filterResults.count = buf.size(); // 结果数量
187 | return filterResults;
188 | }
189 |
190 | /**
191 | * 本方法在UI线程执行,用于更新自动完成列表
192 | */
193 | @Override
194 | protected void publishResults(CharSequence constraint, FilterResults results) {
195 | if (results != null && results.count > 0 && !_abort.isSet()) {
196 | // 有过滤结果,显示自动完成列表
197 | listItems.clear(); // 清空旧列表
198 | ArrayList stringArrayList = (ArrayList) results.values;
199 | for (int i = 0; i < stringArrayList.size(); i++) {
200 | String itemText = stringArrayList.get(i);
201 | if (itemText.contains("(")) {
202 | listItems.add(new ListItem(bitmap, itemText));
203 | } else {
204 | listItems.add(new ListItem(null, itemText));
205 | }
206 | }
207 | int y = mTextFiled.getCaretY() + mTextFiled.rowHeight() / 2 - mTextFiled.getScrollY();
208 | mAutoComplete.setHeight(getItemHeight() * Math.min(2, results.count));
209 |
210 | mAutoComplete.setHorizontalOffset(PADDING);
211 | mAutoComplete.setWidth(mTextFiled.getWidth() - PADDING * 2);
212 | mAutoComplete.setVerticalOffset(y - mTextFiled.getHeight());//_textField.getCaretY()-_textField.getScrollY()-_textField.getHeight());
213 | notifyDataSetChanged();
214 | mAutoComplete.show();
215 | } else {
216 | // 无过滤结果,关闭列表
217 | notifyDataSetInvalidated();
218 | }
219 | }
220 |
221 | };
222 | return filter;
223 | }
224 | }
225 |
--------------------------------------------------------------------------------
/codeeditor/src/main/java/com/mrikso/codeeditor/view/autocomplete/ListItem.java:
--------------------------------------------------------------------------------
1 | package com.mrikso.codeeditor.view.autocomplete;
2 |
3 | import android.graphics.Bitmap;
4 |
5 | public class ListItem {
6 | private Bitmap bitmap;
7 | private String text;
8 |
9 | public ListItem(Bitmap bitmap, String text) {
10 | this.bitmap = bitmap;
11 | this.text = text;
12 | }
13 |
14 | public Bitmap getBitmap() {
15 | return bitmap;
16 | }
17 |
18 | public void setBitmap(Bitmap bitmap) {
19 | this.bitmap = bitmap;
20 | }
21 |
22 | public String getText() {
23 | return text;
24 | }
25 |
26 | public void setText(String text) {
27 | this.text = text;
28 | }
29 |
30 | }
--------------------------------------------------------------------------------
/codeeditor/src/main/res/drawable/icon_method.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MrIkso/CodeEditor/a807a1459eae6a94b84c5687584677ae9eac8013/codeeditor/src/main/res/drawable/icon_method.png
--------------------------------------------------------------------------------
/codeeditor/src/main/res/layout/auto_panel_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
11 |
16 |
17 |
--------------------------------------------------------------------------------
/codeeditor/src/test/java/com/mrikso/codeeditor/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.mrikso.codeeditor;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 |
21 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MrIkso/CodeEditor/a807a1459eae6a94b84c5687584677ae9eac8013/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Mar 03 20:26:06 EET 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name='Text Editor'
2 | include ':app' //, ':lib-n-ide'
3 | include ':codeeditor'
4 |
--------------------------------------------------------------------------------