mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
- Removed files related to previous java auto completion algorithm
This commit is contained in:
Binary file not shown.
@@ -719,11 +719,13 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
|
||||
@Override
|
||||
public void onServerStarted(int currentId) {
|
||||
final JavaLanguageServer server = getApp().getJavaLanguageServer();
|
||||
while(server != null && !pendingMessages.isEmpty()) {
|
||||
Message msg = pendingMessages.pop();
|
||||
server.send(msg);
|
||||
}
|
||||
new Thread(() -> {
|
||||
final JavaLanguageServer server = getApp().getJavaLanguageServer();
|
||||
while(server != null && !pendingMessages.isEmpty()) {
|
||||
Message msg = pendingMessages.pop();
|
||||
server.send(msg);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -9,15 +9,13 @@ import android.view.ViewGroup;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.databinding.LayoutCompletionItemBinding;
|
||||
import com.itsaky.androidide.language.java.parser.internal.JavaUtil;
|
||||
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
|
||||
import com.itsaky.androidide.models.CompletionListItem;
|
||||
import com.itsaky.androidide.models.SuggestItem;
|
||||
import com.itsaky.androidide.utils.Either;
|
||||
import com.itsaky.androidide.utils.TypefaceUtils;
|
||||
import io.github.rosemoe.editor.struct.CompletionItem;
|
||||
import io.github.rosemoe.editor.widget.EditorCompletionAdapter;
|
||||
import java.util.Locale;
|
||||
import com.itsaky.androidide.language.java.parser.internal.IJavaDocCommentable;
|
||||
|
||||
public class CompletionListAdapter extends EditorCompletionAdapter {
|
||||
@Override
|
||||
@@ -45,7 +43,7 @@ public class CompletionListAdapter extends EditorCompletionAdapter {
|
||||
type = item.getItemType().contains(".") ? item.getItemType().substring(item.getItemType().lastIndexOf(".") + 1) : item.getItemType();
|
||||
desc = item.getType() != CompletionListItem.Type.NOT_IMPORTED_CLASS ? item.getDetail() : getContext().getString(R.string.label_not_imported, item.getDetail());
|
||||
}
|
||||
type = JavaUtil.getArrayTypeIfNeeded(type);
|
||||
|
||||
binding.completionIconText.setText(header);
|
||||
binding.completionLabel.setText(label);
|
||||
binding.completionType.setText(type);
|
||||
@@ -60,17 +58,4 @@ public class CompletionListAdapter extends EditorCompletionAdapter {
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getHtmlJavadocAt(int index) {
|
||||
Either<SuggestItem, CompletionItem> item = getItem(index);
|
||||
if(item.isLeft() && item.getLeft() instanceof IJavaDocCommentable) {
|
||||
IJavaDocCommentable commentable = (IJavaDocCommentable) item;
|
||||
String doc = commentable.getJavaDoc();
|
||||
if(doc != null && doc.trim().length() > 0) {
|
||||
return doc;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.interfaces.LanguageServerListener;
|
||||
import com.itsaky.androidide.language.java.manager.JavaCharacter;
|
||||
import com.itsaky.androidide.language.java.provider.JavaCompletionProvider;
|
||||
import com.itsaky.androidide.language.xml.completion.XMLCompletionService;
|
||||
import com.itsaky.androidide.models.AndroidProject;
|
||||
import com.itsaky.androidide.models.ConstantsBridge;
|
||||
@@ -251,10 +250,14 @@ public class StudioApp extends MultiDexApplication
|
||||
}
|
||||
|
||||
public void createCompletionService(AndroidProject project, LanguageClient client) {
|
||||
this.languageServer = new JavaLanguageServer(project, client);
|
||||
this.mXmlCompletionService = new XMLCompletionService();
|
||||
|
||||
this.languageServer.startServer();
|
||||
if(languageServer == null) {
|
||||
this.languageServer = new JavaLanguageServer(project, client);
|
||||
this.languageServer.startServer();
|
||||
} else {
|
||||
this.languageServer.initialize(project);
|
||||
}
|
||||
}
|
||||
|
||||
public JavaLanguageServer getJavaLanguageServer() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.itsaky.androidide.language.groovy;
|
||||
|
||||
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
|
||||
import com.itsaky.androidide.models.GradleSuggestItem;
|
||||
import com.itsaky.androidide.models.SuggestItem;
|
||||
import com.itsaky.androidide.utils.Either;
|
||||
import io.github.rosemoe.editor.interfaces.AutoCompleteProvider;
|
||||
import io.github.rosemoe.editor.struct.CompletionItem;
|
||||
|
||||
@@ -3,9 +3,9 @@ package com.itsaky.androidide.language.java;
|
||||
import androidx.core.util.Pair;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.language.java.JavaLanguageAnalyzer;
|
||||
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
|
||||
import com.itsaky.androidide.language.java.server.JavaLanguageServer;
|
||||
import com.itsaky.androidide.models.CompletionItemWrapper;
|
||||
import com.itsaky.androidide.models.SuggestItem;
|
||||
import com.itsaky.androidide.utils.Either;
|
||||
import com.itsaky.lsp.CompletionList;
|
||||
import com.itsaky.lsp.Position;
|
||||
@@ -17,7 +17,6 @@ import io.github.rosemoe.editor.text.TextAnalyzeResult;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
|
||||
public class JavaAutoComplete implements AutoCompleteProvider {
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser;
|
||||
|
||||
import java.util.List;
|
||||
import javax.tools.Diagnostic;
|
||||
import javax.tools.DiagnosticListener;
|
||||
import java.util.Collections;
|
||||
import java.util.ArrayList;
|
||||
import javax.tools.JavaFileObject;
|
||||
|
||||
public class DiagnosticCollector<S> implements DiagnosticListener<S> {
|
||||
|
||||
private List<Diagnostic<? extends S>> diagnostics = Collections.synchronizedList(new ArrayList<Diagnostic<? extends S>>());
|
||||
|
||||
@Override
|
||||
public void report(Diagnostic<? extends S> diagnostic) {
|
||||
diagnostic.getClass();
|
||||
diagnostics.add(diagnostic);
|
||||
}
|
||||
|
||||
public void clearDiagnostics() {
|
||||
diagnostics.clear();
|
||||
}
|
||||
|
||||
public List<Diagnostic<? extends S>> getDiagnostics() {
|
||||
return Collections.unmodifiableList(diagnostics);
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser;
|
||||
|
||||
import com.sun.tools.javac.tree.JCTree;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.sun.tools.javac.tree.JCTree.JCExpression;
|
||||
|
||||
public class Expression {
|
||||
/**
|
||||
* Parent scope of statement, from higher to lower
|
||||
* parentOfStatement.get(0) will be contains parentOfStatement.get(1)
|
||||
*/
|
||||
public final LinkedList<JCTree> parentOfStatement = new LinkedList<>();
|
||||
|
||||
/**
|
||||
* Expression at cursor
|
||||
*/
|
||||
public JCExpression expression;
|
||||
|
||||
public Expression(JCExpression expression) {
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
public Expression(JCTree root, JCExpression expression) {
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
public void addRoot(JCTree tree) {
|
||||
if (tree != null) {
|
||||
parentOfStatement.addFirst(tree);
|
||||
}
|
||||
}
|
||||
|
||||
public JCTree getRootAt(int level) {
|
||||
return parentOfStatement.get(level);
|
||||
}
|
||||
|
||||
public JCExpression getExpression() {
|
||||
return expression;
|
||||
}
|
||||
|
||||
public int getParentLevelCount() {
|
||||
return parentOfStatement.size();
|
||||
}
|
||||
|
||||
public LinkedList<JCTree> getParentOfExpression() {
|
||||
return parentOfStatement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (expression instanceof JCTree.JCErroneous) {
|
||||
List<? extends JCTree> errorTrees
|
||||
= ((JCTree.JCErroneous) expression).getErrorTrees();
|
||||
return errorTrees.toString();
|
||||
}
|
||||
return expression.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,411 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.sun.tools.javac.tree.JCTree;
|
||||
import com.sun.tools.javac.tree.JCTree.JCAssign;
|
||||
import com.sun.tools.javac.tree.JCTree.JCAssignOp;
|
||||
import com.sun.tools.javac.tree.JCTree.JCBinary;
|
||||
import com.sun.tools.javac.tree.JCTree.JCBlock;
|
||||
import com.sun.tools.javac.tree.JCTree.JCCase;
|
||||
import com.sun.tools.javac.tree.JCTree.JCCatch;
|
||||
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
|
||||
import com.sun.tools.javac.tree.JCTree.JCConditional;
|
||||
import com.sun.tools.javac.tree.JCTree.JCDoWhileLoop;
|
||||
import com.sun.tools.javac.tree.JCTree.JCEnhancedForLoop;
|
||||
import com.sun.tools.javac.tree.JCTree.JCExpression;
|
||||
import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
|
||||
import com.sun.tools.javac.tree.JCTree.JCForLoop;
|
||||
import com.sun.tools.javac.tree.JCTree.JCIf;
|
||||
import com.sun.tools.javac.tree.JCTree.JCInstanceOf;
|
||||
import com.sun.tools.javac.tree.JCTree.JCLabeledStatement;
|
||||
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
|
||||
import com.sun.tools.javac.tree.JCTree.JCReturn;
|
||||
import com.sun.tools.javac.tree.JCTree.JCStatement;
|
||||
import com.sun.tools.javac.tree.JCTree.JCSwitch;
|
||||
import com.sun.tools.javac.tree.JCTree.JCSynchronized;
|
||||
import com.sun.tools.javac.tree.JCTree.JCThrow;
|
||||
import com.sun.tools.javac.tree.JCTree.JCTry;
|
||||
import com.sun.tools.javac.tree.JCTree.JCUnary;
|
||||
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
|
||||
import com.sun.tools.javac.tree.JCTree.JCWhileLoop;
|
||||
import com.sun.tools.javac.tree.JCTree.JCLambda;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.sun.tools.javac.tree.EndPosTable;
|
||||
|
||||
public class ExpressionResolver {
|
||||
private int mCursor;
|
||||
private EndPosTable mEndPositions;
|
||||
private JCTree.JCCompilationUnit mAst;
|
||||
|
||||
public ExpressionResolver(@NonNull JCTree.JCCompilationUnit unit, @NonNull CodeEditor editor) {
|
||||
unit.getClass();
|
||||
editor.getClass();
|
||||
mAst = unit;
|
||||
mCursor = editor.getCursor().getLeft();
|
||||
mEndPositions = unit.endPositions;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Expression getExpressionAtCursor() {
|
||||
for (JCTree jcTree : mAst.getTypeDecls()) {
|
||||
Expression expression = getExpressionContainsCursor(jcTree);
|
||||
if (expression != null) {
|
||||
return expression;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Expression getExpressionContainsCursor(JCTree tree) {
|
||||
if (!isCursorInsideTree(tree)) {
|
||||
return null;
|
||||
}
|
||||
if (tree instanceof JCClassDecl) {
|
||||
return getExpressionFromClass((JCClassDecl) tree);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Expression getExpressionFromStatement(JCTree.JCStatement statement) {
|
||||
if (!isCursorInsideTree(statement)) {
|
||||
return null;
|
||||
}
|
||||
Expression result;
|
||||
if (statement instanceof JCBlock) {
|
||||
JCBlock jcBlock = (JCBlock) statement;
|
||||
List<JCStatement> statements = jcBlock.getStatements();
|
||||
return addRootIfNeeded(jcBlock/*root*/,
|
||||
getExpressionFromStatements(statements));
|
||||
|
||||
} else if (statement instanceof JCClassDecl) {
|
||||
return getExpressionFromClass((JCClassDecl) statement);
|
||||
|
||||
} else if (statement instanceof JCDoWhileLoop) {
|
||||
JCDoWhileLoop jcDoWhileLoop = (JCDoWhileLoop) statement;
|
||||
JCExpression cond = jcDoWhileLoop.cond;
|
||||
if (isCursorInsideTree(cond)) {
|
||||
return resolveExactlyExpression(statement, cond);
|
||||
}
|
||||
|
||||
JCStatement body = jcDoWhileLoop.getStatement();
|
||||
result = getExpressionFromStatement(body);
|
||||
return addRootIfNeeded(statement, result);
|
||||
|
||||
} else if (statement instanceof JCEnhancedForLoop) {
|
||||
JCEnhancedForLoop jcEnhancedForLoop = (JCEnhancedForLoop) statement;
|
||||
JCExpression expression = jcEnhancedForLoop.getExpression();
|
||||
if (isCursorInsideTree(expression)) {
|
||||
return resolveExactlyExpression(jcEnhancedForLoop, expression);
|
||||
}
|
||||
|
||||
JCVariableDecl variable = jcEnhancedForLoop.getVariable();
|
||||
return addRootIfNeeded(
|
||||
jcEnhancedForLoop/*root*/,
|
||||
getExpressionFromStatement(variable));
|
||||
|
||||
} else if (statement instanceof JCExpressionStatement) {
|
||||
JCExpression expression = ((JCExpressionStatement) statement).getExpression();
|
||||
if (isCursorInsideTree(expression)) {
|
||||
return resolveExactlyExpression(statement, expression);
|
||||
}
|
||||
|
||||
} else if (statement instanceof JCForLoop) {
|
||||
JCForLoop jcForLoop = (JCForLoop) statement;
|
||||
List<JCStatement> initializer = jcForLoop.getInitializer();
|
||||
result = getExpressionFromStatements(initializer);
|
||||
if (result != null) {
|
||||
return addRootIfNeeded(jcForLoop, result);
|
||||
}
|
||||
|
||||
JCExpression condition = jcForLoop.getCondition();
|
||||
if (isCursorInsideTree(condition)) {
|
||||
return resolveExactlyExpression(jcForLoop, condition);
|
||||
}
|
||||
|
||||
List<JCExpressionStatement> update = jcForLoop.getUpdate();
|
||||
result = getExpressionFromStatements(update);
|
||||
if (result != null) {
|
||||
return addRootIfNeeded(jcForLoop, result);
|
||||
}
|
||||
|
||||
result = getExpressionFromStatement(jcForLoop.getStatement());
|
||||
if (result != null) {
|
||||
return addRootIfNeeded(jcForLoop, result);
|
||||
}
|
||||
} else if (statement instanceof JCIf) {
|
||||
JCIf jcIf = (JCIf) statement;
|
||||
JCExpression condition = jcIf.getCondition();
|
||||
if (isCursorInsideTree(condition)) {
|
||||
return resolveExactlyExpression(jcIf, condition);
|
||||
}
|
||||
|
||||
JCStatement thenStatement = jcIf.getThenStatement();
|
||||
result = getExpressionFromStatement(thenStatement);
|
||||
if (result != null) {
|
||||
return addRootIfNeeded(jcIf, result);
|
||||
}
|
||||
|
||||
JCStatement elseStatement = jcIf.getElseStatement();
|
||||
if (elseStatement != null) {
|
||||
result = getExpressionFromStatement(thenStatement);
|
||||
if (result != null) {
|
||||
return addRootIfNeeded(jcIf, result);
|
||||
}
|
||||
|
||||
}
|
||||
} else if (statement instanceof JCLabeledStatement) {
|
||||
JCLabeledStatement jcLabeledStatement = (JCLabeledStatement) statement;
|
||||
return addRootIfNeeded(jcLabeledStatement,
|
||||
getExpressionFromStatement(jcLabeledStatement.getStatement()));
|
||||
|
||||
} else if (statement instanceof JCReturn) {
|
||||
JCReturn jcReturn = (JCReturn) statement;
|
||||
JCExpression expression = jcReturn.getExpression();
|
||||
if (isCursorInsideTree(expression)) {
|
||||
return resolveExactlyExpression(jcReturn, expression);
|
||||
}
|
||||
} else if (statement instanceof JCSwitch) {
|
||||
JCSwitch jcSwitch = (JCSwitch) statement;
|
||||
JCExpression jcExpression = jcSwitch.getExpression();
|
||||
if (isCursorInsideTree(jcExpression)) {
|
||||
return resolveExactlyExpression(jcSwitch, jcExpression);
|
||||
}
|
||||
|
||||
List<JCCase> jcCases = jcSwitch.getCases();
|
||||
for (JCCase jcCase : jcCases) {
|
||||
JCExpression jcCaseExpression = jcCase.getExpression();
|
||||
if (isCursorInsideTree(jcCaseExpression)) {
|
||||
return resolveExactlyExpression(jcCase, jcCaseExpression);
|
||||
}
|
||||
List<JCStatement> statements = jcCase.getStatements();
|
||||
result = getExpressionFromStatements(statements);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
} else if (statement instanceof JCSynchronized) {
|
||||
JCSynchronized jcSynchronized = (JCSynchronized) statement;
|
||||
JCExpression jcExpression = jcSynchronized.getExpression();
|
||||
if (isCursorInsideTree(jcExpression)) {
|
||||
return resolveExactlyExpression(jcSynchronized, jcExpression);
|
||||
}
|
||||
|
||||
JCBlock block = jcSynchronized.getBlock();
|
||||
return addRootIfNeeded(
|
||||
jcSynchronized/*root*/,
|
||||
getExpressionFromStatement(block));
|
||||
|
||||
} else if (statement instanceof JCThrow) {
|
||||
JCThrow jcThrow = (JCThrow) statement;
|
||||
if (isCursorInsideTree(jcThrow.getExpression())) {
|
||||
return resolveExactlyExpression(jcThrow, jcThrow.getExpression());
|
||||
}
|
||||
|
||||
} else if (statement instanceof JCTry) {
|
||||
JCTry jcTry = (JCTry) statement;
|
||||
JCBlock block = jcTry.getBlock();
|
||||
result = getExpressionFromStatement(block);
|
||||
if (result != null) {
|
||||
return addRootIfNeeded(jcTry, result);
|
||||
}
|
||||
|
||||
List<JCCatch> catches = jcTry.getCatches();
|
||||
for (JCCatch aCatch : catches) {
|
||||
JCVariableDecl parameter = aCatch.getParameter();
|
||||
result = getExpressionFromStatement(parameter);
|
||||
if (result == null) {
|
||||
block = aCatch.getBlock();
|
||||
result = getExpressionFromStatement(block);
|
||||
}
|
||||
if (result != null) {
|
||||
result.addRoot(aCatch);
|
||||
result.addRoot(jcTry);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
block = jcTry.getFinallyBlock();
|
||||
result = getExpressionFromStatement(block);
|
||||
return addRootIfNeeded(jcTry, result);
|
||||
|
||||
} else if (statement instanceof JCVariableDecl) {
|
||||
JCVariableDecl jcVariableDecl = (JCVariableDecl) statement;
|
||||
JCExpression initializer = jcVariableDecl.getInitializer();
|
||||
if (isCursorInsideTree(initializer)) {
|
||||
return resolveExactlyExpression(jcVariableDecl, initializer);
|
||||
}
|
||||
JCExpression vartype = jcVariableDecl.vartype;
|
||||
if (isCursorInsideTree(vartype)) {
|
||||
return resolveExactlyExpression(jcVariableDecl, vartype);
|
||||
}
|
||||
} else if (statement instanceof JCWhileLoop) {
|
||||
JCWhileLoop jcWhileLoop = (JCWhileLoop) statement;
|
||||
JCExpression condition = jcWhileLoop.getCondition();
|
||||
if (isCursorInsideTree(condition)) {
|
||||
return resolveExactlyExpression(jcWhileLoop, condition);
|
||||
}
|
||||
|
||||
JCStatement body = jcWhileLoop.getStatement();
|
||||
return addRootIfNeeded(
|
||||
jcWhileLoop,
|
||||
getExpressionFromStatement(body));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Expression resolveExactlyExpression(JCTree parent, JCExpression expression) {
|
||||
if (expression instanceof JCAssign) {
|
||||
JCAssign jcAssign = (JCAssign) expression;
|
||||
JCExpression lhs = jcAssign.getVariable();
|
||||
if (isCursorInsideTree(lhs)) {
|
||||
return addRootIfNeeded(
|
||||
parent, resolveExactlyExpression(jcAssign, lhs));
|
||||
}
|
||||
JCExpression rhs = jcAssign.getExpression();
|
||||
if (isCursorInsideTree(rhs)) {
|
||||
return addRootIfNeeded(
|
||||
parent, resolveExactlyExpression(jcAssign, rhs));
|
||||
}
|
||||
} else if (expression instanceof JCAssignOp) {
|
||||
JCAssignOp jcAssign = (JCAssignOp) expression;
|
||||
JCExpression lhs = jcAssign.getVariable();
|
||||
if (isCursorInsideTree(lhs)) {
|
||||
return addRootIfNeeded(
|
||||
parent, resolveExactlyExpression(jcAssign, lhs));
|
||||
}
|
||||
JCExpression rhs = jcAssign.getExpression();
|
||||
if (isCursorInsideTree(rhs)) {
|
||||
return addRootIfNeeded(
|
||||
parent, resolveExactlyExpression(jcAssign, rhs));
|
||||
}
|
||||
} else if (expression instanceof JCBinary) {
|
||||
JCBinary jcBinary = (JCBinary) expression;
|
||||
JCExpression leftOperand = jcBinary.getLeftOperand();
|
||||
if (isCursorInsideTree(leftOperand)) {
|
||||
return addRootIfNeeded(
|
||||
parent, resolveExactlyExpression(jcBinary, leftOperand));
|
||||
}
|
||||
JCExpression rightOperand = jcBinary.getRightOperand();
|
||||
if (isCursorInsideTree(rightOperand)) {
|
||||
return addRootIfNeeded(
|
||||
parent, resolveExactlyExpression(jcBinary, rightOperand));
|
||||
}
|
||||
} else if (expression instanceof JCConditional) {
|
||||
JCConditional jcConditional = (JCConditional) expression;
|
||||
JCExpression condition = jcConditional.getCondition();
|
||||
if (isCursorInsideTree(condition)) {
|
||||
return addRootIfNeeded(
|
||||
parent, resolveExactlyExpression(jcConditional, condition));
|
||||
}
|
||||
|
||||
JCExpression trueExpression = jcConditional.getTrueExpression();
|
||||
if (isCursorInsideTree(trueExpression)) {
|
||||
return addRootIfNeeded(
|
||||
parent, resolveExactlyExpression(jcConditional, trueExpression));
|
||||
}
|
||||
|
||||
JCExpression falseExpression = jcConditional.getFalseExpression();
|
||||
if (isCursorInsideTree(falseExpression)) {
|
||||
return addRootIfNeeded(
|
||||
parent, resolveExactlyExpression(jcConditional, falseExpression));
|
||||
}
|
||||
|
||||
} else if (expression instanceof JCInstanceOf) {
|
||||
JCInstanceOf jcInstanceOf = (JCInstanceOf) expression;
|
||||
JCExpression lhs = jcInstanceOf.getExpression();
|
||||
if (isCursorInsideTree(lhs)) {
|
||||
return addRootIfNeeded(
|
||||
parent, resolveExactlyExpression(jcInstanceOf, lhs));
|
||||
}
|
||||
} else if (expression instanceof JCUnary) {
|
||||
JCUnary jcUnary = (JCUnary) expression;
|
||||
JCExpression jcExpression = jcUnary.getExpression();
|
||||
if (isCursorInsideTree(jcExpression)) {
|
||||
return addRootIfNeeded(
|
||||
parent, resolveExactlyExpression(jcUnary, jcExpression));
|
||||
}
|
||||
}
|
||||
|
||||
return new Expression(parent, expression);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Expression getExpressionFromClass(JCClassDecl tree) {
|
||||
|
||||
if (!isCursorInsideTree(tree)) {
|
||||
return null;
|
||||
}
|
||||
Expression expression = null;
|
||||
List<JCTree> members = tree.getMembers();
|
||||
for (JCTree member : members) {
|
||||
if (member instanceof JCMethodDecl) {
|
||||
expression = getExpressionFromMethod((JCMethodDecl) member);
|
||||
|
||||
} else if (member instanceof JCVariableDecl) {
|
||||
expression = getExpressionFromStatement((JCVariableDecl) member);
|
||||
|
||||
} else if (member instanceof JCClassDecl) {
|
||||
|
||||
expression = getExpressionFromClass((JCClassDecl) member);
|
||||
}
|
||||
|
||||
if (expression != null) {
|
||||
return addRootIfNeeded(tree, expression);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Expression getExpressionFromMethod(JCMethodDecl method) {
|
||||
|
||||
Expression expression;
|
||||
List<JCVariableDecl> parameters = method.getParameters();
|
||||
|
||||
for (JCVariableDecl parameter : parameters) {
|
||||
expression = getExpressionFromStatement(parameter);
|
||||
if (expression != null) {
|
||||
expression.addRoot(method);
|
||||
return expression;
|
||||
}
|
||||
}
|
||||
|
||||
expression = getExpressionFromStatement(method.getBody());
|
||||
|
||||
return addRootIfNeeded(method, expression);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Expression getExpressionFromStatements(
|
||||
@NonNull List<? extends JCStatement> statements) {
|
||||
for (JCStatement statement : statements) {
|
||||
Expression expression = getExpressionFromStatement(statement);
|
||||
if (expression != null) {
|
||||
return expression;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Expression addRootIfNeeded(JCTree root, Expression expression) {
|
||||
if (expression != null) {
|
||||
expression.addRoot(root);
|
||||
return expression;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isCursorInsideTree(JCTree tree) {
|
||||
if (tree == null) {
|
||||
return false;
|
||||
}
|
||||
int startPosition = tree.getStartPosition();
|
||||
int endPosition = tree.getEndPosition(mEndPositions);
|
||||
return startPosition <= mCursor && mCursor <= endPosition;
|
||||
}
|
||||
}
|
||||
@@ -1,283 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.itsaky.androidide.javadoc.JavaDoc;
|
||||
import com.itsaky.androidide.javadoc.JavaDocParser;
|
||||
import com.itsaky.androidide.javadoc.JavaDocParserBuilder;
|
||||
import com.itsaky.androidide.javadoc.OutputType;
|
||||
import com.itsaky.androidide.language.java.parser.internal.IClass;
|
||||
import com.itsaky.androidide.language.java.parser.internal.IJavaDocCommentable;
|
||||
import com.itsaky.androidide.language.java.parser.internal.JavaClassManager;
|
||||
import com.itsaky.androidide.language.java.parser.internal.JavaUtil;
|
||||
import com.itsaky.androidide.language.java.parser.model.ClassDescription;
|
||||
import com.itsaky.androidide.language.java.parser.model.ConstructorDescription;
|
||||
import com.itsaky.androidide.language.java.parser.model.FieldDescription;
|
||||
import com.itsaky.androidide.language.java.parser.model.MethodDescription;
|
||||
import com.sun.source.tree.Tree;
|
||||
import com.sun.tools.javac.file.JavacFileManager;
|
||||
import com.sun.tools.javac.parser.Parser;
|
||||
import com.sun.tools.javac.parser.ParserFactory;
|
||||
import com.sun.tools.javac.parser.Tokens;
|
||||
import com.sun.tools.javac.tree.JCTree;
|
||||
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
|
||||
import com.sun.tools.javac.util.Context;
|
||||
import com.sun.tools.javac.util.Log;
|
||||
import com.sun.tools.javac.util.Options;
|
||||
import io.github.rosemoe.editor.text.Content;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import javax.tools.Diagnostic;
|
||||
import javax.tools.DiagnosticListener;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.SimpleJavaFileObject;
|
||||
import javax.tools.StandardLocation;
|
||||
|
||||
import static com.sun.tools.javac.tree.JCTree.*;
|
||||
|
||||
public class JavacParser {
|
||||
|
||||
private Context context;
|
||||
private ParserFactory parserFactory;
|
||||
private JavaDocParser javadocParser;
|
||||
private DiagnosticCollector<JavaFileObject> diagnostics;
|
||||
private boolean canParse = true;
|
||||
|
||||
private static final String DOT = ".";
|
||||
private static final String CONSTRUCTOR_NAME = "<init>";
|
||||
|
||||
public JavacParser() {
|
||||
context = new Context();
|
||||
diagnostics = new DiagnosticCollector<JavaFileObject>();
|
||||
context.put(DiagnosticListener.class, diagnostics);
|
||||
Options.instance(context).put("allowStringFolding", "false");
|
||||
JavacFileManager fileManager = new JavacFileManager(context, true, Charsets.UTF_8);
|
||||
try {
|
||||
fileManager.setLocation(StandardLocation.PLATFORM_CLASS_PATH, ImmutableList.<File>of());
|
||||
} catch (IOException e) {
|
||||
canParse = false;
|
||||
}
|
||||
parserFactory = ParserFactory.instance(context);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public JCCompilationUnit parse(Content src) {
|
||||
return parse(src.toString());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public JCCompilationUnit parse(final String src) {
|
||||
if (!canParse) return null;
|
||||
SimpleJavaFileObject source = new SimpleJavaFileObject(URI.create("source"), JavaFileObject.Kind.SOURCE) {
|
||||
@Override
|
||||
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
|
||||
return src;
|
||||
}
|
||||
};
|
||||
Log.instance(context).useSource(source);
|
||||
diagnostics.clearDiagnostics();
|
||||
Parser parser =
|
||||
parserFactory.newParser(src,
|
||||
/*keepDocComments=*/ true, // Necessary, because we show doc comments in AndroidIDE
|
||||
/*keepEndPos=*/ true,
|
||||
/*keepLineMap=*/ true);
|
||||
JCCompilationUnit unit;
|
||||
unit = parser.parseCompilationUnit();
|
||||
unit.sourcefile = source;
|
||||
return unit;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public List<Diagnostic<? extends JavaFileObject>> getDiagnostics() {
|
||||
if (!canParse) return null;
|
||||
return diagnostics.getDiagnostics();
|
||||
}
|
||||
|
||||
public List<IClass> parseClasses(JCCompilationUnit unit) {
|
||||
List<IClass> classes = new ArrayList<>();
|
||||
List<JCTree> typeDecls = unit.getTypeDecls();
|
||||
for (JCTree typeDecl : typeDecls) {
|
||||
if (typeDecl instanceof JCClassDecl) {
|
||||
classes.add(parseClass(unit, (JCClassDecl) typeDecl));
|
||||
}
|
||||
}
|
||||
return classes;
|
||||
}
|
||||
|
||||
private IClass parseClass(JCCompilationUnit unit, JCClassDecl classDecl) {
|
||||
final String className = unit.getPackageName() + DOT + classDecl.getSimpleName();
|
||||
final int modifiers = JavaUtil.toJavaModifiers(classDecl.getModifiers().getFlags());
|
||||
|
||||
ClassDescription clazz = new ClassDescription(
|
||||
className,
|
||||
modifiers,
|
||||
false,
|
||||
classDecl.getKind() == Tree.Kind.ANNOTATION_TYPE,
|
||||
classDecl.getKind() == Tree.Kind.ENUM,
|
||||
false);
|
||||
|
||||
IClass extendsClass = JavaUtil.jcTypeToClass(unit, classDecl.getExtendsClause());
|
||||
if (extendsClass != null) {
|
||||
clazz.setSuperclass(extendsClass);
|
||||
} else {
|
||||
clazz.setSuperclass(JavaClassManager.getInstance().getParsedClass(Object.class.getName()));
|
||||
}
|
||||
|
||||
List<JCTree> members = classDecl.getMembers();
|
||||
for (JCTree member : members) {
|
||||
if (member instanceof JCTree.JCMethodDecl) {
|
||||
addMethod(unit, clazz, (JCTree.JCMethodDecl) member);
|
||||
} else if (member instanceof JCTree.JCVariableDecl) {
|
||||
addVariable(unit, clazz, classDecl, (JCTree.JCVariableDecl) member);
|
||||
} else if(member instanceof JCTree.JCClassDecl) {
|
||||
addNestedClass(unit, classDecl, (JCTree.JCClassDecl) member);
|
||||
}
|
||||
}
|
||||
if(clazz != null && unit.docComments != null && unit.docComments.hasComment(classDecl)) {
|
||||
Tokens.Comment comment = unit.docComments.getComment(classDecl);
|
||||
if(comment.getStyle() == Tokens.Comment.CommentStyle.JAVADOC) {
|
||||
processJavaDoc(comment, clazz);
|
||||
}
|
||||
}
|
||||
//now add constructor
|
||||
//add methods
|
||||
return clazz;
|
||||
}
|
||||
|
||||
private void addNestedClass(@NonNull JCCompilationUnit ast, JCClassDecl parent, JCClassDecl classDecl) {
|
||||
final String className = ast.getPackageName() + DOT + parent.getSimpleName() + "$" + classDecl.getSimpleName();
|
||||
final int modifiers = JavaUtil.toJavaModifiers(classDecl.getModifiers().getFlags());
|
||||
ClassDescription clazz = new ClassDescription(
|
||||
className,
|
||||
modifiers,
|
||||
false,
|
||||
classDecl.getKind() == Tree.Kind.ANNOTATION_TYPE,
|
||||
classDecl.getKind() == Tree.Kind.ENUM,
|
||||
false);
|
||||
IClass extendsClass = JavaUtil.jcTypeToClass(ast, classDecl.getExtendsClause());
|
||||
if (extendsClass != null) {
|
||||
clazz.setSuperclass(extendsClass);
|
||||
} else {
|
||||
clazz.setSuperclass(JavaClassManager.getInstance().getParsedClass(Object.class.getName()));
|
||||
}
|
||||
|
||||
|
||||
List<JCTree> members = classDecl.getMembers();
|
||||
for (JCTree member : members) {
|
||||
if (member instanceof JCTree.JCMethodDecl) {
|
||||
addMethod(ast, clazz, (JCTree.JCMethodDecl) member);
|
||||
} else if (member instanceof JCTree.JCVariableDecl) {
|
||||
addVariable(ast, clazz, classDecl, (JCTree.JCVariableDecl) member);
|
||||
} else if(member instanceof JCTree.JCClassDecl) {
|
||||
addNestedClass(ast, classDecl, (JCTree.JCClassDecl) member);
|
||||
}
|
||||
}
|
||||
|
||||
if(clazz != null && ast.docComments != null && ast.docComments.hasComment(classDecl)) {
|
||||
Tokens.Comment comment = ast.docComments.getComment(classDecl);
|
||||
if(comment.getStyle() == Tokens.Comment.CommentStyle.JAVADOC) {
|
||||
processJavaDoc(comment, clazz);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addVariable(@NonNull JCCompilationUnit unit,
|
||||
ClassDescription clazz, @NonNull JCClassDecl classDecl,
|
||||
@NonNull JCTree.JCVariableDecl member) {
|
||||
String name = member.getName().toString();
|
||||
int modifiers = JavaUtil.toJavaModifiers(member.getModifiers().getFlags());
|
||||
IClass type = JavaUtil.jcTypeToClass(unit, member.getType());
|
||||
JCTree.JCExpression initializer = member.getInitializer();
|
||||
FieldDescription fieldDescription = new FieldDescription(
|
||||
modifiers,
|
||||
type,
|
||||
name,
|
||||
initializer != null ? initializer.toString() : null);
|
||||
clazz.addField(fieldDescription);
|
||||
if(fieldDescription != null && unit.docComments != null && unit.docComments.hasComment(member)) {
|
||||
Tokens.Comment comment = unit.docComments.getComment(member);
|
||||
if(comment.getStyle() == Tokens.Comment.CommentStyle.JAVADOC) {
|
||||
processJavaDoc(comment, fieldDescription);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addMethod(@NonNull JCCompilationUnit unit,
|
||||
@NonNull ClassDescription clazz,
|
||||
@NonNull JCTree.JCMethodDecl member) {
|
||||
|
||||
|
||||
final String methodName = member.getName().toString();
|
||||
final int modifiers = JavaUtil.toJavaModifiers(member.getModifiers().getFlags());
|
||||
final List<IClass> methodParameters = new ArrayList<>();
|
||||
List<JCTree.JCVariableDecl> parameters = member.getParameters();
|
||||
for (JCTree.JCVariableDecl parameter : parameters) {
|
||||
JCTree type = parameter.getType();
|
||||
IClass paramType = JavaUtil.jcTypeToClass(unit, type);
|
||||
methodParameters.add(paramType);
|
||||
}
|
||||
|
||||
IClass returnType = JavaUtil.jcTypeToClass(unit, member.getReturnType());
|
||||
IJavaDocCommentable commetable = null;
|
||||
if (member.getName().toString().equals(CONSTRUCTOR_NAME)) {
|
||||
ConstructorDescription constructor = new ConstructorDescription(
|
||||
clazz.getFullClassName(),
|
||||
methodParameters);
|
||||
clazz.addConstructor(constructor);
|
||||
commetable = constructor;
|
||||
} else {
|
||||
MethodDescription methodDescription = new MethodDescription(
|
||||
methodName,
|
||||
modifiers,
|
||||
methodParameters,
|
||||
returnType);
|
||||
clazz.addMethod(methodDescription);
|
||||
commetable = methodDescription;
|
||||
}
|
||||
|
||||
if(commetable != null && unit.docComments != null && unit.docComments.hasComment(member)) {
|
||||
Tokens.Comment comment = unit.docComments.getComment(member);
|
||||
if(comment.getStyle() == Tokens.Comment.CommentStyle.JAVADOC) {
|
||||
processJavaDoc(comment, commetable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processJavaDoc(Tokens.Comment comment, IJavaDocCommentable commetable) {
|
||||
if(javadocParser == null) {
|
||||
javadocParser = JavaDocParserBuilder.withBasicTags()
|
||||
.withOutputType(OutputType.HTML)
|
||||
.build();
|
||||
}
|
||||
final JavaDoc doc = javadocParser.parse(comment.getText());
|
||||
final String html = doc.getSummary() + "\n" + doc.getDescription();
|
||||
commetable.setHtmlJavaDoc(html);
|
||||
}
|
||||
|
||||
|
||||
private void addConstructor(ClassDescription clazz, JCTree.JCMethodDecl member) {
|
||||
ConstructorDescription constructorDescription;
|
||||
}
|
||||
|
||||
private JCClassDecl resolveClassSimpleName(List<JCTree> typeDecls) {
|
||||
for (JCTree typeDecl : typeDecls) {
|
||||
if (typeDecl instanceof JCClassDecl) {
|
||||
JCModifiers modifiers = ((JCClassDecl) typeDecl).getModifiers();
|
||||
Set<Modifier> flags = modifiers.getFlags();
|
||||
for (Modifier flag : flags) {
|
||||
if (flag.equals(Modifier.PUBLIC)) {
|
||||
return (JCClassDecl) typeDecl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
-151
@@ -1,151 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.expression;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import com.itsaky.androidide.language.java.parser.Expression;
|
||||
import com.itsaky.androidide.language.java.parser.internal.IClass;
|
||||
import com.itsaky.androidide.language.java.parser.internal.IField;
|
||||
import com.itsaky.androidide.language.java.parser.internal.IMethod;
|
||||
import com.itsaky.androidide.language.java.parser.internal.JavaClassManager;
|
||||
import com.itsaky.androidide.language.java.parser.internal.JavaDexClassLoader;
|
||||
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
|
||||
import com.sun.tools.javac.tree.EndPosTable;
|
||||
import com.sun.tools.javac.tree.JCTree;
|
||||
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
|
||||
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import static com.sun.tools.javac.tree.JCTree.JCErroneous;
|
||||
import static com.sun.tools.javac.tree.JCTree.JCExpression;
|
||||
|
||||
/**
|
||||
* This class very complex, implement after
|
||||
*/
|
||||
public class CompleteExpression extends JavaCompleteMatcherImpl {
|
||||
private static final String TAG = "CompleteExpression";
|
||||
private final JavaDexClassLoader mClassLoader;
|
||||
private EndPosTable mEndPositions;
|
||||
|
||||
private JCCompilationUnit mAst;
|
||||
private IClass mCurrentType;
|
||||
|
||||
private int mCursor;
|
||||
private CodeEditor mEditor;
|
||||
|
||||
public CompleteExpression(@NonNull JavaDexClassLoader classLoader) {
|
||||
mClassLoader = classLoader;
|
||||
}
|
||||
|
||||
public void prepare(CodeEditor editor, JCCompilationUnit ast) {
|
||||
mEditor = editor;
|
||||
mCursor = editor.getCursor().getLeft();
|
||||
mAst = ast;
|
||||
mEndPositions = ast.endPositions;
|
||||
|
||||
List<JCTree> typeDecls = mAst.getTypeDecls();
|
||||
for (JCTree typeDecl : typeDecls) {
|
||||
if (typeDecl instanceof JCTree.JCClassDecl) {
|
||||
int startPosition = typeDecl.getStartPosition();
|
||||
int endPosition = typeDecl.getEndPosition(mEndPositions);
|
||||
if (startPosition <= mCursor && mCursor <= endPosition) {
|
||||
String simpleName = ((JCTree.JCClassDecl) typeDecl).getSimpleName().toString();
|
||||
JCExpression packageName = ast.getPackageName();
|
||||
mCurrentType = JavaClassManager.getInstance()
|
||||
.getParsedClass(packageName + "." + simpleName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getSuggestion(CodeEditor editor, String incomplete, List<SuggestItem> suggestItems) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(JCCompilationUnit ast, CodeEditor editor, Expression expression,
|
||||
String unused, ArrayList<SuggestItem> result) {
|
||||
try {
|
||||
prepare(editor, ast);
|
||||
return expression != null && performComplete(expression.getExpression(), result);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean performComplete(JCExpression jcExpression, ArrayList<SuggestItem> result) {
|
||||
if (jcExpression instanceof JCErroneous) {
|
||||
List<? extends JCTree> errorTrees = ((JCErroneous) jcExpression).getErrorTrees();
|
||||
JCTree errExpr = errorTrees.get(0);
|
||||
if (errExpr instanceof JCExpression) {
|
||||
return performComplete((JCExpression) errExpr, result);
|
||||
}
|
||||
|
||||
} else if (jcExpression instanceof JCFieldAccess) {
|
||||
return performCompleteFieldAccess((JCFieldAccess) jcExpression, result);
|
||||
|
||||
} else if (jcExpression instanceof JCTree.JCIdent) {
|
||||
return performCompleteIdent((JCTree.JCIdent) jcExpression, result);
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Suggestion method, variable
|
||||
*/
|
||||
private boolean performCompleteIdent(JCTree.JCIdent jcIdent, ArrayList<SuggestItem> result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean performCompleteFieldAccess(@NonNull JCFieldAccess jcFieldAccess,
|
||||
@NonNull ArrayList<SuggestItem> result) {
|
||||
int startPosition = jcFieldAccess.getStartPosition();
|
||||
int cursorOffset = mCursor - startPosition;
|
||||
int incompleteLength = jcFieldAccess.getIdentifier().length();
|
||||
String incomplete = jcFieldAccess.getIdentifier().toString();
|
||||
//simple hack, improve later
|
||||
if (incomplete.equalsIgnoreCase("<error>")) {
|
||||
incomplete = "";
|
||||
}
|
||||
|
||||
JCExpression expression = jcFieldAccess.getExpression();
|
||||
TypeResolver resolver = new TypeResolver(mClassLoader, mAst, mCurrentType);
|
||||
IClass type = resolver.resolveType(expression, mCursor);
|
||||
if (type != null) {
|
||||
|
||||
List<IMethod> methods = type.getMethods();
|
||||
for (IMethod method : methods) {
|
||||
if(Modifier.isPublic(method.getModifiers())) {
|
||||
if(resolver.isStatic() && !Modifier.isStatic(method.getModifiers()))
|
||||
continue;
|
||||
if (method.getMethodName().toLowerCase(Locale.US).startsWith(incomplete.toLowerCase(Locale.US))) {
|
||||
setInfo(method, mEditor, incomplete);
|
||||
result.add(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<IField> fields = type.getFields();
|
||||
for (IField field : fields) {
|
||||
if(Modifier.isPublic(field.getFieldModifiers())) {
|
||||
if(resolver.isStatic() && !Modifier.isStatic(field.getFieldModifiers()))
|
||||
continue;
|
||||
if (field.getFieldName().toLowerCase(Locale.US).startsWith(incomplete.toLowerCase(Locale.US))) {
|
||||
setInfo(field, mEditor, incomplete);
|
||||
result.add(field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
-81
@@ -1,81 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.expression;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import com.itsaky.androidide.language.java.parser.Expression;
|
||||
import com.itsaky.androidide.language.java.parser.internal.IClass;
|
||||
import com.itsaky.androidide.language.java.parser.internal.JavaDexClassLoader;
|
||||
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
|
||||
import com.itsaky.androidide.language.java.parser.model.ConstructorDescription;
|
||||
import com.itsaky.androidide.language.java.parser.model.PrimitiveArrayConstructorDescription;
|
||||
import com.sun.tools.javac.tree.JCTree;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Suggest constructor
|
||||
*/
|
||||
public class CompleteNewKeyword extends JavaCompleteMatcherImpl {
|
||||
private static final Pattern NEW_CLASS = Pattern.compile(
|
||||
"(\\s*new\\s+)(" + CONSTRUCTOR.pattern() + ")$", Pattern.MULTILINE);
|
||||
private static final String[] PRIMITIVE_ARRAY_TYPE =
|
||||
{"boolean", "byte", "double", "char", "float", "int", "long", "short"};
|
||||
|
||||
private final JavaDexClassLoader mClassLoader;
|
||||
|
||||
public CompleteNewKeyword(JavaDexClassLoader classLoader) {
|
||||
mClassLoader = classLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(JCTree.JCCompilationUnit ast,
|
||||
CodeEditor editor, Expression expression,
|
||||
String statement, ArrayList<SuggestItem> result) {
|
||||
Matcher matcher = NEW_CLASS.matcher(statement);
|
||||
if (matcher.find()) {
|
||||
String incompleteCts = matcher.group(2);
|
||||
return getSuggestionInternal(editor, incompleteCts, result);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getSuggestion(@NonNull CodeEditor editor,
|
||||
@NonNull String incomplete,
|
||||
@NonNull List<SuggestItem> suggestItems) {
|
||||
getSuggestionInternal(editor, incomplete, suggestItems);
|
||||
}
|
||||
|
||||
private boolean getSuggestionInternal(@NonNull CodeEditor editor, @NonNull String incomplete,
|
||||
@NonNull List<SuggestItem> suggestItems) {
|
||||
if (incomplete.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean handled = false;
|
||||
//try to find constructor
|
||||
List<IClass> classes = mClassLoader.findAllWithPrefix(incomplete);
|
||||
for (IClass clazz : classes) {
|
||||
List<ConstructorDescription> constructors = clazz.getConstructors();
|
||||
setInfo(constructors, editor, incomplete);
|
||||
suggestItems.addAll(constructors);
|
||||
handled = true;
|
||||
}
|
||||
for (String type : PRIMITIVE_ARRAY_TYPE) {
|
||||
if (type.toLowerCase(Locale.US).startsWith(incomplete.toLowerCase(Locale.US))) {
|
||||
PrimitiveArrayConstructorDescription c
|
||||
= new PrimitiveArrayConstructorDescription(type);
|
||||
setInfo(c, editor, incomplete);
|
||||
suggestItems.add(c);
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
-93
@@ -1,93 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.expression;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import com.itsaky.androidide.language.java.parser.Expression;
|
||||
import com.itsaky.androidide.language.java.parser.internal.PackageManager;
|
||||
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
|
||||
import com.itsaky.androidide.language.java.parser.model.PackageDescription;
|
||||
import com.sun.tools.javac.tree.JCTree;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Complete package
|
||||
* e.g.
|
||||
* <p>
|
||||
* java.i|
|
||||
* java.util.|
|
||||
* java.util.Hash|
|
||||
*/
|
||||
public class CompletePackage extends JavaCompleteMatcherImpl {
|
||||
private static final String TAG = "CompletePackage";
|
||||
|
||||
private static final Pattern PACKAGE
|
||||
= Pattern.compile("^\\s*(package)\\s+([_A-Za-z0-9.]+)");
|
||||
private static final Pattern IMPORT_OR_IMPORT_STATIC
|
||||
= Pattern.compile("^\\s*import(\\s+static)?\\s+([_A-Za-z0-9.]+)");
|
||||
|
||||
@NonNull
|
||||
private final PackageManager mPackageManager;
|
||||
|
||||
public CompletePackage(@NonNull PackageManager packageManager) {
|
||||
mPackageManager = packageManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(JCTree.JCCompilationUnit ast, CodeEditor editor, Expression expression, String statement, ArrayList<SuggestItem> result) {
|
||||
Matcher matcher = PACKAGE.matcher(statement);
|
||||
if (matcher.find()) {
|
||||
String incompletePkg = matcher.group(2);
|
||||
getSuggestion(editor, incompletePkg, result);
|
||||
return true;
|
||||
}
|
||||
|
||||
matcher = IMPORT_OR_IMPORT_STATIC.matcher(statement);
|
||||
if (matcher.find()) {
|
||||
boolean isStatic = matcher.group(1) != null;
|
||||
String incompletePkg = matcher.group(2);
|
||||
getSuggestion(editor, incompletePkg, result);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getSuggestion(CodeEditor editor, String expr, List<SuggestItem> suggestItems) {
|
||||
getSuggestionInternal(editor, expr, suggestItems, false);
|
||||
}
|
||||
|
||||
private void getSuggestionInternal(CodeEditor editor, String expr, List<SuggestItem> suggestItems,
|
||||
boolean addSemicolon) {
|
||||
|
||||
//package must be contains dot (.)
|
||||
if (!expr.contains(".")) {
|
||||
return;
|
||||
}
|
||||
|
||||
//completed package java.lang
|
||||
int lastDotIndex = expr.lastIndexOf(".");
|
||||
String completedPart = expr.substring(0, lastDotIndex /*not contains dot*/);
|
||||
String incompletePart = expr.substring(lastDotIndex + 1);
|
||||
|
||||
PackageDescription packages = mPackageManager.trace(completedPart);
|
||||
if (packages != null) {
|
||||
//members of current package
|
||||
//such as java has more member (util, io, lang)
|
||||
HashMap<String, PackageDescription> members = packages.getChild();
|
||||
for (Map.Entry<String, PackageDescription> entry : members.entrySet()) {
|
||||
PackageDescription packageDescription = entry.getValue();
|
||||
if (packageDescription.getName().toLowerCase(Locale.US).startsWith(incompletePart.toLowerCase(Locale.US))) {
|
||||
setInfo(packageDescription, editor, incompletePart);
|
||||
suggestItems.add(packageDescription);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.expression;
|
||||
|
||||
import com.itsaky.androidide.language.java.parser.Expression;
|
||||
import com.itsaky.androidide.language.java.parser.internal.IClass;
|
||||
import com.itsaky.androidide.language.java.parser.internal.IMethod;
|
||||
import com.itsaky.androidide.language.java.parser.internal.JavaClassManager;
|
||||
import com.itsaky.androidide.language.java.parser.internal.JavaDexClassLoader;
|
||||
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
|
||||
import com.sun.tools.javac.tree.JCTree;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.Locale;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
public class CompleteString extends JavaCompleteMatcherImpl {
|
||||
public static final Pattern STRING_DOT =
|
||||
Pattern.compile("\"\\s*\\.\\s*$", Pattern.MULTILINE);
|
||||
public static final Pattern STRING_DOT_EXPR
|
||||
= Pattern.compile("\"\\s*\\.\\s*(" + METHOD_NAME.pattern() + ")$");
|
||||
private static final String TAG = "CompleteString";
|
||||
private final JavaDexClassLoader mClassLoader;
|
||||
|
||||
public CompleteString(JavaDexClassLoader classLoader) {
|
||||
mClassLoader = classLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(JCTree.JCCompilationUnit ast, CodeEditor editor, Expression expression, String statement, ArrayList<SuggestItem> suggestItems) {
|
||||
Matcher matcher = STRING_DOT.matcher(statement);
|
||||
if (matcher.find()) {
|
||||
getSuggestion(editor, "", suggestItems);
|
||||
return true;
|
||||
}
|
||||
matcher = STRING_DOT_EXPR.matcher(statement);
|
||||
if (matcher.find()) {
|
||||
String incompleteMethod = matcher.group(1);
|
||||
getSuggestion(editor, incompleteMethod, suggestItems);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete string only filter method, not filter field or constructor
|
||||
*/
|
||||
@Override
|
||||
public void getSuggestion(CodeEditor editor, String incomplete, List<SuggestItem> suggestItems) {
|
||||
JavaClassManager reader = mClassLoader.getClassReader();
|
||||
IClass stringClass = reader.getParsedClass(String.class.getName());
|
||||
assert stringClass != null;
|
||||
ArrayList<SuggestItem> methods = new ArrayList<>();
|
||||
for (IMethod method : stringClass.getMethods()) {
|
||||
if(!Modifier.isPublic(method.getModifiers()))
|
||||
continue;
|
||||
if (method.getMethodName().toLowerCase(Locale.US).startsWith(incomplete.toLowerCase(Locale.US))) {
|
||||
suggestItems.add(method);
|
||||
}
|
||||
}
|
||||
setInfo(methods, editor, incomplete);
|
||||
suggestItems.addAll(methods);
|
||||
}
|
||||
}
|
||||
-124
@@ -1,124 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.expression;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import com.itsaky.androidide.language.java.parser.Expression;
|
||||
import com.itsaky.androidide.language.java.parser.internal.IClass;
|
||||
import com.itsaky.androidide.language.java.parser.internal.JavaDexClassLoader;
|
||||
import com.itsaky.androidide.language.java.parser.internal.JavaUtil;
|
||||
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
|
||||
import com.itsaky.androidide.language.java.parser.model.FieldDescription;
|
||||
import com.itsaky.androidide.language.java.parser.model.MethodDescription;
|
||||
import com.sun.tools.javac.tree.JCTree;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.sun.tools.javac.tree.JCTree.JCClassDecl;
|
||||
import static com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
|
||||
import static com.sun.tools.javac.tree.JCTree.JCMethodDecl;
|
||||
import static com.sun.tools.javac.tree.JCTree.JCTypeParameter;
|
||||
import static com.sun.tools.javac.tree.JCTree.JCVariableDecl;
|
||||
|
||||
public class CompleteThisKeyword extends JavaCompleteMatcherImpl {
|
||||
private static final Pattern THIS_DOT = Pattern.compile("(\\W)this\\s*\\.\\s*$");
|
||||
private static final Pattern THIS_DOT_EXPR = Pattern.compile("\\Wthis\\s*\\.\\s*(" + Patterns.IDENTIFIER.pattern() + ")$");
|
||||
private static final String TAG = "CompleteThisKeyword";
|
||||
private static JavaDexClassLoader mClassLoader;
|
||||
|
||||
public CompleteThisKeyword(JavaDexClassLoader classLoader) {
|
||||
this.mClassLoader = classLoader;
|
||||
}
|
||||
|
||||
protected static void addMethod(JCCompilationUnit unit, JCMethodDecl method, CodeEditor editor, String incomplete,
|
||||
List<SuggestItem> result) {
|
||||
if (method.getName().toString().toLowerCase(Locale.US).startsWith(incomplete.toLowerCase(Locale.US))) {
|
||||
List<JCTypeParameter> typeParameters
|
||||
= method.getTypeParameters();
|
||||
ArrayList<String> paramsStr = new ArrayList<>();
|
||||
for (JCTypeParameter typeParameter : typeParameters) {
|
||||
paramsStr.add(typeParameter.toString());
|
||||
}
|
||||
IClass returnType = null;
|
||||
String rName = JavaUtil.findImportedClassName(unit, method.getReturnType().toString());
|
||||
if(rName != null && rName.trim().length() > 0) {
|
||||
returnType = mClassLoader.getClassReader().getParsedClass(rName);
|
||||
}
|
||||
MethodDescription desc = new MethodDescription(
|
||||
method.getName().toString(),
|
||||
returnType,
|
||||
(int) method.getModifiers().flags,
|
||||
paramsStr);
|
||||
setInfo(desc, editor, incomplete);
|
||||
result.add(desc);
|
||||
}
|
||||
}
|
||||
|
||||
protected static void addVariable(JCCompilationUnit unit, JCVariableDecl member, CodeEditor editor, String incomplete,
|
||||
List<SuggestItem> result) {
|
||||
if (member.getName().toString().toLowerCase(Locale.US).startsWith(incomplete.toLowerCase(Locale.US))) {
|
||||
IClass returnType = null;
|
||||
String rName = JavaUtil.findImportedClassName(unit, member.getType().toString());
|
||||
if(rName != null && rName.trim().length() > 0) {
|
||||
returnType = mClassLoader.getClassReader().getParsedClass(rName);
|
||||
}
|
||||
int flags = (int) member.getModifiers().flags;
|
||||
FieldDescription desc = new FieldDescription(
|
||||
flags,
|
||||
returnType,
|
||||
member.getName().toString(),
|
||||
null);
|
||||
setInfo(desc, editor, incomplete);
|
||||
result.add(desc);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(JCCompilationUnit ast, CodeEditor editor, Expression expression, String statement, ArrayList<SuggestItem> result) {
|
||||
Matcher matcher = THIS_DOT.matcher(statement);
|
||||
if (matcher.find()) {
|
||||
getSuggestionInternal(editor, result, ast, "");
|
||||
return true;
|
||||
}
|
||||
matcher = THIS_DOT_EXPR.matcher(statement);
|
||||
if (matcher.find()) {
|
||||
String incomplete = matcher.group(1);
|
||||
getSuggestionInternal(editor, result, ast, incomplete);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getSuggestion(CodeEditor editor, String incomplete, List<SuggestItem> suggestItems) {
|
||||
|
||||
}
|
||||
|
||||
private void getSuggestionInternal(@NonNull CodeEditor editor, @NonNull ArrayList<SuggestItem> result,
|
||||
@NonNull JCCompilationUnit unit,
|
||||
@NonNull String incomplete) {
|
||||
if (unit == null) {
|
||||
return;
|
||||
}
|
||||
//current file declare
|
||||
List<JCTree> typeDecls = unit.getTypeDecls();
|
||||
if (typeDecls.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
JCTree jcTree = typeDecls.get(0);
|
||||
if (jcTree instanceof JCClassDecl) {
|
||||
List<JCTree> members =
|
||||
((JCClassDecl) jcTree).getMembers();
|
||||
for (JCTree member : members) {
|
||||
if (member instanceof JCVariableDecl) {
|
||||
addVariable(unit, (JCVariableDecl) member, editor, incomplete, result);
|
||||
} else if (member instanceof JCMethodDecl) {
|
||||
JCMethodDecl method = (JCMethodDecl) member;
|
||||
addMethod(unit, method, editor, incomplete, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-130
@@ -1,130 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.expression;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.itsaky.androidide.interfaces.Filter;
|
||||
import com.itsaky.androidide.language.java.parser.Expression;
|
||||
import com.itsaky.androidide.language.java.parser.internal.IClass;
|
||||
import com.itsaky.androidide.language.java.parser.internal.JavaDexClassLoader;
|
||||
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
|
||||
import com.sun.tools.javac.tree.JCTree;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Complete class declaration
|
||||
* <p>
|
||||
* public? static? final? class Name (extends otherClass)? (implements otherInterfaces)?
|
||||
* public? enum Name
|
||||
* public? final? interface Name (extends otherInterfaces)?
|
||||
*/
|
||||
public class CompleteTypeDeclared extends JavaCompleteMatcherImpl {
|
||||
//case: public class A
|
||||
//case: public class Name extends OtherClass
|
||||
//case: class Name extends C implements D
|
||||
public static final Pattern CLASS_DECLARE = Pattern.compile(
|
||||
//more modifiers, public static final ....
|
||||
"((public|protected|private|abstract|static|final|strictfp)\\s+)*" +
|
||||
//type
|
||||
"((class|inteface|enum)\\s+)" +
|
||||
//name
|
||||
"([a-zA-Z_][a-zA-Z0-9_]*)" +
|
||||
//inherit
|
||||
"(\\s+extends\\s+([a-zA-Z_][a-zA-Z0-9_]*))?" +
|
||||
"(\\s+implements\\s+([a-zA-Z_][a-zA-Z0-9_]*))?");
|
||||
private static final Pattern END_EXTENDS
|
||||
= Pattern.compile("\\s+extends\\s+([a-zA-Z_][a-zA-Z0-9_]*)$");
|
||||
private static final Pattern END_IMPLEMENTS
|
||||
= Pattern.compile("\\s+implements\\s+([a-zA-Z_][a-zA-Z0-9_]*)$");
|
||||
|
||||
private static final String TAG = "CompleteClassDeclared";
|
||||
private JavaDexClassLoader mClassLoader;
|
||||
|
||||
public CompleteTypeDeclared(JavaDexClassLoader classLoader) {
|
||||
|
||||
mClassLoader = classLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(JCTree.JCCompilationUnit ast, CodeEditor editor, Expression expression, String statement, ArrayList<SuggestItem> result) {
|
||||
Matcher matcher = CLASS_DECLARE.matcher(statement);
|
||||
if (matcher.find()) {
|
||||
matcher = END_IMPLEMENTS.matcher(statement);
|
||||
if (matcher.find()) {
|
||||
String incompleteInterface = matcher.group(1);
|
||||
return getSuggestionInternal(editor, incompleteInterface, result,
|
||||
"interface");
|
||||
|
||||
}
|
||||
|
||||
matcher = END_EXTENDS.matcher(statement);
|
||||
if (matcher.find()) {
|
||||
String incompleteInterface = matcher.group(1);
|
||||
return getSuggestionInternal(editor, incompleteInterface, result,
|
||||
"class");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param declareType - enum, class, annotation, interface
|
||||
*/
|
||||
private boolean getSuggestionInternal(@NonNull CodeEditor editor,
|
||||
@NonNull String incomplete,
|
||||
@NonNull List<SuggestItem> result,
|
||||
@Nullable String declareType) {
|
||||
|
||||
//filter interfaces or classes
|
||||
Filter<IClass> filter = null;
|
||||
switch (declareType) {
|
||||
case "interface":
|
||||
filter = new Filter<IClass>() {
|
||||
@Override
|
||||
public boolean accept(IClass clazz) {
|
||||
if (Modifier.isFinal(clazz.getModifiers())) {
|
||||
return false;
|
||||
}
|
||||
if (!Modifier.isPublic(clazz.getModifiers())) {
|
||||
return false;
|
||||
}
|
||||
return clazz.isInterface();
|
||||
}
|
||||
};
|
||||
break;
|
||||
case "class":
|
||||
filter = new Filter<IClass>() {
|
||||
@Override
|
||||
public boolean accept(IClass clazz) {
|
||||
if (Modifier.isFinal(clazz.getModifiers())) {
|
||||
return false;
|
||||
}
|
||||
if (!Modifier.isPublic(clazz.getModifiers())) {
|
||||
return false;
|
||||
}
|
||||
return !clazz.isInterface();
|
||||
}
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
List<IClass> classes = mClassLoader.findAllWithPrefix(incomplete, filter);
|
||||
if (classes.size() > 0) {
|
||||
setInfo(classes, editor, incomplete);
|
||||
result.addAll(classes);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getSuggestion(CodeEditor editor, String incomplete, List<SuggestItem> suggestItems) {
|
||||
getSuggestionInternal(editor, incomplete, suggestItems, null);
|
||||
}
|
||||
}
|
||||
-157
@@ -1,157 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.expression;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import com.itsaky.androidide.language.java.parser.Expression;
|
||||
import com.itsaky.androidide.language.java.parser.JavacParser;
|
||||
import com.itsaky.androidide.language.java.parser.internal.IClass;
|
||||
import com.itsaky.androidide.language.java.parser.internal.JavaDexClassLoader;
|
||||
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
|
||||
import com.itsaky.androidide.language.java.parser.model.KeywordDescription;
|
||||
import com.sun.tools.javac.tree.JCTree;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.itsaky.androidide.language.java.parser.expression.CompleteThisKeyword.*;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Final complete, no context, suggest class name, method name,
|
||||
* variable or any start with incomplete
|
||||
*/
|
||||
public class CompleteWord extends JavaCompleteMatcherImpl {
|
||||
private static final String TAG = "CompleteWord";
|
||||
private final static Set<String> KEYWORDS;
|
||||
|
||||
static {
|
||||
String[] kws = {
|
||||
"abstract", "continue", "for", "new", "switch",
|
||||
"assert", "default", "if", "package", "synchronized",
|
||||
"boolean", "do", "goto", "private", "this",
|
||||
"break", "double", "implements", "protected", "throw",
|
||||
"byte", "else", "import", "public", "throws",
|
||||
"case", "enum", "instanceof", "return", "transient",
|
||||
"catch", "extends", "int", "short", "try",
|
||||
"char", "final", "interface", "static", "void",
|
||||
"class", "finally", "long", "strictfp", "volatile",
|
||||
"const", "float", "native", "super", "while",
|
||||
"null", "true", "false"
|
||||
};
|
||||
Set<String> s = new HashSet<>(Arrays.asList(kws));
|
||||
KEYWORDS = Collections.unmodifiableSet(s);
|
||||
}
|
||||
|
||||
private JavacParser mJavaParser;
|
||||
private JavaDexClassLoader mClassLoader;
|
||||
|
||||
public CompleteWord(JavacParser javaParser, JavaDexClassLoader classLoader) {
|
||||
this.mJavaParser = javaParser;
|
||||
mClassLoader = classLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(JCTree.JCCompilationUnit ast, CodeEditor editor, Expression expression, String statement, ArrayList<SuggestItem> result) {
|
||||
return getSuggestionInternal(editor, statement, result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getSuggestion(CodeEditor editor, String incomplete, List<SuggestItem> suggestItems) {
|
||||
getSuggestionInternal(editor, incomplete, suggestItems);
|
||||
}
|
||||
|
||||
private boolean getSuggestionInternal(CodeEditor editor, String incomplete, List<SuggestItem> suggestItems) {
|
||||
boolean result = getPossibleInCurrentFile(editor, incomplete, suggestItems);
|
||||
result |= getKeyword(editor, incomplete, suggestItems);
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean getKeyword(CodeEditor editor, String incomplete, List<SuggestItem> suggestItems) {
|
||||
if (incomplete.trim().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
boolean found = false;
|
||||
for (String keyword : KEYWORDS) {
|
||||
if (keyword.toLowerCase(Locale.US).startsWith(incomplete.toLowerCase(Locale.US)) && !keyword.equalsIgnoreCase(incomplete)) {
|
||||
KeywordDescription e = new KeywordDescription(keyword);
|
||||
setInfo(e, editor, incomplete);
|
||||
suggestItems.add(e);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
private boolean getPossibleInCurrentFile(@NonNull CodeEditor editor, @NonNull String incomplete,
|
||||
@NonNull List<SuggestItem> suggestItems) {
|
||||
if (incomplete.trim().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JCTree.JCCompilationUnit ast;
|
||||
try {
|
||||
ast = mJavaParser.parse(editor.getText());
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
getPossibleResult(editor, suggestItems, ast, incomplete);
|
||||
|
||||
List<IClass> classes = mClassLoader.findAllWithPrefix(incomplete);
|
||||
|
||||
setInfo(classes, editor, incomplete);
|
||||
suggestItems.addAll(classes);
|
||||
|
||||
return classes.size() > 0;
|
||||
}
|
||||
|
||||
private void getPossibleResult(CodeEditor editor, List<SuggestItem> result,
|
||||
JCTree.JCCompilationUnit ast, String incomplete) {
|
||||
if (ast == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<JCTree> typeDecls = ast.getTypeDecls();
|
||||
if (typeDecls.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
JCTree jcTree = typeDecls.get(0);
|
||||
if (jcTree instanceof JCTree.JCClassDecl) {
|
||||
List<JCTree> members =
|
||||
((JCTree.JCClassDecl) jcTree).getMembers();
|
||||
for (JCTree member : members) {
|
||||
if (member instanceof JCTree.JCVariableDecl) {
|
||||
addVariable(ast, (JCTree.JCVariableDecl) member, editor, incomplete, result);
|
||||
} else if (member instanceof JCTree.JCMethodDecl) {
|
||||
JCTree.JCMethodDecl method = (JCTree.JCMethodDecl) member;
|
||||
addMethod(ast, method, editor, incomplete, result);
|
||||
if (method.getStartPosition() <= editor.getCursor().getLeft()
|
||||
&& method.getBody().getEndPosition(ast.endPositions) >= editor.getCursor().getLeft()) {
|
||||
collectFromMethod(ast, editor, incomplete, result, method);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void collectFromMethod(JCTree.JCCompilationUnit unit, CodeEditor editor, String incomplete, List<SuggestItem> result,
|
||||
JCTree.JCMethodDecl method) {
|
||||
List<JCTree.JCStatement> statements
|
||||
= method.getBody().getStatements();
|
||||
for (JCTree.JCStatement statement : statements) {
|
||||
if (statement instanceof JCTree.JCVariableDecl) {
|
||||
JCTree.JCVariableDecl field = (JCTree.JCVariableDecl) statement;
|
||||
addVariable(unit, field, editor, incomplete, result);
|
||||
}
|
||||
}
|
||||
|
||||
List<JCTree.JCVariableDecl> parameters = method.getParameters();
|
||||
for (JCTree.JCVariableDecl parameter : parameters) {
|
||||
addVariable(unit, parameter, editor, incomplete, result);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.expression;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import com.itsaky.androidide.language.java.parser.Expression;
|
||||
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
|
||||
import com.sun.tools.javac.tree.JCTree;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public interface IJavaCompleteMatcher {
|
||||
boolean process(JCTree.JCCompilationUnit ast, CodeEditor editor, Expression expression, String statement, ArrayList<SuggestItem> result)
|
||||
throws Exception;
|
||||
|
||||
void getSuggestion(@NonNull CodeEditor editor,
|
||||
@NonNull String incomplete,
|
||||
@NonNull List<SuggestItem> suggestItems);
|
||||
}
|
||||
-58
@@ -1,58 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.expression;
|
||||
|
||||
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
|
||||
import com.itsaky.androidide.language.java.parser.model.JavaSuggestItemImpl;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public abstract class JavaCompleteMatcherImpl implements IJavaCompleteMatcher {
|
||||
//statement end with character, number or dot
|
||||
public static final Pattern END_WITH_CHARACTER_OR_DOT
|
||||
= Pattern.compile("[.0-9A-Za-z_]\\s*$", Pattern.MULTILINE);
|
||||
//statement end with dot "str."
|
||||
public static final Pattern END_WITH_DOT = Pattern.compile("\\.\\s*$", Pattern.MULTILINE);
|
||||
|
||||
/**
|
||||
* Check statement is valid when end with dot
|
||||
*/
|
||||
public static final Pattern VALID_WHEN_END_WITH_DOT
|
||||
= Pattern.compile(
|
||||
"[" +
|
||||
"\"" + //string: "Hello".|
|
||||
")" + //expression in parentheses: (new String("")).|
|
||||
"0-9A-Za-z_" + //name of variable or identifier: variable.|
|
||||
"\\]" + //array access: array[].|
|
||||
"]\\s*\\.\\s*$", Pattern.MULTILINE);
|
||||
public static final Pattern KEYWORD_DOT
|
||||
= Pattern.compile("(" + Patterns.RE_KEYWORDS.pattern() + ")\\s*\\.\\s*$");
|
||||
|
||||
|
||||
public static final Pattern METHOD_NAME = Patterns.IDENTIFIER;
|
||||
public static final Pattern VARIABLE_NAME = Patterns.IDENTIFIER;
|
||||
//String or java.lang.String
|
||||
public static final Pattern CLASS_NAME = Pattern.compile(
|
||||
Patterns.IDENTIFIER.pattern() + "(\\s*\\.\\s*" + Patterns.IDENTIFIER.pattern() + ")*");
|
||||
//java.util.*
|
||||
public static final Pattern PACKAGE_NAME = CLASS_NAME;
|
||||
//java.io.FileInputStream
|
||||
public static final Pattern CONSTRUCTOR = CLASS_NAME;
|
||||
|
||||
private static final String TAG = "JavaCompleteMatcherImpl";
|
||||
|
||||
protected static void setInfo(List<? extends SuggestItem> members, CodeEditor editor, String incomplete) {
|
||||
for (SuggestItem member : members) {
|
||||
if (member instanceof JavaSuggestItemImpl) {
|
||||
setInfo((JavaSuggestItemImpl) member, editor, incomplete);
|
||||
} else {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static void setInfo(Object member, CodeEditor editor, String incomplete) {
|
||||
if (member instanceof JavaSuggestItemImpl) {
|
||||
((JavaSuggestItemImpl) member).setEditor(editor);
|
||||
((JavaSuggestItemImpl) member).setIncomplete(incomplete);
|
||||
}
|
||||
}
|
||||
}
|
||||
-102
@@ -1,102 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.expression;
|
||||
|
||||
import android.text.Editable;
|
||||
import android.widget.EditText;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.itsaky.androidide.language.java.parser.internal.JavaUtil;
|
||||
import com.itsaky.androidide.language.java.parser.util.EditorUtil;
|
||||
import io.github.rosemoe.editor.text.Content;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import io.github.rosemoe.editor.text.CharPosition;
|
||||
import android.text.TextUtils;
|
||||
import com.blankj.utilcode.util.ThrowableUtils;
|
||||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
|
||||
/**
|
||||
* Created by Duy on 21-Jul-17.
|
||||
*/
|
||||
|
||||
public class PackageImporter {
|
||||
private static final String TAG = "ImportUtil";
|
||||
|
||||
/**
|
||||
* Add import statement if import does not already exist.
|
||||
*/
|
||||
public static void importClass(Content editor, String className) {
|
||||
String packageName = JavaUtil.getPackageName(className);
|
||||
if (getImportedClassName(editor, className) == null
|
||||
&& !packageName.isEmpty()
|
||||
&& !packageName.equals("java.lang")
|
||||
&& !packageName.equals(EditorUtil.getCurrentPackage(editor.toString()))) {
|
||||
organizeImports(editor, "import " + className.replace("$", ".") + ";");
|
||||
}
|
||||
}
|
||||
|
||||
public static String getImportedClassName(EditText editor, @Nullable String className) {
|
||||
return getImportedClassName(editor, className);
|
||||
}
|
||||
|
||||
public static String getImportedClassName(CharSequence src, @Nullable String className) {
|
||||
if (className == null) return null;
|
||||
|
||||
Pattern pattern = PatternFactory.makeImportClass(className);
|
||||
Matcher matcher = pattern.matcher(src);
|
||||
if (matcher.find()) {
|
||||
return matcher.group(2);
|
||||
}
|
||||
return PatternFactory.match(src, pattern);
|
||||
}
|
||||
|
||||
|
||||
public static void organizeImports(Content editor, String importStr) {
|
||||
try {
|
||||
ArrayList<String> imprts = getImports(editor);
|
||||
imprts.add(importStr);
|
||||
Collections.sort(imprts);
|
||||
final Set<String> imports = new HashSet<String>();
|
||||
for(String imprt : imprts) {
|
||||
imports.add(imprt.trim());
|
||||
}
|
||||
String imprt = TextUtils.join("\n", imports).trim();
|
||||
|
||||
int s = PatternFactory.firstMatch(editor, PatternFactory.IMPORT);
|
||||
int e = PatternFactory.lastMatch(editor, PatternFactory.IMPORT);
|
||||
|
||||
if (s >= 0 && e >= 0 && e < editor.length()) {
|
||||
CharPosition start = editor.getIndexer().getCharPosition(s);
|
||||
CharPosition end = editor.getIndexer().getCharPosition(e);
|
||||
if (s < e) {
|
||||
editor.replace(start.line, start.column, end.line, end.column, imprt);
|
||||
} else if (s == e) {
|
||||
editor.insert(start.line, start.column, imprt);
|
||||
}
|
||||
} else {
|
||||
int p = PatternFactory.lastMatch(editor, PatternFactory.PACKAGE);
|
||||
int c = PatternFactory.firstMatch(editor, CompleteTypeDeclared.CLASS_DECLARE);
|
||||
|
||||
if(p >= 0 && c >= 0 && p < c) {
|
||||
CharPosition start = editor.getIndexer().getCharPosition(p);
|
||||
CharPosition end = editor.getIndexer().getCharPosition(c);
|
||||
imprt = "\n\n" + imprt + "\n";
|
||||
if(p < c) {
|
||||
editor.replace(start.line, start.column, end.line, end.column, imprt);
|
||||
} else if(p == c) {
|
||||
editor.insert(start.line, start.column, imprt);
|
||||
} else {
|
||||
editor.insert(0, 0, imprt);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable th) {}
|
||||
}
|
||||
|
||||
public static ArrayList<String> getImports(Content editor) {
|
||||
return PatternFactory.allMatch(editor, PatternFactory.IMPORT);
|
||||
}
|
||||
|
||||
}
|
||||
-119
@@ -1,119 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.expression;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class PatternFactory {
|
||||
public static final Pattern PACKAGE = Pattern.compile("package\\s+[^;]*;");
|
||||
public static final Pattern PACKAGE_NAME = Pattern.compile("[A-Za-z_]+(.[A-Za-z][A-Za-z0-9_]*)*");
|
||||
public static final Pattern IMPORT = Pattern.compile("(import\\s+)([^;]*)(\\s?);");
|
||||
public static final Pattern WORD = Pattern.compile("[^\\s-]+$");
|
||||
|
||||
public static final String IDENTIFIER_STR = "[A-Za-z][A-Za-z0-9]*";
|
||||
public static final Pattern IDENTIFIER = Pattern.compile(IDENTIFIER_STR);
|
||||
|
||||
public static final Pattern ANNOTATION = Pattern.compile("@[A-Za-z][A-Za-z0-9]*");
|
||||
public static final Pattern BRACKET = Pattern.compile("\\[(.*?)\\]");
|
||||
public static final Pattern MODIFIERS = Pattern.compile("\\b(public|protected|private|abstract|static|final|strictfp)\\b");
|
||||
public static final Pattern SPLIT_NON_WORD = Pattern.compile("\\W+");
|
||||
public static final String SPLIT_NON_WORD_STR = "\\W+";
|
||||
public static final String[] PRIMITIVE_TYPE = new String[]{"boolean", "byte", "char", "int",
|
||||
"short", "long", "float", "double"};
|
||||
public static final String[] KEYWORD_MODIFIERS = new String[]{"public", "private", "protected",
|
||||
"static", "final", "synchronized", "volatile", "transient", "native", "strictfp"};
|
||||
public static final String[] KEYWORD_TYPE = new String[]{"class", "interface", "enum"};
|
||||
public static final String[] KEYWORD;
|
||||
|
||||
public static final String GENERIC_STR = "<[A-Z][a-zA-Z0-9_<>, ]*>";
|
||||
|
||||
public static final Pattern FILE_NAME = Pattern.compile("[A-Za-z][A-Za-z0-9_\\-.]*");
|
||||
|
||||
static {
|
||||
KEYWORD = new String[PRIMITIVE_TYPE.length + KEYWORD_MODIFIERS.length + KEYWORD_TYPE.length];
|
||||
System.arraycopy(PRIMITIVE_TYPE, 0, KEYWORD, 0, PRIMITIVE_TYPE.length);
|
||||
System.arraycopy(KEYWORD_MODIFIERS, 0, KEYWORD, PRIMITIVE_TYPE.length, KEYWORD_MODIFIERS.length);
|
||||
System.arraycopy(KEYWORD_TYPE, 0, KEYWORD, KEYWORD_MODIFIERS.length, KEYWORD_TYPE.length);
|
||||
}
|
||||
|
||||
public static Pattern makeImportClass(String className) {
|
||||
return Pattern.compile("(import\\s+)(.*" + className + ")(\\s?;)");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String lastMatchStr(CharSequence text, Pattern pattern) {
|
||||
Matcher matcher = pattern.matcher(text);
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
while (matcher.find()) {
|
||||
list.add(matcher.group());
|
||||
}
|
||||
if (list.size() == 0) return null;
|
||||
return list.get(list.size() - 1);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String match(CharSequence text, Pattern pattern) {
|
||||
Matcher matcher = pattern.matcher(text);
|
||||
if (matcher.find()) return matcher.group();
|
||||
else return null;
|
||||
}
|
||||
|
||||
public static ArrayList<String> allMatch(CharSequence text, Pattern pattern) {
|
||||
Matcher matcher = pattern.matcher(text);
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
while (matcher.find()) {
|
||||
list.add(matcher.group());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
public static int lastMatch(CharSequence editor, Pattern pattern) {
|
||||
int last = -1;
|
||||
Matcher matcher = pattern.matcher(editor);
|
||||
while (matcher.find()) last = matcher.end();
|
||||
return last;
|
||||
}
|
||||
|
||||
public static int matchEnd(CharSequence editor, Pattern pattern, int start) {
|
||||
Matcher matcher = pattern.matcher(editor);
|
||||
if (matcher.find(start)) {
|
||||
return matcher.end();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int firstMatch(CharSequence editor, Pattern pattern) {
|
||||
Matcher matcher = pattern.matcher(editor);
|
||||
if (matcher.find()) {
|
||||
return matcher.start();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static Pattern makeInstance(String prefix) {
|
||||
//ArrayList<String> list = new ArrayList();
|
||||
return Pattern.compile("(" + IDENTIFIER_STR + ")" + "(\\s?)" + //type
|
||||
"((" + GENERIC_STR + ")?(\\s?)|(\\s+))" + //generic or space
|
||||
"(" + prefix + ")(\\s?)([,;=)])"); //name
|
||||
}
|
||||
|
||||
public static int lastMatch(String statement, String pattern) {
|
||||
return lastMatch(statement, Pattern.compile(pattern));
|
||||
}
|
||||
|
||||
public static int lastMatch(String statement, Pattern pattern) {
|
||||
return lastMatch(statement, pattern, 0);
|
||||
}
|
||||
|
||||
public static int lastMatch(String expr, Pattern pattern, int start) {
|
||||
Matcher matcher = pattern.matcher(expr.substring(start));
|
||||
int index = -1;
|
||||
while (matcher.find()) {
|
||||
index = matcher.end();
|
||||
}
|
||||
return index != 0 ? index + start : index;
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.expression;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static java.util.regex.Pattern.compile;
|
||||
|
||||
/**
|
||||
* Created by Duy on 11-Feb-17.
|
||||
*/
|
||||
|
||||
@SuppressWarnings("DefaultFileTemplate")
|
||||
public class Patterns {
|
||||
|
||||
public static final Pattern JAVA_COMMENTS = compile("(//.*)|(/\\*(?:.|[\\n\\r])*?\\*/)");
|
||||
/**
|
||||
* match string java
|
||||
* include
|
||||
* <p>
|
||||
* 'string'
|
||||
* <p>
|
||||
* And can not find close quote
|
||||
* <p>
|
||||
* 'sadhasdhasdhashdhas ds asda sd
|
||||
*/
|
||||
public static final Pattern STRINGS = Pattern.compile(
|
||||
"((\")(.*?)(\"))" +//'string'
|
||||
"|((\")(.+))", Pattern.DOTALL); // no end string 'asdasdasd
|
||||
|
||||
|
||||
public static final Pattern PRIMITIVE_TYPES = compile("boolean|byte|char|int|short|long|float|double");
|
||||
public static final Pattern KEYWORD_TYPES = compile("class|interface|enum");
|
||||
public static final Pattern KEYWORDS_MODS = compile("public|private|protected|static|final|" +
|
||||
"synchronized|volatile|transient|native|strictfp|abstract");
|
||||
public static final Pattern KEYWORDS = compile(PRIMITIVE_TYPES.pattern()
|
||||
+ KEYWORDS_MODS.pattern() + "|"
|
||||
+ KEYWORD_TYPES.pattern() + "|" +
|
||||
"super|this|void|assert|break|case|catch|" +
|
||||
"const|continue|default|do|else|extends|finally|for|goto|if|implements|import|instanceof|" +
|
||||
"interface|new|package|return|switch|throw|throws|try|while|true|false|null");
|
||||
|
||||
public static final Pattern RE_BRACKETS = compile("(\\s*\\[\\s*\\])");
|
||||
|
||||
public static final Pattern IDENTIFIER = compile("[a-zA-Z_][a-zA-Z0-9_]*");
|
||||
//case: JCTree.JCClassDecl
|
||||
//case: java.util.ArrayList)
|
||||
public static final Pattern RE_QUALID = compile(IDENTIFIER + "(\\s*\\.\\s*" + IDENTIFIER + ")?");
|
||||
public static final Pattern RE_REFERENCE_TYPE = compile(RE_QUALID + RE_BRACKETS.pattern() + "\\*");
|
||||
public static final Pattern RE_TYPE = RE_REFERENCE_TYPE;
|
||||
|
||||
public static final Pattern TYPE_DECLARE_MODIFIERS = compile("public|protected|private|abstract|static|final|strictfp");
|
||||
public static final Pattern TYPE_DECLARE_HEAD = compile("class|inteface|enum");
|
||||
|
||||
//case: public class A
|
||||
//case: public class Lamborghini extends Car
|
||||
public static final Pattern RE_TYPE_DECL = compile(
|
||||
"((public|protected|private|abstract|static|final|strictfp)\\s+)*" +
|
||||
"(class|inteface|enum)\\s+" +
|
||||
"[a-zA-Z_][a-zA-Z0-9_]*" +
|
||||
"(\\s+(extends|implements)(\\s+)([a-zA-Z_][a-zA-Z0-9_]*))?");
|
||||
|
||||
public static final Pattern RE_ARRAY_TYPE = compile("^\\s*(" + RE_QUALID + ")(" + RE_BRACKETS + ")+\\s*");
|
||||
public static final Pattern RE_SELECT_OR_ACCESS = compile("^\\s*(" + IDENTIFIER + ")\\s*(\\[.*])?\\s*$");
|
||||
public static final Pattern RE_ARRAY_ACCESS = compile("^\\s*(" + IDENTIFIER + ")" + "\\s*(\\[.*])+\\s*$");
|
||||
public static final Pattern RE_CASTING = compile("\\s*\\((" + RE_QUALID + "\\s*)\\)\\s*" + IDENTIFIER);
|
||||
public static final Pattern RE_KEYWORDS = KEYWORDS;
|
||||
|
||||
public static final Pattern PACKAGE_NAME = compile("([A-Za-z0-9_]([.][a-zA-Z0-9_])?)*");
|
||||
}
|
||||
-68
@@ -1,68 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.expression;
|
||||
|
||||
import com.itsaky.androidide.language.java.parser.util.EditorUtil;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
|
||||
public class StatementParser {
|
||||
/**
|
||||
* " Search back from the cursor position till meeting '{' or ';'.
|
||||
* " '{' means statement start, ';' means end of a previous statement.
|
||||
*
|
||||
* @return statement before cursor
|
||||
* " Note: It's the base for parsing. And It's OK for most cases.
|
||||
*/
|
||||
public static String resolveStatementFromCursor(CodeEditor editor) {
|
||||
String lineBeforeCursor = getCurrentLine(editor);
|
||||
if (lineBeforeCursor.matches("^\\s*(import|package)\\s+")) {
|
||||
return lineBeforeCursor;
|
||||
}
|
||||
int oldCursor = editor.getCursor().getLeft();
|
||||
int newCursor = oldCursor - 1;
|
||||
while (true) {
|
||||
if (newCursor == 0) break;
|
||||
char c = editor.getText().charAt(newCursor);
|
||||
if (c == '{' || c == '}' || c == ';') {
|
||||
newCursor++;
|
||||
break;
|
||||
}
|
||||
newCursor--;
|
||||
}
|
||||
String statement = editor.getText().subSequence(newCursor, oldCursor).toString();
|
||||
return mergeLine(statement);
|
||||
}
|
||||
|
||||
|
||||
public static String getCurrentLine(CodeEditor editText) {
|
||||
return EditorUtil.getLineBeforeCursor(editText, editText.getCursor().getLeft());
|
||||
}
|
||||
|
||||
public static String mergeLine(String statement) {
|
||||
statement = cleanStatement(statement);
|
||||
return statement;
|
||||
}
|
||||
|
||||
/**
|
||||
* set string literal empty, remove comments, trim begining or ending spaces
|
||||
* case: ' sb. /* block comment"/ append( "stringliteral" ) // comment '
|
||||
* return 'sb.append("")'
|
||||
*/
|
||||
private static String cleanStatement(String code) {
|
||||
if (code.matches("\\s*")) {
|
||||
return "";
|
||||
}
|
||||
code = removeComment(code); //clear all comment
|
||||
//clear all string content
|
||||
code = code.replaceAll(Patterns.STRINGS.toString(), "\"\"");
|
||||
code = EditorUtil.trimLeft(code);
|
||||
code = code.replaceAll("[\n\t\r]", "");
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* remove all comment
|
||||
*/
|
||||
private static String removeComment(String code) {
|
||||
return code.replaceAll(Patterns.JAVA_COMMENTS.toString(), "");
|
||||
}
|
||||
|
||||
}
|
||||
-376
@@ -1,376 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.expression;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.itsaky.androidide.language.java.parser.internal.IClass;
|
||||
import com.itsaky.androidide.language.java.parser.internal.IField;
|
||||
import com.itsaky.androidide.language.java.parser.internal.IMethod;
|
||||
import com.itsaky.androidide.language.java.parser.internal.JavaDexClassLoader;
|
||||
import com.itsaky.androidide.language.java.parser.internal.JavaUtil;
|
||||
import com.sun.tools.javac.tree.JCTree;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.itsaky.androidide.language.java.parser.internal.JavaUtil.*;
|
||||
import static com.sun.tools.javac.tree.JCTree.JCArrayAccess;
|
||||
import static com.sun.tools.javac.tree.JCTree.JCArrayTypeTree;
|
||||
import static com.sun.tools.javac.tree.JCTree.JCBlock;
|
||||
import static com.sun.tools.javac.tree.JCTree.JCCatch;
|
||||
import static com.sun.tools.javac.tree.JCTree.JCClassDecl;
|
||||
import static com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
|
||||
import static com.sun.tools.javac.tree.JCTree.JCExpression;
|
||||
import static com.sun.tools.javac.tree.JCTree.JCFieldAccess;
|
||||
import static com.sun.tools.javac.tree.JCTree.JCIdent;
|
||||
import static com.sun.tools.javac.tree.JCTree.JCMethodDecl;
|
||||
import static com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
|
||||
import static com.sun.tools.javac.tree.JCTree.JCStatement;
|
||||
import static com.sun.tools.javac.tree.JCTree.JCTry;
|
||||
import static com.sun.tools.javac.tree.JCTree.JCVariableDecl;
|
||||
|
||||
public class TypeResolver {
|
||||
private static final String TAG = "TypeResolver";
|
||||
private JavaDexClassLoader mClassLoader;
|
||||
private JCCompilationUnit mUnit;
|
||||
private IClass mCurrentType;
|
||||
|
||||
private boolean isStatic= false;
|
||||
|
||||
public boolean isStatic() {
|
||||
return isStatic;
|
||||
}
|
||||
|
||||
public TypeResolver(JavaDexClassLoader classLoader,
|
||||
JCCompilationUnit unit,
|
||||
IClass currentType) {
|
||||
mClassLoader = classLoader;
|
||||
mUnit = unit;
|
||||
mCurrentType = currentType;
|
||||
}
|
||||
|
||||
public IClass resolveType(@NonNull JCExpression expression, int cursor) {
|
||||
return resolveTypeImpl(expression, cursor);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private IClass resolveTypeImpl(@NonNull final JCExpression expression, int cursor) {
|
||||
List<JCTree> list = extractExpressionAtCursor(expression, cursor);
|
||||
if (list == null) {
|
||||
return null;
|
||||
}
|
||||
String exceptionMessage = "Can not resolve type of expression ";
|
||||
IClass currentType = mCurrentType;
|
||||
for (JCTree tree : list) {
|
||||
//only once time on this case
|
||||
if (tree instanceof JCIdent) {
|
||||
JCIdent jcIdent = (JCIdent) tree;
|
||||
|
||||
//variable declaration, static import or inner class
|
||||
//case: variableDecl
|
||||
JCVariableDecl variableDecl = getVariableDeclaration(mUnit, jcIdent);
|
||||
if (variableDecl != null) {
|
||||
currentType = JavaUtil.jcTypeToClass(mUnit, variableDecl.getType());
|
||||
} else {
|
||||
//case: System.out -> find imported class, this expression can be static access
|
||||
String className = findImportedClassName(mUnit, jcIdent.getName().toString());
|
||||
currentType = mClassLoader.getClassReader().getParsedClass(className);
|
||||
isStatic = true;
|
||||
}
|
||||
// TODO: 13-Jun-18 case: static import
|
||||
// TODO: 13-Jun-18 case inner class
|
||||
} else if (tree instanceof JCMethodInvocation) {
|
||||
// TODO: 13-Jun-18 find method in current class or import static
|
||||
JCMethodInvocation jcMethod = (JCMethodInvocation) tree;
|
||||
JCExpression methodSelect = jcMethod.getMethodSelect();
|
||||
if (methodSelect instanceof JCFieldAccess) {
|
||||
String methodName = ((JCFieldAccess) methodSelect).getIdentifier().toString();
|
||||
List<JCExpression> arguments = jcMethod.getArguments();
|
||||
IClass[] types = new IClass[arguments.size()];
|
||||
IMethod method = currentType.getMethod(methodName, types);
|
||||
if (method == null) {
|
||||
throw new UnsupportedOperationException(exceptionMessage + tree);
|
||||
}
|
||||
currentType = method.getMethodReturnType();
|
||||
} else if (methodSelect instanceof JCIdent) { //method in current class
|
||||
String methodName = ((JCIdent) methodSelect).getName().toString();
|
||||
IMethod method = currentType.getMethod(methodName, null);
|
||||
if (method == null) {
|
||||
throw new UnsupportedOperationException(exceptionMessage + tree);
|
||||
}
|
||||
currentType = method.getMethodReturnType();
|
||||
} else {
|
||||
throw new UnsupportedOperationException(exceptionMessage + tree);
|
||||
}
|
||||
} else if (tree instanceof JCFieldAccess) {
|
||||
if (currentType == null) {
|
||||
throw new UnsupportedOperationException(exceptionMessage + tree);
|
||||
}
|
||||
|
||||
String name = ((JCFieldAccess) tree).getIdentifier().toString();
|
||||
IField field = currentType.getField(name);
|
||||
if (field == null) {
|
||||
throw new UnsupportedOperationException(exceptionMessage + tree);
|
||||
}
|
||||
currentType = field.getFieldType();
|
||||
} else if (tree instanceof JCArrayAccess) {
|
||||
JCIdent jcIdent = (JCIdent) ((JCArrayAccess) tree).getExpression();
|
||||
|
||||
//variable declaration, static import or inner class
|
||||
//case: variableDecl
|
||||
JCVariableDecl variableDecl = getVariableDeclaration(mUnit, jcIdent);
|
||||
if (variableDecl != null) {
|
||||
if (!(variableDecl.getType() instanceof JCArrayTypeTree)) {
|
||||
throw new UnsupportedOperationException("can not resolve type of array access " + tree);
|
||||
}
|
||||
String className = ((JCArrayTypeTree) variableDecl.getType())
|
||||
.getType().toString();
|
||||
className = findImportedClassName(mUnit, className);
|
||||
currentType = mClassLoader.getClassReader().getParsedClass(className);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return currentType;
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
private List<JCTree> extractExpressionAtCursor(JCExpression expression, int cursor) {
|
||||
JCTree tree = expression;
|
||||
LinkedList<JCTree> list = new LinkedList<>();
|
||||
while (tree != null) {
|
||||
if (getEndPosition(tree) > cursor) {
|
||||
break;
|
||||
}
|
||||
if (tree instanceof JCMethodInvocation) {
|
||||
list.addFirst(tree);
|
||||
|
||||
JCExpression methodSelect = ((JCMethodInvocation) tree).getMethodSelect();
|
||||
if (methodSelect instanceof JCIdent) {
|
||||
break;
|
||||
} else if (methodSelect instanceof JCFieldAccess) {
|
||||
tree = ((JCFieldAccess) methodSelect).getExpression();
|
||||
|
||||
} else { //unsupported
|
||||
return null;
|
||||
}
|
||||
} else if (tree instanceof JCFieldAccess) { //var.field
|
||||
list.addFirst(tree);
|
||||
//select before
|
||||
tree = ((JCFieldAccess) tree).getExpression();
|
||||
|
||||
} else if (tree instanceof JCIdent) { //var, it should be before any expression
|
||||
list.addFirst(tree);
|
||||
break;
|
||||
|
||||
} else if (tree instanceof JCArrayAccess) { //variable.array[i].toString
|
||||
list.addFirst(tree);
|
||||
//select declare name
|
||||
tree = ((JCArrayAccess) tree).getExpression(); //select array name
|
||||
if (tree instanceof JCIdent) {
|
||||
break;
|
||||
} else if (tree instanceof JCFieldAccess) {
|
||||
} else {
|
||||
throw new UnsupportedOperationException("can not extract expression at array " + tree);
|
||||
}
|
||||
} else { //unsupported
|
||||
//should not happen
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private JCVariableDecl getVariableDeclaration(final JCCompilationUnit unit,
|
||||
final JCIdent jcIdent) {
|
||||
List<JCTree> typeDecls = unit.getTypeDecls();
|
||||
for (JCTree typeDecl : typeDecls) {
|
||||
JCVariableDecl variableDecl = getVariableDeclaration(typeDecl, jcIdent);
|
||||
if (variableDecl != null) {
|
||||
return variableDecl;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Visit all scope and find variable declaration when given jcIdent
|
||||
*
|
||||
* @param parent - parent block to find
|
||||
*/
|
||||
@Nullable
|
||||
private JCVariableDecl getVariableDeclaration(final JCTree parent,
|
||||
final JCIdent jcIdent) {
|
||||
// TODO: 14-Jun-18 need add parent if jcIdent to resolve exactly scope
|
||||
if (parent instanceof JCBlock) {
|
||||
List<JCStatement> statements = ((JCBlock) parent).getStatements();
|
||||
return getVariableDeclarationFromStatements(parent, statements, jcIdent);
|
||||
|
||||
} else if (parent instanceof JCTree.JCCase) {
|
||||
JCTree.JCCase jcCase = (JCTree.JCCase) parent;
|
||||
return getVariableDeclarationFromStatements(parent, jcCase.getStatements(), jcIdent);
|
||||
|
||||
} else if (parent instanceof JCClassDecl) {
|
||||
List<JCTree> members = ((JCClassDecl) parent).getMembers();
|
||||
//all member equals scope
|
||||
for (JCTree member : members) {
|
||||
if (member instanceof JCVariableDecl) { //variable
|
||||
JCVariableDecl variableDecl = (JCVariableDecl) member;
|
||||
if (canBeSampleVariable(parent, variableDecl, jcIdent)) {
|
||||
return variableDecl;
|
||||
}
|
||||
} else if (member instanceof JCMethodDecl) { //method
|
||||
JCMethodDecl jcMethodDecl = (JCMethodDecl) member;
|
||||
JCBlock jcMethodDeclBody = jcMethodDecl.getBody();
|
||||
JCVariableDecl variableDecl = getVariableDeclaration(jcMethodDeclBody, jcIdent);
|
||||
if (variableDecl != null) {
|
||||
return variableDecl;
|
||||
}
|
||||
|
||||
variableDecl = getVariableDeclarationFromStatements(member,
|
||||
((JCMethodDecl) member).getParameters(), jcIdent);
|
||||
if (variableDecl != null) {
|
||||
return variableDecl;
|
||||
}
|
||||
} else {
|
||||
JCVariableDecl variableDecl = getVariableDeclaration(member, jcIdent);
|
||||
if (variableDecl != null) {
|
||||
return variableDecl;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (parent instanceof JCTree.JCDoWhileLoop) {
|
||||
return getVariableDeclaration(((JCTree.JCDoWhileLoop) parent).getStatement(), jcIdent);
|
||||
|
||||
} else if (parent instanceof JCTree.JCEnhancedForLoop) {
|
||||
JCVariableDecl variable = ((JCTree.JCEnhancedForLoop) parent).getVariable();
|
||||
if (canBeSampleVariable(parent, variable, jcIdent)) {
|
||||
return variable;
|
||||
}
|
||||
return getVariableDeclaration(((JCTree.JCEnhancedForLoop) parent).getStatement(), jcIdent);
|
||||
|
||||
} else if (parent instanceof JCTree.JCForLoop) {
|
||||
List<JCStatement> initializer = ((JCTree.JCForLoop) parent).getInitializer();
|
||||
JCVariableDecl variableDecl = getVariableDeclarationFromStatements(parent, initializer, jcIdent);
|
||||
if (variableDecl != null) {
|
||||
return variableDecl;
|
||||
}
|
||||
return getVariableDeclaration(((JCTree.JCForLoop) parent).getStatement(), jcIdent);
|
||||
|
||||
} else if (parent instanceof JCTree.JCIf) {
|
||||
JCTree.JCIf jcIf = (JCTree.JCIf) parent;
|
||||
JCStatement thenStatement = jcIf.getThenStatement();
|
||||
JCVariableDecl variableDecl = getVariableDeclaration(thenStatement, jcIdent);
|
||||
if (variableDecl != null) {
|
||||
return variableDecl;
|
||||
}
|
||||
JCStatement elseStatement = jcIf.getElseStatement();
|
||||
if (elseStatement != null) {
|
||||
return getVariableDeclaration(elseStatement, jcIdent);
|
||||
}
|
||||
|
||||
} else if (parent instanceof JCTree.JCLabeledStatement) {
|
||||
return getVariableDeclaration(((JCTree.JCLabeledStatement) parent).getStatement(), jcIdent);
|
||||
|
||||
} else if (parent instanceof JCTree.JCSynchronized) {
|
||||
return getVariableDeclaration(((JCTree.JCSynchronized) parent).getBlock(), jcIdent);
|
||||
|
||||
} else if (parent instanceof JCTry) {
|
||||
JCTry jcTry = (JCTry) parent;
|
||||
JCBlock jcTryBlock = jcTry.getBlock();
|
||||
JCVariableDecl variableDecl = getVariableDeclaration(jcTryBlock, jcIdent);
|
||||
if (variableDecl != null) {
|
||||
return variableDecl;
|
||||
}
|
||||
for (JCCatch jcCatch : jcTry.getCatches()) {
|
||||
JCVariableDecl parameter = jcCatch.getParameter();
|
||||
if (canBeSampleVariable(jcCatch, parameter, jcIdent)) {
|
||||
return parameter;
|
||||
}
|
||||
JCBlock jcCatchBlock = jcCatch.getBlock();
|
||||
variableDecl = getVariableDeclaration(jcCatchBlock, jcIdent);
|
||||
if (variableDecl != null) {
|
||||
return variableDecl;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (parent instanceof JCTree.JCWhileLoop) {
|
||||
return getVariableDeclaration(((JCTree.JCWhileLoop) parent).getStatement(), jcIdent);
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private JCVariableDecl getVariableDeclarationFromStatements(JCTree parent,
|
||||
List<? extends JCStatement> statements,
|
||||
JCIdent jcIdent) {
|
||||
for (JCStatement statement : statements) {
|
||||
if (statement instanceof JCVariableDecl) {
|
||||
JCVariableDecl variableDecl = (JCVariableDecl) statement;
|
||||
if (canBeSampleVariable(parent, variableDecl, jcIdent)) {
|
||||
return variableDecl;
|
||||
}
|
||||
} else {
|
||||
JCVariableDecl variableDecl = getVariableDeclaration(statement, jcIdent);
|
||||
if (variableDecl != null) {
|
||||
return variableDecl;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param parent - scope of variable
|
||||
*/
|
||||
private boolean canBeSampleVariable(@NonNull JCTree parent,
|
||||
@NonNull JCVariableDecl variable,
|
||||
/*should all parent if identifier*/
|
||||
@NonNull JCIdent ident) {
|
||||
if (!variable.getName().equals(ident.getName())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//identifier inside or equal scope if variable o
|
||||
//-------------------------------------------
|
||||
// private ArrayList list = new ArrayList();
|
||||
// void method(){
|
||||
// list.toString()
|
||||
//}
|
||||
//-------------------------------------------
|
||||
// void method(ArrayList list){
|
||||
// list.toString()
|
||||
//}
|
||||
//-------------------------------------------
|
||||
//void method(){
|
||||
// ArrayList list;
|
||||
// list.toString()
|
||||
//}
|
||||
if (isChildOfParent(parent, ident)) {
|
||||
int startPosition = variable.getStartPosition();
|
||||
int startPosition1 = ident.getStartPosition();
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isChildOfParent(JCTree parent, JCTree child) {
|
||||
//scope of child inside parent
|
||||
return parent.getStartPosition() <= child.getStartPosition()
|
||||
&& getEndPosition(parent) >= getEndPosition(child);
|
||||
}
|
||||
|
||||
private int getEndPosition(JCTree tree) {
|
||||
return tree.getEndPosition(mUnit.endPositions);
|
||||
}
|
||||
|
||||
static class c {
|
||||
static void cd() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.internal;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.itsaky.androidide.language.java.parser.model.ConstructorDescription;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public interface IClass extends IJavaDocCommentable {
|
||||
int getModifiers();
|
||||
|
||||
@Nullable
|
||||
String getFullClassName();
|
||||
|
||||
@Nullable
|
||||
String getSimpleName();
|
||||
|
||||
boolean isInterface();
|
||||
|
||||
boolean isEnum();
|
||||
|
||||
boolean isPrimitive();
|
||||
|
||||
boolean isAnnotation();
|
||||
|
||||
boolean isMemberClass();
|
||||
|
||||
@Nullable
|
||||
IMethod getMethod(@NonNull String methodName, @Nullable IClass[] argsType);
|
||||
|
||||
@Nullable
|
||||
IField getField(@NonNull String name);
|
||||
|
||||
List<IMethod> getMethods();
|
||||
|
||||
@Nullable
|
||||
IClass getSuperclass();
|
||||
|
||||
ArrayList<IField> getFields();
|
||||
|
||||
List<SuggestItem> getMember(String prefix);
|
||||
|
||||
List<ConstructorDescription> getConstructors();
|
||||
}
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.internal;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.NonNull;
|
||||
import com.itsaky.androidide.interfaces.Filter;
|
||||
import java.util.List;
|
||||
|
||||
public interface IClassManager {
|
||||
void update(IClass clazz);
|
||||
|
||||
void remove(String fullClassName);
|
||||
|
||||
@NonNull
|
||||
List<IClass> find(@NonNull String simpleNamePrefix,
|
||||
@Nullable Filter<IClass> filter);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.internal;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public interface IField extends IJavaDocCommentable {
|
||||
@NonNull
|
||||
String getFieldName();
|
||||
|
||||
@NonNull
|
||||
IClass getFieldType();
|
||||
|
||||
int getFieldModifiers();
|
||||
|
||||
@Nullable
|
||||
Object getFieldValue();
|
||||
}
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.internal;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public interface IJavaDocCommentable extends SuggestItem {
|
||||
|
||||
IJavaDocCommentable setHtmlJavaDoc(String javadoc);
|
||||
|
||||
@Nullable
|
||||
String getJavaDoc();
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.internal;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public interface IMethod extends IJavaDocCommentable {
|
||||
@NonNull
|
||||
String getMethodName();
|
||||
|
||||
@NonNull
|
||||
IClass getMethodReturnType();
|
||||
|
||||
@NonNull
|
||||
IClass[] getMethodParameterTypes();
|
||||
|
||||
int getModifiers();
|
||||
}
|
||||
-301
@@ -1,301 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.internal;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.util.Pair;
|
||||
import com.blankj.utilcode.util.FileIOUtils;
|
||||
import com.blankj.utilcode.util.FileUtils;
|
||||
import com.itsaky.androidide.interfaces.Filter;
|
||||
import com.itsaky.androidide.language.java.parser.JavacParser;
|
||||
import com.itsaky.androidide.language.java.parser.model.ClassDescription;
|
||||
import com.itsaky.androidide.language.java.parser.model.FieldDescription;
|
||||
import com.itsaky.androidide.language.java.parser.model.MethodDescription;
|
||||
import com.itsaky.androidide.models.AndroidProject;
|
||||
import com.itsaky.androidide.models.ConstantsBridge;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
import com.sun.tools.javac.tree.JCTree;
|
||||
import dalvik.system.DexFile;
|
||||
import dalvik.system.PathClassLoader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
import com.itsaky.androidide.language.java.parser.model.ConstructorDescription;
|
||||
import com.itsaky.androidide.utils.Either;
|
||||
import com.blankj.utilcode.util.ThrowableUtils;
|
||||
import com.itsaky.androidide.utils.Environment;
|
||||
|
||||
public class JavaClassManager implements IClassManager {
|
||||
private static final String TAG = "JavaClassReader";
|
||||
private static final String JAVA_DOT_LANG_DOT = "java.lang.";
|
||||
private static JavaClassManager INSTANCE;
|
||||
|
||||
/**
|
||||
* All classes sorted by simple class name, fastest find list of classes start with prefix
|
||||
*/
|
||||
private final ArrayList<Pair<String, IClass>> mSimpleNames = new ArrayList<>();
|
||||
/**
|
||||
* All classes sorted by full class name, use for faster find class name by using binary search
|
||||
*/
|
||||
private final ArrayList<IClass> mFullNames = new ArrayList<>();
|
||||
/**
|
||||
* Map contains parsed class, parsed class is java class in jar file or from user
|
||||
*/
|
||||
private final HashMap<String, IClass> mLoaded = new HashMap<>();
|
||||
|
||||
private File mBootClasspath;
|
||||
|
||||
private JavaClassManager(File bootClassPath) {
|
||||
mBootClasspath = bootClassPath;
|
||||
}
|
||||
|
||||
public static JavaClassManager getInstance(File classpath) {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new JavaClassManager(classpath);
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public static JavaClassManager getInstance() {
|
||||
if (INSTANCE == null) {
|
||||
throw new RuntimeException("JavaClassManager not init");
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public ArrayList<IClass> getAllClasses() {
|
||||
return mFullNames;
|
||||
}
|
||||
|
||||
public void loadFromProject(AndroidProject project) {
|
||||
try {
|
||||
JavacParser parser = new JavacParser();
|
||||
List<String> javaSrcDirs = project.getSourcePaths();
|
||||
for (String dir : javaSrcDirs) {
|
||||
final File javaSrcDir = new File(dir);
|
||||
Collection<File> javaFiles = FileUtils.listFilesInDirWithFilter(javaSrcDir, (f -> f.isFile() && f.getName().endsWith(".java")), true);
|
||||
for (File javaFile : javaFiles) {
|
||||
String content = FileIOUtils.readFile2String(javaFile);
|
||||
JCTree.JCCompilationUnit ast = parser.parse(content);
|
||||
List<IClass> parseClasses = parser.parseClasses(ast);
|
||||
for (IClass aClass : parseClasses) {
|
||||
update(aClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fullName - full class name
|
||||
*/
|
||||
@Nullable
|
||||
public IClass getParsedClass(String fullName) {
|
||||
IClass cache = mLoaded.get(fullName);
|
||||
if (cache != null) {
|
||||
return cache;
|
||||
}
|
||||
return mLoaded.get(JAVA_DOT_LANG_DOT + fullName);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public IClass getClassWrapper(@NonNull Class clazz) {
|
||||
IClass cache = mLoaded.get(clazz.getName());
|
||||
if (cache != null) {
|
||||
return cache;
|
||||
}
|
||||
ClassDescription wrapper = new ClassDescription(clazz);
|
||||
update(wrapper);
|
||||
wrapper.initMembers(clazz);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(IClass value) {
|
||||
String fullClassName = value.getFullClassName();
|
||||
if (mLoaded.containsKey(fullClassName)) {
|
||||
mLoaded.put(fullClassName, value);
|
||||
return;
|
||||
}
|
||||
mLoaded.put(fullClassName, value);
|
||||
|
||||
if (mFullNames.size() == 0) {
|
||||
mFullNames.add(value);
|
||||
} else {
|
||||
boolean found = false;
|
||||
for (int i = 0; i < mFullNames.size(); i++) {
|
||||
IClass iClass = mFullNames.get(i);
|
||||
if (fullClassName.compareTo(iClass.getFullClassName()) <= 0) {
|
||||
mFullNames.add(i, value);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
mFullNames.add(value);
|
||||
}
|
||||
}
|
||||
String simpleName = value.getSimpleName();
|
||||
Pair<String, IClass> simplePair = new Pair<String, IClass>(simpleName, value);
|
||||
if (mSimpleNames.size() == 0) {
|
||||
mSimpleNames.add(simplePair);
|
||||
} else {
|
||||
boolean found = false;
|
||||
for (int i = 0; i < mSimpleNames.size(); i++) {
|
||||
Pair<String, IClass> pair = mSimpleNames.get(i);
|
||||
if (simpleName.compareTo(pair.first) <= 0) {
|
||||
mSimpleNames.add(i, simplePair);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
mSimpleNames.add(simplePair);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(String fullClassName) {
|
||||
IClass remove = mLoaded.remove(fullClassName);
|
||||
int index = Collections.binarySearch(mFullNames, remove,
|
||||
new Comparator<IClass>() {
|
||||
@Override
|
||||
public int compare(IClass o1, IClass o2) {
|
||||
return o1.getFullClassName().compareTo(o2.getFullClassName());
|
||||
}
|
||||
});
|
||||
if (index >= 0) {
|
||||
mFullNames.remove(index);
|
||||
}
|
||||
Pair<String, IClass> key = new Pair<String, IClass>(remove.getSimpleName(), remove);
|
||||
index = Collections.binarySearch(mSimpleNames, key,
|
||||
new Comparator<Pair<String, IClass>>() {
|
||||
@Override
|
||||
public int compare(Pair<String, IClass> o1,
|
||||
Pair<String, IClass> o2) {
|
||||
return o1.first.compareTo(o2.first);
|
||||
}
|
||||
});
|
||||
if (index >= 0) {
|
||||
mSimpleNames.remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* search class with binary search
|
||||
*
|
||||
* @param classes - sorted classes by name
|
||||
* @param prefix - prefix if class name
|
||||
*/
|
||||
@Nullable
|
||||
private List<Pair<String, IClass>> binarySearch(
|
||||
@NonNull List<Pair<String, IClass>> classes,
|
||||
@NonNull String prefix) {
|
||||
int start = -1, end = -1;
|
||||
int left = 0;
|
||||
int right = classes.size() - 1;
|
||||
while (left <= right) {
|
||||
int mid = (left + right) / 2;
|
||||
String name = classes.get(mid).first;
|
||||
String midValue = name.substring(0, Math.min(name.length(), prefix.length()));
|
||||
if (midValue.trim().toLowerCase(Locale.US).compareTo(prefix.toLowerCase(Locale.US)) < 0) {
|
||||
left = mid + 1;
|
||||
} else if (midValue.trim().toLowerCase(Locale.US).compareTo(prefix.toLowerCase(Locale.US)) > 0) {
|
||||
right = mid - 1;
|
||||
} else {
|
||||
start = mid;
|
||||
end = mid;
|
||||
while (start >= 0 &&
|
||||
classes.get(start)
|
||||
.first.trim().toLowerCase(Locale.US)
|
||||
.startsWith(prefix.toLowerCase(Locale.US))) {
|
||||
start--;
|
||||
}
|
||||
while (end < classes.size() &&
|
||||
classes.get(end)
|
||||
.first.trim().toLowerCase(Locale.US)
|
||||
.startsWith(prefix.toLowerCase(Locale.US))) {
|
||||
end++;
|
||||
}
|
||||
start++;
|
||||
end--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (end >= 0 && start >= 0 && end - start + 1 /*length*/ >= 1) {
|
||||
return classes.subList(start, end + 1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public List<IClass> find(@NonNull String simpleNamePrefix, @Nullable Filter<IClass> filter) {
|
||||
ArrayList<IClass> result = new ArrayList<>();
|
||||
List<Pair<String, IClass>> simple = binarySearch(mSimpleNames, simpleNamePrefix);
|
||||
if (simple != null) {
|
||||
for (Pair<String, IClass> c : simple) {
|
||||
if (filter != null) {
|
||||
if (!filter.accept(c.second)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
result.add(c.second);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kept as a backup if loading jar files fails for some reasons
|
||||
*/
|
||||
private ArrayList<Class> loadFromDexes(AndroidProject project) {
|
||||
ArrayList<Class> classes = new ArrayList<>();
|
||||
try {
|
||||
final List<String> dexes = ConstantsBridge.PROJECT_DEXES;
|
||||
final PathClassLoader loader = new PathClassLoader(TextUtils.join(File.pathSeparator, dexes), ClassLoader.getSystemClassLoader());
|
||||
if (dexes == null || dexes.size() <= 0) return classes;
|
||||
for (String dex : dexes) {
|
||||
final DexFile d = new DexFile(new File(dex));
|
||||
Enumeration<String> entries = d.entries();
|
||||
String name = null;
|
||||
while (entries.hasMoreElements() && (name = entries.nextElement()) != null) {
|
||||
while(name.startsWith(File.separator))
|
||||
name = name.substring(1);
|
||||
if(name.contains(File.separator))
|
||||
name = name.replace(File.separator, ".");
|
||||
Class<?> load = tryLoad(loader, name);
|
||||
if (load != null)
|
||||
classes.add(load);
|
||||
}
|
||||
d.close();
|
||||
}
|
||||
|
||||
ConstantsBridge.CLASS_LOAD_SUCCESS = true;
|
||||
return classes;
|
||||
} catch (Throwable th) {
|
||||
ConstantsBridge.CLASS_LOAD_SUCCESS = false;
|
||||
return classes;
|
||||
}
|
||||
}
|
||||
|
||||
private Class<?> tryLoad(ClassLoader loader, String name) {
|
||||
try {
|
||||
return loader.loadClass(name);
|
||||
} catch (ClassNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-95
@@ -1,95 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.internal;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.itsaky.androidide.interfaces.Filter;
|
||||
import com.itsaky.androidide.language.java.parser.internal.IClass;
|
||||
import com.itsaky.androidide.models.AndroidProject;
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Duy on 20-Jul-17.
|
||||
*/
|
||||
|
||||
public class JavaDexClassLoader {
|
||||
private static final Filter<IClass> mClassFilter = new Filter<IClass>() {
|
||||
@Override
|
||||
public boolean accept(IClass aClass) {
|
||||
return !(aClass.isAnnotation() || aClass.isInterface() || aClass.isEnum() || aClass.isMemberClass());
|
||||
}
|
||||
};
|
||||
private static final Filter<IClass> mEnumFilter = new Filter<IClass>() {
|
||||
@Override
|
||||
public boolean accept(IClass aClass) {
|
||||
return aClass.isEnum();
|
||||
}
|
||||
};
|
||||
private static final Filter<IClass> mInterfaceFilter = new Filter<IClass>() {
|
||||
@Override
|
||||
public boolean accept(IClass aClass) {
|
||||
return aClass.isInterface();
|
||||
}
|
||||
};
|
||||
private static final Filter<IClass> mAnnotationFilter = new Filter<IClass>() {
|
||||
@Override
|
||||
public boolean accept(IClass aClass) {
|
||||
return aClass.isAnnotation();
|
||||
}
|
||||
};
|
||||
|
||||
private JavaClassManager mClassReader;
|
||||
|
||||
public JavaDexClassLoader(File classpath) {
|
||||
mClassReader = JavaClassManager.getInstance(classpath);
|
||||
}
|
||||
|
||||
public JavaClassManager getClassReader() {
|
||||
return mClassReader;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public List<IClass> findAllWithPrefix(String simpleNamePrefix) {
|
||||
return mClassReader.find(simpleNamePrefix, null);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public List<IClass> findAllWithPrefix(@NonNull String simpleNamePrefix,
|
||||
@Nullable Filter<IClass> filter) {
|
||||
return mClassReader.find(simpleNamePrefix, filter);
|
||||
}
|
||||
|
||||
public List<IClass> findClasses(String simpleNamePrefix) {
|
||||
return mClassReader.find(simpleNamePrefix, mClassFilter);
|
||||
}
|
||||
|
||||
public List<IClass> findInterfaces(String simpleNamePrefix) {
|
||||
return mClassReader.find(simpleNamePrefix, mInterfaceFilter);
|
||||
}
|
||||
|
||||
public List<IClass> findEnums(String simpleNamePrefix) {
|
||||
return mClassReader.find(simpleNamePrefix, mEnumFilter);
|
||||
|
||||
}
|
||||
|
||||
public List<IClass> findAnnotations(String simpleNamePrefix) {
|
||||
return mClassReader.find(simpleNamePrefix, mAnnotationFilter);
|
||||
}
|
||||
|
||||
|
||||
public void loadAllClasses(AndroidProject projectFile) {
|
||||
mClassReader.loadFromProject(projectFile);
|
||||
}
|
||||
|
||||
public void updateClasses(List<IClass> classes) {
|
||||
for (IClass aClass : classes) {
|
||||
mClassReader.update(aClass);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateClass(IClass... classes) {
|
||||
for (IClass aClass : classes) {
|
||||
mClassReader.update(aClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,191 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.internal;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.blankj.utilcode.util.FileUtils;
|
||||
import com.sun.tools.javac.tree.JCTree;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.lang.model.element.Modifier;
|
||||
|
||||
/**
|
||||
* Created by Duy on 20-Jul-17.
|
||||
*/
|
||||
|
||||
public class JavaUtil {
|
||||
public static String getSimpleName(String className) {
|
||||
if (className.contains(".")) {
|
||||
className = className.substring(className.lastIndexOf(".") + 1);
|
||||
}
|
||||
|
||||
return getArrayTypeIfNeeded(className);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static String getPackageName(String classname) {
|
||||
if (classname.contains(".")) {
|
||||
return classname.substring(0, classname.lastIndexOf("."));
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static String getInverseName(String className) {
|
||||
String[] split = className.split(".");
|
||||
String result = "";
|
||||
for (String s : split) {
|
||||
result = s + result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public static boolean isValidClassName(@Nullable String name) {
|
||||
return name != null && name.matches("[A-Za-z_][A-Za-z0-9_]*");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getClassName(File javaSrc, String filePath) {
|
||||
if (filePath.startsWith(javaSrc.getPath())) {
|
||||
//hello/src/main/java
|
||||
//hello ->
|
||||
String filename = filePath.substring(filePath.indexOf(javaSrc.getPath()) + javaSrc.getPath().length() + 1);
|
||||
filename = filename.replace(File.separator, ".");
|
||||
if (filename.endsWith(".java")) {
|
||||
filename = filename.substring(0, filename.lastIndexOf(".java"));
|
||||
}
|
||||
return filename;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static int toJavaModifiers(Set<Modifier> modifierSet) {
|
||||
int modifiers = 0;
|
||||
for (Modifier modifier : modifierSet) {
|
||||
switch (modifier) {
|
||||
case FINAL:
|
||||
modifiers |= java.lang.reflect.Modifier.FINAL;
|
||||
break;
|
||||
case PROTECTED:
|
||||
modifiers |= java.lang.reflect.Modifier.PROTECTED;
|
||||
|
||||
break;
|
||||
case PRIVATE:
|
||||
modifiers |= java.lang.reflect.Modifier.PRIVATE;
|
||||
|
||||
break;
|
||||
case ABSTRACT:
|
||||
modifiers |= java.lang.reflect.Modifier.ABSTRACT;
|
||||
|
||||
break;
|
||||
case NATIVE:
|
||||
modifiers |= java.lang.reflect.Modifier.NATIVE;
|
||||
|
||||
case PUBLIC:
|
||||
modifiers |= java.lang.reflect.Modifier.PUBLIC;
|
||||
|
||||
case STATIC:
|
||||
modifiers |= java.lang.reflect.Modifier.STATIC;
|
||||
|
||||
case TRANSIENT:
|
||||
modifiers |= java.lang.reflect.Modifier.TRANSIENT;
|
||||
|
||||
break;
|
||||
case VOLATILE:
|
||||
modifiers |= java.lang.reflect.Modifier.VOLATILE;
|
||||
|
||||
break;
|
||||
case SYNCHRONIZED:
|
||||
modifiers |= java.lang.reflect.Modifier.SYNCHRONIZED;
|
||||
|
||||
break;
|
||||
case STRICTFP:
|
||||
modifiers |= java.lang.reflect.Modifier.STRICT;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static String findImportedClassName(@NonNull JCTree.JCCompilationUnit mUnit,
|
||||
@NonNull String className) {
|
||||
List<JCTree.JCImport> imports = mUnit.getImports();
|
||||
for (JCTree.JCImport jcImport : imports) {
|
||||
String fullName = jcImport.getQualifiedIdentifier().toString();
|
||||
if (fullName.equals(className) || fullName.endsWith("." + className)) {
|
||||
return fullName;
|
||||
}
|
||||
}
|
||||
return className;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static IClass jcTypeToClass(JCTree.JCCompilationUnit unit, JCTree type) {
|
||||
if (type == null) {
|
||||
return null;
|
||||
}
|
||||
String className;
|
||||
if (type instanceof JCTree.JCTypeApply) {
|
||||
return jcTypeToClass(unit, ((JCTree.JCTypeApply) type).getType());
|
||||
} else {
|
||||
className = type.toString();
|
||||
}
|
||||
|
||||
className = findImportedClassName(unit, className);
|
||||
return JavaClassManager.getInstance().getParsedClass(className);
|
||||
}
|
||||
public static ArrayList<String> listClassName(File src) {
|
||||
if (!src.exists()) return new ArrayList<String>();
|
||||
|
||||
Collection<File> files = FileUtils.listFilesInDirWithFilter(src, (f -> f.isFile() && f.getName().endsWith(".java")), true);
|
||||
|
||||
ArrayList<String> classes = new ArrayList<>();
|
||||
String srcPath = src.getPath();
|
||||
for (File file : files) {
|
||||
String javaPath = file.getPath();
|
||||
javaPath = javaPath.substring(srcPath.length() + 1, javaPath.length() - 5); //.java
|
||||
javaPath = javaPath.replace(File.separator, ".");
|
||||
classes.add(javaPath);
|
||||
}
|
||||
return classes;
|
||||
}
|
||||
|
||||
public static String getArrayTypeIfNeeded(String currentType) {
|
||||
if(currentType == null)
|
||||
return "";
|
||||
String type = new String(currentType);
|
||||
if(type.startsWith("[")) {
|
||||
type = type.substring(1).trim();
|
||||
if (type.equals("Z")) {
|
||||
type = "boolean[]";
|
||||
} else if (type.equals("B")) {
|
||||
type = "byte[]";
|
||||
} else if (type.equals("C")) {
|
||||
type = "char[]";
|
||||
} else if (type.equals("S")) {
|
||||
type = "short[]";
|
||||
} else if (type.equals("I")) {
|
||||
type = "int[]";
|
||||
} else if (type.equals("J")) {
|
||||
type = "long[]";
|
||||
} else if (type.equals("F")) {
|
||||
type = "float[]";
|
||||
} else if (type.equals("D")) {
|
||||
type = "double[]";
|
||||
} else {
|
||||
if(type.endsWith(";"))
|
||||
type = type.substring(0, type.length());
|
||||
type = type + "[]";
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.internal;
|
||||
|
||||
import android.os.FileObserver;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.itsaky.androidide.language.java.parser.model.PackageDescription;
|
||||
import com.itsaky.androidide.models.AndroidProject;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class PackageManager {
|
||||
private static final String TAG = "AutoCompletePackage";
|
||||
private PackageDescription root;
|
||||
private FileObserver fileObserver;
|
||||
|
||||
public PackageManager() {
|
||||
root = PackageDescription.root();
|
||||
}
|
||||
|
||||
public void init(AndroidProject projectFile, JavaClassManager classReader) {
|
||||
ArrayList<IClass> classes = classReader.getAllClasses();
|
||||
for (IClass clazz : classes) {
|
||||
root.put(clazz.getFullClassName());
|
||||
}
|
||||
|
||||
final String parentPath = new File(projectFile.getMainModulePath(), "src/main/java").getPath();
|
||||
fileObserver = new FileObserver(parentPath) {
|
||||
@Override
|
||||
public void onEvent(int event, String path) {
|
||||
remove(path, parentPath);
|
||||
add(path, parentPath);
|
||||
}
|
||||
};
|
||||
fileObserver.startWatching();
|
||||
}
|
||||
|
||||
private void add(String child, String parent) {
|
||||
if (child.startsWith(parent)) {
|
||||
child = child.substring(parent.length() + 1);
|
||||
child = child.replace(File.separator, ".");
|
||||
root.put(child);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PackageDescription remove(String child, String parent) {
|
||||
if (child.startsWith(parent)) {
|
||||
child = child.substring(parent.length() + 1);
|
||||
child = child.replace(File.separator, ".");
|
||||
return root.remove(child);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
if (fileObserver != null) fileObserver.stopWatching();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PackageDescription trace(String child) {
|
||||
return this.root.get(child);
|
||||
}
|
||||
}
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.internal;
|
||||
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
|
||||
public interface SuggestItem {
|
||||
|
||||
public String getName();
|
||||
public String getDescription();
|
||||
public String getReturnType();
|
||||
public char getTypeHeader();
|
||||
public int getSuggestionPriority();
|
||||
public void onSelectThis(CodeEditor editor);
|
||||
}
|
||||
-295
@@ -1,295 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.model;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.itsaky.androidide.language.java.parser.expression.PackageImporter;
|
||||
import com.itsaky.androidide.language.java.parser.internal.IClass;
|
||||
import com.itsaky.androidide.language.java.parser.internal.IField;
|
||||
import com.itsaky.androidide.language.java.parser.internal.IJavaDocCommentable;
|
||||
import com.itsaky.androidide.language.java.parser.internal.IMethod;
|
||||
import com.itsaky.androidide.language.java.parser.internal.JavaClassManager;
|
||||
import com.itsaky.androidide.language.java.parser.internal.JavaUtil;
|
||||
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
|
||||
import io.github.rosemoe.editor.text.CharPosition;
|
||||
import io.github.rosemoe.editor.text.Content;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ClassDescription extends JavaSuggestItemImpl implements IClass {
|
||||
private static final String TAG = "ClassDescription";
|
||||
@NonNull
|
||||
private final String mClassName;
|
||||
private final ArrayList<ConstructorDescription> mConstructors = new ArrayList<>();
|
||||
private final ArrayList<IField> mFields = new ArrayList<>();
|
||||
private final ArrayList<IMethod> mMethods = new ArrayList<>();
|
||||
private final ArrayList<IClass> mImplements = new ArrayList<>();
|
||||
private final int mModifiers;
|
||||
private final boolean mPrimitive, mAnnotation, mEum, isMemberClass;
|
||||
|
||||
//init later
|
||||
@Nullable
|
||||
private IClass mSuperClass;
|
||||
|
||||
public ClassDescription(Class c) {
|
||||
mClassName = c.getName();
|
||||
mModifiers = c.getModifiers();
|
||||
mPrimitive = c.isPrimitive();
|
||||
mAnnotation = c.isAnnotation();
|
||||
isMemberClass = c.isMemberClass();
|
||||
mEum = c.isEnum();
|
||||
}
|
||||
|
||||
public ClassDescription(String className, int modifiers,
|
||||
boolean isPrimitive,
|
||||
boolean isAnnotation,
|
||||
boolean isEnum,
|
||||
boolean isMember) {
|
||||
this.mClassName = className;
|
||||
this.mModifiers = modifiers;
|
||||
this.mPrimitive = isPrimitive;
|
||||
this.mAnnotation = isAnnotation;
|
||||
this.isMemberClass = isMember;
|
||||
this.mEum = isEnum;
|
||||
}
|
||||
|
||||
public static ClassDescription ofObject() {
|
||||
ClassDescription desc = new ClassDescription(Object.class);
|
||||
desc.initMembers(Object.class);
|
||||
return desc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMemberClass() {
|
||||
return isMemberClass;
|
||||
}
|
||||
|
||||
private String javadoc;
|
||||
@Override
|
||||
public IJavaDocCommentable setHtmlJavaDoc(String javadoc) {
|
||||
this.javadoc = javadoc;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJavaDoc() {
|
||||
return javadoc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
if (getPackageName().isEmpty()) {
|
||||
return getSimpleName();
|
||||
}
|
||||
return getSimpleName() + " (" + getPackageName() + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String getDescription() {
|
||||
return getPackageName().replace("$", ".");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReturnType() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public char getTypeHeader() {
|
||||
return 'c';
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSuggestionPriority() {
|
||||
return JavaSuggestItemImpl.CLASS_DESC;
|
||||
}
|
||||
|
||||
public void onSelectThis(CodeEditor editorView) {
|
||||
try {
|
||||
final int length = getIncomplete().length();
|
||||
final int start = editorView.getCursor().getLeft() - length;
|
||||
final int end = editorView.getCursor().getLeft();
|
||||
final CharPosition s = editorView.getText().getIndexer().getCharPosition(start);
|
||||
Content editable = editorView.getText();
|
||||
editable.delete(start, end);
|
||||
editable.insert(s.line, s.column, getSimpleName());
|
||||
PackageImporter.importClass(editable, getFullClassName());
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSimpleName() {
|
||||
return JavaUtil.getSimpleName(mClassName).replace("$", ".");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFullClassName() {
|
||||
return mClassName;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public IClass getSuperclass() {
|
||||
return mSuperClass;
|
||||
}
|
||||
|
||||
public String getPackageName() {
|
||||
return JavaUtil.getPackageName(mClassName);
|
||||
}
|
||||
|
||||
public ArrayList<ConstructorDescription> getConstructors() {
|
||||
return mConstructors;
|
||||
}
|
||||
|
||||
public ArrayList<IField> getFields() {
|
||||
return mFields;
|
||||
}
|
||||
|
||||
public void addConstructor(ConstructorDescription constructorDescription) {
|
||||
this.mConstructors.add(constructorDescription);
|
||||
}
|
||||
|
||||
public void addField(IField fieldDescription) {
|
||||
mFields.add(fieldDescription);
|
||||
}
|
||||
|
||||
public void addMethod(IMethod methodDescription) {
|
||||
mMethods.add(methodDescription);
|
||||
}
|
||||
|
||||
public List<IMethod> getMethods() {
|
||||
return mMethods;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mClassName;
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public ArrayList<SuggestItem> getMember(String prefix) {
|
||||
ArrayList<SuggestItem> result = new ArrayList<>();
|
||||
for (ConstructorDescription constructor : mConstructors) {
|
||||
if (!prefix.isEmpty()) {
|
||||
if (constructor.getName().startsWith(prefix)) {
|
||||
result.add(constructor);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (IField field : mFields) {
|
||||
if (prefix.isEmpty() || field.getFieldName().startsWith(prefix)) {
|
||||
result.add(field);
|
||||
}
|
||||
}
|
||||
getMethods(result, prefix);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void getMethods(ArrayList<SuggestItem> result, String prefix) {
|
||||
for (IMethod method : mMethods) {
|
||||
if (prefix.isEmpty() || method.getMethodName().startsWith(prefix)) {
|
||||
result.add(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getModifiers() {
|
||||
return mModifiers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInterface() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public boolean isEnum() {
|
||||
return mEum;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPrimitive() {
|
||||
return mPrimitive;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAnnotation() {
|
||||
return mAnnotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IMethod getMethod(String methodName, IClass[] argsType) {
|
||||
for (IMethod method : mMethods) {
|
||||
if (method.getMethodName().equals(methodName)) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
|
||||
if (getSuperclass() != null) {
|
||||
return getSuperclass().getMethod(methodName, argsType);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IField getField(String name) {
|
||||
for (IField field : mFields) {
|
||||
if (field.getFieldName().equals(name)) {
|
||||
return field;
|
||||
}
|
||||
}
|
||||
if (getSuperclass() != null) {
|
||||
return getSuperclass().getField(name);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void initMembers(Class c) {
|
||||
mImplements.clear();
|
||||
Class[] interfaces = c.getInterfaces();
|
||||
for (Class anInterface : interfaces) {
|
||||
IClass implement = JavaClassManager.getInstance().getClassWrapper(anInterface);
|
||||
mImplements.add(implement);
|
||||
}
|
||||
|
||||
if (c.getSuperclass() != null) {
|
||||
mSuperClass = JavaClassManager.getInstance().getClassWrapper(c.getSuperclass());
|
||||
} else {
|
||||
if (!getFullClassName().equals(Object.class.getName())
|
||||
&& mSuperClass == null) {
|
||||
mSuperClass = JavaClassManager.getInstance().getParsedClass(Object.class.getName());
|
||||
}
|
||||
}
|
||||
|
||||
for (Constructor constructor : c.getConstructors()) {
|
||||
if (Modifier.isPublic(constructor.getModifiers())) {
|
||||
addConstructor(new ConstructorDescription(constructor));
|
||||
}
|
||||
}
|
||||
|
||||
for (Field field : c.getFields()) {
|
||||
if (Modifier.isPublic(field.getModifiers())) {
|
||||
if (!field.getName().equals(field.getDeclaringClass().getName())) {
|
||||
addField(new FieldDescription(field));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Method method : c.getMethods()) {
|
||||
if (Modifier.isPublic(method.getModifiers())) {
|
||||
addMethod(new MethodDescription(method));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setSuperclass(IClass superclass) {
|
||||
this.mSuperClass = superclass;
|
||||
}
|
||||
}
|
||||
-124
@@ -1,124 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.model;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import com.blankj.utilcode.util.ThrowableUtils;
|
||||
import com.itsaky.androidide.language.java.parser.expression.PackageImporter;
|
||||
import com.itsaky.androidide.language.java.parser.internal.IClass;
|
||||
import com.itsaky.androidide.language.java.parser.internal.IJavaDocCommentable;
|
||||
import com.itsaky.androidide.language.java.parser.internal.JavaClassManager;
|
||||
import com.itsaky.androidide.language.java.parser.internal.JavaUtil;
|
||||
import io.github.rosemoe.editor.text.CharPosition;
|
||||
import io.github.rosemoe.editor.text.Content;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ConstructorDescription extends JavaSuggestItemImpl implements IJavaDocCommentable {
|
||||
private ArrayList<String> mParameterTypes = new ArrayList<>();
|
||||
private String mConstructorName;
|
||||
|
||||
public ConstructorDescription(Constructor constructor) {
|
||||
mConstructorName = constructor.getName();
|
||||
Class[] parameterTypes = constructor.getParameterTypes();
|
||||
for (Class parameterType : parameterTypes) {
|
||||
mParameterTypes.add(parameterType.getName());
|
||||
}
|
||||
}
|
||||
|
||||
public ConstructorDescription(String name, List<IClass> paramTypes) {
|
||||
mConstructorName = name;
|
||||
mParameterTypes.addAll(paramTypes.stream().filter(c -> c != null).map(c -> c.getFullClassName()).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
public ConstructorDescription(String name, ArrayList<String> paramTypes) {
|
||||
mConstructorName = name;
|
||||
mParameterTypes.addAll(paramTypes);
|
||||
}
|
||||
|
||||
private String javadoc;
|
||||
@Override
|
||||
public IJavaDocCommentable setHtmlJavaDoc(String javadoc) {
|
||||
this.javadoc = javadoc;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJavaDoc() {
|
||||
return javadoc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectThis(@NonNull CodeEditor editorView) {
|
||||
try {
|
||||
final int length = getIncomplete().length();
|
||||
final int cursor = editorView.getCursor().getLeft();
|
||||
final int start = cursor - length;
|
||||
Content editable = editorView.getText();
|
||||
final CharPosition sPos = editable.getIndexer().getCharPosition(start);
|
||||
final CharPosition cPos = editable.getIndexer().getCharPosition(cursor);
|
||||
|
||||
if (getParameterTypes().size() > 0) {
|
||||
editable.replace(sPos.line, sPos.column, cPos.line, cPos.column, getSimpleName() + "()");
|
||||
CharPosition select = editable.getIndexer().getCharPosition(start + getSimpleName().length() + 1);
|
||||
editorView.setSelection(select.line, select.column);
|
||||
} else {
|
||||
String text = getSimpleName() + "();";
|
||||
editable.replace(sPos.line, sPos.column, cPos.line, cPos.column, text);
|
||||
CharPosition select = editable.getIndexer().getCharPosition(start + text.length());
|
||||
editorView.setSelection(select.line, select.column);
|
||||
}
|
||||
|
||||
PackageImporter.importClass(editorView.getText(), mConstructorName);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return getSimpleName() + "(" + paramsToString(getParameterTypes()) + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public char getTypeHeader() {
|
||||
return 'c';
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Class constructor";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReturnType() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getSimpleName() + "()";
|
||||
}
|
||||
|
||||
private String paramsToString(@NonNull ArrayList<String> parameterTypes) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
boolean firstTime = true;
|
||||
for (String parameterType : parameterTypes) {
|
||||
if (firstTime) {
|
||||
firstTime = false;
|
||||
} else {
|
||||
result.append(",");
|
||||
}
|
||||
result.append(parameterType);
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public ArrayList<String> getParameterTypes() {
|
||||
return mParameterTypes;
|
||||
}
|
||||
|
||||
private String getSimpleName() {
|
||||
return JavaUtil.getSimpleName(mConstructorName);
|
||||
}
|
||||
}
|
||||
-128
@@ -1,128 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.model;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.itsaky.androidide.language.java.parser.internal.IClass;
|
||||
import com.itsaky.androidide.language.java.parser.internal.IField;
|
||||
import com.itsaky.androidide.language.java.parser.internal.IJavaDocCommentable;
|
||||
import com.itsaky.androidide.language.java.parser.internal.JavaClassManager;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
public class FieldDescription extends JavaSuggestItemImpl implements Member, IField {
|
||||
@NonNull
|
||||
private String mName;
|
||||
@NonNull
|
||||
private IClass mType;
|
||||
private int mModifiers;
|
||||
@Nullable
|
||||
private String mValue;
|
||||
|
||||
public FieldDescription(int modifiers, IClass type, @NonNull String name, String initValue) {
|
||||
mName = name;
|
||||
mType = type;
|
||||
mModifiers = modifiers;
|
||||
mValue = initValue;
|
||||
}
|
||||
|
||||
public FieldDescription(Field field) {
|
||||
mName = field.getName();
|
||||
mType = JavaClassManager.getInstance().getClassWrapper(field.getType());
|
||||
mModifiers = field.getModifiers();
|
||||
|
||||
if (Modifier.isStatic(mModifiers)) {
|
||||
try {
|
||||
boolean primitive = field.getType().isPrimitive();
|
||||
Object o = field.get(null);
|
||||
if (primitive) {
|
||||
mValue = o.toString();
|
||||
} else {
|
||||
mValue = o.getClass().getSimpleName();
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String javadoc;
|
||||
@Override
|
||||
public IJavaDocCommentable setHtmlJavaDoc(String javadoc) {
|
||||
this.javadoc = javadoc;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJavaDoc() {
|
||||
return javadoc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectThis(@NonNull CodeEditor editorView) {
|
||||
insertImpl(editorView, mName);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public char getTypeHeader() {
|
||||
return 'f';
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
if (mValue == null) {
|
||||
return mName;
|
||||
} else {
|
||||
return mName + "(" + mValue + ")";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return mValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReturnType() {
|
||||
if (mType == null) {
|
||||
return "";
|
||||
}
|
||||
return mType.getSimpleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSuggestionPriority() {
|
||||
return JavaSuggestItemImpl.FIELD_DESC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getModifiers() {
|
||||
return mModifiers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFieldName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IClass getFieldType() {
|
||||
return mType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFieldModifiers() {
|
||||
return mModifiers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getFieldValue() {
|
||||
return mValue;
|
||||
}
|
||||
}
|
||||
-60
@@ -1,60 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.model;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import com.blankj.utilcode.util.ThrowableUtils;
|
||||
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
|
||||
import io.github.rosemoe.editor.text.CharPosition;
|
||||
import io.github.rosemoe.editor.text.Content;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
|
||||
public abstract class JavaSuggestItemImpl implements SuggestItem, Cloneable {
|
||||
public static final int FIELD_DESC = 0;
|
||||
public static final int METHOD_DESC = 1;
|
||||
public static final int CLASS_DESC = 2;
|
||||
public static final int OTHER_DESC = 3;
|
||||
|
||||
private CodeEditor editor;
|
||||
private String incomplete;
|
||||
|
||||
@Override
|
||||
public int getSuggestionPriority() {
|
||||
return OTHER_DESC;
|
||||
}
|
||||
|
||||
public CodeEditor getEditor() {
|
||||
return editor;
|
||||
}
|
||||
|
||||
public void setEditor(CodeEditor editor) {
|
||||
this.editor = editor;
|
||||
}
|
||||
|
||||
public String getIncomplete() {
|
||||
return incomplete;
|
||||
}
|
||||
|
||||
public void setIncomplete(@NonNull String incomplete) {
|
||||
this.incomplete = incomplete;
|
||||
}
|
||||
|
||||
protected void insertImpl(CodeEditor editorView, String text) {
|
||||
|
||||
try {
|
||||
final int length = getIncomplete().length();
|
||||
final int cursor = getEditor().getCursor().getLeft();
|
||||
final int start = cursor - length;
|
||||
Content editable = editorView.getText();
|
||||
CharPosition s = editable.getIndexer().getCharPosition(start);
|
||||
CharPosition c = editable.getIndexer().getCharPosition(cursor);
|
||||
editable.replace(s.line, s.column, c.line, c.column, text);
|
||||
CharPosition select = editable.getIndexer().getCharPosition(start + text.length());
|
||||
editorView.setSelection(select.line, select.column);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getName();
|
||||
}
|
||||
}
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.model;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
|
||||
public class KeywordDescription extends JavaSuggestItemImpl {
|
||||
private final String keyword;
|
||||
|
||||
public KeywordDescription(String keyword) {
|
||||
this.keyword = keyword;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getName() {
|
||||
return keyword;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getReturnType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char getTypeHeader() {
|
||||
return 'k';
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectThis(@NonNull CodeEditor iEditAreaView) {
|
||||
insertImpl(iEditAreaView, keyword + " ");
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.model;
|
||||
|
||||
public interface Member {
|
||||
String getName();
|
||||
|
||||
int getModifiers();
|
||||
}
|
||||
-200
@@ -1,200 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.model;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.itsaky.androidide.language.java.parser.internal.IClass;
|
||||
import com.itsaky.androidide.language.java.parser.internal.IJavaDocCommentable;
|
||||
import com.itsaky.androidide.language.java.parser.internal.IMethod;
|
||||
import com.itsaky.androidide.language.java.parser.internal.JavaClassManager;
|
||||
import com.itsaky.androidide.language.java.parser.internal.JavaUtil;
|
||||
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
|
||||
import io.github.rosemoe.editor.text.CharPosition;
|
||||
import io.github.rosemoe.editor.text.Content;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MethodDescription extends JavaSuggestItemImpl implements Member, SuggestItem, IMethod {
|
||||
private String mMethodName;
|
||||
private int mModifiers;
|
||||
private ArrayList<String> mParameterTypes = new ArrayList<>();
|
||||
private ArrayList<IClass> mParameters = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Return type of method, null if it is constructor
|
||||
*/
|
||||
@Nullable
|
||||
private IClass mReturnType;
|
||||
|
||||
public MethodDescription(String name, IClass returnType,
|
||||
int modifiers, ArrayList<String> parameterTypes) {
|
||||
mMethodName = name;
|
||||
mReturnType = returnType;
|
||||
mModifiers = modifiers;
|
||||
mParameterTypes = parameterTypes;
|
||||
}
|
||||
|
||||
public MethodDescription(@NonNull String methodName,
|
||||
int modifiers,
|
||||
@NonNull List<IClass> parameters,
|
||||
@Nullable IClass returnType) {
|
||||
mMethodName = methodName;
|
||||
mModifiers = modifiers;
|
||||
mParameters.addAll(parameters);
|
||||
mReturnType = returnType;
|
||||
}
|
||||
|
||||
public MethodDescription(@NonNull Method method) {
|
||||
mMethodName = method.getName();
|
||||
mModifiers = method.getModifiers();
|
||||
mReturnType = JavaClassManager.getInstance().getClassWrapper(method.getReturnType());
|
||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||
for (Class<?> parameterType : parameterTypes) {
|
||||
mParameterTypes.add(parameterType.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private String javadoc;
|
||||
@Override
|
||||
public IJavaDocCommentable setHtmlJavaDoc(String javadoc) {
|
||||
this.javadoc = javadoc;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJavaDoc() {
|
||||
return javadoc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectThis(@NonNull CodeEditor editorView) {
|
||||
try {
|
||||
final int length = getIncomplete().length();
|
||||
final int start = editorView.getCursor().getLeft() - length;
|
||||
Content editable = editorView.getText();
|
||||
CharPosition s = editable.getIndexer().getCharPosition(start);
|
||||
editable.delete(start, editorView.getCursor().getLeft());
|
||||
String simpleName = JavaUtil.getSimpleName(mMethodName);
|
||||
String text = simpleName + "()" + (shouldAddSemicolon(getEditor()) ? ";" : "");
|
||||
if (getParameterTypes().size() > 0) {
|
||||
editable.insert(s.line, s.column, text);
|
||||
CharPosition select = editable.getIndexer().getCharPosition(start + simpleName.length() + 1);
|
||||
editorView.setSelection(select.line, select.column);
|
||||
} else {
|
||||
editable.insert(s.line, s.column, text);
|
||||
CharPosition select = editable.getIndexer().getCharPosition(start + text.length());
|
||||
editorView.setSelection(select.line, select.column);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldAddSemicolon(CodeEditor editor) {
|
||||
int cursor = editor.getCursor().getLeft();
|
||||
String text = editor.getText().toString();
|
||||
while (cursor < text.length()) {
|
||||
char c = text.charAt(cursor);
|
||||
if (c == ' ' || c == '\n') {
|
||||
cursor++;
|
||||
continue;
|
||||
}
|
||||
if (c == ';') {
|
||||
return false;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (getMethodReturnType() == null) {
|
||||
return true;
|
||||
}
|
||||
if (void.class.getName().equals(getMethodReturnType().getFullClassName())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public ArrayList<String> getParameterTypes() {
|
||||
return mParameterTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char getTypeHeader() {
|
||||
return 'm';
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return mMethodName + "(" + paramsToString() + ")";
|
||||
}
|
||||
|
||||
private String paramsToString() {
|
||||
StringBuilder result = new StringBuilder();
|
||||
boolean firstTime = true;
|
||||
ArrayList<String> parameterTypes = getParameterTypes();
|
||||
for (String parameterType : parameterTypes) {
|
||||
if (firstTime) {
|
||||
firstTime = false;
|
||||
} else {
|
||||
result.append(",");
|
||||
|
||||
}
|
||||
result.append(JavaUtil.getSimpleName(parameterType));
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReturnType() {
|
||||
if (mReturnType == null) {
|
||||
return "";
|
||||
}
|
||||
return mReturnType.getSimpleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSuggestionPriority() {
|
||||
return METHOD_DESC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder params = new StringBuilder();
|
||||
for (int i = 0; i < mParameterTypes.size(); i++) {
|
||||
String parameterType = mParameterTypes.get(i);
|
||||
if (i == mParameterTypes.size() - 1) {
|
||||
params.append(JavaUtil.getSimpleName(parameterType));
|
||||
break;
|
||||
}
|
||||
params.append(JavaUtil.getSimpleName(parameterType)).append(",");
|
||||
}
|
||||
return mMethodName + "(" + params.toString() + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getModifiers() {
|
||||
return mModifiers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMethodName() {
|
||||
return mMethodName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IClass[] getMethodParameterTypes() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IClass getMethodReturnType() {
|
||||
return mReturnType;
|
||||
}
|
||||
}
|
||||
-109
@@ -1,109 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.model;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import android.text.Editable;
|
||||
import java.util.HashMap;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
import io.github.rosemoe.editor.text.Content;
|
||||
import io.github.rosemoe.editor.text.CharPosition;
|
||||
|
||||
public class PackageDescription extends JavaSuggestItemImpl {
|
||||
private String mName;
|
||||
private PackageDescription mParentPkg;
|
||||
private HashMap<String, PackageDescription> mChild = new HashMap<>();
|
||||
|
||||
private PackageDescription(String childName, PackageDescription parent) {
|
||||
this.mName = childName;
|
||||
this.mParentPkg = parent;
|
||||
}
|
||||
|
||||
public static PackageDescription root() {
|
||||
return new PackageDescription("", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectThis(@NonNull CodeEditor editorView) {
|
||||
try {
|
||||
final int cursor = getEditor().getCursor().getLeft();
|
||||
final int length = getIncomplete().length();
|
||||
final int start = cursor - length;
|
||||
Content editable = editorView.getText();
|
||||
CharPosition s = editable.getIndexer().getCharPosition(start);
|
||||
CharPosition c = editable.getIndexer().getCharPosition(cursor);
|
||||
String text;
|
||||
text = isLeaf() ? mName.replace("$", ".") : mName + ".";
|
||||
editable.replace(s.line, s.column, c.line, c.column, text);
|
||||
CharPosition select = editable.getIndexer().getCharPosition(start + text.length());
|
||||
editorView.setSelection(select.line, select.column);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public char getTypeHeader() {
|
||||
return 'p';
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReturnType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public HashMap<String, PackageDescription> getChild() {
|
||||
return mChild;
|
||||
}
|
||||
|
||||
private boolean isRoot() {
|
||||
return mName.isEmpty() || mParentPkg == null;
|
||||
}
|
||||
|
||||
private boolean isLeaf() {
|
||||
return mChild.isEmpty();
|
||||
}
|
||||
|
||||
public PackageDescription get(String key) {
|
||||
if (!key.contains(".")) {
|
||||
return mChild.get(key);
|
||||
} else {
|
||||
return get(key.substring(0, key.indexOf(".")))
|
||||
.get(key.substring(key.indexOf(".") + 1));
|
||||
}
|
||||
}
|
||||
|
||||
public void put(String pkg) {
|
||||
if (pkg.contains(".")) {
|
||||
String first = pkg.substring(0, pkg.indexOf("."));
|
||||
if (get(first) == null) {
|
||||
put(first);
|
||||
}
|
||||
get(first).put(pkg.substring(pkg.indexOf(".") + 1));
|
||||
} else {
|
||||
this.mChild.put(pkg, new PackageDescription(pkg, this));
|
||||
}
|
||||
}
|
||||
|
||||
public PackageDescription remove(String child) {
|
||||
if (child.contains(".")) {
|
||||
return this.mChild.remove(child.substring(0, child.indexOf(".")));
|
||||
} else {
|
||||
return this.mChild.remove(child);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mName;
|
||||
}
|
||||
}
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.model;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import io.github.rosemoe.editor.text.CharPosition;
|
||||
import io.github.rosemoe.editor.text.Content;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
|
||||
/**
|
||||
* Created by Duy on 20-Jul-17.
|
||||
*/
|
||||
|
||||
public class PrimitiveArrayConstructorDescription extends JavaSuggestItemImpl {
|
||||
private String name;
|
||||
|
||||
public PrimitiveArrayConstructorDescription(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectThis(@NonNull CodeEditor editorView) {
|
||||
try {
|
||||
final int length = getIncomplete().length();
|
||||
final int cursor = getEditor().getCursor().getLeft();
|
||||
final int start = cursor - length;
|
||||
Content editable = editorView.getText();
|
||||
CharPosition s = editable.getIndexer().getCharPosition(start);
|
||||
CharPosition c = editable.getIndexer().getCharPosition(cursor);
|
||||
editable.replace(s.line, s.column, c.line, c.column, name + "[]");
|
||||
CharPosition select = editable.getIndexer().getCharPosition(start + name.length() + 1);
|
||||
editorView.setSelection(select.line, select.column);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name + "[]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public char getTypeHeader() {
|
||||
return 'a';
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReturnType() {
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name + "[]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.model;
|
||||
|
||||
public class Statement {
|
||||
private final String statement;
|
||||
private final int offset;
|
||||
|
||||
public Statement(String statement, int offset) {
|
||||
this.statement = statement;
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
public String getStatement() {
|
||||
return statement;
|
||||
}
|
||||
|
||||
public int getOffset() {
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.util;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
import android.widget.EditText;
|
||||
import com.itsaky.androidide.language.java.parser.expression.PackageImporter;
|
||||
import com.itsaky.androidide.language.java.parser.expression.PatternFactory;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Matcher;
|
||||
import io.github.rosemoe.editor.text.CharPosition;
|
||||
|
||||
/**
|
||||
* Created by Duy on 20-Jul-17.
|
||||
*/
|
||||
|
||||
public class EditorUtil {
|
||||
|
||||
private static final String TAG = "EditorUtil";
|
||||
|
||||
public EditorUtil() {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getCurrentPackage(String editText) {
|
||||
Matcher matcher = PatternFactory.PACKAGE.matcher(editText);
|
||||
if (matcher.find()) {
|
||||
return matcher.group();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getCurrentClassName(String editor) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static String getCurrentClassSimpleName(String editor) {
|
||||
String className = getCurrentClassName(editor);
|
||||
int i = className.indexOf(".");
|
||||
if (i == -1) return className;
|
||||
else {
|
||||
return className.substring(className.lastIndexOf(".") + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static ArrayList<String> getPossibleClassName(String source, String simpleName, String prefix) {
|
||||
Log.d(TAG, "getPossibleClassName() called with: simpleName = [" + simpleName + "], prefix = [" + prefix + "]");
|
||||
|
||||
ArrayList<String> classList = new ArrayList<>();
|
||||
String importedClassName = PackageImporter.getImportedClassName(source, simpleName);
|
||||
Log.d(TAG, "getPossibleClassName importedClassName = " + importedClassName);
|
||||
if (importedClassName != null) {
|
||||
classList.add(importedClassName);
|
||||
} else {
|
||||
if (!prefix.contains(".")) {
|
||||
classList.add(getCurrentClassName(source)); //current member
|
||||
if (simpleName != null && !simpleName.isEmpty()) {
|
||||
classList.add("java.lang." + simpleName); //default java.lang package
|
||||
} else if (!prefix.isEmpty()) {
|
||||
classList.add("java.lang." + prefix);
|
||||
}
|
||||
} else {
|
||||
classList.add(prefix);
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "getPossibleClassName() returned: " + classList);
|
||||
return classList;
|
||||
}
|
||||
|
||||
public static String getLine(EditText editText, int pos) {
|
||||
if (pos < 0 || pos > editText.length()) return "";
|
||||
int line = LineUtils.getLineFromIndex(pos, editText.getLayout().getLineCount(), editText.getLayout());
|
||||
|
||||
int lineStart = editText.getLayout().getLineStart(line);
|
||||
int lineEnd = editText.getLayout().getLineEnd(line);
|
||||
return editText.getText().subSequence(lineStart, lineEnd).toString();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getWord(EditText editText, int pos, boolean removeParentheses) {
|
||||
String line = getLine(editText, pos).trim();
|
||||
return getLastWord(line, removeParentheses);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static String getWord(EditText editText, int pos) {
|
||||
String line = getLine(editText, pos).trim();
|
||||
return getLastWord(line, false);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static String getLastWord(String line, boolean removeParentheses) {
|
||||
String result = PatternFactory.lastMatchStr(line, PatternFactory.WORD);
|
||||
if (result != null) {
|
||||
return removeParentheses ? result.replaceAll(".*\\(", "") : result;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getPreWord(EditText editor, int pos) {
|
||||
String line = getLine(editor, pos);
|
||||
String[] split = line.split(PatternFactory.SPLIT_NON_WORD_STR);
|
||||
return split.length >= 2 ? split[split.length - 2] : null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static String getLineBeforeCursor(@NonNull CodeEditor editor, final int cursor) {
|
||||
int start = cursor - 1;
|
||||
String text = editor.getText().toString();
|
||||
while (start > 0 && text.charAt(start) != '\n') {
|
||||
start = start - 1;
|
||||
if (start >= 0 && text.charAt(start) == '\n'){
|
||||
start++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
CharPosition s = editor.getText().getIndexer().getCharPosition(start);
|
||||
CharPosition c = editor.getText().getIndexer().getCharPosition(cursor - 1);
|
||||
return editor.getText().subContent(s.line, s.column, c.line, c.column).toString();
|
||||
}
|
||||
|
||||
public static String trimLeft(String code) {
|
||||
if (code == null) {
|
||||
return null;
|
||||
}
|
||||
if (code.length() == 0) {
|
||||
return "";
|
||||
}
|
||||
int index = 0;
|
||||
while (index < code.length()) {
|
||||
if (Character.isWhitespace(code.charAt(index))) {
|
||||
index++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index == code.length()) {
|
||||
return "";
|
||||
}
|
||||
return code.substring(index);
|
||||
}
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.util;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import android.text.Layout;
|
||||
import android.text.TextUtils;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ScrollView;
|
||||
|
||||
public class LineUtils {
|
||||
private boolean[] toCountLinesArray;
|
||||
private int[] realLines;
|
||||
|
||||
public static int getYAtLine(ScrollView scrollView, int lineCount, int line) {
|
||||
if (lineCount == 0) return 0;
|
||||
return scrollView.getChildAt(0).getHeight() / lineCount * line;
|
||||
}
|
||||
|
||||
public static int getFirstVisibleLine(@NonNull ScrollView scrollView, int childHeight,
|
||||
int lineCount) throws ArithmeticException {
|
||||
if (childHeight == 0) return 0;
|
||||
int line = (scrollView.getScrollY() * lineCount) / childHeight;
|
||||
if (line < 0) line = 0;
|
||||
return line;
|
||||
}
|
||||
|
||||
public static int getLastVisibleLine(@NonNull ScrollView scrollView,
|
||||
int childHeight, int lineCount) {
|
||||
if (childHeight == 0) return 0;
|
||||
int line = (scrollView.getScrollY() * lineCount) / childHeight;
|
||||
if (line > lineCount) line = lineCount;
|
||||
return line;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the lineInfo from the index of the letter in the text
|
||||
*/
|
||||
public static int getLineFromIndex(int index, int lineCount, Layout layout) {
|
||||
int line;
|
||||
int currentIndex = 0;
|
||||
|
||||
for (line = 0; line < lineCount; line++) {
|
||||
currentIndex += layout.getLineEnd(line) - layout.getLineStart(line);
|
||||
if (currentIndex >= index) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
public static int getStartIndexAtLine(EditText editable, int line) {
|
||||
Layout layout = editable.getLayout();
|
||||
if (layout != null) {
|
||||
return layout.getLineStart(line);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public boolean[] getGoodLines() {
|
||||
return toCountLinesArray;
|
||||
}
|
||||
|
||||
public int[] getRealLines() {
|
||||
return realLines;
|
||||
}
|
||||
|
||||
public void updateHasNewLineArray(int lineCount, Layout layout, String text) {
|
||||
boolean[] hasNewLineArray = new boolean[lineCount];
|
||||
toCountLinesArray = new boolean[lineCount];
|
||||
realLines = new int[lineCount];
|
||||
if (TextUtils.isEmpty(text)) {
|
||||
toCountLinesArray[0] = false;
|
||||
realLines[0] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (lineCount == 0) return;
|
||||
|
||||
int i;
|
||||
|
||||
// for every lineInfo on the edittext
|
||||
for (i = 0; i < lineCount; i++) {
|
||||
// check if this lineInfo contains "\n"
|
||||
if (layout.getLineEnd(i) == 0) {
|
||||
hasNewLineArray[i] = false;
|
||||
} else {
|
||||
hasNewLineArray[i] = text.charAt(layout.getLineEnd(i) - 1) == '\n';
|
||||
}
|
||||
// if true
|
||||
if (hasNewLineArray[i]) {
|
||||
int j = i - 1;
|
||||
while (j >= 0 && !hasNewLineArray[j]) {
|
||||
j--;
|
||||
}
|
||||
toCountLinesArray[j + 1] = true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
toCountLinesArray[lineCount - 1] = true;
|
||||
|
||||
int realLine = 0;
|
||||
for (i = 0; i < toCountLinesArray.length; i++) {
|
||||
realLines[i] = realLine;
|
||||
if (toCountLinesArray[i]) {
|
||||
realLine++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int firstReadLine() {
|
||||
return realLines[0];
|
||||
}
|
||||
|
||||
public int lastReadLine() {
|
||||
return realLines[realLines.length - 1];
|
||||
}
|
||||
|
||||
public int fakeLineFromRealLine(int realLine) {
|
||||
int i;
|
||||
int fakeLine = 0;
|
||||
for (i = 0; i < realLines.length; i++) {
|
||||
if (realLine == realLines[i]) {
|
||||
fakeLine = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return fakeLine;
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import com.itsaky.androidide.language.java.parser.model.ClassDescription;
|
||||
|
||||
/**
|
||||
* Created by duy on 19/07/2017.
|
||||
*/
|
||||
|
||||
public class SpanUtil {
|
||||
|
||||
|
||||
public static Spannable formatClass(Context context, ClassDescription item) {
|
||||
SpannableString simpleName = new SpannableString(item.getSimpleName());
|
||||
SpannableString packageName = new SpannableString("(" + item.getPackageName() + ")");
|
||||
simpleName.setSpan(new ForegroundColorSpan(ContextCompat.getColor(context,
|
||||
android.R.color.primary_text_dark)), 0, simpleName.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
packageName.setSpan(new ForegroundColorSpan(ContextCompat.getColor(context,
|
||||
android.R.color.secondary_text_dark)), 0, packageName.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
return new SpannableStringBuilder().append(simpleName).append(packageName);
|
||||
}
|
||||
}
|
||||
-101
@@ -1,101 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.provider;
|
||||
|
||||
import androidx.core.util.Pair;
|
||||
import com.blankj.utilcode.util.ThrowableUtils;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.language.java.parser.Expression;
|
||||
import com.itsaky.androidide.language.java.parser.ExpressionResolver;
|
||||
import com.itsaky.androidide.language.java.parser.JavacParser;
|
||||
import com.itsaky.androidide.language.java.parser.expression.CompleteExpression;
|
||||
import com.itsaky.androidide.language.java.parser.expression.CompleteNewKeyword;
|
||||
import com.itsaky.androidide.language.java.parser.expression.CompletePackage;
|
||||
import com.itsaky.androidide.language.java.parser.expression.CompleteString;
|
||||
import com.itsaky.androidide.language.java.parser.expression.CompleteThisKeyword;
|
||||
import com.itsaky.androidide.language.java.parser.expression.CompleteTypeDeclared;
|
||||
import com.itsaky.androidide.language.java.parser.expression.CompleteWord;
|
||||
import com.itsaky.androidide.language.java.parser.expression.IJavaCompleteMatcher;
|
||||
import com.itsaky.androidide.language.java.parser.internal.IClass;
|
||||
import com.itsaky.androidide.language.java.parser.internal.JavaDexClassLoader;
|
||||
import com.itsaky.androidide.language.java.parser.internal.PackageManager;
|
||||
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
|
||||
import com.itsaky.androidide.models.AndroidProject;
|
||||
import com.itsaky.androidide.utils.Environment;
|
||||
import com.sun.tools.javac.tree.JCTree;
|
||||
import io.github.rosemoe.editor.text.CharPosition;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.tools.Diagnostic;
|
||||
import javax.tools.JavaFileObject;
|
||||
|
||||
public class JavaCompletionProvider {
|
||||
|
||||
private final List<IJavaCompleteMatcher> mCompletors;
|
||||
private JavaDexClassLoader mClassLoader;
|
||||
private PackageManager mPackageManager;
|
||||
private JavacParser mJavaParser;
|
||||
|
||||
private boolean initiated = false;
|
||||
|
||||
public JavaCompletionProvider() {
|
||||
this.mCompletors = new ArrayList<>();
|
||||
this.mClassLoader = new JavaDexClassLoader(Environment.BOOTCLASSPATH);
|
||||
this.mPackageManager = new PackageManager();
|
||||
this.mJavaParser = new JavacParser();
|
||||
|
||||
addMatchers();
|
||||
}
|
||||
|
||||
public boolean loadProject(AndroidProject project) {
|
||||
mClassLoader.loadAllClasses(project);
|
||||
mPackageManager.init(project, mClassLoader.getClassReader());
|
||||
return initiated = true;
|
||||
}
|
||||
|
||||
public boolean isInitiated() {
|
||||
return initiated;
|
||||
}
|
||||
|
||||
private void addMatchers() {
|
||||
mCompletors.add(new CompleteExpression(mClassLoader));
|
||||
mCompletors.add(new CompleteNewKeyword(mClassLoader));
|
||||
mCompletors.add(new CompletePackage(mPackageManager));
|
||||
mCompletors.add(new CompleteString(mClassLoader));
|
||||
mCompletors.add(new CompleteTypeDeclared(mClassLoader));
|
||||
mCompletors.add(new CompleteThisKeyword(mClassLoader));
|
||||
mCompletors.add(new CompleteWord(mJavaParser, mClassLoader));
|
||||
}
|
||||
|
||||
public Pair<ArrayList<SuggestItem>, List<Diagnostic<? extends JavaFileObject>>> getSuggestions(CodeEditor editor) {
|
||||
ArrayList<SuggestItem> result = new ArrayList<>();
|
||||
try {
|
||||
JCTree.JCCompilationUnit ast = mJavaParser.parse(editor.getText());
|
||||
List<IClass> classes = mJavaParser.parseClasses(ast);
|
||||
|
||||
mClassLoader.updateClasses(classes);
|
||||
|
||||
ExpressionResolver resolver = new ExpressionResolver(ast, editor);
|
||||
Expression expression = resolver.getExpressionAtCursor();
|
||||
|
||||
JCTree.JCExpression jcExpression = expression.getExpression();
|
||||
int startPosition = jcExpression.getStartPosition();
|
||||
CharPosition s = editor.getText().getIndexer().getCharPosition(startPosition);
|
||||
CharPosition c = new CharPosition();
|
||||
c.line = editor.getCursor().getLeftLine();
|
||||
c.column = editor.getCursor().getLeftColumn();
|
||||
String statement = editor.getText().subContent(s.line, s.column, c.line, c.column).toString();
|
||||
for (IJavaCompleteMatcher autoComplete : mCompletors) {
|
||||
try {
|
||||
boolean handled = autoComplete.process(ast, editor, expression, statement, result);
|
||||
if (handled) {
|
||||
break;
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
}
|
||||
return Pair.create(result, mJavaParser.getDiagnostics());
|
||||
}
|
||||
}
|
||||
+7
-2
@@ -82,7 +82,7 @@ public class JavaLanguageServer implements ShellServer.Callback {
|
||||
new Thread(() -> {
|
||||
try {
|
||||
Socket socket = connectAndGet();
|
||||
onConnectToServer();
|
||||
sendInit();
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
String line;
|
||||
while((line = reader.readLine()) != null) {
|
||||
@@ -96,6 +96,11 @@ public class JavaLanguageServer implements ShellServer.Callback {
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
public void initialize(AndroidProject project) {
|
||||
this.project = project;
|
||||
sendInit();
|
||||
}
|
||||
|
||||
private Socket connectAndGet() {
|
||||
int tryCount = 0;
|
||||
@@ -113,7 +118,7 @@ public class JavaLanguageServer implements ShellServer.Callback {
|
||||
return server;
|
||||
}
|
||||
|
||||
protected void onConnectToServer() {
|
||||
protected void sendInit() {
|
||||
InitializeParams p = new InitializeParams();
|
||||
p.rootUri = new File(project.getProjectPath()).toURI();
|
||||
initialize(p);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.itsaky.androidide.language.logs;
|
||||
|
||||
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
|
||||
import com.itsaky.androidide.models.LogLine;
|
||||
import com.itsaky.androidide.models.SuggestItem;
|
||||
import com.itsaky.androidide.utils.Either;
|
||||
import io.github.rosemoe.editor.interfaces.AutoCompleteProvider;
|
||||
import io.github.rosemoe.editor.interfaces.CodeAnalyzer;
|
||||
|
||||
@@ -3,18 +3,17 @@ package com.itsaky.androidide.language.xml;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.language.xml.completion.XMLCompletionService;
|
||||
import com.itsaky.androidide.models.CompletionListItem;
|
||||
import com.itsaky.androidide.models.SuggestItem;
|
||||
import com.itsaky.androidide.utils.Either;
|
||||
import com.itsaky.androidide.utils.EitherOfResult;
|
||||
import io.github.rosemoe.editor.interfaces.AutoCompleteProvider;
|
||||
import io.github.rosemoe.editor.struct.CompletionItem;
|
||||
import io.github.rosemoe.editor.text.TextAnalyzeResult;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Collections;
|
||||
import com.itsaky.androidide.utils.Either;
|
||||
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
|
||||
import java.util.stream.Collectors;
|
||||
import com.itsaky.androidide.utils.EitherOfResult;
|
||||
|
||||
public class XMLAutoComplete implements AutoCompleteProvider {
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.itsaky.androidide.models;
|
||||
|
||||
import com.blankj.utilcode.util.ThrowableUtils;
|
||||
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
import com.itsaky.lsp.CompletionItem;
|
||||
import com.itsaky.lsp.CompletionItemKind;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.itsaky.androidide.models;
|
||||
|
||||
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
|
||||
import io.github.rosemoe.editor.text.CharPosition;
|
||||
import io.github.rosemoe.editor.text.Content;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.itsaky.androidide.models;
|
||||
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
|
||||
public interface SuggestItem {
|
||||
public String getName();
|
||||
public String getDescription();
|
||||
public String getReturnType();
|
||||
public char getTypeHeader();
|
||||
public int getSuggestionPriority();
|
||||
public void onSelectThis(CodeEditor editor);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.itsaky.androidide.utils;
|
||||
|
||||
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
|
||||
import com.itsaky.androidide.models.SuggestItem;
|
||||
import io.github.rosemoe.editor.struct.CompletionItem;
|
||||
|
||||
public class EitherOfResult extends Either<SuggestItem, CompletionItem> {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package io.github.rosemoe.editor.interfaces;
|
||||
|
||||
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
|
||||
import com.itsaky.androidide.models.SuggestItem;
|
||||
import com.itsaky.androidide.utils.Either;
|
||||
import io.github.rosemoe.editor.struct.CompletionItem;
|
||||
import io.github.rosemoe.editor.text.TextAnalyzeResult;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package io.github.rosemoe.editor.langs;
|
||||
|
||||
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
|
||||
import com.itsaky.androidide.models.SuggestItem;
|
||||
import com.itsaky.androidide.utils.Either;
|
||||
import io.github.rosemoe.editor.interfaces.AutoCompleteProvider;
|
||||
import io.github.rosemoe.editor.interfaces.CodeAnalyzer;
|
||||
|
||||
@@ -16,18 +16,18 @@
|
||||
package io.github.rosemoe.editor.widget;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.TypedValue;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
|
||||
import com.itsaky.androidide.utils.Either;
|
||||
import com.itsaky.androidide.R;
|
||||
import io.github.rosemoe.editor.struct.CompletionItem;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.models.SuggestItem;
|
||||
import com.itsaky.androidide.utils.Either;
|
||||
import io.github.rosemoe.editor.struct.CompletionItem;
|
||||
|
||||
/**
|
||||
* Adapter to display results
|
||||
|
||||
@@ -21,9 +21,9 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
import com.itsaky.androidide.databinding.LayoutCompletionWindowBinding;
|
||||
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
|
||||
import com.itsaky.androidide.models.CompletionEdit;
|
||||
import com.itsaky.androidide.models.CompletionListItem;
|
||||
import com.itsaky.androidide.models.SuggestItem;
|
||||
import com.itsaky.androidide.utils.Either;
|
||||
import io.github.rosemoe.editor.interfaces.AutoCompleteProvider;
|
||||
import io.github.rosemoe.editor.struct.CompletionItem;
|
||||
@@ -32,7 +32,6 @@ import io.github.rosemoe.editor.text.Cursor;
|
||||
import io.github.rosemoe.editor.text.TextAnalyzeResult;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.blankj.utilcode.util.ThreadUtils;
|
||||
|
||||
/**
|
||||
* Auto complete window for editing code quicker
|
||||
|
||||
@@ -19,7 +19,7 @@ import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
|
||||
import com.itsaky.androidide.models.SuggestItem;
|
||||
import com.itsaky.androidide.utils.Either;
|
||||
import io.github.rosemoe.editor.struct.CompletionItem;
|
||||
import java.util.List;
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
<string name="diagnostic_position">Start: %s, %s End: %s, %s</string>
|
||||
<string name="diagnostic_error">ERROR</string>
|
||||
<string name="diagnostic_warning">WARNING</string>
|
||||
<string name="diagnostic_hint">INFO</string>
|
||||
<string name="diagnostic_info">HINT</string>
|
||||
<string name="diagnostic_hint">HINT</string>
|
||||
<string name="diagnostic_info">INFO</string>
|
||||
<string name="msg_project_deleted">Project deleted</string>
|
||||
<string name="msg_project_delete_failed">Failed to delete project!</string>
|
||||
<string name="msg_failed_load_icon_path">Failed to load icon path!</string>
|
||||
@@ -17,8 +17,8 @@
|
||||
<string name="msg_indexing">Indexing: %s</string>
|
||||
<string name="msg_daemons_force_stopping">Gradle build process is running. Killing daemons...</string>
|
||||
<string name="msg_daemons_stopped">Gradle Daemons Stopped</string>
|
||||
<string name="recreate_completion_services">Recreate Completion Services</string>
|
||||
<string name="gradle_daemon_status">Gradle Daemon Status</string>
|
||||
<string name="recreate_completion_services">Recreate services</string>
|
||||
<string name="gradle_daemon_status">Gradle daemon status</string>
|
||||
<string name="msg_newfile_dest">Destination: %s/</string>
|
||||
<string name="restype_drawable">Drawable</string>
|
||||
<string name="restype_layout">Layout</string>
|
||||
@@ -56,22 +56,22 @@
|
||||
<string name="msg_clear_cache">This will delete caches directory created by Gradle. If you delete it, the next build may take a bit longer than normal as Gradle will download required files again.\n\nAre you sure you want to delete caches?</string>
|
||||
<string name="msg_install_gradle">Currently installed gradle version will be DELETE PEEMANENTLY!\n\nPlease enter full path of the zip file which contains Gradle distribution.</string>
|
||||
<string name="reset">Reset</string>
|
||||
<string name="title_change_text_size">Editor Font Size</string>
|
||||
<string name="title_change_text_size">Editor font size</string>
|
||||
<string name="idepref_build_clearCache_title">Clear Gradle caches</string>
|
||||
<string name="idepref_build_clearCache_summary">Clear Gradle caches directory. This will delete all dependencies downloaded by Gradle. Required files will be downloaded again in next build.</string>
|
||||
<string name="idepref_build_choosegradle_title">Choose Gradle Version</string>
|
||||
<string name="idepref_build_choosegradle_title">Choose Gradle version</string>
|
||||
<string name="idepref_build_choosegradle_summary">Choose one of the installed Gradle versions.</string>
|
||||
<string name="idepref_build_installgradle_title">Custom Gradle</string>
|
||||
<string name="idepref_build_installgradle_summary">Define custom Gradle that will be used in AndroidIDE.</string>
|
||||
<string name="idepref_build_customgradlecommands_title">Additional Gradle Commands</string>
|
||||
<string name="idepref_build_customgradlecommands_title">Additional Gradle commands</string>
|
||||
<string name="idepref_build_customgradlecommands_summary">Choose additional commands that will be added while executing every task</string>
|
||||
<string name="idepref_build_editgradleproperties_title">Edit global Gradle Properties</string>
|
||||
<string name="idepref_build_editgradleproperties_title">Edit global Gradle properties</string>
|
||||
<string name="idepref_build_editgradleproperties_summary">Edit gradle.properties in GRADLE_HOME</string>
|
||||
<string name="idepref_editor_fontsize_title">Font Size</string>
|
||||
<string name="idepref_editor_fontsize_title">Font size</string>
|
||||
<string name="idepref_editor_fontsize_summary">Font size of the editor</string>
|
||||
<string name="idepref_editor_paintingflags_title">Non-printable painting flags</string>
|
||||
<string name="idepref_editor_paintingflags_summary">Choose what non-printable characters should be drawn by the editor</string>
|
||||
<string name="idepref_editor_drawhexcolors_title">Draw Hex Color Strings</string>
|
||||
<string name="idepref_editor_drawhexcolors_title">Draw hex color strings</string>
|
||||
<string name="idepref_editor_drawhexcolors_summary">Should the editor draw hex color string?</string>
|
||||
<string name="idepref_appearance_summary">Customize look and feel of AndroidIDE</string>
|
||||
<string name="idepref_appearance_title">Appearance</string>
|
||||
@@ -84,14 +84,14 @@
|
||||
<string name="msg_first_build">This is the first time build after you installed AndroidIDE. First build usually takes time as Gradle downloads all dependencies and plugins. This process may take about 10–15 minutes. But this totally depends on your network connection.\n\nPlease be patient …</string>
|
||||
<string name="msg_share_app">AndroidIDE is an Integrated Development Environment for building Android apps on your Android Smartphone. It supports :\n1. Gradle \n2. OpenJDK 11.0.1.\n3. Advanced Java auto completion\n4. XML Auto Completion \n5. Kotlin Support and much more.\n\nDownload AndroidIDE at https://androidide.com</string>
|
||||
<string name="msg_need_help">If you\'re facing issues while using AndroidIDE or with building Android Apps, you can join the Telegram Group ask for help there.\n\nYou can also send suggesstions on our email.</string>
|
||||
<string name="discuss_amp_help">Discuss & Help</string>
|
||||
<string name="user_suggestions">Issue Tracker</string>
|
||||
<string name="discuss_amp_help">Discuss & help</string>
|
||||
<string name="user_suggestions">Issue tracker</string>
|
||||
<string name="issue_tracker_summary">See what issues, suggesstions, requests we are currently working on.</string>
|
||||
<string name="need_help">Need Help?</string>
|
||||
<string name="other">Other</string>
|
||||
<string name="share_with_friends">Share with Friends</string>
|
||||
<string name="ide_preferences">IDE Preferences</string>
|
||||
<string name="discussions_on_telegram">Discussions On Telegram</string>
|
||||
<string name="ide_preferences">IDE preferences</string>
|
||||
<string name="discussions_on_telegram">Discussions on Telegram</string>
|
||||
<string name="discussions_on_telegram_summary">Discuss issues, suggesstions, requests on Telegram</string>
|
||||
<string name="project_exists">A project with same name already exists!</string>
|
||||
<string name="copying_files">Copying files</string>
|
||||
@@ -104,7 +104,7 @@
|
||||
<string name="project_created_successfully">Project created successfully!</string>
|
||||
<string name="msg_begin_project_write">Starting writing process</string>
|
||||
<string name="msg_first_prepare_failed">Something went wrong while preparing for first build. Would you like to retry?</string>
|
||||
<string name="title_restart">Please Restart</string>
|
||||
<string name="title_restart">Please restart</string>
|
||||
<string name="msg_restart">Installation is finished successfully. Please restart AndroidIDE.</string>
|
||||
<string name="installed">Installed</string>
|
||||
<string name="installing">Installing …</string>
|
||||
@@ -129,14 +129,14 @@
|
||||
<string name="msg_can_contain_slashes">You can use forward slashes (\'/\') to define a relative path to this folder. This will create directories if they do not already exist and will save time in creating multiple directories.</string>
|
||||
<string name="folder_name">Folder Name</string>
|
||||
<string name="file_name">File Name</string>
|
||||
<string name="new_name">New Name</string>
|
||||
<string name="new_file">New File</string>
|
||||
<string name="new_java_class">New Java Class</string>
|
||||
<string name="new_xml_resource">New XML Resource</string>
|
||||
<string name="new_resource">New %s Resource</string>
|
||||
<string name="new_folder">New Folder</string>
|
||||
<string name="new_name">New name</string>
|
||||
<string name="new_file">New file</string>
|
||||
<string name="new_java_class">New Java class</string>
|
||||
<string name="new_xml_resource">New XML resource</string>
|
||||
<string name="new_resource">New %s resource</string>
|
||||
<string name="new_folder">New folder</string>
|
||||
<string name="logs">Logs</string>
|
||||
<string name="title_confirm_delete">Confirm Delete</string>
|
||||
<string name="title_confirm_delete">Confirm delete</string>
|
||||
<string name="msg_confirm_delete">Are you sure you want to delete:\n%s?</string>
|
||||
<string name="copied">Copied successfully!</string>
|
||||
<string name="msg_rename_file">Enter new name for file/folder.</string>
|
||||
@@ -144,17 +144,17 @@
|
||||
<string name="rename_failed">Unable to rename file!</string>
|
||||
<string name="deleted">Deleted successfully!</string>
|
||||
<string name="delete_failed">Unable to delete file!</string>
|
||||
<string name="copy_path">Copy Path</string>
|
||||
<string name="copy_path">Copy path</string>
|
||||
<string name="rename_file">Rename</string>
|
||||
<string name="delete_file">Delete</string>
|
||||
<string name="file_info">File Info</string>
|
||||
<string name="file_options">File Options</string>
|
||||
<string name="action_closeAll">Close All</string>
|
||||
<string name="action_closeOthers">Close Others</string>
|
||||
<string name="action_closeThis">Close This</string>
|
||||
<string name="file_info">File info</string>
|
||||
<string name="file_options">File options</string>
|
||||
<string name="action_closeAll">Close all</string>
|
||||
<string name="action_closeOthers">Close others</string>
|
||||
<string name="action_closeThis">Close this</string>
|
||||
<string name="all_saved">All files saved!</string>
|
||||
<string name="auto_saved">Automatically saved all files</string>
|
||||
<string name="build_failed">Build Failed</string>
|
||||
<string name="build_failed">Build failed</string>
|
||||
<string name="compiling">Compiling</string>
|
||||
<string name="install_failed">Failed to install apk. Please try again!</string>
|
||||
<string name="invalid_app_name">Invalid app name</string>
|
||||
@@ -163,14 +163,14 @@
|
||||
<string name="invalid_target_sdk">Invalid target SDK</string>
|
||||
<string name="invalid_values">Invalid values</string>
|
||||
<string name="label_not_imported">[IMPORT] [%s]</string>
|
||||
<string name="label_splash_image">Splash Image</string>
|
||||
<string name="label_splash_image">Splash image</string>
|
||||
<string name="msg_checking_storage_permissions">Checking storage permissions</string>
|
||||
<string name="msg_failed_list_files">Failed to list project files!</string>
|
||||
<string name="msg_failed_save">Failed to save some files!</string>
|
||||
<string name="msg_service_started">Service Started</string>
|
||||
<string name="msg_starting_completion">Starting Completion Services</string>
|
||||
<string name="msg_starting_completion">Starting services</string>
|
||||
<string name="msg_starting_completion_failed">Failed to start Completion Services</string>
|
||||
<string name="msg_starting_daemon">Starting a Gradle Daemon</string>
|
||||
<string name="msg_starting_daemon">Starting a Gradle daemon</string>
|
||||
<string name="msg_storage_denied">Can\'t proceed without storage permissions</string>
|
||||
<string name="msg_storage_granted">Storage permissions granted!</string>
|
||||
<string name="msg_sync_needed">Gradle files have changed. Please rebuild your project to sync dependencies and configurations.</string>
|
||||
@@ -181,41 +181,41 @@
|
||||
<string name="saved_file">%s saved!</string>
|
||||
<string name="task_merge_dex">Merge Dex</string>
|
||||
<string name="template_basic">Basic Project</string>
|
||||
<string name="template_basic_kotlin">Basic Kotlin Project</string>
|
||||
<string name="template_bottom_navigation">Bottom Navigation Project</string>
|
||||
<string name="template_basic_kotlin">Basic Kotlin project</string>
|
||||
<string name="template_bottom_navigation">Bottom navigation project</string>
|
||||
<string name="template_description_basic">A basic project with AppCompat Toolbar and a Floating Action Button</string>
|
||||
<string name="template_description_bottom_navigation">Project with a BottomNavigationView and without any Toolbar</string>
|
||||
<string name="template_description_empty">An empty project with default toolbar. Suitable when you want to do all the basic things yourself.</string>
|
||||
<string name="template_description_kotlin">Basic project written in kotlin</string>
|
||||
<string name="template_description_navigation_drawer">Project with AppCompat Toolbar, DrawerLayout and NavigationView</string>
|
||||
<string name="template_empty">Empty Project</string>
|
||||
<string name="template_navigation_drawer">Navigation Drawer Project</string>
|
||||
<string name="unknown_type">Unknown Type</string>
|
||||
<string name="template_empty">Empty project</string>
|
||||
<string name="template_navigation_drawer">Navigation drawer project</string>
|
||||
<string name="unknown_type">Unknown type</string>
|
||||
<string name="app_name">Android IDE</string>
|
||||
<string name="build">Build</string>
|
||||
<string name="build_debug">Assemble Debug</string>
|
||||
<string name="build_output">Build Output</string>
|
||||
<string name="build_release">Assemble Release</string>
|
||||
<string name="build_successful">Build Successful</string>
|
||||
<string name="build_tasks">Build Tasks</string>
|
||||
<string name="build_debug">assembleDebug</string>
|
||||
<string name="build_output">Build output</string>
|
||||
<string name="build_release">assembleRelease</string>
|
||||
<string name="build_successful">Build successful</string>
|
||||
<string name="build_tasks">Build tasks</string>
|
||||
<string name="cancel">Cancel</string>
|
||||
<string name="clean_amp_build">Clean Build</string>
|
||||
<string name="clean_amp_build">Clean & build</string>
|
||||
<string name="clean_project">Clean</string>
|
||||
<string name="create_aab">Create AAB</string>
|
||||
<string name="create_project">Create Project</string>
|
||||
<string name="create_project">Create project</string>
|
||||
<string name="do_you_want_to_install_apk">Do you want to install APK?</string>
|
||||
<string name="get_started">Get Started</string>
|
||||
<string name="get_started">Get started</string>
|
||||
<string name="install">Install</string>
|
||||
<string name="lint">Lint</string>
|
||||
<string name="lint_debug">Lint Debug</string>
|
||||
<string name="lint_release">Lint Release</string>
|
||||
<string name="lint_tasks">Lint Tasks</string>
|
||||
<string name="lint_debug">lintDebug</string>
|
||||
<string name="lint_release">lintRelease</string>
|
||||
<string name="lint_tasks">Lint tasks</string>
|
||||
<string name="looks_like_you_havent_created">Looks like you haven\'t created any projects yet.</string>
|
||||
<string name="minimum_sdk">Minimum SDK</string>
|
||||
<string name="new_project">New project</string>
|
||||
<string name="package_name">Package Name</string>
|
||||
<string name="project_app_name">App Name</string>
|
||||
<string name="quick_run_debug">Quick Run</string>
|
||||
<string name="package_name">Package name</string>
|
||||
<string name="project_app_name">App name</string>
|
||||
<string name="quick_run_debug">Quick run</string>
|
||||
<string name="redo">Redo</string>
|
||||
<string name="run">Run</string>
|
||||
<string name="save">Save</string>
|
||||
@@ -239,7 +239,7 @@
|
||||
<item name="last" type="string">Last</item>
|
||||
<item name="next" type="string">Next</item>
|
||||
<item name="replace" type="string">Replace</item>
|
||||
<item name="replaceAll" type="string">Replace All</item>
|
||||
<item name="replaceAll" type="string">Replace all</item>
|
||||
<item name="replacement" type="string">Replacement</item>
|
||||
<item name="text_to_search" type="string">Text to search</item>
|
||||
<item name="more" type="string" translatable="false" >More</item>
|
||||
|
||||
Reference in New Issue
Block a user