Code editor for files (#1182)

Currently highlight supports the following languages:

- Java
- Python
- Go Lang
- PHP

More will come in next iterations/releases.

Closes #1166
Closes #949

**EDIT: by support it means only can highlight that specific languages methods, classes, data types etc etc. By no means it is restricted to only that 4 langs. You can use it for any language you want.**

Co-authored-by: M M Arif <mmarif@swatian.com>
Co-authored-by: 6543 <6543@noreply.codeberg.org>
Reviewed-on: https://codeberg.org/gitnex/GitNex/pulls/1182
Reviewed-by: 6543 <6543@noreply.codeberg.org>
This commit is contained in:
M M Arif 2022-08-14 08:44:04 +02:00
parent 499c559412
commit 9fa2e3f506
9 changed files with 331 additions and 94 deletions

View File

@ -1,10 +1,13 @@
package org.mian.gitnex.activities;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.Typeface;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import com.amrdeveloper.codeview.Code;
import org.apache.commons.lang3.EnumUtils;
import org.mian.gitnex.R;
import org.mian.gitnex.databinding.ActivityCodeEditorBinding;
import org.mian.gitnex.helpers.codeeditor.CustomCodeViewAdapter;
@ -25,7 +28,7 @@ public class CodeEditorActivity extends BaseActivity {
private ActivityCodeEditorBinding binding;
private LanguageManager languageManager;
private final LanguageName currentLanguage = LanguageName.JAVA;
private LanguageName currentLanguage = LanguageName.UNKNOWN;
private final ThemeName currentTheme = ThemeName.FIVE_COLOR;
@Override
@ -34,13 +37,36 @@ public class CodeEditorActivity extends BaseActivity {
binding = ActivityCodeEditorBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
binding.close.setOnClickListener(view -> finish());
binding.close.setOnClickListener(view -> {
sendResults();
finish();
});
configCodeView(currentLanguage);
String fileContent = getIntent().getStringExtra("fileContent");
String fileExtension;
if(getIntent().getStringExtra("fileExtension") != null) {
fileExtension = getIntent().getStringExtra("fileExtension").toUpperCase();
if(EnumUtils.isValidEnum(LanguageName.class, fileExtension)) {
currentLanguage = LanguageName.valueOf(fileExtension);
}
else {
currentLanguage = LanguageName.UNKNOWN;
}
}
configCodeView(currentLanguage, fileContent);
configCodeViewPlugins();
}
private void configCodeView(LanguageName currentLanguage) {
private void sendResults() {
Intent intent = new Intent();
intent.putExtra("fileContentFromActivity", binding.codeView.getText().toString());
setResult(Activity.RESULT_OK, intent);
}
private void configCodeView(LanguageName currentLanguage, String fileContent) {
binding.codeView.setTypeface(Typeface.createFromAsset(ctx.getAssets(), "fonts/sourcecodeproregular.ttf"));
@ -69,6 +95,7 @@ public class CodeEditorActivity extends BaseActivity {
binding.codeView.setPairCompleteMap(pairCompleteMap);
binding.codeView.enablePairComplete(true);
binding.codeView.enablePairCompleteCenterCursor(true);
binding.codeView.setText(fileContent);
// Setup the auto complete and auto indenting for the current language
configLanguageAutoComplete();

View File

@ -1,6 +1,7 @@
package org.mian.gitnex.activities;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
@ -10,7 +11,10 @@ import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import org.apache.commons.io.FilenameUtils;
import org.gitnex.tea4j.v2.models.Branch;
import org.gitnex.tea4j.v2.models.CreateFileOptions;
import org.gitnex.tea4j.v2.models.DeleteFileOptions;
@ -27,6 +31,7 @@ import org.mian.gitnex.helpers.Toasty;
import org.mian.gitnex.helpers.contexts.RepositoryContext;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import retrofit2.Call;
import retrofit2.Callback;
@ -97,7 +102,6 @@ public class CreateFileActivity extends BaseActivity {
binding.newFileNameLayout.setVisibility(View.GONE);
binding.newFileContentLayout.setVisibility(View.GONE);
}
if(getIntent().getStringExtra("filePath") != null && getIntent().getIntExtra("fileAction", FILE_ACTION_EDIT) == FILE_ACTION_EDIT) {
@ -114,20 +118,38 @@ public class CreateFileActivity extends BaseActivity {
binding.newFileName.setFocusable(false);
binding.newFileContent.setText(getIntent().getStringExtra("fileContents"));
}
getBranches(repository.getOwner(), repository.getName());
disableProcessButton();
binding.openCodeEditor.setOnClickListener(v ->
launchCodeEditorActivityForResult(Objects.requireNonNull(binding.newFileContent.getText()).toString(), FilenameUtils.getExtension(String.valueOf(binding.newFileName.getText())))
);
NetworkStatusObserver networkStatusObserver = NetworkStatusObserver.getInstance(ctx);
networkStatusObserver.registerNetworkStatusListener(hasNetworkConnection -> runOnUiThread(() -> binding.newFileCreate.setEnabled(hasNetworkConnection)));
binding.newFileCreate.setOnClickListener(v -> processNewFile());
}
public void launchCodeEditorActivityForResult(String fileContent, String fileExtension) {
Intent intent = new Intent(this, CodeEditorActivity.class);
intent.putExtra("fileExtension", fileExtension);
intent.putExtra("fileContent", fileContent);
codeEditorActivityResultLauncher.launch(intent);
}
ActivityResultLauncher<Intent> codeEditorActivityResultLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(), result -> {
if(result.getResultCode() == Activity.RESULT_OK) {
Intent data = result.getData();
assert data != null;
binding.newFileContent.setText(data.getStringExtra("fileContentFromActivity"));
}
});
private void processNewFile() {
String newFileName = binding.newFileName.getText() != null ? binding.newFileName.getText().toString() : "";
@ -170,7 +192,6 @@ public class CreateFileActivity extends BaseActivity {
case FILE_ACTION_EDIT:
editFile(repository.getOwner(), repository.getName(), filePath, AppUtil.encodeBase64(newFileContent), newFileCommitMessage, newFileBranchName, fileSha);
break;
}
}
@ -179,9 +200,11 @@ public class CreateFileActivity extends BaseActivity {
CreateFileOptions createNewFileJsonStr = new CreateFileOptions();
createNewFileJsonStr.setContent(fileContent);
createNewFileJsonStr.setMessage(fileCommitMessage);
if(branches.contains(branchName)) {
createNewFileJsonStr.setBranch(branchName);
} else {
}
else {
createNewFileJsonStr.setNewBranch(branchName);
}
@ -189,50 +212,47 @@ public class CreateFileActivity extends BaseActivity {
.getApiInterface(ctx)
.repoCreateFile(createNewFileJsonStr, repoOwner, repoName, fileName);
call.enqueue(new Callback<FileResponse>() {
call.enqueue(new Callback<>() {
@Override
public void onResponse(@NonNull Call<FileResponse> call, @NonNull retrofit2.Response<FileResponse> response) {
@Override
public void onResponse(@NonNull Call<FileResponse> call, @NonNull retrofit2.Response<FileResponse> response) {
switch(response.code()) {
switch(response.code()) {
case 201:
enableProcessButton();
Toasty.success(ctx, getString(R.string.newFileSuccessMessage));
Intent result = new Intent();
result.putExtra("fileModified", true);
result.putExtra("fileAction", fileAction);
setResult(200, result);
finish();
break;
case 201:
enableProcessButton();
Toasty.success(ctx, getString(R.string.newFileSuccessMessage));
Intent result = new Intent();
result.putExtra("fileModified", true);
result.putExtra("fileAction", fileAction);
setResult(200, result);
finish();
break;
case 401:
enableProcessButton();
AlertDialogs.authorizationTokenRevokedDialog(ctx);
break;
case 401:
enableProcessButton();
AlertDialogs.authorizationTokenRevokedDialog(ctx);
break;
case 404:
enableProcessButton();
Toasty.warning(ctx, getString(R.string.apiNotFound));
break;
case 404:
enableProcessButton();
Toasty.warning(ctx, getString(R.string.apiNotFound));
break;
default:
enableProcessButton();
Toasty.error(ctx, getString(R.string.genericError));
break;
default:
enableProcessButton();
Toasty.error(ctx, getString(R.string.genericError));
break;
}
}
}
}
@Override
public void onFailure(@NonNull Call<FileResponse> call, @NonNull Throwable t) {
@Override
public void onFailure(@NonNull Call<FileResponse> call, @NonNull Throwable t) {
Log.e("onFailure", t.toString());
enableProcessButton();
}
Log.e("onFailure", t.toString());
enableProcessButton();
}
});
}
private void deleteFile(String repoOwner, String repoName, String fileName, String fileCommitMessage, String branchName, String fileSha) {
@ -240,9 +260,11 @@ public class CreateFileActivity extends BaseActivity {
DeleteFileOptions deleteFileJsonStr = new DeleteFileOptions();
deleteFileJsonStr.setMessage(fileCommitMessage);
deleteFileJsonStr.setSha(fileSha);
if(branches.contains(branchName)) {
deleteFileJsonStr.setBranch(branchName);
} else {
}
else {
deleteFileJsonStr.setNewBranch(branchName);
}
@ -250,7 +272,7 @@ public class CreateFileActivity extends BaseActivity {
.getApiInterface(ctx)
.repoDeleteFileWithBody(repoOwner, repoName, fileName, deleteFileJsonStr);
call.enqueue(new Callback<FileDeleteResponse>() {
call.enqueue(new Callback<>() {
@Override
public void onResponse(@NonNull Call<FileDeleteResponse> call, @NonNull retrofit2.Response<FileDeleteResponse> response) {
@ -281,7 +303,6 @@ public class CreateFileActivity extends BaseActivity {
enableProcessButton();
Toasty.info(ctx, getString(R.string.genericError));
break;
}
}
@ -292,7 +313,6 @@ public class CreateFileActivity extends BaseActivity {
enableProcessButton();
}
});
}
private void editFile(String repoOwner, String repoName, String fileName, String fileContent, String fileCommitMessage, String branchName, String fileSha) {
@ -301,9 +321,11 @@ public class CreateFileActivity extends BaseActivity {
editFileJsonStr.setContent(fileContent);
editFileJsonStr.setMessage(fileCommitMessage);
editFileJsonStr.setSha(fileSha);
if(branches.contains(branchName)) {
editFileJsonStr.setBranch(branchName);
} else {
}
else {
editFileJsonStr.setNewBranch(branchName);
}
@ -311,7 +333,7 @@ public class CreateFileActivity extends BaseActivity {
.getApiInterface(ctx)
.repoUpdateFile(editFileJsonStr, repoOwner, repoName, fileName);
call.enqueue(new Callback<FileResponse>() {
call.enqueue(new Callback<>() {
@Override
public void onResponse(@NonNull Call<FileResponse> call, @NonNull retrofit2.Response<FileResponse> response) {
@ -342,7 +364,6 @@ public class CreateFileActivity extends BaseActivity {
enableProcessButton();
Toasty.info(ctx, getString(R.string.genericError));
break;
}
}
@ -351,10 +372,8 @@ public class CreateFileActivity extends BaseActivity {
Log.e("onFailure", t.toString());
enableProcessButton();
}
});
}
private void getBranches(String repoOwner, String repoName) {
@ -363,33 +382,32 @@ public class CreateFileActivity extends BaseActivity {
.getApiInterface(ctx)
.repoListBranches(repoOwner, repoName, null, null);
call.enqueue(new Callback<List<Branch>>() {
call.enqueue(new Callback<>() {
@Override
public void onResponse(@NonNull Call<List<Branch>> call, @NonNull retrofit2.Response<List<Branch>> response) {
@Override
public void onResponse(@NonNull Call<List<Branch>> call, @NonNull retrofit2.Response<List<Branch>> response) {
if(response.code() == 200) {
if(response.code() == 200) {
assert response.body() != null;
for(Branch branch : response.body()) branches.add(branch.getName());
assert response.body() != null;
for(Branch branch : response.body())
branches.add(branch.getName());
ArrayAdapter<String> adapter = new ArrayAdapter<>(CreateFileActivity.this, R.layout.list_spinner_items, branches);
ArrayAdapter<String> adapter = new ArrayAdapter<>(CreateFileActivity.this, R.layout.list_spinner_items, branches);
binding.newFileBranches.setAdapter(adapter);
binding.newFileBranches.setText(repository.getBranchRef(), false);
binding.newFileBranches.setAdapter(adapter);
binding.newFileBranches.setText(repository.getBranchRef(), false);
enableProcessButton();
enableProcessButton();
}
}
}
}
@Override
public void onFailure(@NonNull Call<List<Branch>> call, @NonNull Throwable t) {
@Override
public void onFailure(@NonNull Call<List<Branch>> call, @NonNull Throwable t) {
Log.e("onFailure", t.toString());
}
Log.e("onFailure", t.toString());
}
});
}
private void disableProcessButton() { binding.newFileCreate.setEnabled(false); }
@ -400,5 +418,4 @@ public class CreateFileActivity extends BaseActivity {
super.onResume();
repository.checkAccountSwitch(this);
}
}

View File

@ -5,6 +5,7 @@ import com.amrdeveloper.codeview.Code;
import com.amrdeveloper.codeview.CodeView;
import org.mian.gitnex.helpers.codeeditor.languages.GoLanguage;
import org.mian.gitnex.helpers.codeeditor.languages.JavaLanguage;
import org.mian.gitnex.helpers.codeeditor.languages.PhpLanguage;
import org.mian.gitnex.helpers.codeeditor.languages.PythonLanguage;
import java.util.ArrayList;
import java.util.HashSet;
@ -36,8 +37,9 @@ public class LanguageManager {
public String[] getLanguageKeywords(LanguageName language) {
switch (language) {
case JAVA: return JavaLanguage.getKeywords(context);
case PYTHON: return PythonLanguage.getKeywords(context);
case GO_LANG: return GoLanguage.getKeywords(context);
case PY: return PythonLanguage.getKeywords(context);
case GO: return GoLanguage.getKeywords(context);
case PHP: return PhpLanguage.getKeywords(context);
default: return new String[]{};
}
}
@ -45,8 +47,9 @@ public class LanguageManager {
public List<Code> getLanguageCodeList(LanguageName language) {
switch (language) {
case JAVA: return JavaLanguage.getCodeList(context);
case PYTHON: return PythonLanguage.getCodeList(context);
case GO_LANG: return GoLanguage.getCodeList(context);
case PY: return PythonLanguage.getCodeList(context);
case GO: return GoLanguage.getCodeList(context);
case PHP: return PhpLanguage.getCodeList(context);
default: return new ArrayList<>();
}
}
@ -54,8 +57,9 @@ public class LanguageManager {
public Set<Character> getLanguageIndentationStarts(LanguageName language) {
switch (language) {
case JAVA: return JavaLanguage.getIndentationStarts();
case PYTHON: return PythonLanguage.getIndentationStarts();
case GO_LANG: return GoLanguage.getIndentationStarts();
case PY: return PythonLanguage.getIndentationStarts();
case GO: return GoLanguage.getIndentationStarts();
case PHP: return PhpLanguage.getIndentationStarts();
default: return new HashSet<>();
}
}
@ -63,8 +67,9 @@ public class LanguageManager {
public Set<Character> getLanguageIndentationEnds(LanguageName language) {
switch (language) {
case JAVA: return JavaLanguage.getIndentationEnds();
case PYTHON: return PythonLanguage.getIndentationEnds();
case GO_LANG: return GoLanguage.getIndentationEnds();
case PY: return PythonLanguage.getIndentationEnds();
case GO: return GoLanguage.getIndentationEnds();
case PHP: return PhpLanguage.getIndentationEnds();
default: return new HashSet<>();
}
}
@ -74,12 +79,15 @@ public class LanguageManager {
case JAVA:
JavaLanguage.applyFiveColorsDarkTheme(context, codeView);
break;
case PYTHON:
case PY:
PythonLanguage.applyFiveColorsDarkTheme(context, codeView);
break;
case GO_LANG:
case GO:
GoLanguage.applyFiveColorsDarkTheme(context, codeView);
break;
case PHP:
PhpLanguage.applyFiveColorsDarkTheme(context, codeView);
break;
}
}
}

View File

@ -6,7 +6,9 @@ package org.mian.gitnex.helpers.codeeditor;
*/
public enum LanguageName {
JAVA,
PYTHON,
GO_LANG
UNKNOWN, // no language is specified or app currently does not support the mentioned language
JAVA, // java
PY, // python with py extension
GO, // go lang
PHP // php
}

View File

@ -0,0 +1,101 @@
package org.mian.gitnex.helpers.codeeditor.languages;
import android.content.Context;
import android.content.res.Resources;
import com.amrdeveloper.codeview.Code;
import com.amrdeveloper.codeview.CodeView;
import com.amrdeveloper.codeview.Keyword;
import org.mian.gitnex.R;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
/**
* @author M M Arif
*/
public class PhpLanguage {
//Language Keywords
private static final Pattern PATTERN_KEYWORDS = Pattern.compile("\\b(php|__construct|var_dump|define|echo|var|float|" +
"int|bool|false|true|function|private|public|protected|interface|return|copy|struct|abstract|extends|" +
"trait|static|namespace|implements|__set|__get|unlink|this|try|catch|Throwable|Exception|pdo|" +
"str_replace|form|date|abs|min|max|strtotime|mktime|" +
"foreach|require_once|include_once|hash|array|range|break|continue|preg_match|preg_match_all|preg_replace|" +
"throw|new|and|or|if|else|elseif|switch|case|default|match|require|include|goto|do|while|for|map|)\\b");
//Brackets and Colons
private static final Pattern PATTERN_BUILTINS = Pattern.compile("[,:;[->]{}()]");
//Data
private static final Pattern PATTERN_NUMBERS = Pattern.compile("\\b(\\d*[.]?\\d+)\\b");
private static final Pattern PATTERN_CHAR = Pattern.compile("['](.*?)[']");
private static final Pattern PATTERN_STRING = Pattern.compile("[\"](.*?)[\"]");
private static final Pattern PATTERN_HEX = Pattern.compile("0x[0-9a-fA-F]+");
private static final Pattern PATTERN_SINGLE_LINE_COMMENT = Pattern.compile("//[^\\n]*");
private static final Pattern PATTERN_MULTI_LINE_COMMENT = Pattern.compile("/\\*[^*]*\\*+(?:[^/*][^*]*\\*+)*/");
private static final Pattern PATTERN_ATTRIBUTE = Pattern.compile("\\.[a-zA-Z0-9_]+");
private static final Pattern PATTERN_OPERATION =Pattern.compile( ":|==|>|<|!=|>=|<=|->|=|>|<|%|-|-=|%=|\\+|\\-|\\-=|\\+=|\\^|\\&|\\|::|\\?|\\*");
public static void applyFiveColorsDarkTheme(Context context, CodeView codeView) {
codeView.resetSyntaxPatternList();
codeView.resetHighlighter();
Resources resources = context.getResources();
//View Background
codeView.setBackgroundColor(resources.getColor(R.color.five_dark_black, null));
//Syntax Colors
codeView.addSyntaxPattern(PATTERN_HEX, resources.getColor(R.color.five_dark_purple, null));
codeView.addSyntaxPattern(PATTERN_CHAR, resources.getColor(R.color.five_dark_yellow, null));
codeView.addSyntaxPattern(PATTERN_STRING, resources.getColor(R.color.five_dark_yellow, null));
codeView.addSyntaxPattern(PATTERN_NUMBERS, resources.getColor(R.color.five_dark_purple, null));
codeView.addSyntaxPattern(PATTERN_KEYWORDS, resources.getColor(R.color.five_dark_purple, null));
codeView.addSyntaxPattern(PATTERN_BUILTINS, resources.getColor(R.color.five_dark_white, null));
codeView.addSyntaxPattern(PATTERN_SINGLE_LINE_COMMENT, resources.getColor(R.color.five_dark_grey, null));
codeView.addSyntaxPattern(PATTERN_MULTI_LINE_COMMENT, resources.getColor(R.color.five_dark_grey, null));
codeView.addSyntaxPattern(PATTERN_ATTRIBUTE, resources.getColor(R.color.five_dark_blue, null));
codeView.addSyntaxPattern(PATTERN_OPERATION, resources.getColor(R.color.five_dark_purple, null));
//Default Color
codeView.setTextColor(resources.getColor(R.color.five_dark_white, null));
codeView.reHighlightSyntax();
}
public static String[] getKeywords(Context context) {
return context.getResources().getStringArray(R.array.php_keywords);
}
public static List<Code> getCodeList(Context context) {
List<Code> codeList = new ArrayList<>();
String[] keywords = getKeywords(context);
for (String keyword : keywords) {
codeList.add(new Keyword(keyword));
}
return codeList;
}
public static Set<Character> getIndentationStarts() {
Set<Character> characterSet = new HashSet<>();
characterSet.add('{');
return characterSet;
}
public static Set<Character> getIndentationEnds() {
Set<Character> characterSet = new HashSet<>();
characterSet.add('}');
return characterSet;
}
public static String getCommentStart() {
return "//";
}
public static String getCommentEnd() {
return "";
}
}

View File

@ -81,7 +81,6 @@
android:textColor="?attr/inputTextColor"
android:textColorHighlight="?attr/hintColor"
android:textColorHint="?attr/hintColor"
android:inputType="textCapSentences"
android:singleLine="true"
android:imeOptions="actionNext"
android:textSize="@dimen/dimen16sp" />
@ -105,17 +104,28 @@
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/newFileContent"
android:layout_width="match_parent"
android:layout_height="@dimen/dimen140dp"
android:layout_height="@dimen/dimen180dp"
android:gravity="top|start"
android:inputType="textMultiLine|textCapSentences"
android:inputType="textMultiLine"
android:scrollbars="vertical"
android:textColor="?attr/inputTextColor"
android:textColorHighlight="?attr/hintColor"
android:textColorHint="?attr/hintColor"
android:textSize="@dimen/dimen16sp" />
android:textSize="@dimen/dimen14sp" />
</com.google.android.material.textfield.TextInputLayout>
<TextView
android:id="@+id/openCodeEditor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen2dp"
android:layout_marginBottom="@dimen/dimen8dp"
android:textSize="@dimen/dimen14sp"
android:layout_gravity="end"
android:textColor="?attr/primaryTextColor"
android:text="@string/openInCodeEditor" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/newFileBranchesLayout"
android:layout_width="match_parent"

View File

@ -47,10 +47,6 @@
android:layout_gravity="start"
android:id="@+id/nav_view"
app:headerLayout="@layout/nav_header"
app:menu="@menu/drawer_menu"
android:background="?attr/primaryBackgroundColor"
app:itemIconTint="?attr/primaryTextColor"
app:itemTextAppearance="@style/DrawerIcon"
app:itemTextColor="?attr/primaryTextColor" />
app:menu="@menu/drawer_menu" />
</androidx.drawerlayout.widget.DrawerLayout>

View File

@ -1,6 +1,81 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--PHP Keywords List-->
<string-array name="php_keywords">
<item>php</item>
<item>__construct</item>
<item>var_dump</item>
<item>define</item>
<item>echo</item>
<item>var</item>
<item>float</item>
<item>int</item>
<item>bool</item>
<item>false</item>
<item>true</item>
<item>function</item>
<item>private</item>
<item>public</item>
<item>protected</item>
<item>interface</item>
<item>return</item>
<item>copy</item>
<item>struct</item>
<item>abstract</item>
<item>extends</item>
<item>trait</item>
<item>static</item>
<item>namespace</item>
<item>implements</item>
<item>__set</item>
<item>__get</item>
<item>unlink</item>
<item>this</item>
<item>try</item>
<item>catch</item>
<item>Throwable</item>
<item>Exception</item>
<item>pdo</item>
<item>throw</item>
<item>new</item>
<item>and</item>
<item>or</item>
<item>if</item>
<item>else</item>
<item>elseif</item>
<item>switch</item>
<item>case</item>
<item>default</item>
<item>match</item>
<item>require</item>
<item>include</item>
<item>require_once</item>
<item>include_once</item>
<item>goto</item>
<item>do</item>
<item>while</item>
<item>for</item>
<item>foreach</item>
<item>map</item>
<item>hash</item>
<item>array</item>
<item>range</item>
<item>break</item>
<item>continue</item>
<item>preg_match</item>
<item>preg_match_all</item>
<item>preg_replace</item>
<item>str_replace</item>
<item>form</item>
<item>date</item>
<item>abs</item>
<item>min</item>
<item>max</item>
<item>strtotime</item>
<item>mktime</item>
</string-array>
<!--Java Keywords List-->
<string-array name="java_keywords">
<item>public</item>

View File

@ -784,4 +784,5 @@
<!-- code editor -->
<string name="sourcePosition" translatable="false">%1$d:%2$d</string>
<string name="codeEditor" translatable="false">Code Editor</string>
<string name="openInCodeEditor">Open in Code Editor</string>
</resources>