├── app ├── .gitignore ├── src │ └── main │ │ ├── assets │ │ ├── cld │ │ ├── Makefile │ │ │ ├── termuc.json │ │ │ └── Makefile │ │ ├── Clang++ │ │ │ └── termuc.json │ │ ├── CMake │ │ │ ├── CMakeLists.txt │ │ │ └── termuc.json │ │ ├── fmt │ │ └── init │ │ ├── res │ │ ├── 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-night │ │ │ └── styles.xml │ │ ├── values │ │ │ ├── dimens.xml │ │ │ ├── colors.xml │ │ │ ├── ids.xml │ │ │ └── styles.xml │ │ ├── values-land │ │ │ └── dimens.xml │ │ ├── layout │ │ │ ├── edit.xml │ │ │ ├── header_item.xml │ │ │ ├── editfrag.xml │ │ │ ├── search_action.xml │ │ │ ├── file_item.xml │ │ │ ├── header_dropdown_item.xml │ │ │ ├── replace.xml │ │ │ ├── new_project.xml │ │ │ ├── list_header.xml │ │ │ ├── activity_main.xml │ │ │ ├── var_item.xml │ │ │ ├── debug_controller.xml │ │ │ ├── key_panel.xml │ │ │ └── debug_panel.xml │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ ├── drawable │ │ │ ├── ic_play_24.xml │ │ │ ├── ic_chevron_left_24.xml │ │ │ ├── ic_chevron_right_24.xml │ │ │ ├── key_panel.xml │ │ │ ├── ic_code_24.xml │ │ │ ├── ic_folder_24.xml │ │ │ ├── ic_file_24.xml │ │ │ ├── ic_redo_24.xml │ │ │ ├── ic_undo_24.xml │ │ │ ├── ic_launcher_background.xml │ │ │ └── ic_find_replace_24.xml │ │ ├── values-v26 │ │ │ └── strings.xml │ │ ├── menu │ │ │ ├── search.xml │ │ │ └── main.xml │ │ ├── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ ├── layout-land │ │ │ └── activity_main.xml │ │ ├── xml │ │ │ └── settings.xml │ │ └── values-zh │ │ │ └── strings.xml │ │ ├── java │ │ └── cn │ │ │ └── rbc │ │ │ └── termuc │ │ │ ├── FileItem.java │ │ │ ├── HeaderAdapter.java │ │ │ ├── FileActivity.java │ │ │ ├── Application.java │ │ │ ├── FileAdapter.java │ │ │ ├── Project.java │ │ │ ├── SearchAction.java │ │ │ └── SettingsActivity.java │ │ └── AndroidManifest.xml ├── proguard-rules.pro └── build.gradle ├── codeeditor ├── .gitignore ├── consumer-rules.pro ├── src │ └── main │ │ ├── assets │ │ └── codicon.ttf │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── cn │ │ │ └── rbc │ │ │ └── codeeditor │ │ │ ├── util │ │ │ ├── Range.java │ │ │ ├── ErrSpan.java │ │ │ ├── Pair.java │ │ │ ├── Flag.java │ │ │ ├── ProgressObserver.java │ │ │ ├── Lexer.java │ │ │ ├── CharSeqReader.java │ │ │ ├── TextWarriorException.java │ │ │ ├── ProgressSource.java │ │ │ ├── HelperUtils.java │ │ │ ├── SearchStrategy.java │ │ │ ├── DLog.java │ │ │ ├── TextBufferCache.java │ │ │ ├── GapIntSet.java │ │ │ ├── FindThread.java │ │ │ └── LinearSearchStrategy.java │ │ │ ├── common │ │ │ ├── OnCaretScrollListener.java │ │ │ ├── OnRowChangedListener.java │ │ │ ├── OnSelectionChangedListener.java │ │ │ ├── OnKeyShortcutListener.java │ │ │ └── OnTextChangeListener.java │ │ │ ├── lang │ │ │ ├── Formatter.java │ │ │ ├── DefFormatter.java │ │ │ ├── xml │ │ │ │ └── XMLLanguage.java │ │ │ ├── LanguageJava.java │ │ │ ├── LanguageJavascript.java │ │ │ ├── LanguageObjectiveC.java │ │ │ ├── LanguageNonProg.java │ │ │ ├── LanguageRuby.java │ │ │ ├── LanguageCsharp.java │ │ │ ├── LanguagePython.java │ │ │ ├── LanguagePHP.java │ │ │ ├── c │ │ │ │ └── CLanguage.java │ │ │ └── LanguageCFamily.java │ │ │ └── view │ │ │ ├── autocomplete │ │ │ ├── ListItem.java │ │ │ └── Edit.java │ │ │ ├── ColorSchemeDark.java │ │ │ ├── ColorSchemeLight.java │ │ │ ├── KeysInterpreter.java │ │ │ ├── SignatureHelpPanel.java │ │ │ ├── ColorScheme.java │ │ │ └── ClipboardPanel.java │ │ └── res │ │ ├── values-zh │ │ └── strings.xml │ │ ├── layout │ │ ├── auto_panel_item.xml │ │ └── signature_panel.xml │ │ └── values │ │ └── strings.xml ├── build.gradle └── proguard-rules.pro ├── fastlane └── metadata │ └── android │ ├── en-US │ ├── title.txt │ ├── short_description.txt │ ├── changelogs │ │ ├── 2.txt │ │ ├── 4.txt │ │ ├── 8.txt │ │ ├── 9.txt │ │ ├── 5.txt │ │ ├── 10.txt │ │ ├── 6.txt │ │ ├── 3.txt │ │ └── 7.txt │ ├── images │ │ ├── icon.png │ │ └── phoneScreenshots │ │ │ ├── 1.jpg │ │ │ ├── 2.jpg │ │ │ ├── 3.jpg │ │ │ └── 4.jpg │ └── full_description.txt │ └── ru-RU │ ├── short_description.txt │ └── full_description.txt ├── .idea ├── .gitignore ├── codeStyles │ ├── codeStyleConfig.xml │ └── Project.xml ├── vcs.xml ├── compiler.xml ├── misc.xml ├── deploymentTargetDropDown.xml ├── gradle.xml └── jarRepositories.xml ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── settings.gradle ├── .github ├── FUNDING.yml └── workflows │ └── build-debug.yml ├── gradle.properties ├── README_zh.md ├── README.md ├── gradlew.bat └── CODE_OF_CONDUCT.md /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /codeeditor/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /codeeditor/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/title.txt: -------------------------------------------------------------------------------- 1 | TermuC -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # 默认忽略的文件 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/short_description.txt: -------------------------------------------------------------------------------- 1 | A simple C/C++ IDE backed on Termux -------------------------------------------------------------------------------- /app/src/main/assets/cld: -------------------------------------------------------------------------------- 1 | # https://clangd.llvm.org/config 2 | CompileFlags: 3 | Add: [-Wall] -------------------------------------------------------------------------------- /fastlane/metadata/android/ru-RU/short_description.txt: -------------------------------------------------------------------------------- 1 | Простая IDE для C/C++, работающая на базе Termux. 2 | -------------------------------------------------------------------------------- /app/src/main/assets/Makefile/termuc.json: -------------------------------------------------------------------------------- 1 | {"buildCmd":"make $o/$e -j8 O=$o","compileCmd":"make $o/$e.o -j8 O=$o"} -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/2.txt: -------------------------------------------------------------------------------- 1 | - Full translated 2 | - Fixed bugs when detecting file changes 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RainbowC0/TermuC/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /codeeditor/src/main/assets/codicon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RainbowC0/TermuC/HEAD/codeeditor/src/main/assets/codicon.ttf -------------------------------------------------------------------------------- /app/src/main/assets/Clang++/termuc.json: -------------------------------------------------------------------------------- 1 | {"buildCmd":"c++ $d/$f -o $o/$e -lm -Wall","compileCmd":"c++ -c $d/$f -o $o/$e.o -lm -Wall"} -------------------------------------------------------------------------------- /app/src/main/assets/CMake/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(main main.cpp) 2 | target_link_libraries(main m) 3 | add_compile_options(-g -Wall) -------------------------------------------------------------------------------- /app/src/main/assets/fmt: -------------------------------------------------------------------------------- 1 | # https://clang.llvm.org/docs/ClangFormatStyleOptions.html 2 | Language: Cpp 3 | BasedOnStyle: LLVM 4 | IndentWidth: 4 -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RainbowC0/TermuC/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RainbowC0/TermuC/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RainbowC0/TermuC/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RainbowC0/TermuC/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RainbowC0/TermuC/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /codeeditor/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RainbowC0/TermuC/HEAD/fastlane/metadata/android/en-US/images/icon.png -------------------------------------------------------------------------------- /app/src/main/assets/CMake/termuc.json: -------------------------------------------------------------------------------- 1 | {"buildCmd":"make -j8","compileCmd":"cmake . -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -DCMAKE_RUNTIME_OUTPUT_DIRECTORY=$o"} -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RainbowC0/TermuC/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RainbowC0/TermuC/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/4.txt: -------------------------------------------------------------------------------- 1 | • Introduce a simple wizard for initializing 2 | • Update opening files menu 3 | • Support local snippets -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RainbowC0/TermuC/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RainbowC0/TermuC/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RainbowC0/TermuC/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/8.txt: -------------------------------------------------------------------------------- 1 | • Implement textDocument/signatureHelp of LSP 2 | • Introduce some project templates 3 | • Add 'replace' function 4 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RainbowC0/TermuC/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/1.jpg -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RainbowC0/TermuC/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/2.jpg -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RainbowC0/TermuC/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/3.jpg -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RainbowC0/TermuC/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/4.jpg -------------------------------------------------------------------------------- /app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 14 | 15 | 20 | 21 | 32 | 33 | 36 | 37 | 42 | 46 | 47 | -------------------------------------------------------------------------------- /codeeditor/src/main/java/cn/rbc/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 cn.rbc.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/cn/rbc/codeeditor/util/HelperUtils.java: -------------------------------------------------------------------------------- 1 | package cn.rbc.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 android.widget.*; 11 | import android.annotation.*; 12 | 13 | public class HelperUtils { 14 | 15 | private static Toast _t; 16 | 17 | public static float getDpi(Context context) { 18 | return context.getResources().getDisplayMetrics().density; 19 | } 20 | 21 | // create bitmap from vector drawable 22 | public static Bitmap getBitmap(Context context, int res) { 23 | Bitmap bitmap = null; 24 | /* ContextCompat.getDrawable */ 25 | Drawable vectorDrawable = context.getDrawable(res); 26 | if (vectorDrawable != null) { 27 | vectorDrawable.setAlpha(210); 28 | //vectorDrawable.setTint(fetchAccentColor(context)); 29 | bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(), vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); 30 | Canvas canvas = new Canvas(bitmap); 31 | vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); 32 | vectorDrawable.draw(canvas); 33 | return bitmap; 34 | } 35 | return bitmap; 36 | } 37 | 38 | public static void show(Toast t) { 39 | if (_t != null) 40 | _t.cancel(); 41 | _t = t; 42 | if (t != null) 43 | t.show(); 44 | } 45 | /* 46 | public static int codePointAt(CharSequence seq, int pos, int limit) { 47 | // 1. 参数校验 48 | if (pos < 0 || pos >= limit || limit > seq.length()) { 49 | throw new IndexOutOfBoundsException( 50 | "pos=" + pos + ", limit=" + limit + ", length=" + seq.length() 51 | ); 52 | } 53 | 54 | // 2. 获取当前字符 55 | char high = seq.charAt(pos); 56 | 57 | // 3. 检查是否为高代理项(High Surrogate) 58 | if (Character.isHighSurrogate(high)) { 59 | // 检查下一个字符是否在有效范围内 60 | if (pos + 1 < limit) { 61 | char low = seq.charAt(pos + 1); 62 | if (Character.isLowSurrogate(low)) { 63 | // 4. 合法代理对:返回组合后的代码点 64 | return Character.toCodePoint(high, low); 65 | } 66 | } 67 | } 68 | 69 | // 5. 单字符或无效代理:返回原始值 70 | return high; 71 | }*/ 72 | } 73 | -------------------------------------------------------------------------------- /codeeditor/src/main/java/cn/rbc/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 cn.rbc.codeeditor.view; 11 | 12 | 13 | /** 14 | * Off-black on off-white background color scheme 15 | */ 16 | public class ColorSchemeLight extends ColorScheme { 17 | 18 | private static ColorScheme mColorScheme; 19 | 20 | private ColorSchemeLight(){ 21 | //文字 22 | setColor(Colorable.FOREGROUND, OFF_BLACK); 23 | //背景 24 | setColor(Colorable.BACKGROUND, OFF_WHITE); 25 | setColor(Colorable.BACKGROUND_PURE, 0xFFFFFFFF); 26 | //选取文字 27 | setColor(Colorable.SELECTION_FOREGROUND, OFF_WHITE); 28 | //选取背景 29 | setColor(Colorable.SELECTION_BACKGROUND, 0xFF999999); 30 | //关键字 31 | setColor(Colorable.KEYWORD, BLUE_DARK); 32 | //函数名 33 | //setColor(Colorable.LITERAL, BLUE_LIGHT); 34 | //字符串、数字 35 | setColor(Colorable.STRING, 0xFFAA2200); 36 | setColor(Colorable.NUMBER, 0xFFAA2200); 37 | //类型 38 | setColor(Colorable.TYPE, BLUE_LIGHT); //0xFF2A40FF); 39 | //操作符 40 | setColor(Colorable.OPERATOR, 0xFF007C1F); 41 | //标点 42 | setColor(Colorable.NOTE, 0xFF0096FF); 43 | //宏 44 | setColor(Colorable.SECONDARY, GREY); 45 | //光标 46 | setColor(Colorable.CARET_DISABLED, 0xFF000000); 47 | //yoyo? 48 | setColor(Colorable.CARET_FOREGROUND, OFF_WHITE); 49 | //yoyo背景 50 | setColor(Colorable.CARET_BACKGROUND, 0xFF29B6F6); 51 | //当前行 52 | setColor(Colorable.LINE_HIGHLIGHT, 0x1E888888); 53 | 54 | //注释 55 | setColor(Colorable.COMMENT, GREEN_LIGHT); 56 | /* 57 | setColor(Colorable.FOREGROUND, OFF_BLACK); 58 | setColor(Colorable.BACKGROUND, OFF_WHITE); 59 | setColor(Colorable.SELECTION_FOREGROUND, OFF_WHITE); 60 | setColor(Colorable.CARET_FOREGROUND, OFF_WHITE);*/ 61 | } 62 | 63 | private static final int OFF_WHITE = 0xFFF0F0ED; 64 | private static final int OFF_BLACK = 0xFF333333; 65 | 66 | private static final int GREY = 0xFF808080; 67 | private static final int GREEN_LIGHT = 0xFF009B00; 68 | //private static final int GREEN_DARK = 0xFF3F7F5F; 69 | private static final int BLUE_LIGHT = 0xFF0F9CFF; 70 | private static final int BLUE_DARK = 0xFF2C82C8; 71 | 72 | public static ColorScheme getInstance() { 73 | if (mColorScheme==null) 74 | mColorScheme = new ColorSchemeLight(); 75 | return mColorScheme; 76 | } 77 | 78 | @Override 79 | public boolean isDark() { 80 | return false; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /app/src/main/java/cn/rbc/termuc/HeaderAdapter.java: -------------------------------------------------------------------------------- 1 | package cn.rbc.termuc; 2 | import android.content.*; 3 | import android.os.*; 4 | import android.view.*; 5 | import android.widget.*; 6 | import java.io.*; 7 | import java.util.*; 8 | 9 | public class HeaderAdapter extends ArrayAdapter 10 | implements Iterable 11 | { 12 | private final static String BITS = "bs"; 13 | private ArrayList bs; 14 | private View.OnClickListener mOnCloseListener; 15 | 16 | public HeaderAdapter(Context context, int id) { 17 | super(context, id, android.R.id.text1); 18 | bs = new ArrayList<>(); 19 | setDropDownViewResource(R.layout.header_dropdown_item); 20 | } 21 | 22 | @Override 23 | public View getView(int position, View convertView, ViewGroup parent) { 24 | View v = super.getView(position, convertView, parent); 25 | if (v instanceof TextView) { 26 | TextView tv = (TextView)v; 27 | String s = new File(tv.getText().toString()).getName(); 28 | tv.setText(getEdit(position)?"*".concat(s):s); 29 | } 30 | return v; 31 | } 32 | 33 | public void setEdit(int idx, boolean b) { 34 | int i = bs.size(); 35 | if (i > idx) 36 | bs.set(idx, b); 37 | else { 38 | for (;i < idx;i++) { 39 | bs.add(false); 40 | } 41 | bs.add(true); 42 | } 43 | } 44 | 45 | public boolean getEdit(int idx) { 46 | return idx)bd.getSerializable(BITS); 55 | } 56 | 57 | @Override 58 | public View getDropDownView(int position, View convertView, ViewGroup parent) { 59 | View v = super.getDropDownView(position, convertView, parent); 60 | View btn = v.findViewById(android.R.id.button1); 61 | btn.setTag(position); 62 | btn.setOnClickListener(mOnCloseListener); 63 | return v; 64 | } 65 | 66 | @Override 67 | public void remove(String object) { 68 | final int pos = this.getPosition(object); 69 | if (bs.size() > pos) 70 | bs.remove(pos); 71 | super.remove(object); 72 | } 73 | 74 | public void setOnCloseListener(View.OnClickListener lis) { 75 | mOnCloseListener = lis; 76 | } 77 | 78 | @Override 79 | public Iterator iterator() { 80 | return new Iterator() { 81 | private int curr = 0; 82 | public boolean hasNext() { 83 | return curr < getCount(); 84 | } 85 | public String next() { 86 | return getItem(curr++); 87 | } 88 | public void remove() {} 89 | }; 90 | } 91 | 92 | @Override 93 | public Spliterator spliterator() { 94 | return null; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /codeeditor/src/main/java/cn/rbc/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 cn.rbc.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(Document 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(Document 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(Document 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(Document 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(Document 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 | -------------------------------------------------------------------------------- /app/src/main/java/cn/rbc/termuc/FileActivity.java: -------------------------------------------------------------------------------- 1 | package cn.rbc.termuc; 2 | import android.app.*; 3 | import android.content.*; 4 | import android.os.*; 5 | import android.view.*; 6 | import android.widget.*; 7 | import java.io.*; 8 | 9 | public class FileActivity extends Activity 10 | implements ListView.OnItemClickListener, FileFilter 11 | { 12 | private ListView lv; 13 | private FileAdapter adp; 14 | private File pwd; 15 | final static String FN = "n", PD = "p"; 16 | 17 | @Override 18 | protected void onCreate(Bundle savedInstanceState) { 19 | Utils.setNightMode(this, Application.theme); 20 | super.onCreate(savedInstanceState); 21 | ActionBar ab = getActionBar(); 22 | ab.setHomeButtonEnabled(true); 23 | setTitle(R.string.select_font); 24 | String path = getIntent().getStringExtra(FN); 25 | if (path != null) 26 | pwd = new File(path).getParentFile(); 27 | else pwd = Utils.ROOT; 28 | lv = new ListView(this); 29 | lv.setLayoutParams(new ViewGroup.LayoutParams( 30 | ViewGroup.LayoutParams.MATCH_PARENT, 31 | ViewGroup.LayoutParams.MATCH_PARENT 32 | )); 33 | lv.setFastScrollEnabled(true); 34 | lv.setAdapter(adp = new FileAdapter(this, pwd, this)); 35 | lv.setOnItemClickListener(this); 36 | setContentView(lv); 37 | } 38 | 39 | @Override 40 | public void onItemClick(AdapterView av, View v, int i, long n) { 41 | String _it = adp.getItem(i).name; 42 | if ("..".equals(_it)) 43 | pwd = pwd.getParentFile(); 44 | else { 45 | File f = new File(pwd, _it); 46 | if (f.isDirectory()) 47 | pwd = f; 48 | else { 49 | Intent it = getIntent(); 50 | it.putExtra(FN, f.getAbsolutePath()); 51 | setResult(RESULT_OK, it); 52 | finish(); 53 | return; 54 | } 55 | } 56 | refresh(); 57 | } 58 | 59 | @Override 60 | public boolean onOptionsItemSelected(MenuItem item) { 61 | if (item.getItemId() == android.R.id.home) { 62 | finish(); 63 | } 64 | return true; 65 | } 66 | 67 | @Override 68 | protected void onSaveInstanceState(Bundle outState) { 69 | super.onSaveInstanceState(outState); 70 | outState.putString(PD, pwd.getAbsolutePath()); 71 | } 72 | 73 | @Override 74 | protected void onRestoreInstanceState(Bundle savedInstanceState) { 75 | super.onRestoreInstanceState(savedInstanceState); 76 | pwd = new File(savedInstanceState.getString(PD)); 77 | } 78 | 79 | public boolean accept(File p1) { 80 | String p2; 81 | return p1.isDirectory() || ((p2=p1.getName()).endsWith(".ttf") || p2.endsWith(".otf")); 82 | } 83 | 84 | private void refresh() { 85 | getActionBar().setSubtitle(pwd.getAbsolutePath()); 86 | adp.setPath(pwd); 87 | adp.notifyDataSetChanged(); 88 | } 89 | 90 | @Override 91 | public void onBackPressed() { 92 | if (Utils.ROOT.compareTo(pwd)==0) 93 | super.onBackPressed(); 94 | else { 95 | pwd = pwd.getParentFile(); 96 | refresh(); 97 | } 98 | } 99 | 100 | @Override 101 | protected void onResume() { 102 | super.onResume(); 103 | refresh(); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /codeeditor/src/main/java/cn/rbc/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 cn.rbc.codeeditor.view; 10 | 11 | import android.view.KeyEvent; 12 | 13 | import cn.rbc.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 | } else if (isDelete(event)) { 38 | c = Language.DELETE; 39 | // This should be before the check for isSpace() because the 40 | // shortcut for TAB uses the SPACE key. 41 | } else if (isTab(event)) { 42 | c = Language.TAB; 43 | } else if (isSpace(event)) { 44 | c = ' '; 45 | } else if (event.isPrintingKey()) { 46 | c = (char) event.getUnicodeChar(event.getMetaState()); 47 | } 48 | 49 | return c; 50 | } 51 | 52 | private static boolean isTab(KeyEvent event) { 53 | return (event.isShiftPressed() && 54 | (event.getKeyCode() == KeyEvent.KEYCODE_SPACE)) || 55 | (event.getKeyCode() == KeyEvent.KEYCODE_TAB); 56 | } 57 | 58 | private static boolean isBackspace(KeyEvent event) { 59 | return (event.getKeyCode() == KeyEvent.KEYCODE_DEL); 60 | } 61 | 62 | private static boolean isDelete(KeyEvent event) { 63 | return event.getKeyCode() == KeyEvent.KEYCODE_FORWARD_DEL; 64 | } 65 | 66 | private static boolean isNewline(KeyEvent event) { 67 | return (event.getKeyCode() == KeyEvent.KEYCODE_ENTER); 68 | } 69 | 70 | private static boolean isSpace(KeyEvent event) { 71 | return (event.getKeyCode() == KeyEvent.KEYCODE_SPACE); 72 | } 73 | 74 | public static boolean isNavigationKey(KeyEvent event) { 75 | int keyCode = event.getKeyCode(); 76 | return keyCode == KeyEvent.KEYCODE_DPAD_DOWN || 77 | keyCode == KeyEvent.KEYCODE_DPAD_UP || 78 | keyCode == KeyEvent.KEYCODE_DPAD_RIGHT || 79 | keyCode == KeyEvent.KEYCODE_DPAD_LEFT; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /codeeditor/src/main/java/cn/rbc/codeeditor/util/DLog.java: -------------------------------------------------------------------------------- 1 | package cn.rbc.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/cn/rbc/codeeditor/lang/c/CLanguage.java: -------------------------------------------------------------------------------- 1 | package cn.rbc.codeeditor.lang.c; 2 | 3 | import cn.rbc.codeeditor.lang.*; 4 | import cn.rbc.codeeditor.util.*; 5 | 6 | public class CLanguage extends Language{ 7 | private static Language _theOne = null; 8 | 9 | private final static String[] keywords = { 10 | "auto", "const", "extern", "register", "static", "volatile", 11 | "sizeof", "typedef", 12 | "enum", "struct", "union", 13 | "break", "case", "continue", "default", "do", "else", "for", 14 | "goto", "if", "return", "switch", "while", 15 | /*}; 16 | private final static String[] types = {*/ 17 | "char", "double", "float", "int", "long", "short", "void", 18 | "signed", "unsigned", "_Bool", "_Complex", "_Imaginary" 19 | }; 20 | private final static String[] functions={ 21 | "abort","abs","acos","asctime","asin","assert","atan","atan2","atexit","atof","atoi","atol" 22 | ,"bsearch","calloc","ceil","clearerr","clock","cos","cosh","ctime","difftime","div" 23 | ,"exit","exp","fabs","fclose","feof","ferror","fflush","fgetc","fgetpos","fgets","floor" 24 | ,"fmod","fopen","fprintf","fputc","fputs","fread","free","freopen","frexp","fscanf","fseek","fsetpos","ftell","fwrite" 25 | ,"getc","getchar","getenv","gets","gmtime","isalnum","isalpha","iscntrl","isdigit","isgraph","islower","isprint","ispunct","isspace","isupper","isxdigit","labs","ldexp","ldiv","localtime","log","log10","longjmp" 26 | ,"main","malloc","memchr","memcmp","memcpy","memmove","memset","mktime","modf","perror","pow","printf" 27 | ,"putc","putchar","puts","qsort","raise","rand","realloc","remove","rename","rewind" 28 | ,"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" 29 | ,"tan","tanh","time","tmpfile","tmpnam","tolower","toupper","ungetc","va_arg","vprintf","vfprintf" 30 | ,"__LINE__","__FILE__","__DATE__","__TIME__","_cplusplus","__STDC__" 31 | }; 32 | private final static String[] header={ 33 | "math.h","stdio.h","stdlib.h","string.h","time.h","errno.h","ctype.h","local.h" 34 | }; 35 | private final static String[] keynames = { 36 | "EOF", "NULL" 37 | }; 38 | private final static String[] extraWord = { 39 | "define","include","ifdef","endif","ifndef","error","elif","line","pragma","undef","main" 40 | }; 41 | private final static char[] BASIC_C_OPERATORS = { 42 | '(', ')', '{', '}', '.', ',', ';', '=', '+', '-', 43 | '/', '*', '&', '!', '|', ':', '[', ']', '<', '>', 44 | '?', '~', '%', '^' 45 | }; 46 | public static Language getInstance(){ 47 | if(_theOne == null){ 48 | _theOne = new CLanguage(); 49 | } 50 | return _theOne; 51 | } 52 | 53 | private CLanguage() { 54 | setKeywords(keywords); 55 | addNames(header); 56 | addNames(functions); 57 | addNames(extraWord); 58 | addKeynames(keynames); 59 | setOperators(BASIC_C_OPERATORS); 60 | } 61 | 62 | @Override 63 | public boolean isProgLang() { 64 | return true; 65 | } 66 | 67 | private CLexer lx = null; 68 | 69 | @Override 70 | public Lexer newLexer(CharSeqReader reader) { 71 | if (lx == null) 72 | lx = new CLexer(reader); 73 | else lx.yyreset(reader); 74 | return lx; 75 | } 76 | } 77 | 78 | -------------------------------------------------------------------------------- /app/src/main/java/cn/rbc/termuc/Application.java: -------------------------------------------------------------------------------- 1 | package cn.rbc.termuc; 2 | import android.content.*; 3 | import android.graphics.*; 4 | import android.preference.*; 5 | import android.util.*; 6 | import cn.rbc.codeeditor.util.*; 7 | import java.util.*; 8 | 9 | public class Application extends android.app.Application { 10 | final static String 11 | KEY_THEME = "theme", 12 | KEY_PUREMODE = "puremode", 13 | KEY_FONT = "font", 14 | KEY_MYFONT = "myfont", 15 | KEY_WORDWRAP = "wordwrap", 16 | KEY_WHITESPACE = "whitespace", 17 | KEY_TEXTSIZE = "fontsize", 18 | KEY_USESPACE = "usespace", 19 | KEY_TABSIZE = "tabsize", 20 | KEY_SUGGUESTION = "suggestion", 21 | KEY_AUTOCAPS = "autocaps", 22 | KEY_SHOW_HIDDEN = "showhidden", 23 | KEY_CHECKAPP = "checkapp", 24 | KEY_INITAPP = "initapp", 25 | KEY_CFLAGS = "cflags", 26 | KEY_COMPLETION = "completion", 27 | KEY_LSP_HOST = "lsphost", 28 | KEY_LSP_PORT = "lspport"; 29 | 30 | public static boolean pure_mode, wordwrap, whitespace, show_hidden, usespace, suggestion, auto_caps; 31 | public static String theme, font, cflags, completion, lsp_host; 32 | public static int lsp_port, textsize, tabsize; 33 | 34 | MainHandler hand; 35 | Lsp lsp; 36 | private Map ls; 37 | private static Application app; 38 | 39 | @Override 40 | public void onCreate() { 41 | super.onCreate(); 42 | PreferenceManager.setDefaultValues(this, R.xml.settings, false); 43 | initConfs(); 44 | ls = new ArrayMap<>(); 45 | app = this; 46 | } 47 | 48 | @Override 49 | public void onTerminate() { 50 | lsp.end(); 51 | ls.clear(); 52 | super.onTerminate(); 53 | app = null; 54 | } 55 | 56 | void store(String key, Document obj) { 57 | ls.put(key, obj); 58 | } 59 | 60 | Document load(String key) { 61 | return ls.remove(key); 62 | } 63 | 64 | public static Application getInstance() { 65 | return app; 66 | } 67 | 68 | private void initConfs() { 69 | SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); 70 | theme = sp.getString(KEY_THEME, getResources().getString(R.string.def_thm)); 71 | pure_mode = sp.getBoolean(KEY_PUREMODE, false); 72 | String f = sp.getString(KEY_FONT, "m"); 73 | if ("c".equals(f)) 74 | f = sp.getString(KEY_MYFONT, ""); 75 | font = f; 76 | textsize = Integer.parseInt(sp.getString(KEY_TEXTSIZE, "14")); 77 | wordwrap = sp.getBoolean(KEY_WORDWRAP, true); 78 | whitespace = sp.getBoolean(KEY_WHITESPACE, false); 79 | usespace = sp.getBoolean(KEY_USESPACE, false); 80 | tabsize = Integer.parseInt(sp.getString(KEY_TABSIZE, "4")); 81 | suggestion = sp.getBoolean(KEY_SUGGUESTION, false); 82 | auto_caps = sp.getBoolean(KEY_AUTOCAPS, false); 83 | show_hidden = sp.getBoolean(KEY_SHOW_HIDDEN, true); 84 | cflags = sp.getString(KEY_CFLAGS, "-lm -Wall"); 85 | completion = sp.getString(KEY_COMPLETION, "s"); 86 | lsp_host = sp.getString(KEY_LSP_HOST, "127.0.0.1"); 87 | lsp_port = Integer.parseInt(sp.getString(KEY_LSP_PORT, "48455")); 88 | } 89 | 90 | static Typeface typeface() { 91 | try { 92 | return "n".equals(font) ? Typeface.SANS_SERIF 93 | : "s".equals(font) ? Typeface.SERIF 94 | : "m".equals(font) ? Typeface.MONOSPACE 95 | : Typeface.createFromFile(font); 96 | } catch (RuntimeException re) { 97 | return Typeface.MONOSPACE; 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /app/src/main/java/cn/rbc/termuc/FileAdapter.java: -------------------------------------------------------------------------------- 1 | package cn.rbc.termuc; 2 | import android.content.*; 3 | import android.view.*; 4 | import android.widget.*; 5 | import java.io.*; 6 | import java.util.*; 7 | 8 | public class FileAdapter extends BaseAdapter implements Comparator, FileFilter 9 | { 10 | private Context mCont; 11 | private static FileItem parent; 12 | private FileItem[] mData; 13 | private boolean mNRoot; 14 | private File mPath; 15 | private LayoutInflater mInflater; 16 | private FileFilter mFilter; 17 | 18 | public FileAdapter(Context context, File path) { 19 | this(context, path, null); 20 | } 21 | 22 | public FileAdapter(Context context, File path, FileFilter filter) { 23 | super(); 24 | mCont = context; 25 | mInflater = LayoutInflater.from(context); 26 | mFilter = filter; 27 | setPath(path); 28 | } 29 | 30 | @Override 31 | public long getItemId(int p1) { 32 | return p1; 33 | } 34 | 35 | @Override 36 | public FileItem getItem(int p1) { 37 | if (mNRoot) { 38 | if (p1==0) return parent; 39 | else p1--; 40 | } 41 | return mData[p1]; 42 | } 43 | 44 | @Override 45 | public int getCount() { 46 | return mNRoot ? mData.length+1 : mData.length; 47 | } 48 | 49 | @Override 50 | public View getView(int pos, View convert, ViewGroup parent) { 51 | ImageView img; 52 | TextView txv; 53 | ViewHolder vh; 54 | if (convert == null) { 55 | convert = mInflater.inflate(R.layout.file_item, parent, false); 56 | img = convert.findViewById(R.id.file_icon); 57 | txv = convert.findViewById(R.id.file_name); 58 | vh = new ViewHolder(); 59 | vh.img = img; 60 | vh.txv = txv; 61 | convert.setTag(vh); 62 | } else { 63 | vh = (ViewHolder)convert.getTag(); 64 | img = vh.img; 65 | txv = vh.txv; 66 | } 67 | FileItem fitm = getItem(pos); 68 | img.setImageResource(fitm.icon); 69 | txv.setText(fitm.name); 70 | return convert; 71 | } 72 | 73 | public void setPath(File path) { 74 | mNRoot = !Utils.ROOT.equals(path); 75 | if (parent==null && mNRoot) 76 | parent = new FileItem(R.drawable.ic_folder_24, ".."); 77 | mPath = path; 78 | File[] lst = path.listFiles(this); 79 | if (lst==null) 80 | lst = new File[0]; 81 | Arrays.sort(lst, this); 82 | mData = new FileItem[lst.length]; 83 | for (int i=0,l=lst.length;i 2 | 4 | 6 | 13 | 18 | 19 | 21 | 28 | 35 | 40 | 45 | 50 | 57 | 62 | 67 | 68 | 70 | 75 | 76 | 78 | 82 | 86 | 91 | 92 | 94 | 101 | 107 | 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /app/src/main/java/cn/rbc/termuc/Project.java: -------------------------------------------------------------------------------- 1 | package cn.rbc.termuc; 2 | import android.util.*; 3 | import java.io.*; 4 | import java.util.*; 5 | 6 | public class Project 7 | { 8 | private static long lastModified; 9 | private static final String 10 | KEY_BUILDCMD = "buildCmd", 11 | KEY_COMPILE = "compileCmd", 12 | KEY_RUNCMD = "runCmd", 13 | KEY_OUTPUTDIR = "outputDir", 14 | KEY_OPENS = "opens"; 15 | public final static String PROJ = "termuc.json"; 16 | public static String rootPath, outputDir; 17 | public static String buildCmd, compileCmd, runCmd; 18 | 19 | private static void setDefault() { 20 | buildCmd = "clang $d/$f -o $o/$e -lm -Wall"; 21 | compileCmd = "clang -c $d/$f -o $o/$e.o -lm -Wall"; 22 | runCmd = "$x/$e"; 23 | outputDir = "build"; 24 | } 25 | 26 | public static void load(File conf, List opens) throws IOException { 27 | JsonReader p = new JsonReader((new BufferedReader(new FileReader(conf)))); 28 | p.beginObject(); 29 | setDefault(); 30 | while (p.hasNext()) { 31 | switch (p.nextName()) { 32 | case KEY_BUILDCMD: buildCmd = p.nextString(); break; 33 | case KEY_COMPILE: compileCmd = p.nextString(); break; 34 | case KEY_RUNCMD: runCmd = p.nextString(); break; 35 | case KEY_OUTPUTDIR: outputDir = p.nextString(); break; 36 | case KEY_OPENS: 37 | if (opens == null) { 38 | p.skipValue(); 39 | break; 40 | } 41 | opens.clear(); 42 | p.beginArray(); 43 | while (p.hasNext()) 44 | opens.add(p.nextString()); 45 | p.endArray(); 46 | break; 47 | default: p.skipValue(); 48 | } 49 | } 50 | p.endObject(); 51 | p.close(); 52 | rootPath = conf.getParent(); 53 | lastModified = conf.lastModified(); 54 | } 55 | 56 | public static boolean save(Iterable opens) { 57 | if (rootPath == null) 58 | return false; 59 | File f = new File(rootPath, PROJ); 60 | try { 61 | JsonWriter w = new JsonWriter(new BufferedWriter(new FileWriter(f))); 62 | w.setIndent(" "); 63 | w.beginObject(); 64 | w.name(KEY_BUILDCMD);w.value(buildCmd); 65 | w.name(KEY_COMPILE);w.value(compileCmd); 66 | w.name(KEY_RUNCMD);w.value(runCmd); 67 | w.name(KEY_OUTPUTDIR);w.value(outputDir); 68 | w.name(KEY_OPENS); 69 | w.beginArray(); 70 | if (opens != null) 71 | for (String s:opens) 72 | w.value(s); 73 | w.endArray(); 74 | w.endObject(); 75 | w.close(); 76 | lastModified = f.lastModified(); 77 | return true; 78 | } catch (IOException e) { 79 | return false; 80 | } 81 | } 82 | 83 | public static boolean create(File f) { 84 | rootPath = f.getAbsolutePath(); 85 | setDefault(); 86 | new File(f, outputDir).mkdirs(); 87 | return save(null); 88 | } 89 | 90 | public static void close() { 91 | rootPath = null; 92 | } 93 | 94 | public static long lastModified() { 95 | return lastModified; 96 | } 97 | 98 | public static void reload() throws IOException { 99 | File f = new File(rootPath, PROJ); 100 | if (f.lastModified()>lastModified) 101 | load(f, null); 102 | } 103 | /** 104 | * Local environment variables 105 | * $f file name 106 | * $e file name without suffix 107 | * $d file directory path 108 | * $p project root path (if in project) 109 | * $o outputDir 110 | */ 111 | public static StringBuilder buildEnvironment(File file) { 112 | StringBuilder sb = new StringBuilder(); 113 | if (rootPath!=null) { 114 | sb.append("p=\""); 115 | sb.append(Utils.escape(rootPath)); 116 | sb.append("\";o=\""); 117 | sb.append(Utils.escape(outputDir)); 118 | sb.append("\";"); 119 | } 120 | if (file!=null) { 121 | sb.append("f=\""); 122 | String name = file.getName(); 123 | sb.append(Utils.escape(name)); 124 | sb.append("\";e=\""); 125 | // remove suffix 126 | int i = name.lastIndexOf('.'); 127 | if (i>=0) 128 | name = name.substring(0, i); 129 | sb.append(Utils.escape(name)); 130 | sb.append("\";d=\""); 131 | sb.append(Utils.escape(file.getParent())); 132 | sb.append("\";"); 133 | } 134 | return sb; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /codeeditor/src/main/java/cn/rbc/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 cn.rbc.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 | //TODO consider extracting common parts with getNearestCharOffset(int) 38 | public Pair getNearestLine(int lineIndex){ 39 | int nearestMatch = 0; 40 | int nearestDistance = Integer.MAX_VALUE; 41 | for(int i = 0; i < CACHE_SIZE; ++i){ 42 | int distance = Math.abs(lineIndex - _cache[i].first); 43 | if (distance < nearestDistance){ 44 | nearestDistance = distance; 45 | nearestMatch = i; 46 | } 47 | } 48 | 49 | Pair nearestEntry = _cache[nearestMatch]; 50 | makeHead(nearestMatch); 51 | return nearestEntry; 52 | } 53 | 54 | public Pair getNearestCharOffset(int charOffset){ 55 | int nearestMatch = 0; 56 | int nearestDistance = Integer.MAX_VALUE; 57 | for(int i = 0; i < CACHE_SIZE; ++i){ 58 | int distance = Math.abs(charOffset - _cache[i].second); 59 | if (distance < nearestDistance){ 60 | nearestDistance = distance; 61 | nearestMatch = i; 62 | } 63 | } 64 | 65 | Pair nearestEntry = _cache[nearestMatch]; 66 | makeHead(nearestMatch); 67 | return nearestEntry; 68 | } 69 | 70 | /** 71 | * Place _cache[newHead] at the top of the list 72 | */ 73 | private void makeHead(int newHead){ 74 | if (newHead == 0) 75 | return; 76 | 77 | Pair temp = _cache[newHead]; 78 | for (int i = newHead; i > 1; --i) 79 | _cache[i] = _cache[i-1]; 80 | _cache[1] = temp; // _cache[0] is always occupied by (0,0) 81 | } 82 | 83 | public void updateEntry(int lineIndex, int charOffset){ 84 | if(lineIndex <= 0) 85 | // lineIndex 0 always has 0 charOffset; ignore. Also ignore negative lineIndex 86 | return; 87 | 88 | if(!replaceEntry(lineIndex, charOffset)) 89 | insertEntry(lineIndex, charOffset); 90 | } 91 | 92 | private boolean replaceEntry(int lineIndex, int charOffset){ 93 | for (int i = 1; i < CACHE_SIZE; ++i) 94 | if(_cache[i].first == lineIndex){ 95 | _cache[i].second = charOffset; 96 | return true; 97 | } 98 | return false; 99 | } 100 | 101 | private void insertEntry(int lineIndex, int charOffset){ 102 | makeHead(CACHE_SIZE-1); // rotate right list of entries 103 | // replace head (most recently used entry) with new entry 104 | _cache[1] = new Pair(lineIndex, charOffset); 105 | } 106 | 107 | /** 108 | * Invalidate all cache entries that have char offset >= fromCharOffset 109 | */ 110 | final protected void invalidateCache(int fromCharOffset){ 111 | for (int i = 1; i < CACHE_SIZE; ++i) 112 | if(_cache[i].second >= fromCharOffset) 113 | _cache[i] = new Pair(-1, -1); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /codeeditor/src/main/java/cn/rbc/codeeditor/util/GapIntSet.java: -------------------------------------------------------------------------------- 1 | package cn.rbc.codeeditor.util; 2 | import java.util.*; 3 | 4 | public class GapIntSet { 5 | private int[] mData; 6 | private int size, gapSt, gapSz; 7 | 8 | public GapIntSet() { 9 | this(4); 10 | } 11 | 12 | public GapIntSet(int cap) { 13 | if (cap<1) 14 | throw new IllegalArgumentException("Illegal Capacity: "+cap); 15 | mData = new int[cap]; 16 | size = 0; 17 | } 18 | 19 | /* copied from ArrayList */ 20 | public void ensureCapacity(int cap) { 21 | int[] dt = mData; 22 | int oldCap = dt.length; 23 | if (cap>oldCap) { 24 | int newCap = (oldCap*3)/2+1; 25 | if (newCap < cap) 26 | newCap = cap; 27 | mData = Arrays.copyOf(dt, newCap); 28 | } 29 | } 30 | 31 | /** 32 | * Toggle the int in mData 33 | * @param l the target int 34 | */ 35 | public void toggle(int l) { 36 | int idx = set(l); 37 | if (idx >= 0) 38 | remove(idx); 39 | } 40 | 41 | /** 42 | * Try to add an int to mData 43 | * @param l the target int 44 | * @return the index of int l if already exists, or its one's complement after added 45 | */ 46 | public int set(int l) { 47 | if (l>=gapSt+gapSz) 48 | l -= gapSz; 49 | else if (l>=gapSt) { 50 | refresh(); 51 | gapSz = 0; 52 | } 53 | int idx = Arrays.binarySearch(mData, 0, size, l); 54 | if (idx<0) 55 | add(~idx, l); 56 | return idx; 57 | } 58 | 59 | private void add(int i, int l) { 60 | ensureCapacity(1+size); 61 | System.arraycopy(mData, i, mData, i+1, size - i); 62 | mData[i] = l; 63 | size++; 64 | } 65 | 66 | private void remove(int i) { 67 | int numMoved = size - i - 1; 68 | if (numMoved > 0) 69 | System.arraycopy(mData, i+1, mData, i, numMoved); 70 | size--; 71 | } 72 | 73 | private void refresh() { 74 | int i, j, t; 75 | if (size>0 && gapSz!=0) { 76 | int[] ls = mData; 77 | for (i=size-1; i>=0&&(j=ls[i])>=gapSt; i--) 78 | ls[i] = j+gapSz; 79 | j=i+1; 80 | if (j=0&&ls[i]>=t; i--); 82 | if (j!=(t=i+1)) { 83 | System.arraycopy(mData, j, mData, t, size - j); 84 | size += t-j; 85 | } 86 | } 87 | } 88 | } 89 | 90 | public int[] getData() { 91 | refresh(); 92 | gapSz = 0; 93 | return Arrays.copyOf(mData, size); 94 | } 95 | 96 | public void clear() { 97 | size = 0; 98 | } 99 | 100 | public void setData(int[] d) { 101 | size = d.length; 102 | if (d.length==0) 103 | return; 104 | mData = d; 105 | gapSz = 0; 106 | } 107 | 108 | public int size() { 109 | return size; 110 | } 111 | 112 | public int get(int i) { 113 | if (i>=size||i<0) 114 | throw new IndexOutOfBoundsException(String.format( 115 | "Index %d out of bounds for length %d", i, size)); 116 | int r = mData[i]; 117 | return r>=gapSt?r+gapSz:r; 118 | } 119 | 120 | public void shift(int l, int off) { 121 | int t; 122 | if (off==0||size==0||(t=Math.min(l,l+off))>get(size-1)) 123 | return; 124 | if (gapSt<=t&&(gapSz>=0&&l<=gapSt+gapSz)) { 125 | gapSz += off; 126 | } else { 127 | refresh(); 128 | int idx = Arrays.binarySearch(mData, 0, size, l-1); 129 | if (idx<0) 130 | idx = (~idx)-1; 131 | if (off>0) { 132 | gapSt = idx<0?0:mData[idx]+1; 133 | gapSz = ++idx==size?off:gapSt-mData[idx]; 134 | } else { 135 | gapSt = (++idx==size?l:mData[idx])-1; 136 | l = Arrays.binarySearch(mData, 0, idx, l+off); 137 | if (l<0) l=~l; 138 | gapSz = (l==0?0:mData[l-1]) - gapSt; 139 | } 140 | refresh(); 141 | gapSz = off-gapSz; 142 | if (off<0) 143 | gapSt = idx<=0?0:mData[idx-1]+1; 144 | } 145 | } 146 | 147 | public int find(int l) { 148 | if (l>=gapSt+gapSz) 149 | l -= gapSz; 150 | else if (l>gapSt) 151 | l = gapSt; 152 | return Arrays.binarySearch(mData, 0, size, l); 153 | } 154 | 155 | public boolean isInGap(int l) { 156 | return gapSt<=l&&l0) { 164 | sb.append(get(0)); 165 | for (int i=1; i _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], Tokenizer.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], Tokenizer.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 | -------------------------------------------------------------------------------- /app/src/main/res/values-zh/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 设置 4 | 通用 5 | 主题 6 | 亮主题 7 | 暗主题 8 | 跟随系统 9 | 纯色模式 10 | 编辑器 11 | 字体 12 | 无衬线 13 | 衬线 14 | 等宽 15 | 自定义 16 | 自动换行 17 | 显示空白符号 18 | 使用空格进行缩进 19 | 制表符大小(\\t) 20 | 允许键盘建议 21 | 自动输入大写 22 | 编译器 23 | 编译选项 24 | 运行... 25 | 构建 26 | 编译 27 | 运行 28 | 调试 29 | 撤销 30 | 重做 31 | 保存 32 | 查找 33 | 关闭 34 | 项目... 35 | 项目配置 36 | 关闭项目 37 | 未打开文件 38 | 代码分析 39 | 自动补全 40 | 引擎 41 | 语言服务器 42 | 内置代码片段 43 | 44 | 服务器地址 45 | 服务器端口 46 | 文件 %s 已被修改,您是否需要重新加载? 47 | 是否删除 %s? 48 | 新建 49 | 文件 50 | 文件夹 51 | 已保存 52 | 正在删除 %s 53 | 已删除 54 | 打开失败! 55 | 浏览文件 56 | 名称(例如 main.cpp) 57 | 项目名称 58 | 打开项目 59 | 打开项目 %s? 60 | 新建文件(夹) 61 | 新建项目 62 | 重命名 63 | 查找… 64 | 下一个 65 | 上一个 66 | 查找结束 67 | 文件浏览器 68 | 显示隐藏文件 69 | 默认字号 70 | 检测&安装 Termux 71 | 已安装 72 | 未安装 73 | 安装 Termux 74 | 未检测到 Termux,是否现在安装? 75 | 不再提醒 76 | 安装途径 77 | 初始化 Termux 78 | 为完成配置,请复制指令并跳转 Termux 粘贴运行。\n运行时需要在所有系统设置页面手动为 Termux 配置权限。 79 | 复制并跳转 80 | STR=设置存储;DOA=设置悬浮窗权限(请按回车);IBO=设置禁用电池优化(请按回车);SBP=设置关联启动与后台弹出权限(请按回车);ALE=设置allow-external-apps;ICL=安装Clang; 87 | 已配置 88 | 申请权限失败 89 | 配置文件解析失败! 90 | 选择字体文件 91 | 模板 92 | 生成 .clangd 93 | 生成 .clang-format 94 | 名称为空! 95 | 替换 96 | 继续 97 | 暂停 98 | 步过 99 | 步入 100 | 步出 101 | 变量 102 | 103 | 控制台 104 | 断点 105 | 信息 106 | GDB命令 107 | 断点于 %s 108 | 无变量信息 109 | 无帧信息 110 | 111 | -------------------------------------------------------------------------------- /app/src/main/res/layout/key_panel.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | 39 | 40 | 44 | 45 | 49 | 50 | 54 | 55 | 59 | 60 | 64 | 65 | 69 | 70 | 74 | 75 | 79 | 80 | 84 | 85 | 89 | 90 | 94 | 95 | 99 | 100 | 104 | 105 | 109 | 110 | 114 | 115 | 119 | 120 | 124 | 125 | 129 | 130 | 134 | 135 | 139 | 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /app/src/main/java/cn/rbc/termuc/SearchAction.java: -------------------------------------------------------------------------------- 1 | package cn.rbc.termuc; 2 | import android.app.AlertDialog.*; 3 | import android.content.*; 4 | import android.text.*; 5 | import android.view.*; 6 | import android.widget.*; 7 | import cn.rbc.codeeditor.util.*; 8 | 9 | public class SearchAction implements 10 | ActionMode.Callback, TextWatcher, TextView.OnEditorActionListener, 11 | DialogInterface.OnClickListener { 12 | private MainActivity ma; 13 | private EditText e; 14 | private int idx = 0; 15 | private View transV; 16 | 17 | public SearchAction(MainActivity ma) { 18 | this.ma = ma; 19 | } 20 | 21 | public void onDestroyActionMode(ActionMode p1) { 22 | transV = null; 23 | TextEditor te = ma.getEditor(); 24 | te.setSelection(te.getCaretPosition()); 25 | te.requestFocus(); 26 | } 27 | 28 | public boolean onPrepareActionMode(ActionMode p1, Menu p2) { 29 | return false; 30 | } 31 | 32 | public boolean onCreateActionMode(ActionMode p1, Menu p2) { 33 | View v = View.inflate(ma, R.layout.search_action, null); 34 | e = v.findViewById(R.id.search_edit); 35 | TextEditor ed = ma.getEditor(); 36 | String st = ed.getSelectedText(); 37 | if (!st.isEmpty()) { 38 | e.setText(st); 39 | idx = ed.getSelectionStart(); 40 | } else 41 | idx = 0; 42 | e.addTextChangedListener(this); 43 | e.setOnEditorActionListener(this); 44 | p1.setCustomView(v); 45 | ma.getMenuInflater().inflate(R.menu.search, p2); 46 | return true; 47 | } 48 | 49 | public boolean onActionItemClicked(ActionMode p1, MenuItem p2) { 50 | int id = p2.getItemId(); 51 | if (id!=R.id.menu_replace) 52 | search(p2.getItemId()); 53 | else { 54 | Builder bd = new Builder(ma); 55 | bd.setTitle(R.string.replace); 56 | View v = View.inflate(ma, R.layout.replace, null); 57 | final EditText ed = v.findViewById(R.id.replace_find); 58 | ed.setText(e.getText()); 59 | transV = v; 60 | bd.setView(v); 61 | bd.setPositiveButton(android.R.string.ok, this); 62 | bd.setNegativeButton(android.R.string.cancel, null); 63 | bd.create().show(); 64 | } 65 | return false; 66 | } 67 | 68 | private void search(int id) { 69 | int i; 70 | CharSequence ed = e.getText(); 71 | int len = ed.length(); 72 | if (len==0) 73 | return; 74 | TextEditor te = ma.getEditor(); 75 | if (id==R.id.menu_last) { 76 | i = rindexDoc(ed, idx-1); 77 | } else { 78 | i = indexDoc(ed, idx+1); 79 | } 80 | if (i<0) { 81 | HelperUtils.show(Toast.makeText(ma, R.string.find_completed, Toast.LENGTH_SHORT)); 82 | idx = 0; 83 | te.setSelection(0,0); 84 | } else { 85 | te.setSelection(i, len); 86 | idx = i; 87 | } 88 | return; 89 | } 90 | 91 | public void beforeTextChanged(CharSequence cs, int p1, int p2, int p3) { 92 | } 93 | 94 | public void onTextChanged(CharSequence cs, int p1, int p2, int p3) { 95 | } 96 | 97 | public void afterTextChanged(Editable ed) { 98 | if (ed.length()==0) 99 | return; 100 | int i = indexDoc(ed, idx); 101 | if (i < 0) { 102 | idx = 0; 103 | } else { 104 | idx = i; 105 | ma.getEditor().setSelection(i, ed.length()); 106 | } 107 | } 108 | 109 | @Override 110 | public boolean onEditorAction(TextView p1, int p2, KeyEvent p3) { 111 | search(R.id.menu_next); 112 | return true; 113 | } 114 | 115 | private int indexDoc(CharSequence cs, int idx) { 116 | int len = cs.length(); 117 | Document doc = ma.getEditor().getText(); 118 | int i, ldp = doc.length()-len; 119 | D: for (i=idx; i=0; i--) { 132 | for (int k=0;k sigs; 37 | private int activeSig; 38 | 39 | public SignatureHelpPanel(FreeScrollingTextField editor) { 40 | _editor = editor; 41 | Context ctx = editor.getContext(); 42 | LayoutInflater inflater = LayoutInflater.from(ctx); 43 | View root = inflater.inflate(R.layout.signature_panel, null); 44 | _root = root; 45 | View tmp = root.findViewById(R.id.sig_nav); 46 | _navi = tmp; 47 | tmp.measure(View.MeasureSpec.AT_MOST, View.MeasureSpec.AT_MOST); 48 | TextView tv = root.findViewById(R.id.sig_prev); 49 | tv.setOnClickListener(this); 50 | _prev = tv; 51 | tv = root.findViewById(R.id.sig_next); 52 | tv.setOnClickListener(this); 53 | _next = tv; 54 | tv = root.findViewById(R.id.sig_text); 55 | _sigView = tv; 56 | 57 | PopupWindow pw = new PopupWindow( 58 | root, 59 | ViewGroup.LayoutParams.WRAP_CONTENT, 60 | ViewGroup.LayoutParams.WRAP_CONTENT, 61 | false 62 | ); 63 | _popupWindow = pw; 64 | 65 | TypedArray array = ctx.getTheme().obtainStyledAttributes( 66 | new int[]{ 67 | android.R.attr.colorBackground, 68 | android.R.attr.textColorPrimary 69 | }); 70 | int textColor = array.getColor(1, 0xFF00FF); 71 | int backgroundColor = array.getColor(0, 0xFF00FF); 72 | array.recycle(); 73 | GradientDrawable g = new GradientDrawable(); 74 | g.setColor(backgroundColor); 75 | pw.setBackgroundDrawable(g); 76 | pw.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED); 77 | pw.setAnimationStyle(0); 78 | pw.setOutsideTouchable(true); 79 | tv.setTextColor(textColor); 80 | g.setStroke(1, textColor); 81 | } 82 | 83 | public void show(List data, int idx) { 84 | if (data == null || data.isEmpty()) { 85 | hide(); 86 | return; 87 | } 88 | sigs = data; 89 | select(idx); 90 | 91 | FreeScrollingTextField ed = _editor; 92 | int[] pos = updatePosition(ed); 93 | 94 | View v = _navi; 95 | if (data.size() > 1) { 96 | v.setVisibility(View.VISIBLE); 97 | pos[0] -= v.getWidth(); 98 | } else v.setVisibility(View.GONE); 99 | 100 | _popupWindow.showAtLocation(ed, Gravity.NO_GRAVITY, pos[0], pos[1]); 101 | } 102 | 103 | public void hide() { 104 | _popupWindow.dismiss(); 105 | } 106 | 107 | public boolean isShowing() { 108 | return _popupWindow.isShowing(); 109 | } 110 | 111 | protected static int[] updatePosition(FreeScrollingTextField ed) { 112 | int[] pos = new int[2]; 113 | ed.getLocationInWindow(pos); 114 | pos[0] += ed.getCaretX() - ed.getScrollX(); 115 | pos[1] += ed.rowHeight() * (1 + ed.getCaretRow()) - ed.getScrollY(); 116 | return pos; 117 | } 118 | /* 119 | public void update(int x, int y) { 120 | _popupWindow.update(x, y, -1, -1, true); 121 | }*/ 122 | 123 | public void setTextSize(float s) { 124 | _sigView.setTextSize(TypedValue.COMPLEX_UNIT_PX, s); 125 | _prev.setTextSize(TypedValue.COMPLEX_UNIT_PX, s); 126 | _next.setTextSize(TypedValue.COMPLEX_UNIT_PX, s); 127 | _navi.measure(View.MeasureSpec.AT_MOST, View.MeasureSpec.AT_MOST); 128 | } 129 | 130 | private void select(int pos) { 131 | if (pos < 0 || pos > sigs.size()) return; 132 | activeSig = pos; 133 | _sigView.setText(sigs.get(pos)); 134 | _prev.setEnabled(pos > 0); 135 | _next.setEnabled(pos+1 < sigs.size()); 136 | } 137 | 138 | @Override 139 | public void onClick(View v) { 140 | int i; 141 | if (v.getId() == R.id.sig_prev) i = -1; 142 | else i = 1; 143 | select(activeSig + i); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /codeeditor/src/main/java/cn/rbc/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 cn.rbc.codeeditor.view; 11 | 12 | import cn.rbc.codeeditor.util.Tokenizer; 13 | import cn.rbc.codeeditor.util.TextWarriorException; 14 | 15 | import java.util.HashMap; 16 | 17 | import android.graphics.*; 18 | 19 | public abstract class ColorScheme { 20 | public enum Colorable { 21 | FOREGROUND, BACKGROUND, BACKGROUND_PURE, SELECTION_FOREGROUND, 22 | SELECTION_BACKGROUND, CARET_FOREGROUND, CARET_BACKGROUND, CARET_DISABLED, 23 | LINE_HIGHLIGHT, NON_PRINTING_GLYPH, COMMENT, KEYWORD, NAME, NUMBER, STRING, 24 | SECONDARY, TYPE, NOTE, OPERATOR 25 | } 26 | 27 | protected HashMap _colors = generateDefaultColors(); 28 | 29 | public void setColor(Colorable colorable, int color) { 30 | _colors.put(colorable, color); 31 | } 32 | 33 | public int getColor(Colorable colorable) { 34 | Integer color = _colors.get(colorable); 35 | if (color == null) { 36 | TextWarriorException.fail("Color not specified for " + colorable); 37 | return 0; 38 | } 39 | return color; 40 | } 41 | 42 | // Currently, color scheme is tightly coupled with semantics of the token types 43 | public int getTokenColor(int tokenType) { 44 | Colorable element; 45 | switch (tokenType) { 46 | case Tokenizer.NORMAL: 47 | element = Colorable.FOREGROUND; 48 | break; 49 | case Tokenizer.KEYWORD: 50 | element = Colorable.KEYWORD; 51 | break; 52 | case Tokenizer.TYPE: 53 | element = Colorable.TYPE; 54 | break; 55 | case Tokenizer.NAME: 56 | element = Colorable.NAME; 57 | break; 58 | case Tokenizer.DOUBLE_SYMBOL_LINE: //fall-through 59 | case Tokenizer.DOUBLE_SYMBOL_DELIMITED_MULTILINE: 60 | element = Colorable.COMMENT; 61 | break; 62 | case Tokenizer.SINGLE_SYMBOL_DELIMITED_A: //fall-through 63 | case Tokenizer.SINGLE_SYMBOL_DELIMITED_B: 64 | case Tokenizer.KEYNAME: 65 | element = Colorable.STRING; 66 | break; 67 | case Tokenizer.NUMBER: 68 | element = Colorable.NUMBER; 69 | break; 70 | case Tokenizer.NOTE: 71 | element = Colorable.NOTE; 72 | break; 73 | case Tokenizer.OPERATOR: 74 | element = Colorable.OPERATOR; 75 | break; 76 | case Tokenizer.SINGLE_SYMBOL_LINE_A: //fall-through 77 | case Tokenizer.SINGLE_SYMBOL_WORD: 78 | element = Colorable.SECONDARY; 79 | break; 80 | case Tokenizer.SINGLE_SYMBOL_LINE_B: //类型 81 | element = Colorable.NAME; 82 | break; 83 | default: 84 | TextWarriorException.fail("Invalid token type"); 85 | element = Colorable.FOREGROUND; 86 | break; 87 | } 88 | return getColor(element); 89 | } 90 | 91 | /** 92 | * Whether this color scheme uses a dark background, like black or dark grey. 93 | */ 94 | public abstract boolean isDark(); 95 | 96 | private HashMap generateDefaultColors() { 97 | // High-contrast, black-on-white color scheme 98 | HashMap colors = new HashMap(Colorable.values().length); 99 | colors.put(Colorable.FOREGROUND, BLACK);//前景色 100 | colors.put(Colorable.BACKGROUND, WHITE); 101 | colors.put(Colorable.SELECTION_FOREGROUND, WHITE);//选择文本的前景色 102 | colors.put(Colorable.SELECTION_BACKGROUND, 0xFF97C024);//选择文本的背景色 103 | colors.put(Colorable.CARET_FOREGROUND, WHITE); 104 | colors.put(Colorable.CARET_BACKGROUND, BLACK); 105 | colors.put(Colorable.CARET_DISABLED, GREY); 106 | colors.put(Colorable.LINE_HIGHLIGHT, 0x20888888); 107 | 108 | colors.put(Colorable.NON_PRINTING_GLYPH, 0xFFBBBBBB);//行号 109 | colors.put(Colorable.COMMENT, OLIVE_GREEN); //注释 110 | colors.put(Colorable.KEYWORD, BLUE); //关键字 111 | colors.put(Colorable.NAME, GREY); // Eclipse default color 112 | colors.put(Colorable.NUMBER, PINK); // 数字 113 | colors.put(Colorable.STRING, DARK_RED); //字符串 114 | colors.put(Colorable.SECONDARY, 0xff6f008a);//宏定义 115 | return colors; 116 | } 117 | 118 | // In ARGB format: 0xAARRGGBB 119 | private static final int BLACK = 0xFF000000; 120 | private static final int BLUE = 0xFF0000FF; 121 | private static final int DARK_RED = 0xFFA31515; 122 | private static final int PINK = 0xFFD040DD; 123 | private static final int GREY = 0xFF808080; 124 | private static final int OLIVE_GREEN = 0xFF3F7F5F; 125 | private static final int WHITE = 0xFFFFFFE0; 126 | private static final int LIGHT_BLUE2 = 0xFF40B0FF; 127 | 128 | static final int[] DIAG = {Color.RED, Color.MAGENTA, 0xFF00BB00, Color.CYAN}; 129 | } 130 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | TermuC.CODE_OF_CONDUCT.md. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /app/src/main/res/layout/debug_panel.xml: -------------------------------------------------------------------------------- 1 | 5 | 9 | 17 | 21 | 22 | 27 | 31 | 43 | 47 | 55 | 56 | 60 | 64 | 72 | 73 | 78 | 84 | 91 | 92 | 100 | 101 | 105 | 109 | 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /codeeditor/src/main/java/cn/rbc/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 cn.rbc.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 Document _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, Document 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.length(); 46 | } 47 | 48 | private FindThread(int requestCode, Document 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.length(); 58 | } 59 | 60 | static public FindThread createFindThread(Document 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(Document 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.first; 91 | _results.newStartPosition = replaceResult.second; 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 | float progressProportion = (_docSize == 0) ? 0.f : (float) FINDER.getProgress() / (float) _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 | } 163 | -------------------------------------------------------------------------------- /app/src/main/java/cn/rbc/termuc/SettingsActivity.java: -------------------------------------------------------------------------------- 1 | package cn.rbc.termuc; 2 | import android.content.*; 3 | import android.os.*; 4 | import android.preference.*; 5 | import android.view.*; 6 | 7 | public class SettingsActivity extends PreferenceActivity 8 | implements Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener 9 | { 10 | private final static String TAG = "SettingsActivity", FT = "f"; 11 | 12 | private CheckBoxPreference mPureModePref, mWordWrapPref, mWhitespacePref, mUseSpacePref, 13 | mShowHidden, mSuggestionPref, mAutoCapsPref; 14 | private EditTextPreference mCFlagsPref, mHost, mPort; 15 | private ListPreference mThemePref, mFontPref, mSizePref, mTabSizePref, mEngine; 16 | 17 | private boolean mPure, mWrap, mSpace, mUseSpace, mSuggestion, mAutoCaps; 18 | private String mTheme, mComp, mFont, tpFont; 19 | private int mTabSize; 20 | 21 | @Override 22 | protected void onCreate(Bundle savedInstanceState) { 23 | Utils.setNightMode(this, Application.theme); 24 | super.onCreate(savedInstanceState); 25 | getActionBar().setDisplayHomeAsUpEnabled(true); 26 | addPreferencesFromResource(R.xml.settings); 27 | 28 | mThemePref = (ListPreference)findPreference(Application.KEY_THEME); 29 | mPureModePref = (CheckBoxPreference)findPreference(Application.KEY_PUREMODE); 30 | mFontPref = (ListPreference)findPreference(Application.KEY_FONT); 31 | mFontPref.setOnPreferenceChangeListener(this); 32 | mSizePref = (ListPreference)findPreference(Application.KEY_TEXTSIZE); 33 | mWordWrapPref = (CheckBoxPreference)findPreference(Application.KEY_WORDWRAP); 34 | mWhitespacePref = (CheckBoxPreference)findPreference(Application.KEY_WHITESPACE); 35 | mUseSpacePref = (CheckBoxPreference)findPreference(Application.KEY_USESPACE); 36 | mTabSizePref = (ListPreference)findPreference(Application.KEY_TABSIZE); 37 | mSuggestionPref = (CheckBoxPreference)findPreference(Application.KEY_SUGGUESTION); 38 | mAutoCapsPref = (CheckBoxPreference)findPreference(Application.KEY_AUTOCAPS); 39 | mShowHidden = (CheckBoxPreference)findPreference(Application.KEY_SHOW_HIDDEN); 40 | mCFlagsPref = (EditTextPreference)findPreference(Application.KEY_CFLAGS); 41 | mEngine = (ListPreference)findPreference(Application.KEY_COMPLETION); 42 | mEngine.setOnPreferenceChangeListener(this); 43 | mHost = (EditTextPreference)findPreference(Application.KEY_LSP_HOST); 44 | mPort = (EditTextPreference)findPreference(Application.KEY_LSP_PORT); 45 | findPreference(Application.KEY_CHECKAPP).setOnPreferenceClickListener(this); 46 | findPreference(Application.KEY_INITAPP).setOnPreferenceClickListener(this); 47 | onPreferenceChange(mEngine, mEngine.getValue()); 48 | 49 | mTheme = Application.theme; 50 | mPure = Application.pure_mode; 51 | mWrap = Application.wordwrap; 52 | mSpace = Application.whitespace; 53 | mUseSpace = Application.usespace; 54 | mTabSize = Application.tabsize; 55 | mSuggestion = Application.suggestion; 56 | mAutoCaps = Application.auto_caps; 57 | mComp = Application.completion; 58 | mFont = tpFont = Application.font; 59 | } 60 | 61 | @Override 62 | public boolean onOptionsItemSelected(MenuItem item) { 63 | switch (item.getItemId()) { 64 | case android.R.id.home: 65 | onBackPressed(); 66 | break; 67 | } 68 | return true; 69 | } 70 | 71 | @Override 72 | public boolean onPreferenceClick(Preference p1) { 73 | if (Application.KEY_CHECKAPP.equals(p1.getKey())) 74 | Utils.testApp(this, true); 75 | else 76 | Utils.initBack(this, true); 77 | return true; 78 | } 79 | 80 | @Override 81 | public boolean onPreferenceChange(Preference p1, Object p2) { 82 | if (p1.compareTo(mEngine)==0) { 83 | boolean enable = "s".equals(p2); 84 | mHost.setEnabled(enable); 85 | mPort.setEnabled(enable); 86 | } else if (p1.compareTo(mFontPref)==0) { 87 | if ("c".equals(p2)) { 88 | Intent it = new Intent(this, FileActivity.class); 89 | it.putExtra(FileActivity.FN, 90 | getPreferenceManager().getSharedPreferences().getString(Application.KEY_MYFONT, null)); 91 | startActivityForResult(it, 0); 92 | return false; 93 | } 94 | tpFont = (String)p2; 95 | } 96 | return true; 97 | } 98 | 99 | @Override 100 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 101 | super.onActivityResult(requestCode, resultCode, data); 102 | if (requestCode==0 && resultCode==RESULT_OK) { 103 | SharedPreferences.Editor edt = getPreferenceManager().getSharedPreferences().edit(); 104 | edt.putString(Application.KEY_MYFONT, tpFont = data.getStringExtra(FileActivity.FN)); 105 | edt.commit(); 106 | mFontPref.setValue("c"); 107 | } 108 | } 109 | 110 | @Override 111 | public void onBackPressed() { 112 | setResult(mTheme != mThemePref.getValue() 113 | ? RESULT_FIRST_USER : 114 | (mPure == mPureModePref.isChecked() 115 | && mFont == tpFont 116 | && mWrap == mWordWrapPref.isChecked() 117 | && mSpace == mWhitespacePref.isChecked() 118 | && mUseSpace == mUseSpacePref.isChecked() 119 | && mTabSize == Integer.parseInt(mTabSizePref.getValue()) 120 | && mSuggestion == mSuggestionPref.isChecked() 121 | && mAutoCaps == mAutoCapsPref.isChecked() 122 | && mComp.equals(mEngine.getValue())) 123 | ? RESULT_CANCELED : RESULT_OK); 124 | super.onBackPressed(); 125 | } 126 | 127 | @Override 128 | protected void onSaveInstanceState(Bundle outState) { 129 | super.onSaveInstanceState(outState); 130 | outState.putString(FT, tpFont); 131 | } 132 | 133 | @Override 134 | protected void onRestoreInstanceState(Bundle state) { 135 | super.onRestoreInstanceState(state); 136 | tpFont = state.getString(FT); 137 | } 138 | 139 | @Override 140 | protected void onPause() { 141 | Application.theme = mThemePref.getValue(); 142 | Application.pure_mode = mPureModePref.isChecked(); 143 | Application.wordwrap = mWordWrapPref.isChecked(); 144 | Application.whitespace = mWhitespacePref.isChecked(); 145 | Application.usespace = mUseSpacePref.isChecked(); 146 | Application.tabsize = Integer.parseInt(mTabSizePref.getValue()); 147 | Application.font = tpFont; 148 | Application.textsize = Integer.parseInt(mSizePref.getValue()); 149 | Application.suggestion = mSuggestionPref.isChecked(); 150 | Application.auto_caps = mAutoCapsPref.isChecked(); 151 | Application.show_hidden = mShowHidden.isChecked(); 152 | Application.cflags = mCFlagsPref.getText(); 153 | Application.completion = mEngine.getValue(); 154 | Application.lsp_host = mHost.getText(); 155 | Application.lsp_port = Integer.parseInt(mPort.getText()); 156 | super.onPause(); 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /codeeditor/src/main/java/cn/rbc/codeeditor/view/ClipboardPanel.java: -------------------------------------------------------------------------------- 1 | package cn.rbc.codeeditor.view; 2 | 3 | import android.content.*; 4 | import android.content.res.*; 5 | import android.graphics.*; 6 | import android.os.*; 7 | import android.view.*; 8 | import cn.rbc.termuc.*; 9 | 10 | public class ClipboardPanel implements ActionMode.Callback { 11 | protected FreeScrollingTextField _textField; 12 | private Context _context; 13 | 14 | private ActionMode _clipboardActionMode; 15 | private ActionMode.Callback2 _clipboardActionModeCallback2; 16 | 17 | public ClipboardPanel(FreeScrollingTextField textField) { 18 | _textField = textField; 19 | _context = textField.getContext(); 20 | } 21 | 22 | public Context getContext() { 23 | return _context; 24 | } 25 | 26 | public void show() { 27 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 28 | initData(); 29 | startClipboardActionNew(); 30 | } else 31 | startClipboardAction(); 32 | } 33 | 34 | public void hide() { 35 | stopClipboardAction(); 36 | } 37 | 38 | public void startClipboardAction() { 39 | if (_clipboardActionMode == null) 40 | _textField.startActionMode(this); 41 | } 42 | 43 | @Override 44 | public boolean onCreateActionMode(ActionMode mode, Menu menu) { 45 | _clipboardActionMode = mode; 46 | //mode.setTitle(android.R.string.selectTextMode); 47 | TypedArray array = _context.getTheme().obtainStyledAttributes( 48 | new int[]{ 49 | android.R.attr.actionModeSelectAllDrawable, 50 | android.R.attr.actionModeCutDrawable, 51 | android.R.attr.actionModeCopyDrawable, 52 | android.R.attr.actionModePasteDrawable 53 | }); 54 | menu.add(0, 0, 0, _context.getString(android.R.string.selectAll)) 55 | .setShowAsActionFlags(2) 56 | .setAlphabeticShortcut('a') 57 | .setIcon(array.getDrawable(0)); 58 | 59 | menu.add(0, 1, 0, _context.getString(android.R.string.cut)) 60 | .setShowAsActionFlags(2) 61 | .setAlphabeticShortcut('x') 62 | .setIcon(array.getDrawable(1)); 63 | 64 | menu.add(0, 2, 0, _context.getString(android.R.string.copy)) 65 | .setShowAsActionFlags(2) 66 | .setAlphabeticShortcut('c') 67 | .setIcon(array.getDrawable(2)); 68 | 69 | menu.add(0, 3, 0, _context.getString(android.R.string.paste)) 70 | .setShowAsActionFlags(2) 71 | .setAlphabeticShortcut('v') 72 | .setIcon(array.getDrawable(3)); 73 | 74 | menu.add(0, 4, 0, _context.getString(R.string.delete)) 75 | .setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_IF_ROOM) 76 | .setAlphabeticShortcut('d'); 77 | 78 | menu.add(0, 5, 0, _context.getString(R.string.format)) 79 | .setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_IF_ROOM) 80 | .setAlphabeticShortcut('f'); 81 | array.recycle(); 82 | return true; 83 | } 84 | 85 | @Override 86 | public boolean onPrepareActionMode(ActionMode mode, Menu menu) { 87 | return false; 88 | } 89 | 90 | @Override 91 | public boolean onActionItemClicked(ActionMode mode, MenuItem item) { 92 | switch (item.getItemId()) { 93 | case 0: 94 | _textField.selectAll(); 95 | break; 96 | case 1: 97 | _textField.cut(); 98 | mode.finish(); 99 | break; 100 | case 2: 101 | _textField.copy(); 102 | mode.finish(); 103 | break; 104 | case 3: 105 | _textField.paste(); 106 | mode.finish(); 107 | break; 108 | case 4: 109 | _textField.delete(); 110 | mode.finish(); 111 | break; 112 | case 5: 113 | _textField.format(); 114 | mode.finish(); 115 | } 116 | return false; 117 | } 118 | 119 | @Override 120 | public void onDestroyActionMode(ActionMode p1) { 121 | _textField.selectText(false); 122 | _clipboardActionMode = null; 123 | } 124 | 125 | public void startClipboardActionNew() { 126 | if (_clipboardActionMode == null) { 127 | _textField.startActionMode(_clipboardActionModeCallback2, ActionMode.TYPE_FLOATING); 128 | } 129 | } 130 | 131 | private void initData() { 132 | _clipboardActionModeCallback2 = new ActionMode.Callback2() { 133 | // private Rect caret; 134 | @Override 135 | public boolean onCreateActionMode(ActionMode mode, Menu menu) { 136 | _clipboardActionMode = mode; 137 | FreeScrollingTextField fld = _textField; 138 | boolean isSel = fld.getSelectionStart() != fld.getSelectionEnd(); 139 | menu.add(0, 0, 0, _context.getString(android.R.string.selectAll)) 140 | .setShowAsActionFlags(2) 141 | .setAlphabeticShortcut('a'); 142 | menu.add(0, 1, 0, _context.getString(android.R.string.cut)) 143 | .setShowAsActionFlags(2) 144 | .setAlphabeticShortcut('x').setEnabled(isSel); 145 | menu.add(0, 2, 0, _context.getString(android.R.string.copy)) 146 | .setShowAsActionFlags(2) 147 | .setAlphabeticShortcut('c').setEnabled(isSel); 148 | menu.add(0, 3, 0, _context.getString(android.R.string.paste)) 149 | .setShowAsActionFlags(2) 150 | .setAlphabeticShortcut('v'); 151 | menu.add(0, 4, 0, _context.getString(R.string.delete)) 152 | .setShowAsActionFlags(2) 153 | .setAlphabeticShortcut('d').setEnabled(isSel); 154 | menu.add(0, 5, 0, _context.getString(R.string.format)) 155 | .setShowAsActionFlags(2) 156 | .setAlphabeticShortcut('f'); 157 | return true; 158 | } 159 | 160 | @Override 161 | public boolean onPrepareActionMode(ActionMode mode, Menu menu) { 162 | return false; 163 | } 164 | 165 | @Override 166 | public boolean onActionItemClicked(ActionMode mode, MenuItem item) { 167 | return ClipboardPanel.this.onActionItemClicked(mode, item); 168 | } 169 | 170 | @Override 171 | public void onDestroyActionMode(ActionMode p1) { 172 | _textField.selectText(false); 173 | _clipboardActionMode = null; 174 | //caret = null; 175 | } 176 | 177 | @Override 178 | public void onGetContentRect(ActionMode mode, View view, Rect outRect) { 179 | FreeScrollingTextField fld = _textField; 180 | Rect caret = fld.getBoundingBox(fld.getSelectionStart()); 181 | int x = fld.getScrollX(), y = fld.getScrollY(); 182 | caret.top -= y; 183 | caret.bottom = Math.max(0, caret.bottom-y); 184 | caret.left -= x; 185 | caret.right -= x; 186 | outRect.set(caret); 187 | Menu menu = mode.getMenu(); 188 | boolean isSel = fld.getSelectionStart() != fld.getSelectionEnd(); 189 | menu.findItem(1).setEnabled(isSel); 190 | menu.findItem(2).setEnabled(isSel); 191 | menu.findItem(4).setEnabled(isSel); 192 | } 193 | }; 194 | 195 | } 196 | 197 | public void stopClipboardAction() { 198 | if (_clipboardActionMode != null) { 199 | //_clipboardActionModeCallback2.onDestroyActionMode(_clipboardActionMode); 200 | _clipboardActionMode.finish(); 201 | _clipboardActionMode = null; 202 | } 203 | } 204 | 205 | public void invalidateContentRect() { 206 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && _clipboardActionMode != null) 207 | _clipboardActionMode.invalidateContentRect(); 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /codeeditor/src/main/java/cn/rbc/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 cn.rbc.codeeditor.util; 10 | 11 | import cn.rbc.codeeditor.lang.Language; 12 | 13 | public class LinearSearchStrategy implements SearchStrategy { 14 | private int _unitsDone = 0; 15 | 16 | @Override 17 | // only applicable to replaceAll operation 18 | public int getProgress() { 19 | return _unitsDone; 20 | } 21 | 22 | @Override 23 | public int wrappedFind(Document src, String target, int start, 24 | boolean isCaseSensitive, boolean isWholeWord) { 25 | 26 | // search towards end of doc first... 27 | int foundOffset = find(src, target, start, src.length(), 28 | isCaseSensitive, isWholeWord); 29 | // ...then from beginning of doc 30 | if (foundOffset < 0) { 31 | foundOffset = find(src, target, 0, start, 32 | isCaseSensitive, isWholeWord); 33 | } 34 | 35 | return foundOffset; 36 | } 37 | 38 | @Override 39 | public int find(Document src, String target, int start, int end, 40 | boolean isCaseSensitive, boolean isWholeWord) { 41 | if (target.length() == 0) { 42 | return -1; 43 | } 44 | if (start < 0) { 45 | TextWarriorException.fail("TextBuffer.find: Invalid start position"); 46 | start = 0; 47 | } 48 | if (end > src.length()) { 49 | TextWarriorException.fail("TextBuffer.find: Invalid end position"); 50 | end = src.length(); 51 | } 52 | 53 | end = Math.min(end, src.length() - target.length() + 1); 54 | int offset = start; 55 | while (offset < end) { 56 | if (equals(src, target, offset, isCaseSensitive) && 57 | (!isWholeWord || isSandwichedByWhitespace(src, offset, target.length()))) { 58 | break; 59 | } 60 | 61 | ++offset; 62 | ++_unitsDone; 63 | } 64 | 65 | if (offset < end) { 66 | return offset; 67 | } else { 68 | return -1; 69 | } 70 | } 71 | 72 | @Override 73 | public int wrappedFindBackwards(Document src, String target, int start, 74 | boolean isCaseSensitive, boolean isWholeWord) { 75 | 76 | // search towards beginning of doc first... 77 | int foundOffset = findBackwards(src, target, start, -1, 78 | isCaseSensitive, isWholeWord); 79 | // ...then from end of doc 80 | if (foundOffset < 0) { 81 | foundOffset = findBackwards(src, target, src.length() - 1, start, 82 | isCaseSensitive, isWholeWord); 83 | } 84 | 85 | return foundOffset; 86 | } 87 | 88 | 89 | @Override 90 | public int findBackwards(Document src, String target, int start, int end, 91 | boolean isCaseSensitive, boolean isWholeWord) { 92 | if (target.length() == 0) { 93 | return -1; 94 | } 95 | if (start >= src.length()) { 96 | TextWarriorException.fail("Invalid start position given to TextBuffer.find"); 97 | start = src.length() - 1; 98 | } 99 | if (end < -1) { 100 | TextWarriorException.fail("Invalid end position given to TextBuffer.find"); 101 | end = -1; 102 | } 103 | int offset = Math.min(start, src.length() - target.length()); 104 | while (offset > end) { 105 | if (equals(src, target, offset, isCaseSensitive) && 106 | (!isWholeWord || isSandwichedByWhitespace(src, offset, target.length()))) { 107 | break; 108 | } 109 | 110 | --offset; 111 | } 112 | 113 | if (offset > end) { 114 | return offset; 115 | } else { 116 | return -1; 117 | } 118 | } 119 | 120 | @Override 121 | public Pair replaceAll(Document src, String searchText, 122 | String replacementText, int mark, 123 | boolean isCaseSensitive, boolean isWholeWord) { 124 | int replacementCount = 0; 125 | int anchor = mark; 126 | _unitsDone = 0; 127 | 128 | final char[] replacement = replacementText.toCharArray(); 129 | int foundIndex = find(src, searchText, 0, src.length(), 130 | isCaseSensitive, isWholeWord); 131 | long timestamp = System.nanoTime(); 132 | 133 | src.beginBatchEdit(); 134 | while (foundIndex != -1) { 135 | src.deleteAt(foundIndex, searchText.length(), timestamp); 136 | src.insertBefore(replacement, foundIndex, timestamp); 137 | if (foundIndex < anchor) { 138 | // adjust anchor because of differences in doc length 139 | // after word replacement 140 | anchor += replacementText.length() - searchText.length(); 141 | } 142 | ++replacementCount; 143 | _unitsDone += searchText.length(); //skip replaced chars 144 | foundIndex = find( 145 | src, 146 | searchText, 147 | foundIndex + replacementText.length(), 148 | src.length(), 149 | isCaseSensitive, 150 | isWholeWord); 151 | } 152 | src.endBatchEdit(); 153 | 154 | return new Pair(replacementCount, Math.max(anchor, 0)); 155 | } 156 | 157 | 158 | protected boolean equals(Document src, String target, 159 | int srcOffset, boolean isCaseSensitive) { 160 | if ((src.length() - srcOffset) < target.length()) { 161 | //compared range in src must at least be as long as target 162 | return false; 163 | } 164 | 165 | int i; 166 | for (i = 0; i < target.length(); ++i) { 167 | if (isCaseSensitive && 168 | target.charAt(i) != src.charAt(i + srcOffset)) { 169 | return false; 170 | } 171 | // for case-insensitive search, compare both strings in lower case 172 | if (!isCaseSensitive && 173 | Character.toLowerCase(target.charAt(i)) != 174 | Character.toLowerCase(src.charAt(i + srcOffset))) { 175 | return false; 176 | } 177 | 178 | } 179 | 180 | return true; 181 | } 182 | 183 | /** 184 | * Checks if a word starting at startPosition with size length is bounded 185 | * by whitespace. 186 | */ 187 | protected boolean isSandwichedByWhitespace(Document src, 188 | int start, int length) { 189 | Language charSet = Tokenizer.getLanguage(); 190 | boolean startWithWhitespace = (start == 0) || charSet.isWhitespace(src.charAt(start - 1)); 191 | 192 | int end = start + length; 193 | boolean endWithWhitespace = (end == src.length()) || charSet.isWhitespace(src.charAt(end)); 194 | 195 | return (startWithWhitespace && endWithWhitespace); 196 | } 197 | 198 | } 199 | --------------------------------------------------------------------------------