diff --git a/app/build.gradle b/app/build.gradle
index 31508fb9e..08974c25b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -124,6 +124,7 @@ dependencies {
implementation project(':spinkit')
implementation project(':slidr')
implementation project(':javadocparser')
+ implementation project(':lsp')
implementation fileTree(dir: 'libs', include: ['*.jar'])
diff --git a/app/libs/ecj-4.6.1.jar b/app/libs/ecj-4.6.1.jar
deleted file mode 100644
index d0323c672..000000000
Binary files a/app/libs/ecj-4.6.1.jar and /dev/null differ
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index 0945229ca..4925fca37 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -17,6 +17,7 @@
-dontwarn androidx.lifecycle.**
-keep class androidx.arch.** { *; }
-keep class androidx.lifecycle.** { *; }
+-keep class com.itsaky.lsp.** { *; }
-keep class org.eclipse.jdt.** { (...); }
-keep class org.eclipse.jdt.** { static *; }
diff --git a/app/src/main/assets/data/jls.zip b/app/src/main/assets/data/jls.zip
new file mode 100644
index 000000000..21fbd0122
Binary files /dev/null and b/app/src/main/assets/data/jls.zip differ
diff --git a/app/src/main/java/com/itsaky/androidide/EditorActivity.java b/app/src/main/java/com/itsaky/androidide/EditorActivity.java
index 55507826f..b35e730d2 100644
--- a/app/src/main/java/com/itsaky/androidide/EditorActivity.java
+++ b/app/src/main/java/com/itsaky/androidide/EditorActivity.java
@@ -42,7 +42,6 @@ import com.itsaky.androidide.fragments.EditorFragment;
import com.itsaky.androidide.fragments.FileTreeFragment;
import com.itsaky.androidide.fragments.sheets.OptionsListFragment;
import com.itsaky.androidide.fragments.sheets.TextSheetFragment;
-import com.itsaky.androidide.interfaces.CompileListener;
import com.itsaky.androidide.interfaces.DexResultListener;
import com.itsaky.androidide.interfaces.DiagnosticClickListener;
import com.itsaky.androidide.language.logs.LogLanguageImpl;
@@ -53,8 +52,6 @@ import com.itsaky.androidide.models.LogLine;
import com.itsaky.androidide.models.SheetOption;
import com.itsaky.androidide.receivers.LogReceiver;
import com.itsaky.androidide.services.IDEService;
-import com.itsaky.androidide.services.Jar2DexService;
-import com.itsaky.androidide.services.compiler.model.CompilerDiagnostic;
import com.itsaky.androidide.shell.ShellServer;
import com.itsaky.androidide.tasks.GradleTask;
import com.itsaky.androidide.tasks.TaskExecutor;
@@ -63,7 +60,11 @@ import com.itsaky.androidide.utils.PreferenceManager;
import com.itsaky.androidide.utils.ProjectWriter;
import com.itsaky.androidide.utils.Symbols;
import com.itsaky.androidide.utils.TypefaceUtils;
+import com.itsaky.androidide.utils.VersionedFileManager;
import com.itsaky.androidide.views.MaterialBanner;
+import com.itsaky.lsp.DidOpenTextDocumentParams;
+import com.itsaky.lsp.LanguageClient;
+import com.itsaky.lsp.TextDocumentItem;
import com.itsaky.toaster.Toaster;
import com.unnamed.b.atv.model.TreeNode;
import io.github.rosemoe.editor.langs.EmptyLanguage;
@@ -77,16 +78,22 @@ import java.util.Map;
import java.util.regex.Pattern;
import me.piruin.quickaction.ActionItem;
import me.piruin.quickaction.QuickAction;
+import com.itsaky.lsp.JavaReportProgressParams;
+import com.itsaky.lsp.PublishDiagnosticsParams;
+import com.itsaky.lsp.JavaStartProgressParams;
+import com.itsaky.lsp.ShowMessageParams;
+import com.google.gson.JsonElement;
+import com.itsaky.lsp.Diagnostic;
public class EditorActivity extends StudioActivity implements FileTreeFragment.FileActionListener,
IDEService.BuildListener,
TabLayout.OnTabSelectedListener,
NavigationView.OnNavigationItemSelectedListener,
- DexResultListener,
- CompileListener,
- DiagnosticClickListener {
-
- private ActivityEditorBinding mBinding;
+ DiagnosticClickListener,
+ EditorFragment.FileOpenListener,
+ LanguageClient, CodeEditor.CursorChangeListener {
+
+ private ActivityEditorBinding mBinding;
private EditorPagerAdapter mPagerAdapter;
private FileTreeFragment mFileTreeFragment;
private EditorFragment mCurrentFragment;
@@ -105,7 +112,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
private boolean recreateCompletionServices = false;
- private final Map> diagnostics = new HashMap<>();
+ private final Map> diagnostics = new HashMap<>();
private final String RES_PATH_REGEX = "/.*/src/.*/res";
private final String LAYOUTRES_PATH_REGEX = "/.*/src/.*/res/layout";
@@ -426,11 +433,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
if(task.canOutput())
setStatus(msg);
- if(recreateCompletionServices) {
- getApp().createCompletionService(mProject);
- } else if(task.getType() == GradleTask.Type.BUILD) {
- getApp().createJavaCompiler(mProject);
- getApp().getJavaCompiler().compileAllAsync(this);
+ if(task.getType() == GradleTask.Type.BUILD) {
if(task.getTaskID() == IDEService.TASK_ASSEMBLE_DEBUG) {
if(task.buildsApk()) {
install(task.getApk(new File(mProject.getMainModulePath(), "build").getAbsolutePath(), mProject.getMainModule()));
@@ -449,11 +452,6 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
if(task.canOutput()) {
setStatus(msg);
}
-
- if(task.getType() == GradleTask.Type.BUILD) {
- getApp().createJavaCompiler(mProject);
- getApp().getJavaCompiler().compileAllAsync(this);
- }
showBuildResult();
@@ -488,37 +486,6 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
saveAll();
}
- /**
- * @deprecated This is replaced by the ClassFileReader. There is no need to dex the JAR files in order to load them.
- */
- @Deprecated
- @Override
- public void onDex(String name) {
- setStatus(getString(R.string.msg_indexing, name));
- }
-
- /**
- * @deprecated This is replaced by the ClassFileReader. There is no need to dex the JAR files in order to load them.
- */
- @Deprecated
- @Override
- public void onDexSuccess(List dexes) {
- ConstantsBridge.PROJECT_DEXES = dexes;
- setStatus(getString(R.string.msg_indexing_success));
- createCompletionServices();
- }
-
- /**
- * @deprecated This is replaced by the ClassFileReader. There is no need to dex the JAR files in order to load them.
- */
- @Deprecated
- @Override
- public void onDexFailed(String message) {
- appendBuildOut(message);
- setStatus(getString(R.string.msg_indexing_failed));
- showBuildResult();
- }
-
// Can be called from a different different, better to run on UI thread
private void appendBuildOut(final String str) {
runOnUiThread(() -> {
@@ -651,7 +618,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
private void createCompletionServices() {
new TaskExecutor().executeAsync(() -> {
- getApp().createCompletionService(mProject);
+ getApp().createCompletionService(mProject, EditorActivity.this);
return null;
}, __ -> {
setStatus(getString(getApp().areCompletorsStarted() ? R.string.msg_service_started : R.string.msg_starting_completion_failed));
@@ -685,18 +652,8 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
.setCancelable(false)
.create().show();
}
-
- @Override
- public void onCompilationResult(boolean singleFile, Map> diagnostics) {
- if(getDiagnosticsList().getParent() == null || !(getDiagnosticsList().getParent() instanceof ViewGroup))
- setupContainers();
- getDiagnosticsList().setAdapter(new DiagnosticsAdapter(mapAsGroup(diagnostics), this));
- this.diagnostics.clear();
- this.diagnostics.putAll(diagnostics);
- provideDiagnosticsToEditors(diagnostics);
- }
-
- private void provideDiagnosticsToEditors(Map> diagnostics) {
+
+ private void provideDiagnosticsToEditors(Map> diagnostics) {
if(mPagerAdapter != null && mPagerAdapter.getFragments() != null) {
List editors = mPagerAdapter.getFragments();
if(editors.size() <= 0) return;
@@ -723,7 +680,50 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
}
@Override
- public void onDiagnosticClick(CompilerDiagnostic diagnostic) {
+ public void onDiagnosticClick(Diagnostic diagnostic) {
+ }
+
+ @Override
+ public void javaProgressStart(JavaStartProgressParams params) {
+ }
+
+ @Override
+ public void javaProgressReport(JavaReportProgressParams params) {
+ }
+
+ @Override
+ public void javaProgressEnd() {
+ }
+
+ @Override
+ public void publishDiagnostics(PublishDiagnosticsParams params) {
+ if(params == null) return;
+ File file = new File(params.uri);
+ if(!(file.exists() && file.isFile())) return;
+ diagnostics.put(file, params.diagnostics);
+
+ getDiagnosticsList().setAdapter(new DiagnosticsAdapter(mapAsGroup(diagnostics), this));
+ }
+
+ @Override
+ public void showMessage(ShowMessageParams params) {
+ }
+
+ @Override
+ public void registerCapability(String method, JsonElement options) {
+ }
+
+ @Override
+ public void customNotification(String method, JsonElement params) {
+ }
+
+ @Override
+ public void onCursorPositionChange(int leftLine, int leftColumn, int rightLine, int rightColumn) {
+ if(leftLine == rightLine && leftColumn == rightColumn) {
+ mBinding.editorToolbar.setSubtitle(String.format("[%d, %d]", leftLine, leftColumn));
+ } else {
+ mBinding.editorToolbar.setSubtitle(String.format("[%d, %d] | [%d, %d]", leftLine, leftColumn, rightLine, rightColumn));
+ }
}
/*****************************
@@ -883,12 +883,12 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
return diagnosticList;
}
- private List mapAsGroup(Map> diags) {
+ private List mapAsGroup(Map> diags) {
List groups = new ArrayList<>();
if(diags == null || diags.size() <= 0)
return groups;
for(File file : diags.keySet()) {
- List fileDiags = diags.get(file);
+ List fileDiags = diags.get(file);
if(fileDiags == null || fileDiags.size() <= 0)
continue;
DiagnosticGroup group = new DiagnosticGroup(R.drawable.ic_language_java, file, fileDiags);
@@ -907,12 +907,26 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
@Override
public void openFile(File file) {
- int i = mPagerAdapter.openFile(file);
+ int i = mPagerAdapter.openFile(file, this, this);
if(i >= 0 && !mBinding.tabs.getTabAt(i).isSelected())
mBinding.tabs.getTabAt(i).select();
mBinding.editorDrawerLayout.closeDrawer(GravityCompat.END);
}
+ @Override
+ public void onOpenSuccessful(File file, String text) {
+ if(file.getName().endsWith(".java")) {
+ TextDocumentItem doc = new TextDocumentItem();
+ doc.languageId = "java";
+ doc.uri = file.toURI();
+ doc.text = text;
+ doc.version = VersionedFileManager.fileOpened(file);
+ DidOpenTextDocumentParams p = new DidOpenTextDocumentParams();
+ p.textDocument = doc;
+ getApp().getJavaLanguageServer().didOpen(p);
+ }
+ }
+
@Override
public void showFileOptions(File thisFile, TreeNode node) {
mLastHolded = node;
@@ -923,8 +937,6 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
boolean isGradleSaved = mPagerAdapter.saveAll();
getApp().toast(R.string.all_saved, Toaster.Type.SUCCESS);
if(isGradleSaved) notifySyncNeeded();
- if(getApp().getJavaCompiler() != null)
- getApp().getJavaCompiler().compileAllAsync(this);
return isGradleSaved;
}
diff --git a/app/src/main/java/com/itsaky/androidide/adapters/DiagnosticItemAdpater.java b/app/src/main/java/com/itsaky/androidide/adapters/DiagnosticItemAdpater.java
index ec1cebd0e..7c5e14982 100644
--- a/app/src/main/java/com/itsaky/androidide/adapters/DiagnosticItemAdpater.java
+++ b/app/src/main/java/com/itsaky/androidide/adapters/DiagnosticItemAdpater.java
@@ -7,17 +7,17 @@ import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
import com.itsaky.androidide.R;
import com.itsaky.androidide.databinding.LayoutDiagnosticItemBinding;
-import com.itsaky.androidide.services.compiler.model.CompilerDiagnostic;
-import java.util.List;
-import javax.tools.Diagnostic;
import com.itsaky.androidide.interfaces.DiagnosticClickListener;
+import com.itsaky.lsp.Diagnostic;
+import com.itsaky.lsp.DiagnosticSeverity;
+import java.util.List;
public class DiagnosticItemAdpater extends RecyclerView.Adapter {
- private List diags;
+ private List diags;
private DiagnosticClickListener listener;
- public DiagnosticItemAdpater(List diags, DiagnosticClickListener listener) {
+ public DiagnosticItemAdpater(List diags, DiagnosticClickListener listener) {
this.diags = diags;
this.listener = listener;
}
@@ -29,12 +29,12 @@ public class DiagnosticItemAdpater extends RecyclerView.Adapter {
if(listener != null) {
@@ -48,8 +48,8 @@ public class DiagnosticItemAdpater extends RecyclerView.Adapter handleLog(line)).bgAppend("logcat -v threadtime");
setupLibsIfNeeded();
-
+ extractJlsIfNeeded();
+
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannels();
}
@@ -144,6 +152,31 @@ public class StudioApp extends MultiDexApplication
}
+ private void extractJlsIfNeeded() {
+ boolean gson = false, jls = false, proto = false;
+ final List files = FileUtils.listFilesInDir(Environment.JLS_HOME, true);
+ for(File f : files) {
+ gson = gson == true ? gson : f.getName().equals("gson.jar");
+ jls = jls == true ? jls : f.getName().equals("jls.jar");
+ proto = proto == true ? proto : f.getName().equals("protobuf.jar");
+ }
+
+ if(!false/* !(gson && jls && proto) */) {
+ try {
+ extractJls();
+ } catch (Throwable e) {
+ Logger.instance("StudioApp").e(ThrowableUtils.getFullStackTrace(e));
+ }
+ }
+ }
+
+ private void extractJls() throws IOException {
+ final File jlsZip = new File(Environment.JLS_HOME, "jls.zip");
+ ResourceUtils.copyFileFromAssets("data/jls.zip", jlsZip.getAbsolutePath());
+ ZipUtils.unzipFile(jlsZip, Environment.JLS_HOME);
+ FileUtils.delete(jlsZip);
+ }
+
private boolean setupLibsIfNeeded() {
File lib = Environment.LIBDIR;
if(lib != null && lib.exists() && lib.isDirectory()) {
@@ -217,35 +250,24 @@ public class StudioApp extends MultiDexApplication
return buildService;
}
- public void createCompletionService(AndroidProject project) {
- this.mXmlCompletionService = new XMLCompletionService();
- this.mCompletionProvider = new JavaCompletionProvider();
- this.mCompletionProvider.loadProject(project);
+ public void createCompletionService(AndroidProject project, LanguageClient client) {
+ this.languageServer = new JavaLanguageServer(project, client);
+ this.mXmlCompletionService = new XMLCompletionService();
+
+ this.languageServer.startServer();
+ }
+
+ public JavaLanguageServer getJavaLanguageServer() {
+ return languageServer;
}
-
- public void createJavaCompiler(AndroidProject project) {
- this.mJavaCompilerService =
- new JavaCompilerService(project);
- }
-
- public JavaCompilerService getJavaCompiler() {
- return mJavaCompilerService;
- }
-
- public JavaCompletionProvider getCompletionProvider() {
- return mCompletionProvider;
- }
public XMLCompletionService getXmlCompletionService() {
return mXmlCompletionService;
}
public boolean areCompletorsStarted() {
- return mCompletionProvider != null
- && mXmlCompletionService != null
- && mCompletionProvider.isInitiated()
- && mXmlCompletionService.isInitiated()
- && ConstantsBridge.CLASS_LOAD_SUCCESS;
+ return mXmlCompletionService != null
+ && mXmlCompletionService.isInitiated();
}
public void setStopGradleDaemon(boolean startGradleDaemon) {
diff --git a/app/src/main/java/com/itsaky/androidide/fragments/EditorFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/EditorFragment.java
index b71c30e4e..ea9b9b627 100644
--- a/app/src/main/java/com/itsaky/androidide/fragments/EditorFragment.java
+++ b/app/src/main/java/com/itsaky/androidide/fragments/EditorFragment.java
@@ -8,36 +8,37 @@ import com.itsaky.androidide.adapters.CompletionListAdapter;
import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.databinding.FragmentEditorBinding;
import com.itsaky.androidide.fragments.preferences.EditorPreferences;
-import com.itsaky.androidide.interfaces.CompileListener;
import com.itsaky.androidide.language.groovy.GroovyLanguage;
import com.itsaky.androidide.language.java.JavaLanguage;
+import com.itsaky.androidide.language.java.server.JavaLanguageServer;
import com.itsaky.androidide.language.xml.XMLLanguage;
import com.itsaky.androidide.language.xml.lexer.XMLLexer;
import com.itsaky.androidide.models.AndroidProject;
import com.itsaky.androidide.models.ConstantsBridge;
-import com.itsaky.androidide.services.compiler.JavaCompilerService;
-import com.itsaky.androidide.services.compiler.model.CompilerDiagnostic;
import com.itsaky.androidide.syntax.colorschemes.SchemeWombat;
import com.itsaky.androidide.tasks.TaskExecutor;
import com.itsaky.androidide.tasks.callables.ReadFileTask;
-import com.itsaky.androidide.tools.SourceJavaFileObject;
import com.itsaky.androidide.utils.FileUtil;
import com.itsaky.androidide.utils.PreferenceManager;
import com.itsaky.androidide.utils.TypefaceUtils;
-import com.sun.tools.javac.resources.compiler;
+import com.itsaky.androidide.utils.VersionedFileManager;
+import com.itsaky.lsp.Diagnostic;
+import com.itsaky.lsp.DidChangeTextDocumentParams;
+import com.itsaky.lsp.TextDocumentContentChangeEvent;
+import com.itsaky.lsp.VersionedTextDocumentIdentifier;
import io.github.rosemoe.editor.interfaces.EditorEventListener;
import io.github.rosemoe.editor.langs.EmptyLanguage;
+import io.github.rosemoe.editor.text.Cursor;
import io.github.rosemoe.editor.widget.CodeEditor;
import io.github.rosemoe.editor.widget.schemes.SchemeVS2019;
import java.io.File;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
-import java.util.Map;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.Token;
-public class EditorFragment extends BaseFragment implements EditorEventListener, CompileListener {
+public class EditorFragment extends BaseFragment implements EditorEventListener, CodeEditor.CursorChangeListener {
public FragmentEditorBinding binding;
private File mFile;
@@ -45,6 +46,8 @@ public class EditorFragment extends BaseFragment implements EditorEventListener,
private boolean isModified = false;
private boolean isFirstCreate = false;
+ private CodeEditor.CursorChangeListener listener;
+ private FileOpenListener openListener;
private static AndroidProject project;
public static final String KEY_FILE_PATH = "file_path";
@@ -56,6 +59,11 @@ public class EditorFragment extends BaseFragment implements EditorEventListener,
public static final String EXT_GROOVY = ".groovy";
public static final String EXT_KOTLIN = ".kt";
public static final String EXT_JSON = ".json";
+
+ public EditorFragment setCursorChangeListener(CodeEditor.CursorChangeListener listener) {
+ this.listener = listener;
+ return this;
+ }
public static EditorFragment newInstance(File file, AndroidProject project) {
Bundle bundle = new Bundle();
@@ -65,6 +73,11 @@ public class EditorFragment extends BaseFragment implements EditorEventListener,
frag.setArguments(bundle);
return frag;
}
+
+ public EditorFragment setFileOpenListener(FileOpenListener openListener) {
+ this.openListener = openListener;
+ return this;
+ }
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -103,25 +116,14 @@ public class EditorFragment extends BaseFragment implements EditorEventListener,
}
@Override
- public void onCompilationResult(boolean singleFile, Map> diagnostics) {
- if(diagnostics != null
- && getFile() != null
- && diagnostics.containsKey(getFile()))
- {
- binding.editorCodeEditor.setDiagnostics(diagnostics.get(getFile()));
- }
- }
-
- private void requestCompilation() {
- final long s = System.currentTimeMillis();
- final SourceJavaFileObject source = new SourceJavaFileObject(getFile(), binding.editorCodeEditor.getText().toString());
- JavaCompilerService compiler = StudioApp.getInstance().getJavaCompiler();
- if(compiler != null) {
- compiler.compileSingleAsync(source, this);
+ public void onCursorPositionChange(int leftLine, int leftColumn, int rightLine, int rightColumn) {
+ if(listener != null) {
+ final Cursor c = binding.editorCodeEditor. getCursor();
+ listener.onCursorPositionChange(c.getLeftLine(), c.getLeftColumn(), c.getRightLine(), c.getRightColumn());
}
}
- public void setDiagnostics(List diags) {
+ public void setDiagnostics(List diags) {
if(binding.editorCodeEditor != null) {
binding.editorCodeEditor.setDiagnostics(diags);
}
@@ -164,6 +166,10 @@ public class EditorFragment extends BaseFragment implements EditorEventListener,
isFirstCreate = false;
}
+
+ public String getText() {
+ return binding.editorCodeEditor.getText().toString();
+ }
public boolean isModified() {
return isModified;
@@ -195,6 +201,7 @@ public class EditorFragment extends BaseFragment implements EditorEventListener,
}
private void postRead() {
+ binding.editorCodeEditor.setFile(getFile());
if (mFile.isFile() && mFile.getName().endsWith(EXT_JAVA)) {
binding.editorCodeEditor.setEditorLanguage(new JavaLanguage(project));
binding.editorCodeEditor.setColorScheme(new SchemeVS2019());
@@ -208,6 +215,8 @@ public class EditorFragment extends BaseFragment implements EditorEventListener,
binding.editorCodeEditor.setEditorLanguage(new EmptyLanguage());
}
isRead = true;
+ if(openListener != null)
+ openListener.onOpenSuccessful(getFile(), getText());
}
private void closeCurrentTag(String text, int line, int col) {
@@ -271,13 +280,46 @@ public class EditorFragment extends BaseFragment implements EditorEventListener,
@Override
public void afterDelete(CodeEditor editor, CharSequence content, int startLine, int startColumn, int endLine, int endColumn, CharSequence deletedContent) {
isModified = true;
-
- requestCompilation();
+ JavaLanguageServer server = StudioApp.getInstance().getJavaLanguageServer();
+ if(server != null) {
+ try {
+ TextDocumentContentChangeEvent event = new TextDocumentContentChangeEvent();
+ VersionedTextDocumentIdentifier id = new VersionedTextDocumentIdentifier();
+ DidChangeTextDocumentParams p = new DidChangeTextDocumentParams();
+ List changes = new ArrayList<>();
+ event.text = getText();
+ event.range = null;
+ changes.add(event);
+ id.uri = getFile().toURI();
+ id.version = VersionedFileManager.incrementVersion(getFile());
+ p.textDocument = id;
+ p.contentChanges = changes;
+ server.didChange(p);
+ } catch (Throwable th) {}
+ }
}
@Override
public void afterInsert(CodeEditor editor, CharSequence content, int startLine, int startColumn, int endLine, int endColumn, CharSequence insertedContent) {
isModified = true;
+ JavaLanguageServer server = StudioApp.getInstance().getJavaLanguageServer();
+ if(server != null) {
+ try {
+ TextDocumentContentChangeEvent event = new TextDocumentContentChangeEvent();
+ VersionedTextDocumentIdentifier id = new VersionedTextDocumentIdentifier();
+ DidChangeTextDocumentParams p = new DidChangeTextDocumentParams();
+ List changes = new ArrayList<>();
+ event.text = getText();
+ event.range = null;
+ changes.add(event);
+ id.uri = getFile().toURI();
+ id.version = VersionedFileManager.incrementVersion(getFile());
+ p.textDocument = id;
+ p.contentChanges = changes;
+ server.didChange(p);
+ } catch (Throwable th) {}
+ }
+
boolean isOpen = false;
try {
isOpen = editor.getText().charAt(editor.getCursor().getLeft() - 2) == '<';
@@ -285,11 +327,13 @@ public class EditorFragment extends BaseFragment implements EditorEventListener,
if(isOpen && insertedContent.toString().equals("/")) {
closeCurrentTag(editor.getText().toString(), endLine, endColumn);
}
-
- requestCompilation();
}
@Override
public void beforeReplace(CodeEditor editor, CharSequence content) {
}
+
+ public static interface FileOpenListener {
+ public void onOpenSuccessful(File file, String text);
+ }
}
diff --git a/app/src/main/java/com/itsaky/androidide/interfaces/CompileListener.java b/app/src/main/java/com/itsaky/androidide/interfaces/CompileListener.java
deleted file mode 100644
index 28c164a6e..000000000
--- a/app/src/main/java/com/itsaky/androidide/interfaces/CompileListener.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.itsaky.androidide.interfaces;
-
-import com.itsaky.androidide.services.compiler.model.CompilerDiagnostic;
-import java.io.File;
-import java.util.List;
-import java.util.Map;
-
-public interface CompileListener {
- void onCompilationResult(boolean singleFile, Map> diagnostics);
-}
diff --git a/app/src/main/java/com/itsaky/androidide/interfaces/DiagnosticClickListener.java b/app/src/main/java/com/itsaky/androidide/interfaces/DiagnosticClickListener.java
index 2d8b9dc54..19924d539 100644
--- a/app/src/main/java/com/itsaky/androidide/interfaces/DiagnosticClickListener.java
+++ b/app/src/main/java/com/itsaky/androidide/interfaces/DiagnosticClickListener.java
@@ -1,9 +1,9 @@
package com.itsaky.androidide.interfaces;
import com.itsaky.androidide.models.DiagnosticGroup;
-import com.itsaky.androidide.services.compiler.model.CompilerDiagnostic;
+import com.itsaky.lsp.Diagnostic;
public interface DiagnosticClickListener {
void onGroupClick(DiagnosticGroup group);
- void onDiagnosticClick(CompilerDiagnostic diagnostic);
+ void onDiagnosticClick(Diagnostic diagnostic);
}
diff --git a/app/src/main/java/com/itsaky/androidide/language/java/JavaAutoComplete.java b/app/src/main/java/com/itsaky/androidide/language/java/JavaAutoComplete.java
index 1bbe9c2f9..b6bfeb539 100644
--- a/app/src/main/java/com/itsaky/androidide/language/java/JavaAutoComplete.java
+++ b/app/src/main/java/com/itsaky/androidide/language/java/JavaAutoComplete.java
@@ -1,44 +1,57 @@
package com.itsaky.androidide.language.java;
-import android.text.TextUtils;
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.utils.Either;
-import com.itsaky.androidide.utils.EitherOfResult;
+import com.itsaky.lsp.CompletionList;
+import com.itsaky.lsp.Position;
+import com.itsaky.lsp.TextDocumentIdentifier;
+import com.itsaky.lsp.TextDocumentPositionParams;
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.List;
-import javax.tools.Diagnostic;
-import javax.tools.JavaFileObject;
import com.itsaky.androidide.utils.Logger;
-import java.util.stream.Collectors;
-import com.itsaky.androidide.services.compiler.model.CompilerDiagnosticImpl;
public class JavaAutoComplete implements AutoCompleteProvider {
+
private JavaLanguageAnalyzer analyzer;
-
+ private int lastId = -1;
+
public JavaAutoComplete(JavaLanguageAnalyzer analyzer) {
this.analyzer = analyzer;
}
@Override
public List> getAutoCompleteItems(CodeEditor editor, String prefix, boolean isInCodeBlock, TextAnalyzeResult colors, int line) {
- Pair, List>> pair = StudioApp.getInstance().getCompletionProvider().getSuggestions(editor);
- final ArrayList suggestions = pair.first;
-// final List> diags = pair.second;
-// editor.setDiagnostics(diags.stream().map(CompilerDiagnosticImpl::fromDiagnostic).collect(Collectors.toList()));
- if(suggestions != null) {
- List> result = new ArrayList<>();
- for(SuggestItem item : suggestions) {
- result.add(new EitherOfResult(item, null));
- }
- return result;
- }
+ final JavaLanguageServer server = StudioApp.getInstance().getJavaLanguageServer();
+ if(server != null) {
+ TextDocumentIdentifier id = new TextDocumentIdentifier();
+ id.uri = editor.getFile().toURI();
+ TextDocumentPositionParams p = new TextDocumentPositionParams();
+ p.position = new Position(line, editor.getCursor().getLeftColumn());
+ p.textDocument = id;
+ Pair response = server.completion(p);
+ lastId = response.first;
+ CompletionList list = response.second;
+ if(list != null && list.items != null) {
+ List> result = new ArrayList<>();
+ for(int i=0;i either = Either.forLeft(new CompletionItemWrapper(item, prefix));
+ result.add(either);
+ }
+
+ return result;
+ }
+ }
return new ArrayList>();
}
}
diff --git a/app/src/main/java/com/itsaky/androidide/language/java/JavaLanguage.java b/app/src/main/java/com/itsaky/androidide/language/java/JavaLanguage.java
index 0f9ff2ad5..a71f85545 100644
--- a/app/src/main/java/com/itsaky/androidide/language/java/JavaLanguage.java
+++ b/app/src/main/java/com/itsaky/androidide/language/java/JavaLanguage.java
@@ -16,49 +16,41 @@ import java.io.StringReader;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.Token;
-public class JavaLanguage implements EditorLanguage
-{
+public class JavaLanguage implements EditorLanguage {
+
private JavaLanguageAnalyzer analyzer;
private JavaAutoComplete complete;
private AndroidProject project;
- public JavaLanguage(AndroidProject project)
- {
+ public JavaLanguage(AndroidProject project) {
this.analyzer = new JavaLanguageAnalyzer();
this.complete = new JavaAutoComplete(analyzer);
this.project = project;
}
@Override
- public CodeAnalyzer getAnalyzer()
- {
+ public CodeAnalyzer getAnalyzer() {
return analyzer;
}
@Override
- public AutoCompleteProvider getAutoCompleteProvider()
- {
+ public AutoCompleteProvider getAutoCompleteProvider() {
return complete;
}
@Override
- public boolean isAutoCompleteChar(char p1)
- {
+ public boolean isAutoCompleteChar(char p1) {
return JavaCharacter.isJavaIdentifierPart(p1) || p1 == '.';
}
@Override
- public int getIndentAdvance(String p1)
- {
- try
- {
+ public int getIndentAdvance(String p1) {
+ try {
JavaLexer lexer = new JavaLexer(CharStreams.fromReader(new StringReader(p1)));
Token token = null;
int advance = 0;
- while (((token = lexer.nextToken()) != null && token.getType() != token.EOF))
- {
- switch (token.getType())
- {
+ while (((token = lexer.nextToken()) != null && token.getType() != token.EOF)) {
+ switch (token.getType()) {
case JavaLexer.LBRACE:
advance++;
break;
@@ -69,14 +61,13 @@ public class JavaLanguage implements EditorLanguage
}
advance = Math.max(0, advance);
return advance * 4;
- } catch (Throwable e)
- {}
+ } catch (Throwable e) {}
return 0;
}
@Override
public SymbolPairMatch getSymbolPairs() {
- return new SymbolPairMatch.DefaultSymbolPairs();
+ return new JavaSymbolPairs();
}
@Override
@@ -116,9 +107,8 @@ public class JavaLanguage implements EditorLanguage
return new HandleResult(sb, shiftLeft);
}
}
-
- public static String[] getKeywords()
- {
+
+ public static String[] getKeywords() {
return new String[] {
"abstract", "assert", "boolean", "break", "byte", "case",
"catch", "char", "class", "const", "continue", "default",
@@ -130,4 +120,15 @@ public class JavaLanguage implements EditorLanguage
"transient", "try", "void", "volatile", "while", "null"
};
}
+
+ private class JavaSymbolPairs extends SymbolPairMatch {
+ public JavaSymbolPairs() {
+ super.putPair('{', new Replacement("{}", 1));
+ super.putPair('(', new Replacement("()", 1));
+ super.putPair('[', new Replacement("[]", 1));
+ super.putPair('"', new Replacement("\"\"", 1));
+ super.putPair('\'', new Replacement("''", 1));
+ super.putPair('<', new Replacement("<>", 1));
+ }
+ }
}
diff --git a/app/src/main/java/com/itsaky/androidide/language/java/parser/internal/JavaClassManager.java b/app/src/main/java/com/itsaky/androidide/language/java/parser/internal/JavaClassManager.java
index c9837f6d4..c80edc829 100644
--- a/app/src/main/java/com/itsaky/androidide/language/java/parser/internal/JavaClassManager.java
+++ b/app/src/main/java/com/itsaky/androidide/language/java/parser/internal/JavaClassManager.java
@@ -29,13 +29,6 @@ import java.util.List;
import java.util.Locale;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
-import org.eclipse.jdt.core.Signature;
-import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
-import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
-import org.eclipse.jdt.internal.compiler.classfmt.FieldInfo;
-import org.eclipse.jdt.internal.compiler.env.IBinaryField;
-import org.eclipse.jdt.internal.compiler.env.IBinaryMethod;
-import org.eclipse.jdt.internal.compiler.env.IBinaryType;
import com.itsaky.androidide.language.java.parser.model.ConstructorDescription;
import com.itsaky.androidide.utils.Either;
import com.blankj.utilcode.util.ThrowableUtils;
@@ -86,9 +79,6 @@ public class JavaClassManager implements IClassManager {
public void loadFromProject(AndroidProject project) {
try {
- try {
- loadClasspaths(project);
- } catch (Throwable e) {}
JavacParser parser = new JavacParser();
List javaSrcDirs = project.getSourcePaths();
for (String dir : javaSrcDirs) {
@@ -267,174 +257,6 @@ public class JavaClassManager implements IClassManager {
return result;
}
- private void loadClasspaths(AndroidProject project) throws IOException, ClassFormatException, ClassNotFoundException {
- final List classpaths = project.getClassPaths();
- if(!classpaths.contains(Environment.BOOTCLASSPATH))
- classpaths.add(Environment.BOOTCLASSPATH.getAbsolutePath());
- for(int i=0;i entries = zip.entries();
-
- ZipEntry entry = null;
- while(entries.hasMoreElements() && ((entry = entries.nextElement()) != null)) {
- if(!entry.getName().endsWith(".class"))
- continue;
- try {
- ClassFileReader reader = ClassFileReader.read(zip, entry.getName());
- describe(zip, reader);
- } catch (Throwable th) {}
- }
- }
- }
-
- private ClassDescription describe(ZipFile zip, IBinaryType type) throws IOException, ClassFormatException, ClassNotFoundException {
- if(type == null)
- return null;
- final char[] name = type.getName();
- final char[] superClassName = type.getSuperclassName();
- if(name == null) return null;
- final int mods = type.getModifiers();
- final boolean isPremitive = false;
- final boolean isAnnotation = false;
- final boolean isEnum = false;
- final boolean isMember = type.getEnclosingTypeName() != null;
- final IBinaryField[] fields = type.getFields();
- final IBinaryMethod[] methods = type.getMethods();
- final char[][] interfaces = type.getInterfaceNames();
- ClassDescription clazz = new ClassDescription(bytecodeToSourceType(new String(name)), mods, isPremitive, isAnnotation, isEnum, isMember);
-
- int index = 0;
- while(fields != null && index < fields.length) {
- IBinaryField f = fields[index];
- if(f == null) continue;
- FieldDescription field = describe(zip, f);
- if(field == null) continue;
- clazz.addField(field);
- ++index;
- }
-
- index = 0;
- while(methods != null && index < methods.length) {
- IBinaryMethod m = methods[index];
- if(m == null) continue;
- Either result = describe(zip, m);
- if(result == null) continue;
- if(result.isLeft())
- clazz.addMethod(result.getLeft());
- else clazz.addConstructor(result.getRight());
- ++index;
- }
-
- // TODO: Add interfaces
-
- clazz.setSuperclass(superClassName == null ? ClassDescription.ofObject() : findType(zip, new String(superClassName)));
-
- update(clazz);
- return clazz;
- }
-
- private Either describe(ZipFile zip, IBinaryMethod m) throws IOException, IllegalArgumentException, ClassFormatException, ClassNotFoundException {
- if(zip == null || m == null) return null;
- final char[] descriptor = m.getMethodDescriptor();
- final char[] signature = m.getGenericSignature();
- final char[] name = m.getSelector();
- final int mods = m.getModifiers();
- if(name == null || descriptor == null) return null;
- IClass returns = findType(zip, Signature.getReturnType(new String(descriptor)));
- String[] paramTypes = Signature.getParameterTypes(new String(descriptor));
- ArrayList params = new ArrayList<>();
- for(String type : paramTypes) {
- params.add(bytecodeToSourceType(type));
- }
- return m.isConstructor() ? Either.forRight(new ConstructorDescription(new String(name).replace("/", "."), params)) : Either.forLeft(new MethodDescription(new String(name).replace("/", "."), returns, mods, params));
- }
-
- private FieldDescription describe(ZipFile zip, IBinaryField field) throws IOException, ClassFormatException, ClassNotFoundException {
- if(field == null)
- return null;
- final char[] n = field.getName();
- final char[] t = field.getTypeName();
- if(n == null || t == null) return null;
- final String name = new String(n);
- final String type = new String(t);
- final int mods = field.getModifiers();
- final String value = field instanceof FieldInfo && ((FieldInfo) field).hasConstant() ? field.getConstant().stringValue() : "";
- IClass typeClass = findType(zip, type);
- if(typeClass == null)
- typeClass = new ClassDescription(Object.class);
- FieldDescription f = new FieldDescription(mods, typeClass, name.replace("/", "."), value);
- return f;
- }
-
- private IClass findType(ZipFile zip, String name) throws IOException, ClassFormatException, ClassNotFoundException {
- if(zip == null || name == null)
- return null;
- final int arrayCount = Signature.getArrayCount(name);
- if(arrayCount == 0) {
- if(name.length() == 1) {
- // A primitive type
- final Class load = primitiveType(name.charAt(0));
- if(load != null) {
- ClassDescription desc = new ClassDescription(load);
- desc.initMembers(load);
- update(desc);
- return desc;
- }
- }
- IClass found = getParsedClass(bytecodeToSourceType(name));
- if(found != null) {
- return found;
- } else {
- ClassFileReader reader = ClassFileReader.read(zip, name);
- if(reader == null)
- return null;
- else {
- // This class will be updated in describe() method
- return describe(zip, reader);
- }
- }
- } else {
- if(Signature.getElementType(name).length() == 1) {
- // This type may be an array of primitive
- Class load = Class.forName(name);
- if(load != null) {
- ClassDescription desc = new ClassDescription(load);
- desc.initMembers(load);
- update(desc);
- return desc;
- }
- }
- // TODO: Load array type classes of objects
- // Return without loading members of this class
- return new ClassDescription(Void.TYPE);
- }
- }
-
- private String bytecodeToSourceType(String type) {
- type = Signature.getElementType(type);
- if(type.startsWith("L") && type.endsWith(";")) {
- type = type.substring(1, type.lastIndexOf(";"));
- }
- return type.replace("/", ".");
- }
-
- public Class primitiveType(char typeId) {
- switch (typeId) {
- case 'I' : return int.class;
- case 'B' : return byte.class;
- case 'S' : return short.class;
- case 'C' : return char.class;
- case 'F' : return float.class;
- case 'D' : return double.class;
- case 'Z' : return boolean.class;
- case 'J' : return long.class;
- default : return null;
- }
- }
-
-
/**
* Kept as a backup if loading jar files fails for some reasons
*/
diff --git a/app/src/main/java/com/itsaky/androidide/language/java/server/JavaLanguageServer.java b/app/src/main/java/com/itsaky/androidide/language/java/server/JavaLanguageServer.java
new file mode 100644
index 000000000..ad6439771
--- /dev/null
+++ b/app/src/main/java/com/itsaky/androidide/language/java/server/JavaLanguageServer.java
@@ -0,0 +1,279 @@
+package com.itsaky.androidide.language.java.server;
+
+import androidx.core.util.Pair;
+import com.blankj.utilcode.util.ThreadUtils;
+import com.blankj.utilcode.util.ThrowableUtils;
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.itsaky.androidide.app.StudioApp;
+import com.itsaky.androidide.models.AndroidProject;
+import com.itsaky.androidide.shell.ShellServer;
+import com.itsaky.androidide.utils.Environment;
+import com.itsaky.androidide.utils.Logger;
+import com.itsaky.lsp.CancelParams;
+import com.itsaky.lsp.CompletionList;
+import com.itsaky.lsp.DidChangeConfigurationParams;
+import com.itsaky.lsp.DidChangeTextDocumentParams;
+import com.itsaky.lsp.DidOpenTextDocumentParams;
+import com.itsaky.lsp.InitializeParams;
+import com.itsaky.lsp.JavaReportProgressParams;
+import com.itsaky.lsp.JavaStartProgressParams;
+import com.itsaky.lsp.LanguageClient;
+import com.itsaky.lsp.Message;
+import com.itsaky.lsp.PublishDiagnosticsParams;
+import com.itsaky.lsp.TextDocumentPositionParams;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class JavaLanguageServer implements ShellServer.Callback {
+
+ private AndroidProject project;
+ private ShellServer shell;
+
+ private LanguageClient client;
+ private Socket server;
+ private OutputStream send;
+
+ private final ConcurrentHashMap completionRequests = new ConcurrentHashMap<>();
+ private final BlockingQueue responseQueue = new ArrayBlockingQueue(1);
+
+ private static final String START_COMMAND = "java " +
+ "--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED " +
+ "--add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED " +
+ "--add-exports jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED " +
+ "--add-exports jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED " +
+ "--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED " +
+ "--add-exports jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED " +
+ "--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED " +
+ "--add-opens jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED " +
+ "-cp $JLS_HOME/lib/jls.jar:$JLS_HOME/lib/gson.jar:$JLS_HOME/lib/protobuf.jar " +
+ "org.javacs.Main";
+
+
+ private static final String CONTENT_LENGTH = "Content-Length: ";
+ private static final String SOCKET_HOST = "localhost";
+ private static final int SOCKET_PORT = 5443;
+ private static int UNIVERSION_ID = 1;
+ private static final Logger logger = Logger.instance("JavaLanguageServer");
+ private static final Gson gson = new Gson();
+
+ public JavaLanguageServer(AndroidProject project, LanguageClient client) {
+ this.project = project;
+ this.client = client;
+ this.shell = StudioApp.getInstance().newShell(this);
+ }
+
+ public void startServer() {
+ this.shell.bgAppend(START_COMMAND);
+
+ new Thread(() -> {
+ try {
+ Socket socket = connectAndGet();
+ onConnectToServer();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+ String line;
+ while((line = reader.readLine()) != null) {
+ final String temp = line;
+ ThreadUtils.runOnUiThread(() -> {
+ onServerOut(temp);
+ });
+ }
+ } catch (Throwable e) {
+ logEx(e);
+ }
+ }).start();
+ }
+
+ private Socket connectAndGet() {
+ int tryCount = 0;
+ while(server == null || send == null || !server.isConnected()) {
+ try {
+ server = new Socket(SOCKET_HOST, SOCKET_PORT);
+ send = server.getOutputStream();
+ } catch (IOException e) {
+ tryCount++;
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e2) {}
+ }
+ }
+ return server;
+ }
+
+ protected void onConnectToServer() {
+ InitializeParams p = new InitializeParams();
+ p.rootUri = new File(project.getProjectPath()).toURI();
+ initialize(p);
+ initialized();
+
+ final List cps = project.getClassPaths();
+ if(!cps.contains(Environment.BOOTCLASSPATH.getAbsolutePath())) cps.add(Environment.BOOTCLASSPATH.getAbsolutePath());
+ JsonObject settings = new JsonObject();
+ JsonObject java = new JsonObject();
+ JsonArray classpaths = new JsonArray();
+ for(int i=0;i completion(TextDocumentPositionParams p) {
+ int id = write(Method.COMPLETION, gson.toJson(p));
+ completionRequests.put(id, id);
+ CompletionList result = null;
+ try {
+ result = responseQueue.take();
+ } catch (InterruptedException e) {}
+ return Pair.create(id, result);
+ }
+
+ public void cancelRequest(int requestId) {
+ CancelParams p = new CancelParams();
+ p.id = requestId;
+ int id = write(Method.CANCEL_REQUEST, gson.toJson(p));
+ }
+
+ private int write(String method, String data) {
+ final int id = UNIVERSION_ID++;
+ Message msg = new Message();
+ msg.id = id;
+ msg.jsonrpc = "2.0";
+ msg.method = method;
+ msg.params = new JsonParser().parse(data);
+
+ if(this.send != null) {
+ final String body = gson.toJson(msg);
+ final String head = String.format("Content-Length: %d\r\n\r\n", body.length());
+ new Thread(() -> {
+ try {
+ send.write(head.concat(body).getBytes(StandardCharsets.UTF_8));
+ } catch (Throwable e) {
+ logger.e("Error writing to server: " + ThrowableUtils.getFullStackTrace(e));
+ }
+ }).start();
+ }
+
+ return id;
+ }
+
+ private void logEx(Throwable th) {
+ logger.e(ThrowableUtils.getFullStackTrace(th));
+ }
+
+ @Override
+ public void output(CharSequence charSequence) {
+ logger.d(charSequence);
+ }
+
+ /**
+ * This method is called on UI Thread, do not perform time consuming or networking tasks
+ */
+ private void onServerOut(String line) {
+ if(line == null || line.trim().length() <= 0) return;
+ if(line.contains(CONTENT_LENGTH)) line = line.substring(0, line.lastIndexOf(CONTENT_LENGTH));
+ if(this.client == null) return;
+ try {
+ JsonObject obj = new JsonParser().parse(line.trim()).getAsJsonObject();
+ if(obj.has(Key.METHOD)) {
+ final String method = obj.get(Key.METHOD).getAsString();
+ if(method.equals(Method.PUBLISH_DIAGNOSTICS)) {
+ PublishDiagnosticsParams params = gson.fromJson(obj.get(Key.PARAMS).toString(), PublishDiagnosticsParams.class);
+ client.publishDiagnostics(params);
+ } else if(method.equals(Method.JLS_PROGRESS_START)) {
+ JavaStartProgressParams params = gson.fromJson(obj.get(Key.PARAMS).toString(), JavaStartProgressParams.class);
+ client.javaProgressStart(params);
+ } if(method.equals(Method.JLS_PROGRESS_REPORT)) {
+ JavaReportProgressParams params = gson.fromJson(obj.get(Key.PARAMS).toString(), JavaReportProgressParams.class);
+ client.javaProgressReport(params);
+ } if(method.equals(Method.JLS_PROGRESS_END)) {
+ client.javaProgressEnd();
+ }
+ } else if(obj.has(Key.ID)) {
+ int id = obj.get(Key.ID).getAsInt();
+ if(completionRequests.containsKey(id)) {
+ final CompletionList list = gson.fromJson(obj.get(Key.RESULT).getAsJsonObject(), CompletionList.class);
+ responseQueue.put(list);
+ }
+ }
+ } catch (Throwable th) {
+ logger.e("onServerOut", ThrowableUtils.getFullStackTrace(th));
+ }
+ }
+
+ private class Method {
+ public static final String INITIALIZE = "initialize";
+ public static final String INITIALIZED = "initialized";
+ public static final String SHUTDOWN = "shutdown";
+ public static final String EXIT = "exit";
+
+ public static final String CHANGED_CONFIGURATION = "workspace/didChangeConfiguration";
+ public static final String CHANGED_WATCHED_FILES = "workspace/didChangeWatchedFiles";
+
+ public static final String DID_OPEN = "textDocument/didOpen";
+ public static final String DID_CLOSE = "textDocument/didClose";
+ public static final String DID_CHANGE = "textDocument/didChange";
+ public static final String DID_SAVE = "textDocument/didSave";
+ public static final String WILL_SAVE = "textDocument/willSave";
+
+ public static final String COMPLETION = "textDocument/completion";
+ public static final String RESOLVE_COMPLETION = "completionItem/resolve";
+
+ public static final String HOVER = "textDocument/hover";
+ public static final String SIGNATURE_HELP = "textDocument/signatureHelp";
+ public static final String DEFINITION = "textDocument/definition";
+
+ public static final String PREPARE_RENAME = "textDocument/prepareRename";
+ public static final String RENAME = "textDocument/rename";
+
+ public static final String CODE_ACTION = "textDocument/codeAction";
+
+ public static final String CANCEL_REQUEST = "$/cancelRequest";
+
+ public static final String JLS_PROGRESS_START = "java/startProgress";
+ public static final String JLS_PROGRESS_REPORT = "java/reportProgress";
+ public static final String JLS_PROGRESS_END = "java/endProgress";
+
+ public static final String PUBLISH_DIAGNOSTICS = "textDocument/publishDiagnostics";
+ public static final String JAVA_COLORS = "java/colors";
+ }
+
+ private class Key {
+ public static final String METHOD = "method";
+ public static final String PARAMS = "params";
+ public static final String ID = "id";
+ public static final String RESULT = "result";
+ }
+}
diff --git a/app/src/main/java/com/itsaky/androidide/models/CompletionItemWrapper.java b/app/src/main/java/com/itsaky/androidide/models/CompletionItemWrapper.java
new file mode 100644
index 000000000..a863ba145
--- /dev/null
+++ b/app/src/main/java/com/itsaky/androidide/models/CompletionItemWrapper.java
@@ -0,0 +1,76 @@
+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;
+import io.github.rosemoe.editor.text.CharPosition;
+import io.github.rosemoe.editor.text.Cursor;
+import io.github.rosemoe.editor.widget.CodeEditor;
+
+public class CompletionItemWrapper implements SuggestItem {
+
+ private CompletionItem item;
+ private String prefix;
+
+ public CompletionItemWrapper(CompletionItem item, String prefix) {
+ this.item = item;
+ this.prefix = prefix;
+ }
+
+ public String getInsertText() {
+ return item.insertText == null ? item.label : item.insertText;
+ }
+
+ @Override
+ public String getName() {
+ return item.label;
+ }
+
+ @Override
+ public String getDescription() {
+ return item.detail;
+ }
+
+ @Override
+ public String getReturnType() {
+ return CompletionItemKind.asString(item.kind);
+ }
+
+ @Override
+ public char getTypeHeader() {
+ return getReturnType().charAt(0);
+ }
+
+ @Override
+ public int getSuggestionPriority() {
+ return item.kind;
+ }
+
+ @Override
+ public void onSelectThis(CodeEditor editor) {
+ try {
+ final Cursor cursor = editor.getCursor();
+ if(cursor.isSelected()) return;
+ final int length = getInsertLength();
+ String text = getInsertText();
+ final boolean shiftLeft = text.contains("$0");
+ final boolean atEnd = text.endsWith("$0");
+ text = text.replace("$0", "");
+ editor.getText().delete(cursor.getLeftLine(), cursor.getLeftColumn() - length, cursor.getLeftLine(), cursor.getLeftColumn());
+ cursor.onCommitText(text);
+
+ if(shiftLeft && !atEnd)
+ editor.moveSelectionLeft();
+ } catch (Throwable th) {
+ Logger.instance("CompletionItemWrapper").e("onSelectThis, Error: ", ThrowableUtils.getFullStackTrace(th));
+ }
+ }
+
+ private int getInsertLength() {
+ if (prefix.endsWith(".")) return 0;
+ else if (prefix.contains(".")) return prefix.substring(prefix.lastIndexOf(".") + 1).length();
+ else return prefix.length();
+ }
+}
diff --git a/app/src/main/java/com/itsaky/androidide/models/DiagnosticGroup.java b/app/src/main/java/com/itsaky/androidide/models/DiagnosticGroup.java
index 89acdd8dc..f1f93b14e 100644
--- a/app/src/main/java/com/itsaky/androidide/models/DiagnosticGroup.java
+++ b/app/src/main/java/com/itsaky/androidide/models/DiagnosticGroup.java
@@ -1,5 +1,6 @@
package com.itsaky.androidide.models;
-import com.itsaky.androidide.services.compiler.model.CompilerDiagnostic;
+
+import com.itsaky.lsp.Diagnostic;
import java.io.File;
import java.util.List;
@@ -7,9 +8,9 @@ public class DiagnosticGroup {
public int icon;
public String text;
public File file;
- public List diagnostics;
+ public List diagnostics;
- public DiagnosticGroup(int icon, File file, List diagnostics) {
+ public DiagnosticGroup(int icon, File file, List diagnostics) {
this.icon = icon;
this.file = file;
this.text = file.getName();
diff --git a/app/src/main/java/com/itsaky/androidide/services/compiler/CompilerProgress.java b/app/src/main/java/com/itsaky/androidide/services/compiler/CompilerProgress.java
deleted file mode 100644
index 99f9a8e92..000000000
--- a/app/src/main/java/com/itsaky/androidide/services/compiler/CompilerProgress.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.itsaky.androidide.services.compiler;
-
-import org.eclipse.jdt.core.compiler.CompilationProgress;
-
-public class CompilerProgress extends CompilationProgress {
-
- private boolean canceled = false;
-
- public void setCanceled(boolean canceled) {
- this.canceled = canceled;
- }
-
- @Override
- public void begin(int p1) {
- }
-
- @Override
- public void done() {
- }
-
- @Override
- public boolean isCanceled() {
- return canceled;
- }
-
- @Override
- public void setTaskName(String p1) {
- }
-
- @Override
- public void worked(int p1, int p2) {
- }
-}
diff --git a/app/src/main/java/com/itsaky/androidide/services/compiler/CompilerRequestor.java b/app/src/main/java/com/itsaky/androidide/services/compiler/CompilerRequestor.java
deleted file mode 100644
index f644b19b0..000000000
--- a/app/src/main/java/com/itsaky/androidide/services/compiler/CompilerRequestor.java
+++ /dev/null
@@ -1,101 +0,0 @@
-package com.itsaky.androidide.services.compiler;
-
-import java.io.File;
-import java.nio.charset.Charset;
-import java.util.Locale;
-import javax.tools.Diagnostic;
-import javax.tools.DiagnosticListener;
-import javax.tools.JavaFileObject;
-import org.eclipse.jdt.core.compiler.CategorizedProblem;
-import org.eclipse.jdt.internal.compiler.CompilationResult;
-import org.eclipse.jdt.internal.compiler.ICompilerRequestor;
-import org.eclipse.jdt.internal.compiler.problem.DefaultProblem;
-import org.eclipse.jdt.internal.compiler.tool.EclipseCompilerImpl;
-import org.eclipse.jdt.internal.compiler.tool.EclipseFileManager;
-import org.eclipse.jdt.internal.compiler.tool.EclipseFileObject;
-
-public class CompilerRequestor implements ICompilerRequestor {
-
- private DiagnosticListener super JavaFileObject> listener;
-
- public CompilerRequestor(DiagnosticListener super JavaFileObject> listener) {
- this.listener = listener;
- }
-
- @Override
- public void acceptResult(CompilationResult p1) {
- if (p1 != null && p1.hasProblems()) {
- for (CategorizedProblem problem : p1.getAllProblems()) {
- report(problem);
- }
- }
- }
-
- private void report(final CategorizedProblem problem) {
- this.listener.report(new Diagnostic() {
- @Override
- public String getCode() {
- return null;
- }
- @Override
- public long getColumnNumber() {
- if (problem instanceof DefaultProblem) {
- return ((DefaultProblem) problem).column;
- }
- return Diagnostic.NOPOS;
- }
- @Override
- public long getEndPosition() {
- if (problem instanceof DefaultProblem) {
- return ((DefaultProblem) problem).getSourceEnd();
- }
- return Diagnostic.NOPOS;
- }
- @Override
- public Kind getKind() {
- if (problem.isError()) {
- return Diagnostic.Kind.ERROR;
- }
- if (problem.isWarning()) {
- return Diagnostic.Kind.WARNING;
- } else if (problem instanceof DefaultProblem && ((DefaultProblem) problem).isInfo()) {
- return Diagnostic.Kind.NOTE;
- }
- return Diagnostic.Kind.OTHER;
- }
- @Override
- public long getLineNumber() {
- if (problem instanceof DefaultProblem) {
- return ((DefaultProblem) problem).getSourceLineNumber();
- }
- return Diagnostic.NOPOS;
- }
- @Override
- public String getMessage(Locale locale) {
- return problem.getMessage();
- }
- @Override
- public long getPosition() {
- if (problem instanceof DefaultProblem) {
- return ((DefaultProblem) problem).getSourceStart();
- }
- return Diagnostic.NOPOS;
- }
- @Override
- public JavaFileObject getSource() {
- if (problem instanceof DefaultProblem) {
- File f = new File(new String(problem.getOriginatingFileName()));
- if (f.exists()) {
- return new EclipseFileObject(null, f.toURI(), JavaFileObject.Kind.SOURCE, null);
- }
- return null;
- }
- return null;
- }
- @Override
- public long getStartPosition() {
- return getPosition();
- }
- });
- }
-}
diff --git a/app/src/main/java/com/itsaky/androidide/services/compiler/CompilerWrapper.java b/app/src/main/java/com/itsaky/androidide/services/compiler/CompilerWrapper.java
deleted file mode 100644
index fcdf35c50..000000000
--- a/app/src/main/java/com/itsaky/androidide/services/compiler/CompilerWrapper.java
+++ /dev/null
@@ -1,138 +0,0 @@
-package com.itsaky.androidide.services.compiler;
-
-import java.io.PrintWriter;
-import org.eclipse.jdt.core.compiler.CompilationProgress;
-import org.eclipse.jdt.internal.compiler.CompilationResult;
-import org.eclipse.jdt.internal.compiler.Compiler;
-import org.eclipse.jdt.internal.compiler.ICompilerRequestor;
-import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy;
-import org.eclipse.jdt.internal.compiler.IProblemFactory;
-import org.eclipse.jdt.internal.compiler.ReadManager;
-import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
-import org.eclipse.jdt.internal.compiler.ast.ImportReference;
-import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
-import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
-import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
-import org.eclipse.jdt.internal.compiler.lookup.SourceTypeCollisionException;
-import org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
-import org.eclipse.jdt.internal.compiler.util.Messages;
-
-public class CompilerWrapper extends Compiler {
-
- public CompilerWrapper(INameEnvironment env,
- IErrorHandlingPolicy errorHandlingPolicy,
- CompilerOptions options,
- ICompilerRequestor compilerRequestor,
- IProblemFactory problemFactory,
- PrintWriter out,
- CompilationProgress progress) {
- super(env, errorHandlingPolicy, options, compilerRequestor, problemFactory, out, progress);
- this.useSingleThread = true;
- }
-
- public CompilerWrapper setProgress(CompilationProgress progress) {
- this.progress = progress;
- return this;
- }
-
- @Override
- public void compile(ICompilationUnit[] sourceUnits) {
- compileInternal(sourceUnits, false);
- }
-
- private void compileInternal(ICompilationUnit[] sourceUnits, boolean lastRound) {
- this.stats.startTime = System.currentTimeMillis();
- try {
- reportProgress(Messages.compilation_beginningToCompile);
- if (this.annotationProcessorManager == null) {
- beginToCompile(sourceUnits);
- } else {
- ICompilationUnit[] originalUnits = sourceUnits.clone(); // remember source units in case a source type collision occurs
- try {
- beginToCompile(sourceUnits);
- if (!lastRound) processAnnotations();
- if (!this.options.generateClassFiles) return;
- } catch (SourceTypeCollisionException e) {
- backupAptProblems();
- reset();
- int originalLength = originalUnits.length;
- int newProcessedLength = e.newAnnotationProcessorUnits.length;
- ICompilationUnit[] combinedUnits = new ICompilationUnit[originalLength + newProcessedLength];
- System.arraycopy(originalUnits, 0, combinedUnits, 0, originalLength);
- System.arraycopy(e.newAnnotationProcessorUnits, 0, combinedUnits, originalLength, newProcessedLength);
- this.annotationProcessorStartIndex = originalLength;
- compileInternal(combinedUnits, e.isLastRound);
- return;
- }
- }
- restoreAptProblems();
- processCompiledUnits(0, lastRound);
- } catch (AbortCompilation e) {
- this.handleInternalException(e, null);
- } catch (Throwable th) {
- AbortCompilation a = new AbortCompilation();
- a.isSilent = true;
- handleInternalException(a, null);
- }
- if (this.options.verbose) {
- if (this.totalUnits > 1) {
- this.out.println(Messages.bind(Messages.compilation_units, String.valueOf(this.totalUnits)));
- } else {
- this.out.println(Messages.bind(Messages.compilation_unit, String.valueOf(this.totalUnits)));
- }
- }
- }
-
- @Override
- protected void internalBeginToCompile(ICompilationUnit[] sourceUnits, int maxUnits) {
- if (!this.useSingleThread && maxUnits >= ReadManager.THRESHOLD)
- this.parser.readManager = new ReadManager(sourceUnits, maxUnits);
-
- for (int i = 0; i < maxUnits; i++) {
- CompilationResult unitResult = null;
- try {
- if (this.options.verbose) {
- this.out.println(
- Messages.bind(Messages.compilation_request,
- new String[] {
- String.valueOf(i + 1),
- String.valueOf(maxUnits),
- new String(sourceUnits[i].getFileName())
- }));
- }
- CompilationUnitDeclaration parsedUnit;
- unitResult = new CompilationResult(sourceUnits[i], i, maxUnits, this.options.maxProblemsPerUnit);
- long parseStart = System.currentTimeMillis();
- if (this.totalUnits < this.parseThreshold) {
- parsedUnit = this.parser.parse(sourceUnits[i], unitResult);
- } else {
- parsedUnit = this.parser.dietParse(sourceUnits[i], unitResult);
- }
- long resolveStart = System.currentTimeMillis();
- this.stats.parseTime += resolveStart - parseStart;
- this.lookupEnvironment.buildTypeBindings(parsedUnit, null);
- this.stats.resolveTime += System.currentTimeMillis() - resolveStart;
- addCompilationUnit(sourceUnits[i], parsedUnit);
- ImportReference currentPackage = parsedUnit.currentPackage;
- if (currentPackage != null) {
- unitResult.recordPackageName(currentPackage.tokens);
- }
- if (this.parser.readManager != null) {
- this.parser.readManager.shutdown();
- this.parser.readManager = null;
- }
- this.lookupEnvironment.completeTypeBindings();
- } catch (AbortCompilation a) {
- if (a.compilationResult == null)
- a.compilationResult = unitResult;
- throw a;
- } catch (Throwable th) {
- AbortCompilation a = new AbortCompilation();
- a.isSilent = true;
- throw a;
- } finally {
- sourceUnits[i] = null;
- }
- }
- }
-}
diff --git a/app/src/main/java/com/itsaky/androidide/services/compiler/JavaCompilerService.java b/app/src/main/java/com/itsaky/androidide/services/compiler/JavaCompilerService.java
deleted file mode 100644
index e4318d42a..000000000
--- a/app/src/main/java/com/itsaky/androidide/services/compiler/JavaCompilerService.java
+++ /dev/null
@@ -1,411 +0,0 @@
-package com.itsaky.androidide.services.compiler;
-
-import android.text.TextUtils;
-import com.blankj.utilcode.util.FileUtils;
-import com.blankj.utilcode.util.ThreadUtils;
-import com.itsaky.androidide.interfaces.CompileListener;
-import com.itsaky.androidide.models.AndroidProject;
-import com.itsaky.androidide.services.compiler.model.CompilerDiagnostic;
-import com.itsaky.androidide.services.compiler.model.CompilerDiagnosticImpl;
-import com.itsaky.androidide.services.compiler.request.CompilationRequest;
-import com.itsaky.androidide.tools.SourceJavaFileObject;
-import com.itsaky.androidide.utils.Environment;
-import com.itsaky.androidide.utils.Logger;
-import java.io.File;
-import java.io.FileFilter;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import javax.tools.Diagnostic;
-import javax.tools.DiagnosticListener;
-import javax.tools.JavaCompiler;
-import javax.tools.JavaFileObject;
-import org.eclipse.jdt.core.compiler.CategorizedProblem;
-import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies;
-import org.eclipse.jdt.internal.compiler.ICompilerRequestor;
-import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy;
-import org.eclipse.jdt.internal.compiler.IProblemFactory;
-import org.eclipse.jdt.internal.compiler.batch.CompilationUnit;
-import org.eclipse.jdt.internal.compiler.batch.FileSystem;
-import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
-import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
-import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
-import org.eclipse.jdt.internal.compiler.tool.EclipseCompiler;
-import org.eclipse.jdt.internal.compiler.tool.EclipseFileManager;
-import org.eclipse.jdt.internal.compiler.tool.EclipseFileObject;
-import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
-
-public class JavaCompilerService implements DiagnosticListener {
-
- private AndroidProject mProject;
-
- private EclipseCompiler mCompiler;
- private CompilerWrapper mSingleFileCompiler;
- private EclipseFileManager mFileManager;
-
- private FileSystem sf_nameEnvironment;
- private CompilerOptions sf_options;
- private IProblemFactory sf_problemFactory;
- private ICompilerRequestor sf_compilerRequest;
- private CompilerProgress sf_progress;
- private IErrorHandlingPolicy sf_policy;
- private PrintWriter sf_outWriter;
-
- private List mOptions;
- private List mSources;
-
- private static final Map> mDiagnostics = new HashMap<>();
-
- private FileFilter JAVA_FILTER = new FileFilter(){
-
- @Override
- public boolean accept(File p1) {
- return p1.isFile() && p1.getName().endsWith(".java");
- }
- };
-
- public JavaCompilerService(AndroidProject project) {
- this.mProject = project;
- this.createOptions();
- }
-
- private void createOptions() {
- this.mOptions = new ArrayList<>();
-
- mOptions.add("-1.8");
- mOptions.add("-d");
- mOptions.add(Environment.TMP_DIR.getAbsolutePath());
- mOptions.add("-proc:none");
- mOptions.add("-warn:all");
- mOptions.add("-warn:-nls");
- mOptions.add("-g");
- mOptions.add("-bootclasspath");
- mOptions.add(Environment.BOOTCLASSPATH.getAbsolutePath());
- mOptions.add("-cp");
- mOptions.add(TextUtils.join(File.pathSeparator, mProject.getClassPaths()));
- }
-
- private EclipseCompiler getCompiler() {
- return getCompiler(false);
- }
-
- private EclipseCompiler getCompiler(boolean forceCreate) {
- if(mCompiler == null || forceCreate) {
- this.mCompiler = new EclipseCompiler();
- this.mFileManager = (EclipseFileManager) mCompiler.getStandardFileManager(this, Locale.US, null);
- }
- return mCompiler;
- }
-
- private EclipseFileManager getFileManager() {
- if(mFileManager == null)
- getCompiler(true);
-
- return mFileManager;
- }
-
- private CompilerWrapper getSingleFileCompiler() {
- if(this.sf_nameEnvironment == null)
- this.sf_nameEnvironment = new FileSystem(createClasspaths(mProject.getClassPaths()), null, null);
-
- if(this.sf_compilerRequest == null)
- this.sf_compilerRequest = new CompilerRequestor(this);
-
- if(this.sf_problemFactory == null)
- this.sf_problemFactory = getProblemFactory();
-
- if(this.sf_options == null) {
- this.sf_options = new CompilerOptions();
- putOptions(sf_options);
- }
-
- if(sf_policy == null)
- this.sf_policy = DefaultErrorHandlingPolicies.exitAfterAllProblems();
-
- if(sf_outWriter == null)
- sf_outWriter = new PrintWriter(System.out);
-
- if(mSingleFileCompiler == null) {
- mSingleFileCompiler = new CompilerWrapper(
- sf_nameEnvironment,
- sf_policy,
- sf_options,
- sf_compilerRequest,
- sf_problemFactory,
- sf_outWriter,
- sf_progress = new CompilerProgress()
- );
- }
- return mSingleFileCompiler;
- }
-
- private void putOptions(CompilerOptions options) {
- final long languageLevel = ClassFileConstants.JDK1_8;
- options.sourceLevel = options.targetJDK = options.complianceLevel = languageLevel;
- options.processAnnotations = false;
- Map map = options.getMap();
- for(String warn : CompilerOptions.warningTokens) {
- map.put(warn, CompilerOptions.ENABLED);
- }
- map.put(CompilerOptions.OPTION_ReportNonExternalizedStringLiteral, CompilerOptions.DISABLED);
- map.put(CompilerOptions.OPTION_LocalVariableAttribute, CompilerOptions.DO_NOT_GENERATE);
- map.put(CompilerOptions.OPTION_LineNumberAttribute, CompilerOptions.DO_NOT_GENERATE);
- map.put(CompilerOptions.OPTION_SourceFileAttribute, CompilerOptions.DO_NOT_GENERATE);
- options.set(map);
- }
-
- private String[] createClasspaths(List classpaths) {
- if (classpaths == null) {
- classpaths = new ArrayList<>();
- }
-
- if(!classpaths.contains(Environment.BOOTCLASSPATH.getAbsolutePath())) {
- classpaths.add(Environment.BOOTCLASSPATH.getAbsolutePath());
- }
-
- String[] result = new String[classpaths.size()];
- return classpaths.toArray(result);
- }
-
- private CompilationRequest createCompilationRequestForAll(CompileListener listener) {
- if(mSources == null || mSources.size() <= 0) {
- loadSources();
- }
- return CompilationRequest.Builder
- .newInstance()
- .withCallback(listener)
- .withFiles(mSources)
- .build();
- }
-
- private void notifyCompiled(CompileListener listener, Map> diags) {
- ThreadUtils.runOnUiThread(() -> {
- listener.onCompilationResult(true, diags);
- });
- }
-
- protected void cancelCompilation() {
- getCompiler().setCompilationCancelled(true);
- if(sf_progress != null) {
- sf_progress.setCanceled(true);
- }
- }
-
- public List getClasspaths() {
- return mProject.getClassPaths();
- }
-
- public void loadSources() {
- mSources = new ArrayList();
- for(String path : mProject.getSourcePaths()) {
- if(path == null || path.trim().length() <= 0) continue;
- File p = new File(path);
- if(!p.exists() || !p.isDirectory()) continue;
- mSources.addAll(FileUtils.listFilesInDirWithFilter(p, JAVA_FILTER, true));
- }
- }
-
- public void compileAllAsync(CompileListener listener) {
- new Thread(() -> {
- compile(createCompilationRequestForAll(listener));
- }).start();
- }
-
- public void compileAsync(CompilationRequest request) {
- new Thread(() -> {
- compile(request);
- }).start();
- }
-
- public void compileSingleAsync(SourceJavaFileObject source, CompileListener listener) {
- new Thread(() -> {
- compileSingle(source, listener);
- }).start();
- }
-
- public boolean compile(final CompilationRequest request) {
- this.mDiagnostics.clear();
- final List fileObjects = (ArrayList) getFileManager().getJavaFileObjectsFromFiles(request.files());
- JavaCompiler.CompilationTask task = getCompiler()
- .setCompilationCancelled()
- .newProgress()
- .getTask(null, getFileManager(), this, mOptions, null, fileObjects);
- boolean success = task.call();
- notifyCompiled(request.callback(), mDiagnostics);
- return success;
- }
-
- public void compileSingle(final SourceJavaFileObject source, final CompileListener listener) {
- mDiagnostics.clear();
- cancelCompilation();
- if(mDiagnostics != null) mDiagnostics.clear();
- getSingleFileCompiler()
- .setProgress(this.sf_progress = new CompilerProgress())
- .compile(new CompilationUnit[]{source.asUnit()});
- notifyCompiled(listener, mDiagnostics);
- }
-
- @Override
- public void report(Diagnostic extends JavaFileObject> diagnostic) {
- CompilerDiagnostic d = CompilerDiagnosticImpl.fromDiagnostic(diagnostic);
- if(mDiagnostics.containsKey(d.file())) {
- List diags = mDiagnostics.get(d.file());
- if(diags == null)
- diags = new ArrayList<>();
- diags.add(d);
- mDiagnostics.put(d.file(), diags);
- } else {
- List diags = new ArrayList<>();
- diags.add(d);
- mDiagnostics.put(d.file(), diags);
- }
- }
-
- private IProblemFactory getProblemFactory() {
- return new DefaultProblemFactory() {
- @Override
- public CategorizedProblem createProblem(
- final char[] originatingFileName,
- final int problemId,
- final String[] problemArguments,
- final String[] messageArguments,
- final int severity,
- final int startPosition,
- final int endPosition,
- final int lineNumber,
- final int columnNumber) {
-
- report(new Diagnostic() {
- @Override
- public String getCode() {
- return Integer.toString(problemId);
- }
- @Override
- public long getColumnNumber() {
- return columnNumber;
- }
- @Override
- public long getEndPosition() {
- return endPosition;
- }
- @Override
- public Kind getKind() {
- if ((severity & ProblemSeverities.Error) != 0) {
- return Diagnostic.Kind.ERROR;
- }
- if ((severity & ProblemSeverities.Optional) != 0) {
- return Diagnostic.Kind.WARNING;
- }
- if ((severity & ProblemSeverities.Warning) != 0) {
- return Diagnostic.Kind.MANDATORY_WARNING;
- }
- return Diagnostic.Kind.OTHER;
- }
- @Override
- public long getLineNumber() {
- return lineNumber;
- }
- @Override
- public String getMessage(Locale locale) {
- if (locale != null) {
- setLocale(locale);
- }
- return getLocalizedMessage(problemId, problemArguments);
- }
- @Override
- public long getPosition() {
- return startPosition;
- }
- @Override
- public JavaFileObject getSource() {
- File f = new File(new String(originatingFileName));
- if (f.exists()) {
- return new EclipseFileObject(null, f.toURI(), JavaFileObject.Kind.SOURCE, null);
- }
- return null;
- }
- @Override
- public long getStartPosition() {
- return startPosition;
- }
- });
- return super.createProblem(originatingFileName, problemId, problemArguments, messageArguments, severity, startPosition, endPosition, lineNumber, columnNumber);
- }
- @Override
- public CategorizedProblem createProblem(
- final char[] originatingFileName,
- final int problemId,
- final String[] problemArguments,
- final int elaborationID,
- final String[] messageArguments,
- final int severity,
- final int startPosition,
- final int endPosition,
- final int lineNumber,
- final int columnNumber) {
-
- report(new Diagnostic() {
- @Override
- public String getCode() {
- return Integer.toString(problemId);
- }
- @Override
- public long getColumnNumber() {
- return columnNumber;
- }
- @Override
- public long getEndPosition() {
- return endPosition;
- }
- @Override
- public Kind getKind() {
- if ((severity & ProblemSeverities.Error) != 0) {
- return Diagnostic.Kind.ERROR;
- }
- if ((severity & ProblemSeverities.Info) != 0) {
- return Diagnostic.Kind.NOTE;
- }
- if ((severity & ProblemSeverities.Optional) != 0) {
- return Diagnostic.Kind.WARNING;
- }
- if ((severity & ProblemSeverities.Warning) != 0) {
- return Diagnostic.Kind.MANDATORY_WARNING;
- }
- return Diagnostic.Kind.OTHER;
- }
- @Override
- public long getLineNumber() {
- return lineNumber;
- }
- @Override
- public String getMessage(Locale locale) {
- if (locale != null) {
- setLocale(locale);
- }
- return getLocalizedMessage(problemId, problemArguments);
- }
- @Override
- public long getPosition() {
- return startPosition;
- }
- @Override
- public JavaFileObject getSource() {
- File f = new File(new String(originatingFileName));
- if (f.exists()) {
- return new EclipseFileObject(null, f.toURI(), JavaFileObject.Kind.SOURCE, null);
- }
- return null;
- }
- @Override
- public long getStartPosition() {
- return startPosition;
- }
- });
- return super.createProblem(originatingFileName, problemId, problemArguments, elaborationID, messageArguments, severity, startPosition, endPosition, lineNumber, columnNumber);
- }
- };
- }
-}
diff --git a/app/src/main/java/com/itsaky/androidide/services/compiler/model/CompilerDiagnostic.java b/app/src/main/java/com/itsaky/androidide/services/compiler/model/CompilerDiagnostic.java
deleted file mode 100644
index 5c3ae0004..000000000
--- a/app/src/main/java/com/itsaky/androidide/services/compiler/model/CompilerDiagnostic.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.itsaky.androidide.services.compiler.model;
-
-import java.io.File;
-import javax.tools.Diagnostic;
-
-public interface CompilerDiagnostic {
- File file();
- long line();
- long column();
- long start();
- long end();
- String message();
- Diagnostic.Kind kind();
-
- int color();
-}
diff --git a/app/src/main/java/com/itsaky/androidide/services/compiler/model/CompilerDiagnosticImpl.java b/app/src/main/java/com/itsaky/androidide/services/compiler/model/CompilerDiagnosticImpl.java
deleted file mode 100644
index 5ad0c28b1..000000000
--- a/app/src/main/java/com/itsaky/androidide/services/compiler/model/CompilerDiagnosticImpl.java
+++ /dev/null
@@ -1,117 +0,0 @@
-package com.itsaky.androidide.services.compiler.model;
-
-import java.io.File;
-import java.util.Locale;
-import javax.tools.Diagnostic;
-import javax.tools.JavaFileObject;
-
-public class CompilerDiagnosticImpl implements CompilerDiagnostic {
-
- protected File file;
- protected String message;
- protected long line = 0;
- protected long column = 0;
- protected long start = 0;
- protected long end = 0;
-
- protected Diagnostic.Kind kind;
-
- public static final int COLOR_ERROR = 0xfff44336;
- public static final int COLOR_WARNING = 0xffffb300;
- public static final int COLOR_NOTE = 0xffffffff;
-
- private CompilerDiagnosticImpl(){}
-
- private CompilerDiagnosticImpl(Diagnostic extends JavaFileObject> diagnostic) {
- this.file = new File(diagnostic.getSource().toUri());
- this.message = diagnostic.getMessage(Locale.US);
- this.line = diagnostic.getLineNumber() - 1;
- this.column = diagnostic.getColumnNumber();
- this.start = diagnostic.getStartPosition();
- this.end = diagnostic.getEndPosition() + 1;
- this.kind = diagnostic.getKind();
- }
-
- private CompilerDiagnosticImpl(CompilerDiagnosticImpl d) {
- this.file = d.file();
- this.message = d.message();
- this.line = d.line();
- this.column = d.column();
- this.start = d.start();
- this.end = d.end();
- this.kind = d.kind();
- }
-
- public static CompilerDiagnosticImpl fromDiagnostic(Diagnostic extends JavaFileObject> d) {
- return new CompilerDiagnosticImpl(d);
- }
-
- public static CompilerDiagnosticImpl copy(CompilerDiagnosticImpl d) {
- return new CompilerDiagnosticImpl(d);
- }
-
- @Override
- public Diagnostic.Kind kind() {
- return kind;
- }
-
- @Override
- public File file() {
- return file;
- }
-
- @Override
- public long line() {
- return line;
- }
-
- @Override
- public long column() {
- return column;
- }
-
- @Override
- public long start() {
- return start;
- }
-
- @Override
- public long end() {
- return end;
- }
-
- @Override
- public String message() {
- return message;
- }
-
- @Override
- public int color() {
- if(kind() == Diagnostic.Kind.ERROR) {
- return COLOR_ERROR;
- } else if(kind() == Diagnostic.Kind.WARNING || kind() == Diagnostic.Kind.MANDATORY_WARNING) {
- return COLOR_WARNING;
- }
- return COLOR_NOTE;
- }
-
- @Override
- public String toString() {
- return "CompilerDiagnosticImpl(file=" + file() + ", line=" + line() + ", column=" + column() + ", start=" + start() + ", end=" + end() + ", message=" + message() + ", kind=" + kind().toString() + ")";
- }
-
- @Override
- public boolean equals(Object obj) {
- if(obj instanceof CompilerDiagnosticImpl) {
- CompilerDiagnosticImpl that = (CompilerDiagnosticImpl) obj;
- return this.file().equals(that.file()) &&
- this.line() == that.line() &&
- this.column() == that.column() &&
- this.start() == that.start() &&
- this.end() == that.end() &&
- this.message().equals(that.message()) &&
- this.kind() == that.kind();
- }
- return false;
- }
-}
diff --git a/app/src/main/java/com/itsaky/androidide/services/compiler/request/CompilationRequest.java b/app/src/main/java/com/itsaky/androidide/services/compiler/request/CompilationRequest.java
deleted file mode 100644
index 8d6ccb737..000000000
--- a/app/src/main/java/com/itsaky/androidide/services/compiler/request/CompilationRequest.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package com.itsaky.androidide.services.compiler.request;
-
-import com.itsaky.androidide.interfaces.CompileListener;
-import com.itsaky.androidide.services.compiler.model.CompilerDiagnostic;
-import com.itsaky.androidide.tools.SourceJavaFileObject;
-import java.io.File;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public interface CompilationRequest {
-
- List files();
- CompileListener callback();
-
- public static class Builder {
-
- private List files;
- private CompileListener callback;
-
- private Builder(){
- files = new ArrayList<>();
- }
-
- public static Builder newInstance() {
- return new Builder();
- }
-
- public Builder withFiles(File... files) {
- if(files == null) {
- this.files = new ArrayList<>();
- return this;
- }
- this.files = new ArrayList<>();
- for(File file : files) {
- this.files.add(file);
- }
- return this;
- }
-
- public Builder withFiles(List files) {
- if(files == null) {
- this.files = new ArrayList<>();
- return this;
- }
- this.files = files;
- return this;
- }
-
- public Builder withCallback(CompileListener callback) {
- this.callback = callback;
- return this;
- }
-
- public CompilationRequest build() {
- return new CompilationRequest(){
-
- @Override
- public List files() {
- return files;
- }
-
- @Override
- public CompileListener callback() {
- return callback;
- }
- };
- }
- }
-}
diff --git a/app/src/main/java/com/itsaky/androidide/shell/ShellServer.java b/app/src/main/java/com/itsaky/androidide/shell/ShellServer.java
index 9d7ee42ab..75713846b 100644
--- a/app/src/main/java/com/itsaky/androidide/shell/ShellServer.java
+++ b/app/src/main/java/com/itsaky/androidide/shell/ShellServer.java
@@ -36,6 +36,7 @@ public class ShellServer extends Thread {
stringBuilder.append("\nexport PATH=$PATH:" + Environment.path(Environment.GRADLE_DIR) + "/bin");
stringBuilder.append("\nexport PATH=$PATH:" + Environment.path(Environment.ANDROID_HOME) + "/cmdline-tools/latest/bin");
stringBuilder.append("\nexport PATH=$PATH:" + Environment.path(Environment.ANDROID_HOME) + "/cmake/bin");
+ stringBuilder.append("\nexport JLS_HOME=" + Environment.path(Environment.JLS_HOME));
// Provide paths to .so files needed to execute JDK
final String lib = Environment.LIBDIR.getAbsolutePath();
@@ -49,8 +50,8 @@ public class ShellServer extends Thread {
}
// Monotonic clock fix, needed for JDK
- stringBuilder.append("\nls -l /system/lib64/libc.so && " +
- "ln -sf /apex/com.android.runtime/lib64/bionic/libc.so " + lib + "/librt.so && " +
+ stringBuilder.append( // "\nls -l /system/lib64/libc.so && " +
+ "\nln -sf /apex/com.android.runtime/lib64/bionic/libc.so " + lib + "/librt.so && " +
"ln -sf /apex/com.android.runtime/lib64/bionic/libc.so " + lib + "/libpthread.so");
// Make sure there are proper permissions set to the $HOME directory
diff --git a/app/src/main/java/com/itsaky/androidide/tools/SourceJavaFileObject.java b/app/src/main/java/com/itsaky/androidide/tools/SourceJavaFileObject.java
index c6ff217a4..8f11321b4 100644
--- a/app/src/main/java/com/itsaky/androidide/tools/SourceJavaFileObject.java
+++ b/app/src/main/java/com/itsaky/androidide/tools/SourceJavaFileObject.java
@@ -3,7 +3,6 @@ package com.itsaky.androidide.tools;
import java.io.File;
import java.net.URI;
import javax.tools.SimpleJavaFileObject;
-import org.eclipse.jdt.internal.compiler.batch.CompilationUnit;
public class SourceJavaFileObject extends SimpleJavaFileObject {
@@ -29,11 +28,4 @@ public class SourceJavaFileObject extends SimpleJavaFileObject {
public CharSequence getCharContent(boolean p1) {
return source;
}
-
- public CompilationUnit asUnit() {
- return new CompilationUnit(
- getCharContent(true).toString().toCharArray(),
- getFile().getAbsolutePath(),
- "UTF-8");
- }
}
diff --git a/app/src/main/java/com/itsaky/androidide/utils/Environment.java b/app/src/main/java/com/itsaky/androidide/utils/Environment.java
index 24524ee46..882674b73 100644
--- a/app/src/main/java/com/itsaky/androidide/utils/Environment.java
+++ b/app/src/main/java/com/itsaky/androidide/utils/Environment.java
@@ -15,6 +15,7 @@ public final class Environment {
public static File HOME;
public static File JAVA_HOME;
public static File ANDROID_HOME;
+ public static File JLS_HOME;
public static File TMP_DIR;
public static File USRDIR;
public static File LIBDIR;
@@ -59,6 +60,7 @@ public final class Environment {
HOME = app.getRootDir();
JAVA_HOME = getJavaHome();
ANDROID_HOME = new File(HOME, ANDROID_SDK_FOLDER_NAME);
+ JLS_HOME = mkdirIfNotExits(new File(HOME, "jls"));
TMP_DIR = mkdirIfNotExits(new File(HOME, "tmp"));
GRADLE_DIR = new File(HOME, GRADLE_FOLDER_NAME);
USRDIR = mkdirIfNotExits(new File(HOME.getParentFile(), "usr"));
diff --git a/app/src/main/java/com/itsaky/androidide/utils/Logger.java b/app/src/main/java/com/itsaky/androidide/utils/Logger.java
index 0520f9286..a171ce119 100644
--- a/app/src/main/java/com/itsaky/androidide/utils/Logger.java
+++ b/app/src/main/java/com/itsaky/androidide/utils/Logger.java
@@ -27,10 +27,10 @@ public class Logger {
private static Logger createInstance(String tag) {
LogConfiguration config = new LogConfiguration.Builder()
+ .disableStackTrace()
+ .disableThreadInfo()
.logLevel(com.itsaky.androidide.BuildConfig.DEBUG ? LogLevel.ALL : LogLevel.NONE)
- .tag("AndroidIDE")
- .enableThreadInfo()
- .enableStackTrace(10)
+ .tag("AndroidIDE")
.disableBorder()
.build();
Printer filePrinter = new FilePrinter
diff --git a/app/src/main/java/com/itsaky/androidide/utils/Symbols.java b/app/src/main/java/com/itsaky/androidide/utils/Symbols.java
index f8cd275b6..528dc6462 100644
--- a/app/src/main/java/com/itsaky/androidide/utils/Symbols.java
+++ b/app/src/main/java/com/itsaky/androidide/utils/Symbols.java
@@ -27,13 +27,13 @@ public class Symbols {
new Symbol(")"),
new Symbol(";"),
new Symbol("="),
- new Symbol("\""),
+ new Symbol("\"", "\"\""),
new Symbol("|"),
new Symbol("&"),
new Symbol("!"),
new Symbol("[", "[]"),
new Symbol("]"),
- new Symbol("<"),
+ new Symbol("<", "<>"),
new Symbol(">"),
new Symbol("+"),
new Symbol("-"),
@@ -50,7 +50,7 @@ public class Symbols {
new Symbol(">"),
new Symbol("/"),
new Symbol("="),
- new Symbol("\""),
+ new Symbol("\"", "\"\""),
new Symbol(":"),
new Symbol("@"),
new Symbol("+"),
diff --git a/app/src/main/java/com/itsaky/androidide/utils/VersionedFileManager.java b/app/src/main/java/com/itsaky/androidide/utils/VersionedFileManager.java
new file mode 100644
index 000000000..96833e091
--- /dev/null
+++ b/app/src/main/java/com/itsaky/androidide/utils/VersionedFileManager.java
@@ -0,0 +1,24 @@
+package com.itsaky.androidide.utils;
+import java.io.File;
+import java.util.HashMap;
+
+public class VersionedFileManager {
+ private static final HashMap map = new HashMap<>();
+
+ public static int fileOpened(File file) {
+ // Initial version of file should always be 1
+ map.put(file, 1);
+ return 1;
+ }
+
+ public static int incrementVersion(File file) {
+ if(!map.containsKey(file)) {
+ map.put(file, 1);
+ }
+
+ int version = map.get(file);
+ version++;
+ map.put(file, version);
+ return version;
+ }
+}
diff --git a/app/src/main/java/io/github/rosemoe/editor/text/Content.java b/app/src/main/java/io/github/rosemoe/editor/text/Content.java
index 307b54301..72a6c55c0 100644
--- a/app/src/main/java/io/github/rosemoe/editor/text/Content.java
+++ b/app/src/main/java/io/github/rosemoe/editor/text/Content.java
@@ -272,7 +272,8 @@ public class Content implements CharSequence {
if (text == null) {
throw new IllegalArgumentException("text can not be null");
}
-
+
+ text = text.toString().replace("\t", " ");
//-----Notify------
if (mCursor != null)
mCursor.beforeInsert(line, column);
diff --git a/app/src/main/java/io/github/rosemoe/editor/text/Cursor.java b/app/src/main/java/io/github/rosemoe/editor/text/Cursor.java
index 8ce0cf577..45876ccfa 100644
--- a/app/src/main/java/io/github/rosemoe/editor/text/Cursor.java
+++ b/app/src/main/java/io/github/rosemoe/editor/text/Cursor.java
@@ -16,9 +16,10 @@
package io.github.rosemoe.editor.text;
import android.util.Log;
-
import io.github.rosemoe.editor.interfaces.EditorLanguage;
import io.github.rosemoe.editor.util.IntPair;
+import java.util.List;
+import java.util.ArrayList;
/**
* @author Rose
@@ -33,6 +34,8 @@ public final class Cursor {
private boolean mAutoIndentEnabled;
private EditorLanguage mLanguage;
private int mTabWidth;
+
+ private final List pairs;
/**
* Create a new Cursor for Content
@@ -45,6 +48,14 @@ public final class Cursor {
mLeft = new CharPosition().zero();
mRight = new CharPosition().zero();
mTabWidth = 4;
+
+ pairs = new ArrayList<>();
+ pairs.add('}');
+ pairs.add(')');
+ pairs.add(']');
+ pairs.add('"');
+ pairs.add('\'');
+ pairs.add('>');
}
/**
@@ -253,35 +264,43 @@ public final class Cursor {
if (isSelected()) {
mContent.replace(getLeftLine(), getLeftColumn(), getRightLine(), getRightColumn(), text);
} else {
- if (mAutoIndentEnabled && text.length() != 0 && applyAutoIndent) {
- char first = text.charAt(0);
- if (first == '\n') {
- String line = mContent.getLineString(getLeftLine());
- int p = 0, count = 0;
- while (p < getLeftColumn()) {
- if (isWhitespace(line.charAt(p))) {
- if (line.charAt(p) == '\t') {
- count += mTabWidth;
+ if(getLeftColumn() < mContent.getColumnCount(getLeftLine())
+ && text.length() == 1
+ && text.charAt(0) == mContent.charAt(getLeftLine(), getLeftColumn())
+ && pairs.contains(text.charAt(0))) {
+
+ set(getLeftLine(), getLeftColumn() + 1);
+ } else {
+ if (mAutoIndentEnabled && text.length() != 0 && applyAutoIndent) {
+ char first = text.charAt(0);
+ if (first == '\n') {
+ String line = mContent.getLineString(getLeftLine());
+ int p = 0, count = 0;
+ while (p < getLeftColumn()) {
+ if (isWhitespace(line.charAt(p))) {
+ if (line.charAt(p) == '\t') {
+ count += mTabWidth;
+ } else {
+ count++;
+ }
+ p++;
} else {
- count++;
+ break;
}
- p++;
- } else {
- break;
}
+ String sub = line.substring(0, getLeftColumn());
+ try {
+ count += mLanguage.getIndentAdvance(sub);
+ } catch (Exception e) {
+ Log.w("EditorCursor", "Language object error", e);
+ }
+ StringBuilder sb = new StringBuilder(text);
+ sb.insert(1, createIndent(count));
+ text = sb;
}
- String sub = line.substring(0, getLeftColumn());
- try {
- count += mLanguage.getIndentAdvance(sub);
- } catch (Exception e) {
- Log.w("EditorCursor", "Language object error", e);
- }
- StringBuilder sb = new StringBuilder(text);
- sb.insert(1, createIndent(count));
- text = sb;
}
+ mContent.insert(getLeftLine(), getLeftColumn(), text);
}
- mContent.insert(getLeftLine(), getLeftColumn(), text);
}
}
@@ -294,12 +313,12 @@ public final class Cursor {
private String createIndent(int p) {
int tab = 0;
int space;
- if (mLanguage.useTab()) {
- tab = p / mTabWidth;
- space = p % mTabWidth;
- } else {
+// if (mLanguage.useTab()) {
+// tab = p / mTabWidth;
+// space = p % mTabWidth;
+// } else {
space = p;
- }
+// }
StringBuilder s = new StringBuilder();
for (int i = 0; i < tab; i++) {
s.append('\t');
diff --git a/app/src/main/java/io/github/rosemoe/editor/widget/CodeEditor.java b/app/src/main/java/io/github/rosemoe/editor/widget/CodeEditor.java
index ffd1315ca..3f00f4d65 100644
--- a/app/src/main/java/io/github/rosemoe/editor/widget/CodeEditor.java
+++ b/app/src/main/java/io/github/rosemoe/editor/widget/CodeEditor.java
@@ -25,6 +25,7 @@ import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
+import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
@@ -57,11 +58,16 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Px;
import com.itsaky.androidide.R;
+import com.itsaky.lsp.Diagnostic;
+import com.itsaky.lsp.DiagnosticSeverity;
+import com.itsaky.lsp.Position;
+import com.itsaky.lsp.Range;
import io.github.rosemoe.editor.interfaces.EditorEventListener;
import io.github.rosemoe.editor.interfaces.EditorLanguage;
import io.github.rosemoe.editor.interfaces.NewlineHandler;
import io.github.rosemoe.editor.langs.EmptyLanguage;
import io.github.rosemoe.editor.struct.BlockLine;
+import io.github.rosemoe.editor.struct.HexColor;
import io.github.rosemoe.editor.struct.Span;
import io.github.rosemoe.editor.text.CharPosition;
import io.github.rosemoe.editor.text.Content;
@@ -80,15 +86,12 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
-import com.blankj.utilcode.util.DeviceUtils;
-import io.github.rosemoe.editor.struct.HexColor;
-import android.graphics.Path;
-import android.graphics.Point;
-import android.graphics.PointF;
-import com.itsaky.androidide.services.compiler.model.CompilerDiagnostic;
-import javax.tools.Diagnostic;
-import java.util.stream.Collectors;
-import com.itsaky.androidide.utils.Logger;
+import java.io.File;
+import com.itsaky.lsp.CompletionList;
+import com.itsaky.androidide.utils.Either;
+import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
+import io.github.rosemoe.editor.struct.CompletionItem;
+import com.itsaky.androidide.models.CompletionItemWrapper;
/**
* CodeEditor is a editor that can highlight text regions by doing basic syntax analyzing
@@ -251,7 +254,9 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
KeyMetaStates mKeyMetaStates = new KeyMetaStates(this);
private HashMap mLineColors;
- private HashMap> mDiagnostics;
+ private HashMap> mDiagnostics;
+
+ private File currentFile;
public CodeEditor(Context context) {
this(context, null);
@@ -277,6 +282,14 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
public boolean isLineColorsEnabled() {
return mLineColorEnabled;
}
+
+ public File getFile() {
+ return currentFile;
+ }
+
+ public void setFile(File file) {
+ this.currentFile = file;
+ }
/**
* Whether this region has visible region on screen
@@ -477,21 +490,21 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
}
public void showOrHideDiagnosticWindow() {
- int line = getCursor().getLeftLine();
- int index = getCursor().getLeft();
+ int leftLine = getCursor().getLeftLine();
+ int leftCol = getCursor().getLeftColumn();
+ int rightLine = getCursor().getRightLine();
+ int rightCol = getCursor().getRightColumn();
+ final Range thisRange = new Range(new Position(leftLine, leftCol), new Position(rightLine, rightCol));
boolean hide = false;
- boolean has = mDiagnostics.containsKey(Long.valueOf(line));
+ boolean has = mDiagnostics.containsKey(thisRange);
if(has) {
- List diags = mDiagnostics.get(Long.valueOf(line));
- if(diags != null) {
- diags = diags.stream().filter(d -> d.start() <= index && d.end() >= index).collect(Collectors.toList());
- if(diags != null && diags.size() > 0){
- CompilerDiagnostic d = diags.get(0);
- mDiagnosticWindow.setDiagnostic(d);
- if(!mDiagnosticWindow.isShowing())
- mDiagnosticWindow.show();
- else updateDiagnosticWindowPosition();
- } else hide = true;
+ List diags = mDiagnostics.get(thisRange);
+ if(diags != null && diags.size() > 0) {
+ Diagnostic d = diags.get(0);
+ mDiagnosticWindow.setDiagnostic(d);
+ if(!mDiagnosticWindow.isShowing())
+ mDiagnosticWindow.show();
+ else updateDiagnosticWindowPosition();
} else hide = true;
} else hide = true;
if(hide && mDiagnosticWindow.isShowing()) {
@@ -499,28 +512,29 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
}
}
- public void setDiagnostics(List diags) {
+ public void setDiagnostics(List diags) {
this.mDiagnostics = new HashMap<>();
mapDiagnostics(diags);
}
- private void mapDiagnostics(List diags) {
+ private void mapDiagnostics(List diags) {
if(diags == null)
return;
for(int i=0;i ds = mDiagnostics.get(d.line());
+ if(mDiagnostics.containsKey(d.range)) {
+ List ds = mDiagnostics.get(d.range);
if(ds == null)
ds = new ArrayList<>();
if(!ds.contains(d))
ds.add(d);
+ mDiagnostics.put(d.range, ds);
} else {
- List ds = new ArrayList<>();
+ List ds = new ArrayList<>();
ds.add(d);
- mDiagnostics.put(Long.valueOf(d.line()), ds);
+ mDiagnostics.put(d.range, ds);
}
}
}
@@ -1222,28 +1236,26 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
}
// Draw errors and warnings
- if(mDiagnostics != null && mDiagnostics.containsKey(Long.valueOf(line))) {
- List diags = mDiagnostics.get(Long.valueOf(line));
+ int leftLine = getCursor().getLeftLine();
+ int leftCol = getCursor().getLeftColumn();
+ int rightLine = getCursor().getRightLine();
+ int rightCol = getCursor().getRightColumn();
+ final Range thisRange = new Range(new Position(leftLine, leftCol), new Position(rightLine, rightCol));
+ if(mDiagnostics != null && mDiagnostics.containsKey(thisRange)) {
+ List diags = mDiagnostics.get(thisRange);
if(diags != null && diags.size() > 0) {
for(int i=0;i> results, long requestTime) {
- if (mRequestTime != requestTime) {
- return;
- }
+ public void displayResults(final List> results, long requestTime) {
mEditor.post(() -> {
setLoading(false);
if (results == null || results.isEmpty()) {
hide();
return;
}
+ mAdapter.clear();
mAdapter.attachAttributes(this, results);
mBinding.list.setAdapter(mAdapter);
mCurrent = 0;
diff --git a/app/src/main/java/io/github/rosemoe/editor/widget/EditorCompletionAdapter.java b/app/src/main/java/io/github/rosemoe/editor/widget/EditorCompletionAdapter.java
index 9bb59ce97..435d394d3 100644
--- a/app/src/main/java/io/github/rosemoe/editor/widget/EditorCompletionAdapter.java
+++ b/app/src/main/java/io/github/rosemoe/editor/widget/EditorCompletionAdapter.java
@@ -34,6 +34,11 @@ public abstract class EditorCompletionAdapter extends BaseAdapter {
private EditorAutoCompleteWindow mWindow;
private List> mItems;
+ public void clear() {
+ if(mItems != null)
+ mItems.clear();
+ }
+
/**
* Called by {@link EditorAutoCompleteWindow} to attach some arguments
*/
diff --git a/app/src/main/java/io/github/rosemoe/editor/widget/SymbolChannel.java b/app/src/main/java/io/github/rosemoe/editor/widget/SymbolChannel.java
index 85134d26a..91e7f2be9 100644
--- a/app/src/main/java/io/github/rosemoe/editor/widget/SymbolChannel.java
+++ b/app/src/main/java/io/github/rosemoe/editor/widget/SymbolChannel.java
@@ -16,6 +16,8 @@
package io.github.rosemoe.editor.widget;
import io.github.rosemoe.editor.text.Cursor;
+import java.util.ArrayList;
+import java.util.List;
/**
* A channel to insert symbols in {@link CodeEditor}
@@ -24,9 +26,18 @@ import io.github.rosemoe.editor.text.Cursor;
public class SymbolChannel {
private CodeEditor mEditor;
+
+ private final List pairs;
protected SymbolChannel(CodeEditor editor) {
mEditor = editor;
+ pairs = new ArrayList<>();
+ pairs.add('}');
+ pairs.add(')');
+ pairs.add(']');
+ pairs.add('"');
+ pairs.add('\'');
+ pairs.add('>');
}
/**
@@ -40,8 +51,8 @@ public class SymbolChannel {
* @param selectionOffset New selection position relative to the start of text to insert.
* Ranging from 0 to symbolText.length()
*/
- public void insertSymbol(String symbolText, int selectionOffset) {
- if (selectionOffset < 0 || selectionOffset > symbolText.length()) {
+ public void insertSymbol(String text, int selectionOffset) {
+ if (selectionOffset < 0 || selectionOffset > text.length()) {
throw new IllegalArgumentException("selectionOffset is invalid");
}
Cursor cur = mEditor.getText().getCursor();
@@ -49,10 +60,19 @@ public class SymbolChannel {
cur.onDeleteKeyPressed();
mEditor.notifyExternalCursorChange();
}
- mEditor.getText().insert(cur.getRightLine(), cur.getRightColumn(), symbolText);
+
+ if(cur.getLeftColumn() < mEditor.getText().getColumnCount(cur.getLeftLine())
+ && text.length() == 1
+ && text.charAt(0) == mEditor.getText().charAt(cur.getLeftLine(), cur.getLeftColumn())
+ && pairs.contains(text.charAt(0))) {
+ mEditor.moveSelectionRight();
+ }
+
+ mEditor.getText().insert(cur.getRightLine(), cur.getRightColumn(), text);
+
mEditor.notifyExternalCursorChange();
- if (selectionOffset != symbolText.length()) {
- mEditor.setSelection(cur.getRightLine(), cur.getRightColumn() - (symbolText.length() - selectionOffset));
+ if (selectionOffset != text.length()) {
+ mEditor.setSelection(cur.getRightLine(), cur.getRightColumn() - (text.length() - selectionOffset));
}
}
diff --git a/app/src/main/java/io/github/rosemoe/editor/widget/SymbolPairMatch.java b/app/src/main/java/io/github/rosemoe/editor/widget/SymbolPairMatch.java
index 0273b02fa..52646e62f 100644
--- a/app/src/main/java/io/github/rosemoe/editor/widget/SymbolPairMatch.java
+++ b/app/src/main/java/io/github/rosemoe/editor/widget/SymbolPairMatch.java
@@ -95,7 +95,6 @@ public class SymbolPairMatch {
}
public final static class DefaultSymbolPairs extends SymbolPairMatch {
-
public DefaultSymbolPairs() {
super.putPair('{', new Replacement("{}", 1));
super.putPair('(', new Replacement("()", 1));
@@ -103,7 +102,6 @@ public class SymbolPairMatch {
super.putPair('"', new Replacement("\"\"", 1));
super.putPair('\'', new Replacement("''", 1));
}
-
}
}
diff --git a/app/src/main/java/org/eclipse/jdt/core/Signature.java b/app/src/main/java/org/eclipse/jdt/core/Signature.java
deleted file mode 100644
index 1846d3ff8..000000000
--- a/app/src/main/java/org/eclipse/jdt/core/Signature.java
+++ /dev/null
@@ -1,2927 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2000, 2015 IBM Corporation and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * IBM Corporation - initial API and implementation
- * IBM Corporation - added J2SE 1.5 support
- * Stephan Herrmann - Contribution for
- * Bug 463533 - Signature.getSignatureSimpleName() returns different results for resolved and unresolved extends
- *******************************************************************************/
-package org.eclipse.jdt.core;
-
-import java.util.ArrayList;
-
-import org.eclipse.jdt.core.compiler.CharOperation;
-import org.eclipse.jdt.internal.compiler.parser.ScannerHelper;
-import org.eclipse.jdt.internal.compiler.util.Util;
-
-/**
- * Provides methods for encoding and decoding type and method signature strings.
- *
- * Signatures obtained from parsing source files (i.e. files with one of the
- * {@link JavaCore#getJavaLikeExtensions() Java-like extensions}) differ subtly
- * from ones obtained from pre-compiled binary (".class") files in class names are
- * usually left unresolved in the former. For example, the normal resolved form
- * of the type "String" embeds the class's package name ("Ljava.lang.String;"
- * or "Ljava/lang/String;"), whereas the unresolved form contains only what is
- * written "QString;".
- *
- *
- * Generic types introduce to the Java language in J2SE 1.5 add three new
- * facets to signatures: type variables, parameterized types with type arguments,
- * and formal type parameters. Rich signatures containing these facets
- * only occur when dealing with code that makes overt use of the new language
- * features. All other code, and certainly all Java code written or compiled
- * with J2SE 1.4 or earlier, involved only simple signatures.
- *
- *
- * Note that the "Q", "!" and "|" formats are specific to Eclipse; the remainder
- * are specified in the JVM spec.
- *
- *
- * The syntax for a type signature is:
- *
- * TypeSignature ::=
- * "B" // byte
- * | "C" // char
- * | "D" // double
- * | "F" // float
- * | "I" // int
- * | "J" // long
- * | "S" // short
- * | "V" // void
- * | "Z" // boolean
- * | "T" + Identifier + ";" // type variable
- * | "[" + TypeSignature // array X[]
- * | "!" + TypeSignature // capture-of ?
- * | "|" + TypeSignature + (":" + TypeSignature)+ // intersection type
- * | ResolvedClassTypeSignature
- * | UnresolvedClassTypeSignature
- *
- * ResolvedClassTypeSignature ::= // resolved named type (in compiled code)
- * "L" + Identifier + OptionalTypeArguments
- * ( ( "." | "/" ) + Identifier + OptionalTypeArguments )* + ";"
- * | OptionalTypeParameters + "L" + Identifier +
- * ( ( "." | "/" ) + Identifier )* + ";"
- *
- * UnresolvedClassTypeSignature ::= // unresolved named type (in source code)
- * "Q" + Identifier + OptionalTypeArguments
- * ( ( "." | "/" ) + Identifier + OptionalTypeArguments )* + ";"
- * | OptionalTypeParameters "Q" + Identifier +
- * ( ( "." | "/" ) + Identifier )* + ";"
- *
- * OptionalTypeArguments ::=
- * "<" + TypeArgument+ + ">"
- * |
- *
- * TypeArgument ::=
- * | TypeSignature
- * | "*" // wildcard ?
- * | "+" TypeSignature // wildcard ? extends X
- * | "-" TypeSignature // wildcard ? super X
- *
- * OptionalTypeParameters ::=
- * "<" + FormalTypeParameterSignature+ + ">"
- * |
- *
- *
- *
- * Examples:
- *
- * "[[I" denotes int[][]
- * "Ljava.lang.String;" denotes java.lang.String in compiled code
- * "QString;" denotes String in source code
- * "Qjava.lang.String;" denotes java.lang.String in source code
- * "[QString;" denotes String[] in source code
- * "QMap<QString;*>;" denotes Map<String,?> in source code
- * "Qjava.util.List<TV;>;" denotes java.util.List<V> in source code
- * "<E;>Ljava.util.List;" denotes <E>java.util.List in source code
- *
- *
- *
- * The syntax for a method signature is:
- *
- * MethodSignature ::= OptionalTypeParameters + "(" + ParamTypeSignature* + ")" + ReturnTypeSignature
- * ParamTypeSignature ::= TypeSignature
- * ReturnTypeSignature ::= TypeSignature
- *
- *
- * Examples:
- *
- * "()I" denotes int foo()
- * "([Ljava.lang.String;)V" denotes void foo(java.lang.String[]) in compiled code
- * "(QString;)QObject;" denotes Object foo(String) in source code
- *
- *
- *
- * The syntax for a formal type parameter signature is:
- *
- * FormalTypeParameterSignature ::=
- * TypeVariableName + OptionalClassBound + InterfaceBound*
- * TypeVariableName ::= Identifier
- * OptionalClassBound ::=
- * ":"
- * | ":" + TypeSignature
- * InterfaceBound ::=
- * ":" + TypeSignature
- *
- *
- * Examples:
- *
- * "X:" denotes X
- * "X:QReader;" denotes X extends Reader in source code
- * "X:QReader;:QSerializable;" denotes X extends Reader & Serializable in source code
- *
- *
- *
- * This class provides static methods and constants only.
- *
- * Note: An empty signature is considered to be syntactically incorrect. So most methods will throw
- * an IllegalArgumentException if an empty signature is provided.
- *
- * @noinstantiate This class is not intended to be instantiated by clients.
- */
-@SuppressWarnings({"rawtypes", "unchecked"})
-public final class Signature {
-
- /**
- * Kind constant for an array type signature.
- * @see #getTypeSignatureKind(String)
- * @since 3.0
- */
- public static final int ARRAY_TYPE_SIGNATURE = 4;
-
- /**
- * Kind constant for a base (primitive or void) type signature.
- * @see #getTypeSignatureKind(String)
- * @since 3.0
- */
- public static final int BASE_TYPE_SIGNATURE = 2;
-
- private static final char[] BOOLEAN = "boolean".toCharArray(); //$NON-NLS-1$
-
- private static final char[] BYTE = "byte".toCharArray(); //$NON-NLS-1$
-
- /**
- * Character constant indicating an array type in a signature.
- * Value is '['.
- */
- public static final char C_ARRAY = org.eclipse.jdt.internal.compiler.util.Util.C_ARRAY;
-
- /**
- * Character constant indicating the primitive type boolean in a signature.
- * Value is 'Z'.
- */
- public static final char C_BOOLEAN = org.eclipse.jdt.internal.compiler.util.Util.C_BOOLEAN;
-
- /**
- * Character constant indicating the primitive type byte in a signature.
- * Value is 'B'.
- */
- public static final char C_BYTE = org.eclipse.jdt.internal.compiler.util.Util.C_BYTE;
-
- /**
- * Character constant indicating a capture of a wildcard type in a
- * signature. Value is '!'.
- * @since 3.1
- */
- public static final char C_CAPTURE = org.eclipse.jdt.internal.compiler.util.Util.C_CAPTURE;
-
- /**
- * Character constant indicating the primitive type char in a signature.
- * Value is 'C'.
- */
- public static final char C_CHAR = org.eclipse.jdt.internal.compiler.util.Util.C_CHAR;
-
- /**
- * Character constant indicating the colon in a signature.
- * Value is ':'.
- * @since 3.0
- */
- public static final char C_COLON = org.eclipse.jdt.internal.compiler.util.Util.C_COLON;
-
- /**
- * Character constant indicating the dollar in a signature.
- * Value is '$'.
- */
- public static final char C_DOLLAR = org.eclipse.jdt.internal.compiler.util.Util.C_DOLLAR;
-
- /**
- * Character constant indicating the dot in a signature.
- * Value is '.'.
- */
- public static final char C_DOT = org.eclipse.jdt.internal.compiler.util.Util.C_DOT;
-
- /**
- * Character constant indicating the primitive type double in a signature.
- * Value is 'D'.
- */
- public static final char C_DOUBLE = org.eclipse.jdt.internal.compiler.util.Util.C_DOUBLE;
-
- /**
- * Character constant indicating an exception in a signature.
- * Value is '^'.
- * @since 3.1
- */
- public static final char C_EXCEPTION_START = org.eclipse.jdt.internal.compiler.util.Util.C_EXCEPTION_START;
-
- /**
- * Character constant indicating a bound wildcard type argument
- * in a signature with extends clause.
- * Value is '+'.
- * @since 3.1
- */
- public static final char C_EXTENDS = org.eclipse.jdt.internal.compiler.util.Util.C_EXTENDS;
-
- /**
- * Character constant indicating the primitive type float in a signature.
- * Value is 'F'.
- */
- public static final char C_FLOAT = org.eclipse.jdt.internal.compiler.util.Util.C_FLOAT;
-
- /**
- * Character constant indicating the end of a generic type list in a
- * signature. Value is '>'.
- * @since 3.0
- */
- public static final char C_GENERIC_END = org.eclipse.jdt.internal.compiler.util.Util.C_GENERIC_END;
-
- /**
- * Character constant indicating the start of a formal type parameter
- * (or type argument) list in a signature. Value is '<'.
- * @since 3.0
- */
- public static final char C_GENERIC_START = org.eclipse.jdt.internal.compiler.util.Util.C_GENERIC_START;
-
- /**
- * Character constant indicating the primitive type int in a signature.
- * Value is 'I'.
- */
- public static final char C_INT = org.eclipse.jdt.internal.compiler.util.Util.C_INT;
-
- /**
- * Character constant indicating an intersection type in a
- * signature. Value is '|'.
- * @since 3.7.1
- */
- public static final char C_INTERSECTION = '|';
-
- /**
- * Character constant indicating the primitive type long in a signature.
- * Value is 'J'.
- */
- public static final char C_LONG = org.eclipse.jdt.internal.compiler.util.Util.C_LONG;
-
- /**
- * Character constant indicating the end of a named type in a signature.
- * Value is ';'.
- */
- public static final char C_NAME_END = org.eclipse.jdt.internal.compiler.util.Util.C_NAME_END;
-
- /**
- * Character constant indicating the end of a parameter type list in a
- * signature. Value is ')'.
- */
- public static final char C_PARAM_END = org.eclipse.jdt.internal.compiler.util.Util.C_PARAM_END;
-
- /**
- * Character constant indicating the start of a parameter type list in a
- * signature. Value is '('.
- */
- public static final char C_PARAM_START = org.eclipse.jdt.internal.compiler.util.Util.C_PARAM_START;
-
- /**
- * Character constant indicating the start of a resolved, named type in a
- * signature. Value is 'L'.
- */
- public static final char C_RESOLVED = org.eclipse.jdt.internal.compiler.util.Util.C_RESOLVED;
-
- /**
- * Character constant indicating the semicolon in a signature.
- * Value is ';'.
- */
- public static final char C_SEMICOLON = org.eclipse.jdt.internal.compiler.util.Util.C_SEMICOLON;
-
- /**
- * Character constant indicating the primitive type short in a signature.
- * Value is 'S'.
- */
- public static final char C_SHORT = org.eclipse.jdt.internal.compiler.util.Util.C_SHORT;
-
- /**
- * Character constant indicating an unbound wildcard type argument
- * in a signature.
- * Value is '*'.
- * @since 3.0
- */
- public static final char C_STAR = org.eclipse.jdt.internal.compiler.util.Util.C_STAR;
-
- /**
- * Character constant indicating a bound wildcard type argument
- * in a signature with super clause.
- * Value is '-'.
- * @since 3.1
- */
- public static final char C_SUPER = org.eclipse.jdt.internal.compiler.util.Util.C_SUPER;
-
- /**
- * Character constant indicating the start of a resolved type variable in a
- * signature. Value is 'T'.
- * @since 3.0
- */
- public static final char C_TYPE_VARIABLE = org.eclipse.jdt.internal.compiler.util.Util.C_TYPE_VARIABLE;
-
- /**
- * Character constant indicating the start of an unresolved, named type in a
- * signature. Value is 'Q'.
- */
- public static final char C_UNRESOLVED = org.eclipse.jdt.internal.compiler.util.Util.C_UNRESOLVED;
-
- /**
- * Character constant indicating result type void in a signature.
- * Value is 'V'.
- */
- public static final char C_VOID = org.eclipse.jdt.internal.compiler.util.Util.C_VOID;
-
- private static final char[] CAPTURE = "capture-of".toCharArray(); //$NON-NLS-1$
-
- /**
- * Kind constant for the capture of a wildcard type signature.
- * @see #getTypeSignatureKind(String)
- * @since 3.1
- */
- public static final int CAPTURE_TYPE_SIGNATURE = 6;
-
- private static final char[] CHAR = "char".toCharArray(); //$NON-NLS-1$
-
- /**
- * Kind constant for a class type signature.
- * @see #getTypeSignatureKind(String)
- * @since 3.0
- */
- public static final int CLASS_TYPE_SIGNATURE = 1;
-
- private static final char[] DOUBLE = "double".toCharArray(); //$NON-NLS-1$
-
-
- private static final char[] EXTENDS = "extends".toCharArray(); //$NON-NLS-1$
-
- private static final char[] FLOAT = "float".toCharArray(); //$NON-NLS-1$
-
- private static final char[] INT = "int".toCharArray(); //$NON-NLS-1$
-
- /**
- * Kind constant for the intersection type signature.
- * @see #getTypeSignatureKind(String)
- * @since 3.7.1
- */
- public static final int INTERSECTION_TYPE_SIGNATURE = 7;
-
- private static final char[] LONG = "long".toCharArray(); //$NON-NLS-1$
-
- private static final char[] SHORT = "short".toCharArray(); //$NON-NLS-1$
-
- /**
- * String constant for the signature of the primitive type boolean.
- * Value is "Z".
- */
- public static final String SIG_BOOLEAN = "Z"; //$NON-NLS-1$
-
- /**
- * String constant for the signature of the primitive type byte.
- * Value is "B".
- */
- public static final String SIG_BYTE = "B"; //$NON-NLS-1$
- /**
- * String constant for the signature of the primitive type char.
- * Value is "C".
- */
- public static final String SIG_CHAR = "C"; //$NON-NLS-1$
- /**
- * String constant for the signature of the primitive type double.
- * Value is "D".
- */
- public static final String SIG_DOUBLE = "D"; //$NON-NLS-1$
- /**
- * String constant for the signature of the primitive type float.
- * Value is "F".
- */
- public static final String SIG_FLOAT = "F"; //$NON-NLS-1$
- /**
- * String constant for the signature of the primitive type int.
- * Value is "I".
- */
- public static final String SIG_INT = "I"; //$NON-NLS-1$
- /**
- * String constant for the signature of the primitive type long.
- * Value is "J".
- */
- public static final String SIG_LONG = "J"; //$NON-NLS-1$
- /**
- * String constant for the signature of the primitive type short.
- * Value is "S".
- */
- public static final String SIG_SHORT = "S"; //$NON-NLS-1$
- /** String constant for the signature of result type void.
- * Value is "V".
- */
- public static final String SIG_VOID = "V"; //$NON-NLS-1$
- private static final char[] SUPER = "super".toCharArray(); //$NON-NLS-1$
- /**
- * Kind constant for a type variable signature.
- * @see #getTypeSignatureKind(String)
- * @since 3.0
- */
- public static final int TYPE_VARIABLE_SIGNATURE = 3;
- private static final char[] VOID = "void".toCharArray(); //$NON-NLS-1$
- /**
- * Kind constant for a wildcard type signature.
- * @see #getTypeSignatureKind(String)
- * @since 3.1
- */
- public static final int WILDCARD_TYPE_SIGNATURE = 5;
-
-// .d> --> >
-private static void appendArgumentSimpleNames(char[] name, int start, int end, StringBuffer buffer) {
- buffer.append('<');
- int depth = 0;
- int argumentStart = -1;
- int argumentCount = 0;
- for (int i = start; i <= end; i++) {
- switch(name[i]) {
- case '<' :
- depth++;
- if (depth == 1) {
- argumentStart = i+1;
- }
- break;
- case '>' :
- if (depth == 1) {
- if (argumentCount > 0) buffer.append(',');
- appendSimpleName(name, argumentStart, i-1, buffer);
- argumentCount++;
- }
- depth--;
- break;
- case ',' :
- if (depth == 1) {
- if (argumentCount > 0) buffer.append(',');
- appendSimpleName(name, argumentStart, i-1, buffer);
- argumentCount++;
- argumentStart = i+1;
- }
- break;
- }
- }
- buffer.append('>');
-}
-
-/**
- * Scans the given string for an array type signature starting at the given
- * index and appends it to the given buffer, and returns the index of the last
- * character.
- *
- * @param string the signature string
- * @param start the 0-based character index of the first character
- * @param fullyQualifyTypeNames true if type names should be fully
- * qualified, and false to use only simple names
- * @return the 0-based character index of the last character
- * @exception IllegalArgumentException if this is not an array type signature
- * @see Util#scanArrayTypeSignature(char[], int)
- */
-private static int appendArrayTypeSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) {
- return appendArrayTypeSignature(string, start, fullyQualifyTypeNames, buffer, false);
-}
-
-/**
- * Scans the given string for an array type signature starting at the given
- * index and appends it to the given buffer, and returns the index of the last
- * character.
- *
- * @param string the signature string
- * @param start the 0-based character index of the first character
- * @param fullyQualifyTypeNames true if type names should be fully
- * qualified, and false to use only simple names
- * @param isVarArgs true if the array type must be displayed as a
- * variable argument, false otherwise
- * @return the 0-based character index of the last character
- * @exception IllegalArgumentException if this is not an array type signature
- * @see Util#scanArrayTypeSignature(char[], int)
- */
-private static int appendArrayTypeSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer, boolean isVarArgs) {
- int length = string.length;
- // need a minimum 2 char
- if (start >= length - 1) {
- throw new IllegalArgumentException();
- }
- char c = string[start];
- if (c != C_ARRAY) {
- throw new IllegalArgumentException();
- }
-
- int index = start;
- c = string[++index];
- while(c == C_ARRAY) {
- // need a minimum 2 char
- if (index >= length - 1) {
- throw new IllegalArgumentException();
- }
- c = string[++index];
- }
-
- int e = appendTypeSignature(string, index, fullyQualifyTypeNames, buffer);
-
- for(int i = 1, dims = index - start; i < dims; i++) {
- buffer.append('[').append(']');
- }
-
- if (isVarArgs) {
- buffer.append('.').append('.').append('.');
- } else {
- buffer.append('[').append(']');
- }
- return e;
-}
-/**
- * Scans the given string for an capture type signature starting at the given
- * index and appends it to the given buffer, and returns the index of the last
- * character.
- *
- * @param string the signature string
- * @param start the 0-based character index of the first character
- * @param fullyQualifyTypeNames true if type names should be fully
- * qualified, and false to use only simple names
- * @return the 0-based character index of the last character
- * @exception IllegalArgumentException if this is not an array type signature
- * @see Util#scanArrayTypeSignature(char[], int)
- */
-private static int appendCaptureTypeSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) {
- // need a minimum 2 char
- if (start >= string.length - 1) {
- throw new IllegalArgumentException();
- }
- char c = string[start];
- if (c != C_CAPTURE) {
- throw new IllegalArgumentException();
- }
- buffer.append(CAPTURE).append(' ');
- return appendTypeArgumentSignature(string, start + 1, fullyQualifyTypeNames, buffer);
-}
-/**
- * Scans the given string for a class type signature starting at the given
- * index and appends it to the given buffer, and returns the index of the last
- * character.
- *
- * @param string the signature string
- * @param start the 0-based character index of the first character
- * @param fullyQualifyTypeNames true if type names should be fully
- * qualified, and false to use only simple names
- * @param buffer the string buffer to append to
- * @return the 0-based character index of the last character
- * @exception IllegalArgumentException if this is not a class type signature
- * @see Util#scanClassTypeSignature(char[], int)
- */
-private static int appendClassTypeSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) {
- // need a minimum 3 chars "Lx;"
- if (start >= string.length - 2) {
- throw new IllegalArgumentException();
- }
- // must start in "L" or "Q"
- char c = string[start];
- if (c != C_RESOLVED && c != C_UNRESOLVED) {
- throw new IllegalArgumentException();
- }
- boolean resolved = (c == C_RESOLVED);
- boolean removePackageQualifiers = !fullyQualifyTypeNames;
- if (!resolved) {
- // keep everything in an unresolved name
- removePackageQualifiers = false;
- }
- int p = start + 1;
- int checkpoint = buffer.length();
- int innerTypeStart = -1;
- boolean inAnonymousType = false;
- while (true) {
- if (p >= string.length) {
- throw new IllegalArgumentException();
- }
- c = string[p];
- switch(c) {
- case C_SEMICOLON :
- // all done
- return p;
- case C_GENERIC_START :
- int e = appendTypeArgumentSignatures(string, p, fullyQualifyTypeNames, buffer);
- // once we hit type arguments there are no more package prefixes
- removePackageQualifiers = false;
- p = e;
- break;
- case C_DOT :
- if (removePackageQualifiers) {
- // erase package prefix
- buffer.setLength(checkpoint);
- } else {
- buffer.append('.');
- }
- break;
- case '/' :
- if (removePackageQualifiers) {
- // erase package prefix
- buffer.setLength(checkpoint);
- } else {
- buffer.append('/');
- }
- break;
- case C_DOLLAR :
- innerTypeStart = buffer.length();
- inAnonymousType = false;
- if (resolved) {
- // once we hit "$" there are no more package prefixes
- removePackageQualifiers = false;
- /**
- * Convert '$' in resolved type signatures into '.'.
- * NOTE: This assumes that the type signature is an inner type
- * signature. This is true in most cases, but someone can define a
- * non-inner type name containing a '$'.
- */
- buffer.append('.');
- }
- break;
- default :
- if (innerTypeStart != -1 && !inAnonymousType && Character.isDigit(c)) {
- inAnonymousType = true;
- buffer.setLength(innerTypeStart); // remove '.'
- buffer.insert(checkpoint, "new "); //$NON-NLS-1$
- buffer.append("(){}"); //$NON-NLS-1$
- }
- if (!inAnonymousType)
- buffer.append(c);
- innerTypeStart = -1;
- }
- p++;
- }
-}
-/**
- * Scans the given string for an intersection type signature starting at the given
- * index and appends it to the given buffer, and returns the index of the last
- * character.
- *
- * @param string the signature string
- * @param start the 0-based character index of the first character
- * @param fullyQualifyTypeNames true if type names should be fully
- * qualified, and false to use only simple names
- * @return the 0-based character index of the last character
- * @exception IllegalArgumentException if this is not an array type signature
- * @see Util#scanArrayTypeSignature(char[], int)
- */
-private static int appendIntersectionTypeSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) {
- // need a minimum 2 char
- if (start >= string.length - 1) {
- throw new IllegalArgumentException();
- }
- char c = string[start];
- if (c != C_INTERSECTION) {
- throw new IllegalArgumentException();
- }
- start = appendClassTypeSignature(string, start + 1, fullyQualifyTypeNames, buffer);
- if (start < string.length - 1) {
- start++;
- if (string[start] != C_COLON) {
- throw new IllegalArgumentException("should be a colon at this location"); //$NON-NLS-1$
- }
- while (string[start] == C_COLON) {
- buffer.append(" | "); //$NON-NLS-1$
- start = appendClassTypeSignature(string, start + 1, fullyQualifyTypeNames, buffer);
- if (start == string.length - 1) {
- return start;
- } else if (start > string.length - 1) {
- throw new IllegalArgumentException("Should be at the end"); //$NON-NLS-1$
- }
- start++;
- }
- }
- return start;
-}
-private static void appendSimpleName(char[] name, int start, int end, StringBuffer buffer) {
- int lastDot = -1, lastGenericStart = -1, lastGenericEnd = -1;
- int depth = 0;
- if (name[start] == '?') { // wildcard
- buffer.append("?"); //$NON-NLS-1$
- int index = consumeWhitespace(name, start+1, end+1);
- switch (name[index]) {
- case 'e' :
- int checkPos = checkName(EXTENDS, name, index, end);
- if (checkPos > 0) {
- buffer.append(' ').append(EXTENDS).append(' ');
- index = consumeWhitespace(name, checkPos, end+1);
- }
- break;
- case 's' :
- checkPos = checkName(SUPER, name, index, end+1);
- if (checkPos > 0) {
- buffer.append(' ').append(SUPER).append(' ');
- index = consumeWhitespace(name, checkPos, end+1);
- }
- break;
- }
- start = index; // leading segment got processed
- }
- lastDotLookup: for (int i = end; i >= start; i--) {
- switch (name[i]) {
- case '.':
- if (depth == 0) {
- lastDot = i;
- char c = name[start];
- if (c == C_EXTENDS || c == C_SUPER) {
- buffer.append(c);
- }
- break lastDotLookup;
- }
- break;
- case '<':
- depth--;
- if (depth == 0) lastGenericStart = i;
- break;
- case '>':
- if (depth == 0) lastGenericEnd = i;
- depth++;
- break;
- }
- }
- int nameStart = lastDot < 0 ? start : lastDot+1;
- int nameEnd = lastGenericStart < 0 ? end+1 : lastGenericStart;
- buffer.append(name, nameStart, nameEnd - nameStart);
- if (lastGenericStart >= 0) {
- appendArgumentSimpleNames(name, lastGenericStart, lastGenericEnd, buffer);
- buffer.append(name, lastGenericEnd+1, end - lastGenericEnd); // copy trailing portion, may contain dimensions
- }
-}
-
-/**
- * Scans the given string for a type argument signature starting at the given
- * index and appends it to the given buffer, and returns the index of the last
- * character.
- *
- * @param string the signature string
- * @param start the 0-based character index of the first character
- * @param fullyQualifyTypeNames true if type names should be fully
- * qualified, and false to use only simple names
- * @param buffer the string buffer to append to
- * @return the 0-based character index of the last character
- * @exception IllegalArgumentException if this is not a type argument signature
- * @see Util#scanTypeArgumentSignature(char[], int)
- */
-private static int appendTypeArgumentSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) {
- // need a minimum 1 char
- if (start >= string.length) {
- throw new IllegalArgumentException();
- }
- char c = string[start];
- switch(c) {
- case C_STAR :
- buffer.append('?');
- return start;
- case C_EXTENDS :
- buffer.append("? extends "); //$NON-NLS-1$
- return appendTypeSignature(string, start + 1, fullyQualifyTypeNames, buffer);
- case C_SUPER :
- buffer.append("? super "); //$NON-NLS-1$
- return appendTypeSignature(string, start + 1, fullyQualifyTypeNames, buffer);
- default :
- return appendTypeSignature(string, start, fullyQualifyTypeNames, buffer);
- }
-}
-
-/**
- * Scans the given string for a list of type arguments signature starting at the
- * given index and appends it to the given buffer, and returns the index of the
- * last character.
- *
- * @param string the signature string
- * @param start the 0-based character index of the first character
- * @param fullyQualifyTypeNames true if type names should be fully
- * qualified, and false to use only simple names
- * @param buffer the string buffer to append to
- * @return the 0-based character index of the last character
- * @exception IllegalArgumentException if this is not a list of type argument
- * signatures
- * @see Util#scanTypeArgumentSignatures(char[], int)
- */
-private static int appendTypeArgumentSignatures(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) {
- // need a minimum 2 char "<>"
- if (start >= string.length - 1) {
- throw new IllegalArgumentException();
- }
- char c = string[start];
- if (c != C_GENERIC_START) {
- throw new IllegalArgumentException();
- }
- buffer.append('<');
- int p = start + 1;
- int count = 0;
- while (true) {
- if (p >= string.length) {
- throw new IllegalArgumentException();
- }
- c = string[p];
- if (c == C_GENERIC_END) {
- buffer.append('>');
- return p;
- }
- if (count != 0) {
- buffer.append(',');
- }
- int e = appendTypeArgumentSignature(string, p, fullyQualifyTypeNames, buffer);
- count++;
- p = e + 1;
- }
-}
-/**
- * Scans the given string for a type signature starting at the given
- * index and appends it to the given buffer, and returns the index of the last
- * character.
- *
- * @param string the signature string
- * @param start the 0-based character index of the first character
- * @param fullyQualifyTypeNames true if type names should be fully
- * qualified, and false to use only simple names
- * @param buffer the string buffer to append to
- * @return the 0-based character index of the last character
- * @exception IllegalArgumentException if this is not a type signature
- * @see Util#scanTypeSignature(char[], int)
- */
-private static int appendTypeSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) {
- return appendTypeSignature(string, start, fullyQualifyTypeNames, buffer, false);
-}
-
-/**
- * Scans the given string for a type signature starting at the given
- * index and appends it to the given buffer, and returns the index of the last
- * character.
- *
- * @param string the signature string
- * @param start the 0-based character index of the first character
- * @param fullyQualifyTypeNames true if type names should be fully
- * qualified, and false to use only simple names
- * @param buffer the string buffer to append to
- * @param isVarArgs true if the type must be displayed as a
- * variable argument, false otherwise. In this case, the type must be an array type
- * @return the 0-based character index of the last character
- * @exception IllegalArgumentException if this is not a type signature, or if isVarArgs is true,
- * and the type is not an array type signature.
- * @see Util#scanTypeSignature(char[], int)
- */
-private static int appendTypeSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer, boolean isVarArgs) {
- // need a minimum 1 char
- if (start >= string.length) {
- throw new IllegalArgumentException();
- }
- char c = string[start];
- if (isVarArgs) {
- switch (c) {
- case C_ARRAY :
- return appendArrayTypeSignature(string, start, fullyQualifyTypeNames, buffer, true);
- case C_RESOLVED :
- case C_UNRESOLVED :
- case C_TYPE_VARIABLE :
- case C_BOOLEAN :
- case C_BYTE :
- case C_CHAR :
- case C_DOUBLE :
- case C_FLOAT :
- case C_INT :
- case C_LONG :
- case C_SHORT :
- case C_VOID :
- case C_STAR:
- case C_EXTENDS:
- case C_SUPER:
- case C_CAPTURE:
- case C_INTERSECTION :
- default:
- throw new IllegalArgumentException(); // a var args is an array type
- }
- } else {
- switch (c) {
- case C_ARRAY :
- return appendArrayTypeSignature(string, start, fullyQualifyTypeNames, buffer);
- case C_RESOLVED :
- case C_UNRESOLVED :
- return appendClassTypeSignature(string, start, fullyQualifyTypeNames, buffer);
- case C_TYPE_VARIABLE :
- int e = Util.scanTypeVariableSignature(string, start);
- buffer.append(string, start + 1, e - start - 1);
- return e;
- case C_BOOLEAN :
- buffer.append(BOOLEAN);
- return start;
- case C_BYTE :
- buffer.append(BYTE);
- return start;
- case C_CHAR :
- buffer.append(CHAR);
- return start;
- case C_DOUBLE :
- buffer.append(DOUBLE);
- return start;
- case C_FLOAT :
- buffer.append(FLOAT);
- return start;
- case C_INT :
- buffer.append(INT);
- return start;
- case C_LONG :
- buffer.append(LONG);
- return start;
- case C_SHORT :
- buffer.append(SHORT);
- return start;
- case C_VOID :
- buffer.append(VOID);
- return start;
- case C_CAPTURE :
- return appendCaptureTypeSignature(string, start, fullyQualifyTypeNames, buffer);
- case C_INTERSECTION :
- return appendIntersectionTypeSignature(string, start, fullyQualifyTypeNames, buffer);
- case C_STAR:
- case C_EXTENDS:
- case C_SUPER:
- return appendTypeArgumentSignature(string, start, fullyQualifyTypeNames, buffer);
- default :
- throw new IllegalArgumentException();
- }
- }
-}
-
-private static int checkArrayDimension(char[] typeName, int pos, int length) {
- int genericBalance = 0;
- while (pos < length) {
- switch(typeName[pos]) {
- case '<' :
- genericBalance++;
- break;
- case ',' :
- if (genericBalance == 0) return -1;
- break;
- case '>':
- if (genericBalance == 0) return -1;
- genericBalance--;
- break;
- case '[':
- if (genericBalance == 0) {
- return pos;
- }
- }
- pos++;
- }
- return -1;
-}
-private static int checkName(char[] name, char[] typeName, int pos, int length) {
- if (CharOperation.fragmentEquals(name, typeName, pos, true)) {
- pos += name.length;
- if (pos == length) return pos;
- char currentChar = typeName[pos];
- switch (currentChar) {
- case ' ' :
- case '.' :
- case '<' :
- case '>' :
- case '[' :
- case ',' :
- return pos;
- default:
- if (ScannerHelper.isWhitespace(currentChar))
- return pos;
-
- }
- }
- return -1;
-}
-private static int checkNextChar(char[] typeName, char expectedChar, int pos, int length, boolean isOptional) {
- pos = consumeWhitespace(typeName, pos, length);
- if (pos < length && typeName[pos] == expectedChar)
- return pos + 1;
- if (!isOptional) throw new IllegalArgumentException(new String(typeName));
- return -1;
-}
-
-private static int consumeWhitespace(char[] typeName, int pos, int length) {
- while (pos < length) {
- char currentChar = typeName[pos];
- if (currentChar != ' ' && !ScannerHelper.isWhitespace(currentChar)) {
- break;
- }
- pos++;
- }
- return pos;
-}
-/**
- * Creates a new type signature with the given amount of array nesting added
- * to the given type signature.
- *
- * @param typeSignature the type signature
- * @param arrayCount the desired number of levels of array nesting
- * @return the encoded array type signature
- *
- * @since 2.0
- */
-public static char[] createArraySignature(char[] typeSignature, int arrayCount) {
- if (arrayCount == 0) return typeSignature;
- int sigLength = typeSignature.length;
- char[] result = new char[arrayCount + sigLength];
- for (int i = 0; i < arrayCount; i++) {
- result[i] = C_ARRAY;
- }
- System.arraycopy(typeSignature, 0, result, arrayCount, sigLength);
- return result;
-}
-/**
- * Creates a new type signature with the given amount of array nesting added
- * to the given type signature.
- *
- * @param typeSignature the type signature
- * @param arrayCount the desired number of levels of array nesting
- * @return the encoded array type signature
- */
-public static String createArraySignature(String typeSignature, int arrayCount) {
- return new String(createArraySignature(typeSignature.toCharArray(), arrayCount));
-}
-
-/**
- * Creates a new type signature from the given type name encoded as a character
- * array. The type name may contain primitive types or array types or parameterized types.
- * This method is equivalent to
- * createTypeSignature(new String(typeName),isResolved).toCharArray(),
- * although more efficient for callers with character arrays rather than strings.
- * If the type name is qualified, then it is expected to be dot-based.
- *
- * @param typeName the possibly qualified type name
- * @param isResolved true if the type name is to be considered
- * resolved (for example, a type name from a binary class file), and
- * false if the type name is to be considered unresolved
- * (for example, a type name found in source code)
- * @return the encoded type signature
- * @see #createTypeSignature(java.lang.String,boolean)
- *
- * @since 2.0
- */
-public static char[] createCharArrayTypeSignature(char[] typeName, boolean isResolved) {
- if (typeName == null) throw new IllegalArgumentException("null"); //$NON-NLS-1$
- int length = typeName.length;
- if (length == 0) throw new IllegalArgumentException(new String(typeName));
- StringBuffer buffer = new StringBuffer(5);
- int pos = encodeTypeSignature(typeName, 0, isResolved, length, buffer);
- pos = consumeWhitespace(typeName, pos, length);
- if (pos < length) throw new IllegalArgumentException(new String(typeName));
- char[] result = new char[length = buffer.length()];
- buffer.getChars(0, length, result, 0);
- return result;
-}
-
-/**
- * Creates a new intersection type signature from the given type signatures.
- *
- * The encoded type signature is dot-based.
- *
- * @param typeSignatures the given type signatures
- * @return the encoded type signature
- * @since 3.7.1
- */
-public static String createIntersectionTypeSignature(char[][] typeSignatures) {
- StringBuffer buffer = new StringBuffer();
- buffer.append(Signature.C_INTERSECTION);
- for (int i = 0, max = typeSignatures.length; i < max; i++) {
- if (i > 0) {
- buffer.append(Signature.C_COLON);
- }
- buffer.append(typeSignatures[i]);
- }
- return String.valueOf(buffer);
-}
-
-/**
- * Creates a new intersection type signature from the given type signatures.
- *
- * The encoded type signature is dot-based.
- *
- * @param typeSignatures the given type signatures
- * @return the encoded type signature
- * @since 3.7.1
- */
-public static String createIntersectionTypeSignature(String[] typeSignatures) {
- int typeSignaturesLenth = typeSignatures.length;
- char[][] signatures = new char[typeSignaturesLenth][];
- for (int i = 0; i < typeSignaturesLenth; i++) {
- signatures[i] = typeSignatures[i].toCharArray();
- }
- return createIntersectionTypeSignature(signatures);
-}
-/**
- * Creates a method signature from the given parameter and return type
- * signatures. The encoded method signature is dot-based.
- *
- * @param parameterTypes the list of parameter type signatures
- * @param returnType the return type signature
- * @return the encoded method signature
- *
- * @since 2.0
- */
-public static char[] createMethodSignature(char[][] parameterTypes, char[] returnType) {
- int parameterTypesLength = parameterTypes.length;
- int parameterLength = 0;
- for (int i = 0; i < parameterTypesLength; i++) {
- parameterLength += parameterTypes[i].length;
-
- }
- int returnTypeLength = returnType.length;
- char[] result = new char[1 + parameterLength + 1 + returnTypeLength];
- result[0] = C_PARAM_START;
- int index = 1;
- for (int i = 0; i < parameterTypesLength; i++) {
- char[] parameterType = parameterTypes[i];
- int length = parameterType.length;
- System.arraycopy(parameterType, 0, result, index, length);
- index += length;
- }
- result[index] = C_PARAM_END;
- System.arraycopy(returnType, 0, result, index+1, returnTypeLength);
- return result;
-}
-/**
- * Creates a method signature from the given parameter and return type
- * signatures. The encoded method signature is dot-based. This method
- * is equivalent to
- * createMethodSignature(parameterTypes, returnType).
- *
- * @param parameterTypes the list of parameter type signatures
- * @param returnType the return type signature
- * @return the encoded method signature
- * @see Signature#createMethodSignature(char[][], char[])
- */
-public static String createMethodSignature(String[] parameterTypes, String returnType) {
- int parameterTypesLenth = parameterTypes.length;
- char[][] parameters = new char[parameterTypesLenth][];
- for (int i = 0; i < parameterTypesLenth; i++) {
- parameters[i] = parameterTypes[i].toCharArray();
- }
- return new String(createMethodSignature(parameters, returnType.toCharArray()));
-}
-/**
- * Creates a new type parameter signature with the given name and bounds.
- *
- * @param typeParameterName the type parameter name
- * @param boundSignatures the signatures of associated bounds or empty array if none
- * @return the encoded type parameter signature
- *
- * @since 3.1
- */
-public static char[] createTypeParameterSignature(char[] typeParameterName, char[][] boundSignatures) {
- int length = boundSignatures.length;
- if (length == 0) {
- return CharOperation.append(typeParameterName, C_COLON); // param signature with no bounds still gets trailing colon
- }
- int boundsSize = 0;
- for (int i = 0; i < length; i++) {
- boundsSize += boundSignatures[i].length + 1;
- }
- int nameLength = typeParameterName.length;
- char[] result = new char[nameLength + boundsSize];
- System.arraycopy(typeParameterName, 0, result, 0, nameLength);
- int index = nameLength;
- for (int i = 0; i < length; i++) {
- result[index++] = C_COLON;
- int boundLength = boundSignatures[i].length;
- System.arraycopy(boundSignatures[i], 0, result, index, boundLength);
- index += boundLength;
- }
- return result;
-}
-/**
- * Creates a new type parameter signature with the given name and bounds.
- *
- * @param typeParameterName the type parameter name
- * @param boundSignatures the signatures of associated bounds or empty array if none
- * @return the encoded type parameter signature
- *
- * @since 3.1
- */
-public static String createTypeParameterSignature(String typeParameterName, String[] boundSignatures) {
- int length = boundSignatures.length;
- char[][] boundSignatureChars = new char[length][];
- for (int i = 0; i < length; i++) {
- boundSignatureChars[i] = boundSignatures[i].toCharArray();
- }
- return new String(createTypeParameterSignature(typeParameterName.toCharArray(), boundSignatureChars));
-}
-
-/**
- * Creates a new type signature from the given type name encoded as a character
- * array. The type name may contain primitive types, array types or parameterized types.
- * This method is equivalent to
- * createTypeSignature(new String(typeName),isResolved), although
- * more efficient for callers with character arrays rather than strings. If the
- * type name is qualified, then it is expected to be dot-based.
- *
- * @param typeName the possibly qualified type name
- * @param isResolved true if the type name is to be considered
- * resolved (for example, a type name from a binary class file), and
- * false if the type name is to be considered unresolved
- * (for example, a type name found in source code)
- * @return the encoded type signature
- * @see #createTypeSignature(java.lang.String,boolean)
- */
-public static String createTypeSignature(char[] typeName, boolean isResolved) {
- return new String(createCharArrayTypeSignature(typeName, isResolved));
-}
-
-/**
- * Creates a new type signature from the given type name. If the type name is qualified,
- * then it is expected to be dot-based. The type name may contain primitive
- * types or array types. However, parameterized types are not supported.
- *
- * For example:
- *
- *
- * createTypeSignature("int", hucairz) -> "I"
- * createTypeSignature("java.lang.String", true) -> "Ljava.lang.String;"
- * createTypeSignature("String", false) -> "QString;"
- * createTypeSignature("java.lang.String", false) -> "Qjava.lang.String;"
- * createTypeSignature("int []", false) -> "[I"
- *
- *
- *
- *
- * @param typeName the possibly qualified type name
- * @param isResolved true if the type name is to be considered
- * resolved (for example, a type name from a binary class file), and
- * false if the type name is to be considered unresolved
- * (for example, a type name found in source code)
- * @return the encoded type signature
- */
-public static String createTypeSignature(String typeName, boolean isResolved) {
- return createTypeSignature(typeName == null ? null : typeName.toCharArray(), isResolved);
-}
-
-private static int encodeArrayDimension(char[] typeName, int pos, int length, StringBuffer buffer) {
- int checkPos;
- while (pos < length && (checkPos = checkNextChar(typeName, '[', pos, length, true)) > 0) {
- pos = checkNextChar(typeName, ']', checkPos, length, false);
- buffer.append(C_ARRAY);
- }
- return pos;
-}
-
-private static int encodeQualifiedName(char[] typeName, int pos, int length, StringBuffer buffer) {
- int count = 0;
- char lastAppendedChar = 0;
- nameLoop: while (pos < length) {
- char currentChar = typeName[pos];
- switch (currentChar) {
- case '<' :
- case '>' :
- case '[' :
- case ',' :
- break nameLoop;
- case '.' :
- buffer.append(C_DOT);
- lastAppendedChar = C_DOT;
- count++;
- break;
- default:
- if (currentChar == ' ' || ScannerHelper.isWhitespace(currentChar)) {
- if (lastAppendedChar == C_DOT) { // allow spaces after a dot
- pos = consumeWhitespace(typeName, pos, length) - 1; // will be incremented
- break;
- }
- // allow spaces before a dot
- int checkPos = checkNextChar(typeName, '.', pos, length, true);
- if (checkPos > 0) {
- buffer.append(C_DOT); // process dot immediately to avoid one iteration
- lastAppendedChar = C_DOT;
- count++;
- pos = checkPos;
- break;
- }
- break nameLoop;
- }
- buffer.append(currentChar);
- lastAppendedChar = currentChar;
- count++;
- break;
- }
- pos++;
- }
- if (count == 0) throw new IllegalArgumentException(new String(typeName));
- return pos;
-}
-
-private static int encodeTypeSignature(char[] typeName, int start, boolean isResolved, int length, StringBuffer buffer) {
- int pos = start;
- pos = consumeWhitespace(typeName, pos, length);
- if (pos >= length) throw new IllegalArgumentException(new String(typeName));
- int checkPos;
- char currentChar = typeName[pos];
- switch (currentChar) {
- // primitive type?
- case 'b' :
- checkPos = checkName(BOOLEAN, typeName, pos, length);
- if (checkPos > 0) {
- pos = encodeArrayDimension(typeName, checkPos, length, buffer);
- buffer.append(C_BOOLEAN);
- return pos;
- }
- checkPos = checkName(BYTE, typeName, pos, length);
- if (checkPos > 0) {
- pos = encodeArrayDimension(typeName, checkPos, length, buffer);
- buffer.append(C_BYTE);
- return pos;
- }
- break;
- case 'd':
- checkPos = checkName(DOUBLE, typeName, pos, length);
- if (checkPos > 0) {
- pos = encodeArrayDimension(typeName, checkPos, length, buffer);
- buffer.append(C_DOUBLE);
- return pos;
- }
- break;
- case 'f':
- checkPos = checkName(FLOAT, typeName, pos, length);
- if (checkPos > 0) {
- pos = encodeArrayDimension(typeName, checkPos, length, buffer);
- buffer.append(C_FLOAT);
- return pos;
- }
- break;
- case 'i':
- checkPos = checkName(INT, typeName, pos, length);
- if (checkPos > 0) {
- pos = encodeArrayDimension(typeName, checkPos, length, buffer);
- buffer.append(C_INT);
- return pos;
- }
- break;
- case 'l':
- checkPos = checkName(LONG, typeName, pos, length);
- if (checkPos > 0) {
- pos = encodeArrayDimension(typeName, checkPos, length, buffer);
- buffer.append(C_LONG);
- return pos;
- }
- break;
- case 's':
- checkPos = checkName(SHORT, typeName, pos, length);
- if (checkPos > 0) {
- pos = encodeArrayDimension(typeName, checkPos, length, buffer);
- buffer.append(C_SHORT);
- return pos;
- }
- break;
- case 'v':
- checkPos = checkName(VOID, typeName, pos, length);
- if (checkPos > 0) {
- pos = encodeArrayDimension(typeName, checkPos, length, buffer);
- buffer.append(C_VOID);
- return pos;
- }
- break;
- case 'c':
- checkPos = checkName(CHAR, typeName, pos, length);
- if (checkPos > 0) {
- pos = encodeArrayDimension(typeName, checkPos, length, buffer);
- buffer.append(C_CHAR);
- return pos;
- } else {
- checkPos = checkName(CAPTURE, typeName, pos, length);
- if (checkPos > 0) {
- pos = consumeWhitespace(typeName, checkPos, length);
- if (typeName[pos] != '?') {
- break;
- }
- } else {
- break;
- }
- }
- buffer.append(C_CAPTURE);
- //$FALL-THROUGH$ for wildcard part of capture typecheckPos
- case '?':
- // wildcard
- pos = consumeWhitespace(typeName, pos+1, length);
- checkPos = checkName(EXTENDS, typeName, pos, length);
- if (checkPos > 0) {
- buffer.append(C_EXTENDS);
- pos = encodeTypeSignature(typeName, checkPos, isResolved, length, buffer);
- return pos;
- }
- checkPos = checkName(SUPER, typeName, pos, length);
- if (checkPos > 0) {
- buffer.append(C_SUPER);
- pos = encodeTypeSignature(typeName, checkPos, isResolved, length, buffer);
- return pos;
- }
- buffer.append(C_STAR);
- return pos;
- }
- // non primitive type
- checkPos = checkArrayDimension(typeName, pos, length);
- int end;
- if (checkPos > 0) {
- end = encodeArrayDimension(typeName, checkPos, length, buffer);
- } else {
- end = -1;
- }
- buffer.append(isResolved ? C_RESOLVED : C_UNRESOLVED);
- while (true) { // loop on qualifiedName[][.qualifiedName[]*
- pos = encodeQualifiedName(typeName, pos, length, buffer);
- checkPos = checkNextChar(typeName, '<', pos, length, true);
- if (checkPos > 0) {
- buffer.append(C_GENERIC_START);
- // Stop gap fix for <>.
- if ((pos = checkNextChar(typeName, '>', checkPos, length, true)) > 0) {
- buffer.append(C_GENERIC_END);
- } else {
- pos = encodeTypeSignature(typeName, checkPos, isResolved, length, buffer);
- while ((checkPos = checkNextChar(typeName, ',', pos, length, true)) > 0) {
- pos = encodeTypeSignature(typeName, checkPos, isResolved, length, buffer);
- }
- pos = checkNextChar(typeName, '>', pos, length, false);
- buffer.append(C_GENERIC_END);
- }
- }
- checkPos = checkNextChar(typeName, '.', pos, length, true);
- if (checkPos > 0) {
- buffer.append(C_DOT);
- pos = checkPos;
- } else {
- break;
- }
- }
- buffer.append(C_NAME_END);
- if (end > 0) pos = end; // skip array dimension which were preprocessed
- return pos;
-}
-
-/**
- * Returns the array count (array nesting depth) of the given type signature.
- *
- * @param typeSignature the type signature
- * @return the array nesting depth, or 0 if not an array
- * @exception IllegalArgumentException if the signature is not syntactically
- * correct
- *
- * @since 2.0
- */
-public static int getArrayCount(char[] typeSignature) throws IllegalArgumentException {
- try {
- int count = 0;
- while (typeSignature[count] == C_ARRAY) {
- ++count;
- }
- return count;
- } catch (ArrayIndexOutOfBoundsException e) { // signature is syntactically incorrect if last character is C_ARRAY
- throw new IllegalArgumentException();
- }
-}
-
-/**
- * Returns the array count (array nesting depth) of the given type signature.
- *
- * @param typeSignature the type signature
- * @return the array nesting depth, or 0 if not an array
- * @exception IllegalArgumentException if the signature is not syntactically
- * correct
- */
-public static int getArrayCount(String typeSignature) throws IllegalArgumentException {
- return getArrayCount(typeSignature.toCharArray());
-}
-
-/**
- * Returns the type signature without any array nesting.
- *
- * For example:
- *
- *
- * getElementType({'[', '[', 'I'}) --> {'I'}.
- *
- *
- *
- *
- * @param typeSignature the type signature
- * @return the type signature without arrays
- * @exception IllegalArgumentException if the signature is not syntactically
- * correct
- *
- * @since 2.0
- */
-public static char[] getElementType(char[] typeSignature) throws IllegalArgumentException {
- int count = getArrayCount(typeSignature);
- if (count == 0) return typeSignature;
- int length = typeSignature.length;
- char[] result = new char[length-count];
- System.arraycopy(typeSignature, count, result, 0, length-count);
- return result;
-}
-
-/**
- * Returns the type signature without any array nesting.
- *
- * For example:
- *
- *
- * getElementType("[[I") --> "I".
- *
- *
- *
- *
- * @param typeSignature the type signature
- * @return the type signature without arrays
- * @exception IllegalArgumentException if the signature is not syntactically
- * correct
- */
-public static String getElementType(String typeSignature) throws IllegalArgumentException {
- char[] signature = typeSignature.toCharArray();
- char[] elementType = getElementType(signature);
- return signature == elementType ? typeSignature : new String(elementType);
-}
-/**
- * Extracts the type bounds' signatures from the given intersection type signature.
- * Returns an empty array if the type signature is not an intersection type signature.
- *
- * @param intersectionTypeSignature the intersection type signature
- * @return the signatures of the type bounds
- * @exception IllegalArgumentException if the signature is syntactically incorrect
- *
- * @since 3.7.1
- */
-public static char[][] getIntersectionTypeBounds(char[] intersectionTypeSignature) throws IllegalArgumentException {
- if (getTypeSignatureKind(intersectionTypeSignature) != INTERSECTION_TYPE_SIGNATURE) {
- return CharOperation.NO_CHAR_CHAR;
- }
- ArrayList args = new ArrayList();
- int i = 1; // skip the '|'
- int length = intersectionTypeSignature.length;
- for (;;) {
- int e = Util.scanClassTypeSignature(intersectionTypeSignature, i);
- if (e < 0) {
- throw new IllegalArgumentException("Invalid format"); //$NON-NLS-1$
- }
- args.add(CharOperation.subarray(intersectionTypeSignature, i, e + 1));
- if (e == length - 1) {
- int size = args.size();
- char[][] result = new char[size][];
- args.toArray(result);
- return result;
- } else if (intersectionTypeSignature[e + 1] != C_COLON) {
- throw new IllegalArgumentException("Invalid format"); //$NON-NLS-1$
- }
- i = e + 2; // add one to skip C_COLON
- }
-}
-/**
- * Extracts the type bounds' signatures from the given intersection type signature.
- * Returns an empty array if the type signature is not an intersection type signature.
- *
- * @param intersectionTypeSignature the intersection type signature
- * @return the signatures of the type bounds
- * @exception IllegalArgumentException if the signature is syntactically incorrect
- *
- * @since 3.7.1
- */
-public static String[] getIntersectionTypeBounds(String intersectionTypeSignature) throws IllegalArgumentException {
- char[][] args = getIntersectionTypeBounds(intersectionTypeSignature.toCharArray());
- return CharOperation.toStrings(args);
-}
-/**
- * Returns the number of parameter types in the given method signature.
- *
- * @param methodSignature the method signature
- * @return the number of parameters
- * @exception IllegalArgumentException if the signature is not syntactically
- * correct
- * @since 2.0
- */
-public static int getParameterCount(char[] methodSignature) throws IllegalArgumentException {
- try {
- int count = 0;
- int i = CharOperation.indexOf(C_PARAM_START, methodSignature);
- if (i < 0) {
- throw new IllegalArgumentException();
- } else {
- i++;
- }
- for (;;) {
- if (methodSignature[i] == C_PARAM_END) {
- return count;
- }
- int e= Util.scanTypeSignature(methodSignature, i);
- if (e < 0) {
- throw new IllegalArgumentException();
- } else {
- i = e + 1;
- }
- count++;
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- throw new IllegalArgumentException();
- }
-}
-
-/**
- * Returns the number of parameter types in the given method signature.
- *
- * @param methodSignature the method signature
- * @return the number of parameters
- * @exception IllegalArgumentException if the signature is not syntactically
- * correct
- */
-public static int getParameterCount(String methodSignature) throws IllegalArgumentException {
- return getParameterCount(methodSignature.toCharArray());
-}
-
-/**
- * Extracts the parameter type signatures from the given method signature.
- * The method signature is expected to be dot-based.
- *
- * @param methodSignature the method signature
- * @return the list of parameter type signatures
- * @exception IllegalArgumentException if the signature is syntactically
- * incorrect
- *
- * @since 2.0
- */
-public static char[][] getParameterTypes(char[] methodSignature) throws IllegalArgumentException {
- try {
- int count = getParameterCount(methodSignature);
- char[][] result = new char[count][];
- if (count == 0) {
- return result;
- }
- int i = CharOperation.indexOf(C_PARAM_START, methodSignature);
- if (i < 0) {
- throw new IllegalArgumentException();
- } else {
- i++;
- }
- int t = 0;
- for (;;) {
- if (methodSignature[i] == C_PARAM_END) {
- return result;
- }
- int e = Util.scanTypeSignature(methodSignature, i);
- if (e < 0) {
- throw new IllegalArgumentException();
- }
- result[t] = CharOperation.subarray(methodSignature, i, e + 1);
- t++;
- i = e + 1;
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- throw new IllegalArgumentException();
- }
-}
-/**
- * Extracts the parameter type signatures from the given method signature.
- * The method signature is expected to be dot-based.
- *
- * @param methodSignature the method signature
- * @return the list of parameter type signatures
- * @exception IllegalArgumentException if the signature is syntactically
- * incorrect
- */
-public static String[] getParameterTypes(String methodSignature) throws IllegalArgumentException {
- char[][] parameterTypes = getParameterTypes(methodSignature.toCharArray());
- return CharOperation.toStrings(parameterTypes);
-}
-
-/**
- * Returns a char array containing all but the last segment of the given
- * dot-separated qualified name. Returns the empty char array if it is not qualified.
- *
- * For example:
- *
- *
- * getQualifier({'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'O', 'b', 'j', 'e', 'c', 't'}) -> {'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g'}
- * getQualifier({'O', 'u', 't', 'e', 'r', '.', 'I', 'n', 'n', 'e', 'r'}) -> {'O', 'u', 't', 'e', 'r'}
- * getQualifier({'j', 'a', 'v', 'a', '.', 'u', 't', 'i', 'l', '.', 'L', 'i', 's', 't', '<', 'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'S', 't', 'r', 'i', 'n', 'g', '>'}) -> {'j', 'a', 'v', 'a', '.', 'u', 't', 'i', 'l'}
- *
- *
- *
- *
- * @param name the name
- * @return the qualifier prefix, or the empty char array if the name contains no
- * dots
- * @exception NullPointerException if name is null
- * @since 2.0
- */
-public static char[] getQualifier(char[] name) {
- int firstGenericStart = CharOperation.indexOf(C_GENERIC_START, name);
- int lastDot = CharOperation.lastIndexOf(C_DOT, name, 0, firstGenericStart == -1 ? name.length-1 : firstGenericStart);
- if (lastDot == -1) {
- return CharOperation.NO_CHAR;
- }
- return CharOperation.subarray(name, 0, lastDot);
-}
-
-/**
- * Returns a string containing all but the last segment of the given
- * dot-separated qualified name. Returns the empty string if it is not qualified.
- *
- * For example:
- *
- *
- * getQualifier("java.lang.Object") -> "java.lang"
- * getQualifier("Outer.Inner") -> "Outer"
- * getQualifier("java.util.List<java.lang.String>") -> "java.util"
- *
- *
- *
- *
- * @param name the name
- * @return the qualifier prefix, or the empty string if the name contains no
- * dots
- * @exception NullPointerException if name is null
- */
-public static String getQualifier(String name) {
- char[] qualifier = getQualifier(name.toCharArray());
- if (qualifier.length == 0) return org.eclipse.jdt.internal.compiler.util.Util.EMPTY_STRING;
- return new String(qualifier);
-}
-
-/**
- * Extracts the return type from the given method signature. The method signature is
- * expected to be dot-based.
- *
- * @param methodSignature the method signature
- * @return the type signature of the return type
- * @exception IllegalArgumentException if the signature is syntactically
- * incorrect
- *
- * @since 2.0
- */
-public static char[] getReturnType(char[] methodSignature) throws IllegalArgumentException {
- // skip type parameters
- int paren = CharOperation.lastIndexOf(C_PARAM_END, methodSignature);
- if (paren == -1) {
- throw new IllegalArgumentException();
- }
- // there could be thrown exceptions behind, thus scan one type exactly
- int last = Util.scanTypeSignature(methodSignature, paren+1);
- return CharOperation.subarray(methodSignature, paren + 1, last+1);
-}
-
-/**
- * Extracts the return type from the given method signature. The method signature is
- * expected to be dot-based.
- *
- * @param methodSignature the method signature
- * @return the type signature of the return type
- * @exception IllegalArgumentException if the signature is syntactically
- * incorrect
- */
-public static String getReturnType(String methodSignature) throws IllegalArgumentException {
- return new String(getReturnType(methodSignature.toCharArray()));
-}
-
-/**
- * Returns package fragment of a type signature. The package fragment separator must be '.'
- * and the type fragment separator must be '$'.
- *
- * For example:
- *
- *
- * getSignatureQualifier({'L', 'j', 'a', 'v', 'a', '.', 'u', 't', 'i', 'l', '.', 'M', 'a', 'p', '$', 'E', 'n', 't', 'r', 'y', ';'}) -> {'j', 'a', 'v', 'a', '.', 'u', 't', 'i', 'l'}
- *
- *
- *
- *
- * @param typeSignature the type signature
- * @return the package fragment (separators are '.')
- * @since 3.1
- */
-public static char[] getSignatureQualifier(char[] typeSignature) {
- if(typeSignature == null) return CharOperation.NO_CHAR;
-
- char[] qualifiedType = Signature.toCharArray(typeSignature);
-
- int dotCount = 0;
- indexFound: for(int i = 0; i < typeSignature.length; i++) {
- switch(typeSignature[i]) {
- case C_DOT:
- dotCount++;
- break;
- case C_GENERIC_START:
- break indexFound;
- case C_DOLLAR:
- break indexFound;
- }
- }
-
- if(dotCount > 0) {
- for(int i = 0; i < qualifiedType.length; i++) {
- if(qualifiedType[i] == '.') {
- dotCount--;
- }
- if(dotCount <= 0) {
- return CharOperation.subarray(qualifiedType, 0, i);
- }
- }
- }
- return CharOperation.NO_CHAR;
-}
-/**
- * Returns package fragment of a type signature. The package fragment separator must be '.'
- * and the type fragment separator must be '$'.
- *
- * For example:
- *
- *
- * getSignatureQualifier("Ljava.util.Map$Entry") -> "java.util"
- *
- *
- *
- *
- * @param typeSignature the type signature
- * @return the package fragment (separators are '.')
- * @since 3.1
- */
-public static String getSignatureQualifier(String typeSignature) {
- return new String(getSignatureQualifier(typeSignature == null ? null : typeSignature.toCharArray()));
-}
-/**
- * Returns type fragment of a type signature. The package fragment separator must be '.'
- * and the type fragment separator must be '$'.
- *
- * For example:
- *
- *
- * getSignatureSimpleName({'L', 'j', 'a', 'v', 'a', '.', 'u', 't', 'i', 'l', '.', 'M', 'a', 'p', '$', 'E', 'n', 't', 'r', 'y', ';'}) -> {'M', 'a', 'p', '.', 'E', 'n', 't', 'r', 'y'}
- *
- *
- *
- *
- * @param typeSignature the type signature
- * @return the type fragment (separators are '.')
- * @since 3.1
- */
-public static char[] getSignatureSimpleName(char[] typeSignature) {
- if(typeSignature == null) return CharOperation.NO_CHAR;
-
- char[] qualifiedType = Signature.toCharArray(typeSignature);
-
- int dotCount = 0;
- indexFound: for(int i = 0; i < typeSignature.length; i++) {
- switch(typeSignature[i]) {
- case C_DOT:
- dotCount++;
- break;
- case C_GENERIC_START:
- break indexFound;
- case C_DOLLAR:
- break indexFound;
- }
- }
-
- if(dotCount > 0) {
- int typeStart = 0;
- for(int i = 0; i < qualifiedType.length; i++) {
- switch (qualifiedType[i]) {
- case '.':
- dotCount--;
- break;
- case ' ':
- typeStart = i+1;
- break;
- }
- if(dotCount <= 0) {
- char[] simpleName = CharOperation.subarray(qualifiedType, i + 1, qualifiedType.length);
- if (typeStart > 0 && typeStart < qualifiedType.length)
- return CharOperation.concat(CharOperation.subarray(qualifiedType, 0, typeStart), simpleName);
- return simpleName;
- }
- }
- }
- return qualifiedType;
-}
-/**
- * Returns type fragment of a type signature. The package fragment separator must be '.'
- * and the type fragment separator must be '$'.
- *
- * For example:
- *
- *
- * getSignatureSimpleName("Ljava.util.Map$Entry") -> "Map.Entry"
- *
- *
- *
- *
- * @param typeSignature the type signature
- * @return the type fragment (separators are '.')
- * @since 3.1
- */
-public static String getSignatureSimpleName(String typeSignature) {
- return new String(getSignatureSimpleName(typeSignature == null ? null : typeSignature.toCharArray()));
-}
-/**
- * Returns the last segment of the given dot-separated qualified name.
- * Returns the given name if it is not qualified.
- *
- * For example:
- *
- *
- * getSimpleName({'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'O', 'b', 'j', 'e', 'c', 't'}) -> {'O', 'b', 'j', 'e', 'c', 't'}
- *
- *
- *
- *
- * @param name the name
- * @return the last segment of the qualified name
- * @exception NullPointerException if name is null
- * @since 2.0
- */
-public static char[] getSimpleName(char[] name) {
-
- int lastDot = -1, lastGenericStart = -1, lastGenericEnd = -1;
- int depth = 0;
- int length = name.length;
- lastDotLookup: for (int i = length -1; i >= 0; i--) {
- switch (name[i]) {
- case '.':
- if (depth == 0) {
- lastDot = i;
- break lastDotLookup;
- }
- break;
- case '<':
- depth--;
- if (depth == 0) lastGenericStart = i;
- break;
- case '>':
- if (depth == 0) lastGenericEnd = i;
- depth++;
- break;
- }
- }
- if (lastGenericStart < 0) {
- if (lastDot < 0) {
- return name;
- }
- return CharOperation.subarray(name, lastDot + 1, length);
- }
- StringBuffer buffer = new StringBuffer(10);
- int nameStart = lastDot < 0 ? 0 : lastDot+1;
- buffer.append(name, nameStart, lastGenericStart - nameStart);
- appendArgumentSimpleNames(name, lastGenericStart, lastGenericEnd, buffer);
- buffer.append(name, lastGenericEnd+1, length-lastGenericEnd-1); // copy trailing portion, may contain dimensions
- char[] result = new char[length = buffer.length()];
- buffer.getChars(0, length, result, 0);
- return result;
-}
-/**
- * Returns the last segment of the given dot-separated qualified name.
- * Returns the given name if it is not qualified.
- *
- * For example:
- *
- *
- * getSimpleName("java.lang.Object") -> "Object"
- *
- *
- * getSimpleName("java.util.Map<java.lang.String, java.lang.Object>") -> "Map<String,Object>"
- *
- *
- *
- *
- * @param name the name
- * @return the last segment of the qualified name
- * @exception NullPointerException if name is null
- */
-public static String getSimpleName(String name) {
- int lastDot = -1, lastGenericStart = -1, lastGenericEnd = -1;
- int depth = 0;
- int length = name.length();
- lastDotLookup: for (int i = length -1; i >= 0; i--) {
- switch (name.charAt(i)) {
- case '.':
- if (depth == 0) {
- lastDot = i;
- break lastDotLookup;
- }
- break;
- case '<':
- depth--;
- if (depth == 0) lastGenericStart = i;
- break;
- case '>':
- if (depth == 0) lastGenericEnd = i;
- depth++;
- break;
- }
- }
- if (lastGenericStart < 0) {
- if (lastDot < 0) {
- return name;
- }
- return name.substring(lastDot + 1, length);
- }
- StringBuffer buffer = new StringBuffer(10);
- char[] nameChars = name.toCharArray();
- int nameStart = lastDot < 0 ? 0 : lastDot+1;
- buffer.append(nameChars, nameStart, lastGenericStart - nameStart);
- appendArgumentSimpleNames(nameChars, lastGenericStart, lastGenericEnd, buffer);
- buffer.append(nameChars, lastGenericEnd+1, length-lastGenericEnd-1); // copy trailing portion, may contain dimensions
- return buffer.toString();
-}
-/**
- * Returns all segments of the given dot-separated qualified name.
- * Returns an array with only the given name if it is not qualified.
- * Returns an empty array if the name is empty.
- *
- * For example:
- *
- *
- * getSimpleNames({'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'O', 'b', 'j', 'e', 'c', 't'}) -> {{'j', 'a', 'v', 'a'}, {'l', 'a', 'n', 'g'}, {'O', 'b', 'j', 'e', 'c', 't'}}
- * getSimpleNames({'O', 'b', 'j', 'e', 'c', 't'}) -> {{'O', 'b', 'j', 'e', 'c', 't'}}
- * getSimpleNames({}) -> {}
- * getSimpleNames({'j', 'a', 'v', 'a', '.', 'u', 't', 'i', 'l', '.', 'L', 'i', 's', 't', '<', 'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'S', 't', 'r', 'i', 'n', 'g', '>'}) -> {{'j', 'a', 'v', 'a'}, {'l', 'a', 'n', 'g'}, {'L', 'i', 's', 't', '<', 'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'S', 't', 'r', 'i', 'n', 'g'}}
- *
- *
- *
- * @param name the name
- * @return the list of simple names, possibly empty
- * @exception NullPointerException if name is null
- * @since 2.0
- */
-public static char[][] getSimpleNames(char[] name) {
- int length = name == null ? 0 : name.length;
- if (length == 0)
- return CharOperation.NO_CHAR_CHAR;
-
- int wordCount = 1;
- countingWords: for (int i = 0; i < length; i++)
- switch(name[i]) {
- case C_DOT:
- wordCount++;
- break;
- case C_GENERIC_START:
- break countingWords;
- }
- char[][] split = new char[wordCount][];
- int last = 0, currentWord = 0;
- for (int i = 0; i < length; i++) {
- if (name[i] == C_GENERIC_START) break;
- if (name[i] == C_DOT) {
- split[currentWord] = new char[i - last];
- System.arraycopy(
- name,
- last,
- split[currentWord++],
- 0,
- i - last);
- last = i + 1;
- }
- }
- split[currentWord] = new char[length - last];
- System.arraycopy(name, last, split[currentWord], 0, length - last);
- return split;
-}
-/**
- * Returns all segments of the given dot-separated qualified name.
- * Returns an array with only the given name if it is not qualified.
- * Returns an empty array if the name is empty.
- *
- * For example:
- *
- *
- * getSimpleNames("java.lang.Object") -> {"java", "lang", "Object"}
- * getSimpleNames("Object") -> {"Object"}
- * getSimpleNames("") -> {}
- * getSimpleNames("java.util.List<java.lang.String>") ->
- * {"java", "util", "List<java.lang.String>"}
- *
- *
- *
- * @param name the name
- * @return the list of simple names, possibly empty
- * @exception NullPointerException if name is null
- */
-public static String[] getSimpleNames(String name) {
- return CharOperation.toStrings(getSimpleNames(name.toCharArray()));
-}
-
-/**
- * Extracts the thrown exception type signatures from the given method signature if any
- * The method signature is expected to be dot-based.
- *
- * @param methodSignature the method signature
- * @return the list of thrown exception type signatures
- * @exception IllegalArgumentException if the signature is syntactically
- * incorrect
- *
- * @since 3.1
- */
-public static char[][] getThrownExceptionTypes(char[] methodSignature) throws IllegalArgumentException {
- // skip type parameters
- int exceptionStart = CharOperation.indexOf(C_EXCEPTION_START, methodSignature);
- if (exceptionStart == -1) {
- int paren = CharOperation.lastIndexOf(C_PARAM_END, methodSignature);
- if (paren == -1) {
- throw new IllegalArgumentException();
- }
- // ignore return type
- exceptionStart = Util.scanTypeSignature(methodSignature, paren+1) + 1;
- int length = methodSignature.length;
- if (exceptionStart == length) return CharOperation.NO_CHAR_CHAR;
- throw new IllegalArgumentException();
- }
- int length = methodSignature.length;
- int i = exceptionStart;
- ArrayList exceptionList = new ArrayList(1);
- while (i < length) {
- if (methodSignature[i] == C_EXCEPTION_START) {
- exceptionStart++;
- i++;
- } else {
- throw new IllegalArgumentException();
- }
- i = Util.scanTypeSignature(methodSignature, i) + 1;
- exceptionList.add(CharOperation.subarray(methodSignature, exceptionStart,i));
- exceptionStart = i;
- }
- char[][] result;
- exceptionList.toArray(result = new char[exceptionList.size()][]);
- return result;
-}
-/**
- * Extracts the thrown exception type signatures from the given method signature if any
- * The method signature is expected to be dot-based.
- *
- * @param methodSignature the method signature
- * @return the list of thrown exception type signatures
- * @exception IllegalArgumentException if the signature is syntactically
- * incorrect
- *
- * @since 3.1
- */
-public static String[] getThrownExceptionTypes(String methodSignature) throws IllegalArgumentException {
- char[][] parameterTypes = getThrownExceptionTypes(methodSignature.toCharArray());
- return CharOperation.toStrings(parameterTypes);
-}
-/**
- * Extracts the type argument signatures from the given type signature.
- * Returns an empty array if the type signature is not a parameterized type signature.
- *
- * @param parameterizedTypeSignature the parameterized type signature
- * @return the signatures of the type arguments
- * @exception IllegalArgumentException if the signature is syntactically incorrect
- *
- * @since 3.1
- */
-public static char[][] getTypeArguments(char[] parameterizedTypeSignature) throws IllegalArgumentException {
- int length = parameterizedTypeSignature.length;
- if (length < 2 || parameterizedTypeSignature[length-2] != C_GENERIC_END)
- // cannot have type arguments otherwise signature would end by ">;"
- return CharOperation.NO_CHAR_CHAR;
- int count = 1; // start to count generic end/start peers
- int start = length - 2;
- while (start >= 0 && count > 0) {
- switch (parameterizedTypeSignature[--start]) {
- case C_GENERIC_START:
- count--;
- break;
- case C_GENERIC_END:
- count++;
- break;
- }
- }
- if (start < 0) // invalid number of generic start/end
- throw new IllegalArgumentException();
- ArrayList args = new ArrayList();
- int p = start + 1;
- while (true) {
- if (p >= parameterizedTypeSignature.length) {
- throw new IllegalArgumentException();
- }
- char c = parameterizedTypeSignature[p];
- if (c == C_GENERIC_END) {
- int size = args.size();
- char[][] result = new char[size][];
- args.toArray(result);
- return result;
- }
- int e = Util.scanTypeArgumentSignature(parameterizedTypeSignature, p);
- args.add(CharOperation.subarray(parameterizedTypeSignature, p, e+1));
- p = e + 1;
- }
-}
-/**
- * Extracts the type argument signatures from the given type signature.
- * Returns an empty array if the type signature is not a parameterized type signature.
- *
- * @param parameterizedTypeSignature the parameterized type signature
- * @return the signatures of the type arguments
- * @exception IllegalArgumentException if the signature is syntactically incorrect
- *
- * @since 3.1
- */
-public static String[] getTypeArguments(String parameterizedTypeSignature) throws IllegalArgumentException {
- char[][] args = getTypeArguments(parameterizedTypeSignature.toCharArray());
- return CharOperation.toStrings(args);
-}
-/**
- * Extracts the type erasure signature from the given parameterized type signature.
- * Returns the given type signature if it is not parameterized.
- *
- * @param parameterizedTypeSignature the parameterized type signature
- * @return the signature of the type erasure
- * @exception IllegalArgumentException if the signature is syntactically
- * incorrect
- *
- * @since 3.1
- */
-public static char[] getTypeErasure(char[] parameterizedTypeSignature) throws IllegalArgumentException {
- int end = CharOperation.indexOf(C_GENERIC_START, parameterizedTypeSignature);
- if (end == -1) return parameterizedTypeSignature;
- int length = parameterizedTypeSignature.length;
- char[] result = new char[length];
- int pos = 0;
- int start = 0;
- int deep= 0;
- for (int idx=end; idx 0) throw new IllegalArgumentException();
- int size = pos+length-start;
- char[] resized = new char[size];
- System.arraycopy(result, 0, resized, 0, pos);
- System.arraycopy(parameterizedTypeSignature, start, resized, pos, length-start);
- return resized;
-}
-/**
- * Extracts the type erasure signature from the given parameterized type signature.
- * Returns the given type signature if it is not parameterized.
- *
- * @param parameterizedTypeSignature the parameterized type signature
- * @return the signature of the type erasure
- * @exception IllegalArgumentException if the signature is syntactically
- * incorrect
- *
- * @since 3.1
- */
-public static String getTypeErasure(String parameterizedTypeSignature) throws IllegalArgumentException {
- char[] signature = parameterizedTypeSignature.toCharArray();
- char[] erasure = getTypeErasure(signature);
- return signature == erasure ? parameterizedTypeSignature : new String(erasure);
-}
-
-/**
- * Extracts the class and interface bounds from the given formal type
- * parameter signature. The class bound, if present, is listed before
- * the interface bounds. The signature is expected to be dot-based.
- *
- * @param formalTypeParameterSignature the formal type parameter signature
- * @return the (possibly empty) list of type signatures for the bounds
- * @exception IllegalArgumentException if the signature is syntactically
- * incorrect
- * @since 3.0
- */
-public static char[][] getTypeParameterBounds(char[] formalTypeParameterSignature) throws IllegalArgumentException {
- int p1 = CharOperation.indexOf(C_COLON, formalTypeParameterSignature);
- if (p1 < 0) {
- // no ":" means can't be a formal type parameter signature
- throw new IllegalArgumentException();
- }
- if (p1 == formalTypeParameterSignature.length - 1) {
- // no class or interface bounds
- return CharOperation.NO_CHAR_CHAR;
- }
- int p2 = CharOperation.indexOf(C_COLON, formalTypeParameterSignature, p1 + 1);
- char[] classBound;
- if (p2 < 0) {
- // no interface bounds
- classBound = CharOperation.subarray(formalTypeParameterSignature, p1 + 1, formalTypeParameterSignature.length);
- return new char[][] {classBound};
- }
- if (p2 == p1 + 1) {
- // no class bound, but 1 or more interface bounds
- classBound = null;
- } else {
- classBound = CharOperation.subarray(formalTypeParameterSignature, p1 + 1, p2);
- }
- char[][] interfaceBounds = CharOperation.splitOn(C_COLON, formalTypeParameterSignature, p2 + 1, formalTypeParameterSignature.length);
- if (classBound == null) {
- return interfaceBounds;
- }
- int resultLength = interfaceBounds.length + 1;
- char[][] result = new char[resultLength][];
- result[0] = classBound;
- System.arraycopy(interfaceBounds, 0, result, 1, interfaceBounds.length);
- return result;
-}
-
-/**
- * Extracts the class and interface bounds from the given formal type
- * parameter signature. The class bound, if present, is listed before
- * the interface bounds. The signature is expected to be dot-based.
- *
- * @param formalTypeParameterSignature the formal type parameter signature
- * @return the (possibly empty) list of type signatures for the bounds
- * @exception IllegalArgumentException if the signature is syntactically
- * incorrect
- * @since 3.0
- */
-public static String[] getTypeParameterBounds(String formalTypeParameterSignature) throws IllegalArgumentException {
- char[][] bounds = getTypeParameterBounds(formalTypeParameterSignature.toCharArray());
- return CharOperation.toStrings(bounds);
-}
-
-/**
- * Extracts the type parameter signatures from the given method or type signature.
- * The method or type signature is expected to be dot-based.
- *
- * @param methodOrTypeSignature the method or type signature
- * @return the list of type parameter signatures
- * @exception IllegalArgumentException if the signature is syntactically
- * incorrect
- *
- * @since 3.1
- */
-public static char[][] getTypeParameters(char[] methodOrTypeSignature) throws IllegalArgumentException {
- try {
- int length = methodOrTypeSignature.length;
- if (length == 0) return CharOperation.NO_CHAR_CHAR;
- if (methodOrTypeSignature[0] != C_GENERIC_START) return CharOperation.NO_CHAR_CHAR;
-
- ArrayList paramList = new ArrayList(1);
- int paramStart = 1, i = 1; // start after leading '<'
- while (i < length) {
- if (methodOrTypeSignature[i] == C_GENERIC_END) {
- int size = paramList.size();
- if (size == 0) throw new IllegalArgumentException();
- char[][] result;
- paramList.toArray(result = new char[size][]);
- return result;
- }
- i = CharOperation.indexOf(C_COLON, methodOrTypeSignature, i);
- if (i < 0 || i >= length)
- throw new IllegalArgumentException();
- // iterate over bounds
- while (methodOrTypeSignature[i] == ':') {
- i++; // skip colon
- switch (methodOrTypeSignature[i]) {
- case ':':
- // no class bound
- break;
- case C_GENERIC_END:
- break;
- case C_RESOLVED:
- try {
- i = Util.scanClassTypeSignature(methodOrTypeSignature, i);
- i++; // position at start of next param if any
- } catch (IllegalArgumentException e) {
- // not a class type signature -> it is a new type parameter
- }
- break;
- case C_ARRAY:
- try {
- i = Util.scanArrayTypeSignature(methodOrTypeSignature, i);
- i++; // position at start of next param if any
- } catch (IllegalArgumentException e) {
- // not an array type signature -> it is a new type parameter
- }
- break;
- case C_TYPE_VARIABLE:
- try {
- i = Util.scanTypeVariableSignature(methodOrTypeSignature, i);
- i++; // position at start of next param if any
- } catch (IllegalArgumentException e) {
- // not a type variable signature -> it is a new type parameter
- }
- break;
- case C_CAPTURE:
- try {
- i = Util.scanCaptureTypeSignature(methodOrTypeSignature, i);
- i++; // position at start of next param if any
- } catch (IllegalArgumentException e) {
- // not a capture variable signature -> it is a new type parameter
- }
- break;
- // default: another type parameter is starting
- }
- }
- paramList.add(CharOperation.subarray(methodOrTypeSignature, paramStart, i));
- paramStart = i; // next param start from here
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- // invalid signature, fall through
- }
- throw new IllegalArgumentException();
-}
-/**
- * Extracts the type parameter signatures from the given method or type signature.
- * The method or type signature is expected to be dot-based.
- *
- * @param methodOrTypeSignature the method or type signature
- * @return the list of type parameter signatures
- * @exception IllegalArgumentException if the signature is syntactically
- * incorrect
- *
- * @since 3.1
- */
-public static String[] getTypeParameters(String methodOrTypeSignature) throws IllegalArgumentException {
- char[][] params = getTypeParameters(methodOrTypeSignature.toCharArray());
- return CharOperation.toStrings(params);
-}
-/**
- * Returns the kind of type signature encoded by the given string.
- *
- * @param typeSignature the type signature string
- * @return the kind of type signature; one of the kind constants:
- * {@link #ARRAY_TYPE_SIGNATURE}, {@link #CLASS_TYPE_SIGNATURE},
- * {@link #BASE_TYPE_SIGNATURE}, or {@link #TYPE_VARIABLE_SIGNATURE},
- * or (since 3.1) {@link #WILDCARD_TYPE_SIGNATURE} or {@link #CAPTURE_TYPE_SIGNATURE},
- * or (since 3.7) {@link #INTERSECTION_TYPE_SIGNATURE}
- * @exception IllegalArgumentException if this is not a type signature
- * @since 3.0
- */
-public static int getTypeSignatureKind(char[] typeSignature) {
- // need a minimum 1 char
- if (typeSignature.length < 1) {
- throw new IllegalArgumentException();
- }
- char c = typeSignature[0];
- if (c == C_GENERIC_START) {
- int count = 1;
- for (int i = 1, length = typeSignature.length; i < length; i++) {
- switch (typeSignature[i]) {
- case C_GENERIC_START:
- count++;
- break;
- case C_GENERIC_END:
- count--;
- break;
- }
- if (count == 0) {
- if (i+1 < length)
- c = typeSignature[i+1];
- break;
- }
- }
- }
- switch (c) {
- case C_ARRAY :
- return ARRAY_TYPE_SIGNATURE;
- case C_RESOLVED :
- case C_UNRESOLVED :
- return CLASS_TYPE_SIGNATURE;
- case C_TYPE_VARIABLE :
- return TYPE_VARIABLE_SIGNATURE;
- case C_BOOLEAN :
- case C_BYTE :
- case C_CHAR :
- case C_DOUBLE :
- case C_FLOAT :
- case C_INT :
- case C_LONG :
- case C_SHORT :
- case C_VOID :
- return BASE_TYPE_SIGNATURE;
- case C_STAR :
- case C_SUPER :
- case C_EXTENDS :
- return WILDCARD_TYPE_SIGNATURE;
- case C_CAPTURE :
- return CAPTURE_TYPE_SIGNATURE;
- case C_INTERSECTION :
- return INTERSECTION_TYPE_SIGNATURE;
- default :
- throw new IllegalArgumentException();
- }
-}
-
-/**
- * Returns the kind of type signature encoded by the given string.
- *
- * @param typeSignature the type signature string
- * @return the kind of type signature; one of the kind constants:
- * {@link #ARRAY_TYPE_SIGNATURE}, {@link #CLASS_TYPE_SIGNATURE},
- * {@link #BASE_TYPE_SIGNATURE}, or {@link #TYPE_VARIABLE_SIGNATURE},
- * or (since 3.1) {@link #WILDCARD_TYPE_SIGNATURE} or {@link #CAPTURE_TYPE_SIGNATURE}
- * or (since 3.7) {@link #INTERSECTION_TYPE_SIGNATURE}
- * @exception IllegalArgumentException if this is not a type signature
- * @since 3.0
- */
-public static int getTypeSignatureKind(String typeSignature) {
- return getTypeSignatureKind(typeSignature.toCharArray());
-}
-/**
- * Extracts the type variable name from the given formal type parameter
- * signature. The signature is expected to be dot-based.
- *
- * @param formalTypeParameterSignature the formal type parameter signature
- * @return the name of the type variable
- * @exception IllegalArgumentException if the signature is syntactically
- * incorrect
- * @since 3.0
- */
-public static char[] getTypeVariable(char[] formalTypeParameterSignature) throws IllegalArgumentException {
- int p = CharOperation.indexOf(C_COLON, formalTypeParameterSignature);
- if (p < 0) {
- // no ":" means can't be a formal type parameter signature
- throw new IllegalArgumentException();
- }
- return CharOperation.subarray(formalTypeParameterSignature, 0, p);
-}
-/**
- * Extracts the type variable name from the given formal type parameter
- * signature. The signature is expected to be dot-based.
- *
- * @param formalTypeParameterSignature the formal type parameter signature
- * @return the name of the type variable
- * @exception IllegalArgumentException if the signature is syntactically
- * incorrect
- * @since 3.0
- */
-public static String getTypeVariable(String formalTypeParameterSignature) throws IllegalArgumentException {
- return new String(getTypeVariable(formalTypeParameterSignature.toCharArray()));
-}
-/**
- * Removes any capture information from the given type or method signature
- * and returns the resulting signature.
- * Returns the type or method signature itself if no capture information is
- * present.
- *
- * For example (using equivalent string-based method):
- *
- *
- * removeCapture("LTest<!+Ljava.lang.Throwable;>;")
- * will return: "LTest<+Ljava.lang.Throwable;>;"
- *
- *
- *
- *
- * @param methodOrTypeSignature the signature which may have been captured
- * @return a new signature without capture information or the signature itself
- * if no specific capture information is present
- * @exception NullPointerException if methodOrTypeSignature is null
- *
- * @since 3.1
- */
-public static char[] removeCapture(char[] methodOrTypeSignature) {
- return CharOperation.remove(methodOrTypeSignature, C_CAPTURE);
-}
-/**
- * Removes any capture information from the given type or method signature
- * and returns the resulting signature.
- * Returns the type or method signature itself if no capture information is
- * present.
- *
- * For example:
- *
- *
- * removeCapture("LTest<!+Ljava.lang.Throwable;>;")
- * will return: "LTest<+Ljava.lang.Throwable;>;"
- *
- *
- *
- *
- * @param methodOrTypeSignature the signature which may have been captured
- * @return a new signature without capture information or the signature itself
- * if no specific capture information is present
- * @exception NullPointerException if methodOrTypeSignature is null
- *
- * @since 3.1
- */
-public static String removeCapture(String methodOrTypeSignature) {
- char[] array = methodOrTypeSignature.toCharArray();
- char[] result = removeCapture(array);
- if (array == result) return methodOrTypeSignature;
- return new String(result);
-}
-/**
- * Converts the given type signature to a readable string. The signature is expected to
- * be dot-based.
- *
- *
- * For example:
- *
- *
- * toString({'[', 'L', 'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'S', 't', 'r', 'i', 'n', 'g', ';'}) -> {'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'S', 't', 'r', 'i', 'n', 'g', '[', ']'}
- * toString({'I'}) -> {'i', 'n', 't'}
- * toString({'+', 'L', 'O', 'b', 'j', 'e', 'c', 't', ';'}) -> {'?', ' ', 'e', 'x', 't', 'e', 'n', 'd', 's', ' ', 'O', 'b', 'j', 'e', 'c', 't'}
- *
- *
- *
- *
- * Note: This method assumes that a type signature containing a '$'
- * is an inner type signature. While this is correct in most cases, someone could
- * define a non-inner type name containing a '$'. Handling this
- * correctly in all cases would have required resolving the signature, which
- * generally not feasible.
- *
- *
- * @param signature the type signature
- * @return the string representation of the type
- * @exception IllegalArgumentException if the signature is syntactically incorrect
- *
- * @since 2.0
- */
-public static char[] toCharArray(char[] signature) throws IllegalArgumentException {
- int sigLength = signature.length;
- if (sigLength == 0) {
- throw new IllegalArgumentException();
- }
- if (signature[0] == C_PARAM_START || signature[0] == C_GENERIC_START) {
- return toCharArray(signature, CharOperation.NO_CHAR, null, true, true);
- }
-
- StringBuffer buffer = new StringBuffer(signature.length + 10);
- appendTypeSignature(signature, 0, true, buffer);
- char[] result = new char[buffer.length()];
- buffer.getChars(0, buffer.length(), result, 0);
- return result;
-}
-/**
- * Converts the given method signature to a readable form. The method signature is expected to
- * be dot-based.
- *
- * For example:
- *
- *
- * toString("([Ljava.lang.String;)V", "main", new String[] {"args"}, false, true) -> "void main(String[] args)"
- *
- *
- *
- *
- * @param methodSignature the method signature to convert
- * @param methodName the name of the method to insert in the result, or
- * null if no method name is to be included
- * @param parameterNames the parameter names to insert in the result, or
- * null if no parameter names are to be included; if supplied,
- * the number of parameter names must match that of the method signature
- * @param fullyQualifyTypeNames true if type names should be fully
- * qualified, and false to use only simple names
- * @param includeReturnType true if the return type is to be
- * included
- * @return the char array representation of the method signature
- * @throws IllegalArgumentException if the method signature is syntactically incorrect
- * @since 2.0
- */
-public static char[] toCharArray(char[] methodSignature, char[] methodName, char[][] parameterNames, boolean fullyQualifyTypeNames, boolean includeReturnType) {
- return toCharArray(methodSignature, methodName, parameterNames, fullyQualifyTypeNames, includeReturnType, false);
-}
-
-/**
- * Converts the given method signature to a readable form. The method signature is expected to
- * be dot-based.
- *
- * For example:
- *
- *
- * toString("([Ljava.lang.String;)V", "main", new String[] {"args"}, false, true) -> "void main(String[] args)"
- *
- *
- *
- *
- * @param methodSignature the method signature to convert
- * @param methodName the name of the method to insert in the result, or
- * null if no method name is to be included
- * @param parameterNames the parameter names to insert in the result, or
- * null if no parameter names are to be included; if supplied,
- * the number of parameter names must match that of the method signature
- * @param fullyQualifyTypeNames true if type names should be fully
- * qualified, and false to use only simple names
- * @param includeReturnType true if the return type is to be
- * included
- * @param isVargArgs true if the last argument should be displayed as a
- * variable argument, false otherwise.
- * @return the char array representation of the method signature
- * @throws IllegalArgumentException if the method signature is syntactically incorrect
- *
- * @since 3.1
- */
-public static char[] toCharArray(char[] methodSignature, char[] methodName, char[][] parameterNames, boolean fullyQualifyTypeNames, boolean includeReturnType, boolean isVargArgs) {
- int firstParen = CharOperation.indexOf(C_PARAM_START, methodSignature);
- if (firstParen == -1) {
- throw new IllegalArgumentException();
- }
-
- StringBuffer buffer = new StringBuffer(methodSignature.length + 10);
-
- // return type
- if (includeReturnType) {
- char[] rts = getReturnType(methodSignature);
- appendTypeSignature(rts, 0 , fullyQualifyTypeNames, buffer);
- buffer.append(' ');
- }
-
- // selector
- if (methodName != null) {
- buffer.append(methodName);
- }
-
- // parameters
- buffer.append('(');
- char[][] pts = getParameterTypes(methodSignature);
- // search for the last array in the signature
- int max = pts.length;
- int index = max - 1;
- loop: for (int i = index; i >= 0; i--) {
- if (pts[i][0] == Signature.C_ARRAY) {
- break loop;
- }
- index--;
- }
- for (int i = 0; i < max; i++) {
- if (i == index) {
- appendTypeSignature(pts[i], 0 , fullyQualifyTypeNames, buffer, isVargArgs);
- } else {
- appendTypeSignature(pts[i], 0 , fullyQualifyTypeNames, buffer);
- }
- if (parameterNames != null) {
- buffer.append(' ');
- buffer.append(parameterNames[i]);
- }
- if (i != pts.length - 1) {
- buffer.append(',');
- buffer.append(' ');
- }
- }
- buffer.append(')');
- char[] result = new char[buffer.length()];
- buffer.getChars(0, buffer.length(), result, 0);
- return result;
-}
-
-/**
- * Converts the given array of qualified name segments to a qualified name.
- *
- * For example:
- *
- *
- * toQualifiedName({{'j', 'a', 'v', 'a'}, {'l', 'a', 'n', 'g'}, {'O', 'b', 'j', 'e', 'c', 't'}}) -> {'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'O', 'b', 'j', 'e', 'c', 't'}
- * toQualifiedName({{'O', 'b', 'j', 'e', 'c', 't'}}) -> {'O', 'b', 'j', 'e', 'c', 't'}
- * toQualifiedName({{}}) -> {}
- *
- *
- *
- *
- * @param segments the list of name segments, possibly empty
- * @return the dot-separated qualified name, or the empty string
- *
- * @since 2.0
- */
-public static char[] toQualifiedName(char[][] segments) {
- int length = segments.length;
- if (length == 0) return CharOperation.NO_CHAR;
- if (length == 1) return segments[0];
-
- int resultLength = 0;
- for (int i = 0; i < length; i++) {
- resultLength += segments[i].length+1;
- }
- resultLength--;
- char[] result = new char[resultLength];
- int index = 0;
- for (int i = 0; i < length; i++) {
- char[] segment = segments[i];
- int segmentLength = segment.length;
- System.arraycopy(segment, 0, result, index, segmentLength);
- index += segmentLength;
- if (i != length-1) {
- result[index++] = C_DOT;
- }
- }
- return result;
-}
-
-/**
- * Converts the given array of qualified name segments to a qualified name.
- *
- * For example:
- *
- *
- * toQualifiedName(new String[] {"java", "lang", "Object"}) -> "java.lang.Object"
- * toQualifiedName(new String[] {"Object"}) -> "Object"
- * toQualifiedName(new String[0]) -> ""
- *
- *
- *
- *
- * @param segments the list of name segments, possibly empty
- * @return the dot-separated qualified name, or the empty string
- */
-public static String toQualifiedName(String[] segments) {
- int length = segments.length;
- char[][] charArrays = new char[length][];
- for (int i = 0; i < length; i++) {
- charArrays[i] = segments[i].toCharArray();
- }
- return new String(toQualifiedName(charArrays));
-}
-/**
- * Converts the given type signature to a readable string. The signature is expected to
- * be dot-based.
- *
- *
- * For example:
- *
- *
- * toString("[Ljava.lang.String;") -> "java.lang.String[]"
- * toString("I") -> "int"
- * toString("+QObject;") -> "? extends Object"
- *
- *
- *
- *
- * Note: This method assumes that a type signature containing a '$'
- * is an inner type signature. While this is correct in most cases, someone could
- * define a non-inner type name containing a '$'. Handling this
- * correctly in all cases would have required resolving the signature, which
- * generally not feasible.
- *
- *
- * @param signature the type signature
- * @return the string representation of the type
- * @exception IllegalArgumentException if the signature is not syntactically
- * correct
- */
-public static String toString(String signature) throws IllegalArgumentException {
- return new String(toCharArray(signature.toCharArray()));
-}
-/**
- * Converts the given method signature to a readable string. The method signature is expected to
- * be dot-based.
- *
- * @param methodSignature the method signature to convert
- * @param methodName the name of the method to insert in the result, or
- * null if no method name is to be included
- * @param parameterNames the parameter names to insert in the result, or
- * null if no parameter names are to be included; if supplied,
- * the number of parameter names must match that of the method signature
- * @param fullyQualifyTypeNames true if type names should be fully
- * qualified, and false to use only simple names
- * @param includeReturnType true if the return type is to be
- * included
- * @see #toCharArray(char[], char[], char[][], boolean, boolean)
- * @return the string representation of the method signature
- */
-public static String toString(String methodSignature, String methodName, String[] parameterNames, boolean fullyQualifyTypeNames, boolean includeReturnType) {
- return toString(methodSignature, methodName, parameterNames, fullyQualifyTypeNames, includeReturnType, false);
-}
-/**
- * Converts the given method signature to a readable string. The method signature is expected to
- * be dot-based.
- *
- * @param methodSignature the method signature to convert
- * @param methodName the name of the method to insert in the result, or
- * null if no method name is to be included
- * @param parameterNames the parameter names to insert in the result, or
- * null if no parameter names are to be included; if supplied,
- * the number of parameter names must match that of the method signature
- * @param fullyQualifyTypeNames true if type names should be fully
- * qualified, and false to use only simple names
- * @param includeReturnType true if the return type is to be
- * included
- * @param isVarArgs true if the last argument should be displayed as a
- * variable argument, false otherwise
- * @see #toCharArray(char[], char[], char[][], boolean, boolean)
- * @return the string representation of the method signature
- *
- * @since 3.1
- */
-public static String toString(String methodSignature, String methodName, String[] parameterNames, boolean fullyQualifyTypeNames, boolean includeReturnType, boolean isVarArgs) {
- char[][] params;
- if (parameterNames == null) {
- params = null;
- } else {
- int paramLength = parameterNames.length;
- params = new char[paramLength][];
- for (int i = 0; i < paramLength; i++) {
- params[i] = parameterNames[i].toCharArray();
- }
- }
- return new String(toCharArray(methodSignature.toCharArray(), methodName == null ? null : methodName.toCharArray(), params, fullyQualifyTypeNames, includeReturnType, isVarArgs));
-}
-private Signature() {
- // Not instantiable
-}
-}
diff --git a/app/src/main/java/org/eclipse/jdt/internal/compiler/tool/EclipseCompiler.java b/app/src/main/java/org/eclipse/jdt/internal/compiler/tool/EclipseCompiler.java
deleted file mode 100644
index 732695f62..000000000
--- a/app/src/main/java/org/eclipse/jdt/internal/compiler/tool/EclipseCompiler.java
+++ /dev/null
@@ -1,253 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2006, 2015 IBM Corporation and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * IBM Corporation - initial API and implementation
- *******************************************************************************/
-package org.eclipse.jdt.internal.compiler.tool;
-
-import com.itsaky.androidide.services.compiler.CompilerProgress;
-import java.io.File;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.io.Writer;
-import java.net.URI;
-import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.Iterator;
-import java.util.Locale;
-import java.util.Set;
-import java.util.WeakHashMap;
-import javax.annotation.processing.Processor;
-import javax.lang.model.SourceVersion;
-import javax.tools.DiagnosticListener;
-import javax.tools.JavaCompiler;
-import javax.tools.JavaFileManager;
-import javax.tools.JavaFileObject;
-import javax.tools.StandardJavaFileManager;
-import javax.tools.StandardLocation;
-import org.eclipse.jdt.internal.compiler.batch.CompilationUnit;
-import org.eclipse.jdt.internal.compiler.batch.Main;
-import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
-
-/**
- * Implementation of a batch compiler that supports the jsr199
- */
-public class EclipseCompiler implements JavaCompiler {
-
- private static Set SupportedSourceVersions;
-
- static {
- // Eclipse compiler supports all possible versions from version 0 to
- // version 6
- // we don't care about the order
- EnumSet enumSet = EnumSet.range(SourceVersion.RELEASE_0, SourceVersion.RELEASE_6);
- // we don't want anybody to modify this list
- EclipseCompiler.SupportedSourceVersions = Collections.unmodifiableSet(enumSet);
- }
-
- WeakHashMap threadCache;
- public DiagnosticListener super JavaFileObject> diagnosticListener;
-
- private CompilerProgress progress;
-
- public EclipseCompiler() {
- this.threadCache = new WeakHashMap<>();
- }
-
- public EclipseCompiler setCompilationCancelled() {
- return setCompilationCancelled(true);
- }
-
- public EclipseCompiler setCompilationCancelled(boolean p0) {
- if(this.progress != null)
- this.progress.setCanceled(p0);
- return this;
- }
-
- public EclipseCompiler newProgress() {
- this.progress = new CompilerProgress();
- return this;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see javax.tools.Tool#getSourceVersions()
- */
- @Override
- public Set getSourceVersions() {
- return EclipseCompiler.SupportedSourceVersions;
- }
- /*
- * (non-Javadoc)
- *
- * @see javax.tools.JavaCompiler#getStandardFileManager(javax.tools.DiagnosticListener,
- * java.util.Locale, java.nio.charset.Charset)
- */
- @Override
- public StandardJavaFileManager getStandardFileManager(DiagnosticListener super JavaFileObject> someDiagnosticListener, Locale locale, Charset charset) {
- this.diagnosticListener = someDiagnosticListener;
- return new EclipseFileManager(locale, charset);
- }
- /*
- * (non-Javadoc)
- *
- * @see javax.tools.JavaCompiler#getTask(java.io.Writer,
- * javax.tools.JavaFileManager, javax.tools.DiagnosticListener,
- * java.lang.Iterable, java.lang.Iterable, java.lang.Iterable)
- */
- @Override
- public CompilationTask getTask(Writer out, JavaFileManager fileManager, DiagnosticListener super JavaFileObject> someDiagnosticListener, Iterable options, Iterable classes, Iterable extends JavaFileObject> compilationUnits) {
- PrintWriter writerOut = null;
- PrintWriter writerErr = null;
- if (out == null) {
- writerOut = new PrintWriter(System.err);
- writerErr = new PrintWriter(System.err);
- } else {
- writerOut = new PrintWriter(out);
- writerErr = new PrintWriter(out);
- }
- final Thread currentThread = Thread.currentThread();
- EclipseCompilerImpl eclipseCompiler = this.threadCache.get(currentThread);
- if (eclipseCompiler == null) {
- eclipseCompiler = new EclipseCompilerImpl(writerOut, writerErr, false);
- this.threadCache.put(currentThread, eclipseCompiler);
- } else {
- eclipseCompiler.initialize(writerOut, writerErr, false, null/*options*/, null/*progress*/);
- }
- final EclipseCompilerImpl eclipseCompiler2 = new EclipseCompilerImpl(writerOut, writerErr, false);
- eclipseCompiler2.compilationUnits = compilationUnits;
- eclipseCompiler2.diagnosticListener = someDiagnosticListener;
- eclipseCompiler2.progress = eclipseCompiler.progress = this.progress;
- if (fileManager != null) {
- eclipseCompiler2.fileManager = fileManager;
- } else {
- eclipseCompiler2.fileManager = this.getStandardFileManager(someDiagnosticListener, null, null);
- }
-
- eclipseCompiler2.options.put(CompilerOptions.OPTION_Compliance, CompilerOptions.VERSION_1_6);
- eclipseCompiler2.options.put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_1_6);
- eclipseCompiler2.options.put(CompilerOptions.OPTION_TargetPlatform, CompilerOptions.VERSION_1_6);
-
- ArrayList allOptions = new ArrayList<>();
- if (options != null) {
- for (Iterator iterator = options.iterator(); iterator.hasNext(); ) {
- eclipseCompiler2.fileManager.handleOption(iterator.next(), iterator);
- }
- for (String option : options) {
- allOptions.add(option);
- }
- }
-
- if (compilationUnits != null) {
- for (JavaFileObject javaFileObject : compilationUnits) {
- // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6419926
- // compells us to check that the returned URIs are absolute,
- // which they happen not to be for the default compiler on some
- // unices
- URI uri = javaFileObject.toUri();
- if (!uri.isAbsolute()) {
- uri = URI.create("file://" + uri.toString()); //$NON-NLS-1$
- }
- if (uri.getScheme().equals("file")) { //$NON-NLS-1$
- allOptions.add(new File(uri).getAbsolutePath());
- } else {
- allOptions.add(uri.toString());
- }
- }
- }
-
- if (classes != null) {
- allOptions.add("-classNames"); //$NON-NLS-1$
- StringBuilder builder = new StringBuilder();
- int i = 0;
- for (String className : classes) {
- if (i != 0) {
- builder.append(',');
- }
- builder.append(className);
- i++;
- }
- allOptions.add(String.valueOf(builder));
- }
-
- final String[] optionsToProcess = new String[allOptions.size()];
- allOptions.toArray(optionsToProcess);
- try {
- eclipseCompiler2.configure(optionsToProcess);
- } catch (IllegalArgumentException e) {
- throw e;
- }
-
- if (eclipseCompiler2.fileManager instanceof StandardJavaFileManager) {
- StandardJavaFileManager javaFileManager = (StandardJavaFileManager) eclipseCompiler2.fileManager;
- Iterable extends File> location = javaFileManager.getLocation(StandardLocation.CLASS_OUTPUT);
- if (location != null) {
- eclipseCompiler2.setDestinationPath(location.iterator().next().getAbsolutePath());
- }
- }
-
- return new CompilationTask() {
- private boolean hasRun = false;
- @Override
- public Boolean call() {
- // set up compiler with passed options
- if (this.hasRun) {
- throw new IllegalStateException("This task has already been run"); //$NON-NLS-1$
- }
- Boolean value = eclipseCompiler2.call() ? Boolean.TRUE : Boolean.FALSE;
- this.hasRun = true;
- return value;
- }
- @Override
- public void setLocale(Locale locale) {
- eclipseCompiler2.setLocale(locale);
- }
- @Override
- public void setProcessors(Iterable extends Processor> processors) {
- ArrayList temp = new ArrayList<>();
- for (Processor processor : processors) {
- temp.add(processor);
- }
- Processor[] processors2 = new Processor[temp.size()];
- temp.toArray(processors2);
- eclipseCompiler2.processors = processors2;
- }
- };
- }
- /*
- * (non-Javadoc)
- *
- * @see javax.tools.OptionChecker#isSupportedOption(java.lang.String)
- */
- @Override
- public int isSupportedOption(String option) {
- return Options.processOptions(option);
- }
-
- /*
- * (non-Javadoc)
- *
- * @see javax.tools.Tool#run(java.io.InputStream, java.io.OutputStream,
- * java.io.OutputStream, java.lang.String[])
- */
- @Override
- public int run(InputStream in, OutputStream out, OutputStream err, String... arguments) {
- boolean succeed = new Main(
- new PrintWriter(new OutputStreamWriter(out != null ? out : System.out)),
- new PrintWriter(new OutputStreamWriter(err != null ? err : System.err)),
- true/* systemExit */,
- null/* options */,
- null/* progress */).compile(arguments);
- return succeed ? 0 : -1;
- }
-}
diff --git a/app/src/main/java/org/eclipse/jdt/internal/compiler/tool/EclipseCompilerImpl.java b/app/src/main/java/org/eclipse/jdt/internal/compiler/tool/EclipseCompilerImpl.java
deleted file mode 100644
index d0f7e40a7..000000000
--- a/app/src/main/java/org/eclipse/jdt/internal/compiler/tool/EclipseCompilerImpl.java
+++ /dev/null
@@ -1,594 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2007, 2015 IBM Corporation and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * IBM Corporation - initial API and implementation
- * IBM Corporation - fix for 342936
- * Kenneth Olson - Contribution for bug 188796 - [jsr199] Using JSR199 to extend ECJ
- * Dennis Hendriks - Contribution for bug 188796 - [jsr199] Using JSR199 to extend ECJ
- *******************************************************************************/
-package org.eclipse.jdt.internal.compiler.tool;
-
-import com.blankj.utilcode.util.ThrowableUtils;
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Locale;
-import java.util.Map;
-import javax.annotation.processing.Processor;
-import javax.tools.Diagnostic;
-import javax.tools.DiagnosticListener;
-import javax.tools.JavaFileManager;
-import javax.tools.JavaFileObject;
-import javax.tools.StandardJavaFileManager;
-import javax.tools.StandardLocation;
-import org.eclipse.jdt.core.compiler.CategorizedProblem;
-import org.eclipse.jdt.core.compiler.CharOperation;
-import org.eclipse.jdt.core.compiler.CompilationProgress;
-import org.eclipse.jdt.internal.compiler.ClassFile;
-import org.eclipse.jdt.internal.compiler.CompilationResult;
-import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy;
-import org.eclipse.jdt.internal.compiler.IProblemFactory;
-import org.eclipse.jdt.internal.compiler.batch.ClasspathJsr199;
-import org.eclipse.jdt.internal.compiler.batch.CompilationUnit;
-import org.eclipse.jdt.internal.compiler.batch.FileSystem;
-import org.eclipse.jdt.internal.compiler.batch.FileSystem.Classpath;
-import org.eclipse.jdt.internal.compiler.batch.Main;
-import org.eclipse.jdt.internal.compiler.problem.AbortCompilationUnit;
-import org.eclipse.jdt.internal.compiler.problem.DefaultProblem;
-import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
-import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
-import org.eclipse.jdt.internal.compiler.util.Messages;
-import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
-
-public class EclipseCompilerImpl extends Main {
- private static final CompilationUnit[] NO_UNITS = new CompilationUnit[0];
- private HashMap javaFileObjectMap;
- Iterable extends JavaFileObject> compilationUnits;
- public JavaFileManager fileManager;
- protected Processor[] processors;
- public DiagnosticListener super JavaFileObject> diagnosticListener;
-
- public EclipseCompilerImpl(PrintWriter out, PrintWriter err, boolean systemExitWhenFinished) {
- super(out, err, systemExitWhenFinished, null/*options*/, null/*progress*/);
- }
-
- public boolean call() {
- try {
- if(proceed) {
- this.globalProblemsCount = 0;
- this.globalErrorsCount = 0;
- this.globalWarningsCount = 0;
- this.globalTasksCount = 0;
- this.exportedClassFilesCounter = 0;
- // request compilation
- performCompilation();
- }
- } catch(IllegalArgumentException e) {
- this.logger.logException(e);
- com.itsaky.androidide.utils.Logger.instance().e("[EclipseCompilerImpl]", ThrowableUtils.getFullStackTrace(e));
- if (this.systemExitWhenFinished) {
- cleanup();
- System.exit(-1);
- }
- return false;
- } catch (RuntimeException e) { // internal compiler failure
- com.itsaky.androidide.utils.Logger.instance().e("[EclipseCompilerImpl]", ThrowableUtils.getFullStackTrace(e));
- this.logger.logException(e);
- return false;
- } finally {
- cleanup();
- }
- if (this.globalErrorsCount == 0)
- return true;
- return false;
- }
-
- private void cleanup() {
- this.logger.flush();
- this.logger.close();
- this.processors = null;
- try {
- if (this.fileManager != null) {
- this.fileManager.flush();
- }
- } catch (IOException e) {
- // ignore
- }
- }
-
- @Override
- public CompilationUnit[] getCompilationUnits() {
- if (this.compilationUnits == null) return EclipseCompilerImpl.NO_UNITS;
- ArrayList units = new ArrayList<>();
- for (final JavaFileObject javaFileObject : this.compilationUnits) {
- if (javaFileObject.getKind() != JavaFileObject.Kind.SOURCE) {
- throw new IllegalArgumentException();
- }
- String name = javaFileObject.getName();
- name = name.replace('\\', '/');
- CompilationUnit compilationUnit = new CompilationUnit(null,
- name,
- null) {
-
- @Override
- public char[] getContents() {
- try {
- return javaFileObject.getCharContent(true).toString().toCharArray();
- } catch(IOException e) {
- e.printStackTrace();
- throw new AbortCompilationUnit(null, e, null);
- }
- }
- };
- units.add(compilationUnit);
- this.javaFileObjectMap.put(compilationUnit, javaFileObject);
- }
- CompilationUnit[] result = new CompilationUnit[units.size()];
- units.toArray(result);
- return result;
- }
- /*
- * Low-level API performing the actual compilation
- */
- @Override
- public IErrorHandlingPolicy getHandlingPolicy() {
- // passes the initial set of files to the batch oracle (to avoid finding more than once the same units when case insensitive match)
- return new IErrorHandlingPolicy() {
- @Override
- public boolean proceedOnErrors() {
- return false; // stop if there are some errors
- }
- @Override
- public boolean stopOnFirstError() {
- return false;
- }
- @Override
- public boolean ignoreAllErrors() {
- return false;
- }
- };
- }
-
- @Override
- public IProblemFactory getProblemFactory() {
- return new DefaultProblemFactory() {
- @Override
- public CategorizedProblem createProblem(
- final char[] originatingFileName,
- final int problemId,
- final String[] problemArguments,
- final String[] messageArguments,
- final int severity,
- final int startPosition,
- final int endPosition,
- final int lineNumber,
- final int columnNumber) {
-
- DiagnosticListener super JavaFileObject> diagListener = EclipseCompilerImpl.this.diagnosticListener;
- if (diagListener != null) {
- diagListener.report(new Diagnostic() {
- @Override
- public String getCode() {
- return Integer.toString(problemId);
- }
- @Override
- public long getColumnNumber() {
- return columnNumber;
- }
- @Override
- public long getEndPosition() {
- return endPosition;
- }
- @Override
- public Kind getKind() {
- if ((severity & ProblemSeverities.Error) != 0) {
- return Diagnostic.Kind.ERROR;
- }
- if ((severity & ProblemSeverities.Optional) != 0) {
- return Diagnostic.Kind.WARNING;
- }
- if ((severity & ProblemSeverities.Warning) != 0) {
- return Diagnostic.Kind.MANDATORY_WARNING;
- }
- return Diagnostic.Kind.OTHER;
- }
- @Override
- public long getLineNumber() {
- return lineNumber;
- }
- @Override
- public String getMessage(Locale locale) {
- if (locale != null) {
- setLocale(locale);
- }
- return getLocalizedMessage(problemId, problemArguments);
- }
- @Override
- public long getPosition() {
- return startPosition;
- }
- @Override
- public JavaFileObject getSource() {
- File f = new File(new String(originatingFileName));
- if (f.exists()) {
- return new EclipseFileObject(null, f.toURI(), JavaFileObject.Kind.SOURCE, null);
- }
- return null;
- }
- @Override
- public long getStartPosition() {
- return startPosition;
- }
- });
- }
- return super.createProblem(originatingFileName, problemId, problemArguments, messageArguments, severity, startPosition, endPosition, lineNumber, columnNumber);
- }
- @Override
- public CategorizedProblem createProblem(
- final char[] originatingFileName,
- final int problemId,
- final String[] problemArguments,
- final int elaborationID,
- final String[] messageArguments,
- final int severity,
- final int startPosition,
- final int endPosition,
- final int lineNumber,
- final int columnNumber) {
-
- DiagnosticListener super JavaFileObject> diagListener = EclipseCompilerImpl.this.diagnosticListener;
- if (diagListener != null) {
- diagListener.report(new Diagnostic() {
- @Override
- public String getCode() {
- return Integer.toString(problemId);
- }
- @Override
- public long getColumnNumber() {
- return columnNumber;
- }
- @Override
- public long getEndPosition() {
- return endPosition;
- }
- @Override
- public Kind getKind() {
- if ((severity & ProblemSeverities.Error) != 0) {
- return Diagnostic.Kind.ERROR;
- }
- if ((severity & ProblemSeverities.Info) != 0) {
- return Diagnostic.Kind.NOTE;
- }
- if ((severity & ProblemSeverities.Optional) != 0) {
- return Diagnostic.Kind.WARNING;
- }
- if ((severity & ProblemSeverities.Warning) != 0) {
- return Diagnostic.Kind.MANDATORY_WARNING;
- }
- return Diagnostic.Kind.OTHER;
- }
- @Override
- public long getLineNumber() {
- return lineNumber;
- }
- @Override
- public String getMessage(Locale locale) {
- if (locale != null) {
- setLocale(locale);
- }
- return getLocalizedMessage(problemId, problemArguments);
- }
- @Override
- public long getPosition() {
- return startPosition;
- }
- @Override
- public JavaFileObject getSource() {
- File f = new File(new String(originatingFileName));
- if (f.exists()) {
- return new EclipseFileObject(null, f.toURI(), JavaFileObject.Kind.SOURCE, null);
- }
- return null;
- }
- @Override
- public long getStartPosition() {
- return startPosition;
- }
- });
- }
- return super.createProblem(originatingFileName, problemId, problemArguments, elaborationID, messageArguments, severity, startPosition, endPosition, lineNumber, columnNumber);
- }
- };
- }
-
- @Override
- protected void initialize(PrintWriter outWriter, PrintWriter errWriter, boolean systemExit, Map customDefaultOptions, CompilationProgress compilationProgress) {
- super.initialize(outWriter, errWriter, systemExit, customDefaultOptions, compilationProgress);
- this.javaFileObjectMap = new HashMap<>();
- }
-
- @Override
- protected void initializeAnnotationProcessorManager() {
- super.initializeAnnotationProcessorManager();
- if (this.batchCompiler.annotationProcessorManager != null && this.processors != null) {
- this.batchCompiler.annotationProcessorManager.setProcessors(this.processors);
- } else if (this.processors != null) {
- throw new UnsupportedOperationException("Cannot handle annotation processing"); //$NON-NLS-1$
- }
- }
-
- // Dump classfiles onto disk for all compilation units that where successful
- // and do not carry a -d none spec, either directly or inherited from Main.
- @Override
- public void outputClassFiles(CompilationResult unitResult) {
- if (!((unitResult == null) || (unitResult.hasErrors() && !this.proceedOnError))) {
- ClassFile[] classFiles = unitResult.getClassFiles();
- boolean generateClasspathStructure = this.fileManager.hasLocation(StandardLocation.CLASS_OUTPUT);
- String currentDestinationPath = this.destinationPath;
- File outputLocation = null;
- if (currentDestinationPath != null) {
- outputLocation = new File(currentDestinationPath);
- outputLocation.mkdirs();
- }
- for (int i = 0, fileCount = classFiles.length; i < fileCount; i++) {
- // retrieve the key and the corresponding classfile
- ClassFile classFile = classFiles[i];
- char[] filename = classFile.fileName();
- int length = filename.length;
- char[] relativeName = new char[length + 6];
- System.arraycopy(filename, 0, relativeName, 0, length);
- System.arraycopy(SuffixConstants.SUFFIX_class, 0, relativeName, length, 6);
- CharOperation.replace(relativeName, '/', File.separatorChar);
- String relativeStringName = new String(relativeName);
- if (this.compilerOptions.verbose) {
- EclipseCompilerImpl.this.out.println(
- Messages.bind(
- Messages.compilation_write,
- new String[] {
- String.valueOf(this.exportedClassFilesCounter+1),
- relativeStringName
- }));
- }
- try {
- JavaFileObject javaFileForOutput =
- this.fileManager.getJavaFileForOutput(
- StandardLocation.CLASS_OUTPUT,
- new String(filename),
- JavaFileObject.Kind.CLASS,
- this.javaFileObjectMap.get(unitResult.compilationUnit));
-
- if (generateClasspathStructure) {
- if (currentDestinationPath != null) {
- int index = CharOperation.lastIndexOf(File.separatorChar, relativeName);
- if (index != -1) {
- File currentFolder = new File(currentDestinationPath, relativeStringName.substring(0, index));
- currentFolder.mkdirs();
- }
- } else {
- // create the subfolfers is necessary
- // need a way to retrieve the folders to create
- String path = javaFileForOutput.toUri().getPath();
- int index = path.lastIndexOf('/');
- if (index != -1) {
- File file = new File(path.substring(0, index));
- file.mkdirs();
- }
- }
- }
-
- try (OutputStream openOutputStream = javaFileForOutput.openOutputStream(); BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(openOutputStream)) {
- bufferedOutputStream.write(classFile.header, 0, classFile.headerOffset);
- bufferedOutputStream.write(classFile.contents, 0, classFile.contentsOffset);
- bufferedOutputStream.flush();
- }
- } catch (IOException e) {
- this.logger.logNoClassFileCreated(currentDestinationPath, relativeStringName, e);
- }
- this.logger.logClassFile(
- generateClasspathStructure,
- currentDestinationPath,
- relativeStringName);
- this.exportedClassFilesCounter++;
- }
- this.batchCompiler.lookupEnvironment.releaseClassFiles(classFiles);
- }
- }
-
- @Override
- protected void setPaths(ArrayList bootclasspaths,
- String sourcepathClasspathArg,
- ArrayList sourcepathClasspaths,
- ArrayList classpaths,
- ArrayList extdirsClasspaths,
- ArrayList endorsedDirClasspaths,
- String customEncoding) {
-
- ArrayList fileSystemClasspaths = new ArrayList<>();
- EclipseFileManager eclipseJavaFileManager = null;
- StandardJavaFileManager standardJavaFileManager = null;
- JavaFileManager javaFileManager = null;
- boolean havePlatformPaths = false;
- boolean haveClassPaths = false;
- if (this.fileManager instanceof EclipseFileManager) {
- eclipseJavaFileManager = (EclipseFileManager) this.fileManager;
- }
- if (this.fileManager instanceof StandardJavaFileManager) {
- standardJavaFileManager = (StandardJavaFileManager) this.fileManager;
- }
- javaFileManager = this.fileManager;
-
- if (eclipseJavaFileManager != null) {
- if ((eclipseJavaFileManager.flags & EclipseFileManager.HAS_ENDORSED_DIRS) == 0
- && (eclipseJavaFileManager.flags & EclipseFileManager.HAS_BOOTCLASSPATH) != 0) {
- fileSystemClasspaths.addAll(this.handleEndorseddirs(null));
- }
- }
- Iterable extends File> location = null;
- if (standardJavaFileManager != null) {
- location = standardJavaFileManager.getLocation(StandardLocation.PLATFORM_CLASS_PATH);
- if (location != null) {
- for (File file : location) {
- Classpath classpath = FileSystem.getClasspath(
- file.getAbsolutePath(),
- null,
- null, this.options);
- if (classpath != null) {
- fileSystemClasspaths.add(classpath);
- havePlatformPaths = true;
- }
- }
- }
- } else if (javaFileManager != null) {
- Classpath classpath = new ClasspathJsr199(this.fileManager, StandardLocation.PLATFORM_CLASS_PATH);
- fileSystemClasspaths.add(classpath);
- havePlatformPaths = true;
- }
- if (eclipseJavaFileManager != null) {
- if ((eclipseJavaFileManager.flags & EclipseFileManager.HAS_EXT_DIRS) == 0
- && (eclipseJavaFileManager.flags & EclipseFileManager.HAS_BOOTCLASSPATH) != 0) {
- fileSystemClasspaths.addAll(this.handleExtdirs(null));
- }
- }
- if (standardJavaFileManager != null) {
- location = standardJavaFileManager.getLocation(StandardLocation.SOURCE_PATH);
- if (location != null) {
- for (File file : location) {
- Classpath classpath = FileSystem.getClasspath(
- file.getAbsolutePath(),
- null,
- null, this.options);
- if (classpath != null) {
- fileSystemClasspaths.add(classpath);
- }
- }
- }
- location = standardJavaFileManager.getLocation(StandardLocation.CLASS_PATH);
- if (location != null) {
- for (File file : location) {
- Classpath classpath = FileSystem.getClasspath(
- file.getAbsolutePath(),
- null,
- null, this.options);
- if (classpath != null) {
- fileSystemClasspaths.add(classpath);
- haveClassPaths = true;
- }
- }
- }
- } else if (javaFileManager != null) {
- Classpath classpath = null;
- if (this.fileManager.hasLocation(StandardLocation.SOURCE_PATH)) {
- classpath = new ClasspathJsr199(this.fileManager, StandardLocation.SOURCE_PATH);
- fileSystemClasspaths.add(classpath);
- }
- classpath = new ClasspathJsr199(this.fileManager, StandardLocation.CLASS_PATH);
- fileSystemClasspaths.add(classpath);
- haveClassPaths = true;
- }
- if (this.checkedClasspaths == null) {
- // It appears to be necessary to handleBootclasspath() for IBM JVMs
- // in order to have visibility to java.lang.String (not present in rt.jar).
- // The jars returned by StandardFileManager.getLocation(PLATFORM_CLASS_PATH) are
- // not sufficient to resolve all standard classes.
- if (!havePlatformPaths) fileSystemClasspaths.addAll(this.handleBootclasspath(null, null));
- if (!haveClassPaths) fileSystemClasspaths.addAll(this.handleClasspath(null, null));
- }
- fileSystemClasspaths = FileSystem.ClasspathNormalizer.normalize(fileSystemClasspaths);
- final int size = fileSystemClasspaths.size();
- if (size != 0) {
- this.checkedClasspaths = new FileSystem.Classpath[size];
- int i = 0;
- for (FileSystem.Classpath classpath : fileSystemClasspaths) {
- this.checkedClasspaths[i++] = classpath;
- }
- }
- }
- @Override
- protected void loggingExtraProblems() {
- super.loggingExtraProblems();
- for (@SuppressWarnings("rawtypes")
- Iterator iterator = this.extraProblems.iterator(); iterator.hasNext(); ) {
- final CategorizedProblem problem = (CategorizedProblem) iterator.next();
- if (this.diagnosticListener != null) {
- this.diagnosticListener.report(new Diagnostic() {
- @Override
- public String getCode() {
- return null;
- }
- @Override
- public long getColumnNumber() {
- if (problem instanceof DefaultProblem) {
- return ((DefaultProblem) problem).column;
- }
- return Diagnostic.NOPOS;
- }
- @Override
- public long getEndPosition() {
- if (problem instanceof DefaultProblem) {
- return ((DefaultProblem) problem).getSourceEnd();
- }
- return Diagnostic.NOPOS;
- }
- @Override
- public Kind getKind() {
- if (problem.isError()) {
- return Diagnostic.Kind.ERROR;
- }
- if (problem.isWarning()) {
- return Diagnostic.Kind.WARNING;
- } else if (problem instanceof DefaultProblem && ((DefaultProblem) problem).isInfo()) {
- return Diagnostic.Kind.NOTE;
- }
- return Diagnostic.Kind.OTHER;
- }
- @Override
- public long getLineNumber() {
- if (problem instanceof DefaultProblem) {
- return ((DefaultProblem) problem).getSourceLineNumber();
- }
- return Diagnostic.NOPOS;
- }
- @Override
- public String getMessage(Locale locale) {
- return problem.getMessage();
- }
- @Override
- public long getPosition() {
- if (problem instanceof DefaultProblem) {
- return ((DefaultProblem) problem).getSourceStart();
- }
- return Diagnostic.NOPOS;
- }
- @Override
- public JavaFileObject getSource() {
- if (problem instanceof DefaultProblem) {
- File f = new File(new String(((DefaultProblem) problem).getOriginatingFileName()));
- if (f.exists()) {
- Charset charset = (EclipseCompilerImpl.this.fileManager instanceof EclipseFileManager) ?
- ((EclipseFileManager) EclipseCompilerImpl.this.fileManager).charset : Charset.defaultCharset();
- return new EclipseFileObject(null, f.toURI(), JavaFileObject.Kind.SOURCE, charset);
- }
- return null;
- }
- return null;
- }
- @Override
- public long getStartPosition() {
- return getPosition();
- }
- });
- }
- }
- }
-}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 4e5188394..72ea536db 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,6 +1,6 @@
Diagnostics
- Line: %s, Column: %s
+ Start: %s, %s End: %s, %s
ERROR
WARNING
NOTE
diff --git a/lsp/.gitignore b/lsp/.gitignore
new file mode 100644
index 000000000..e08d3d510
--- /dev/null
+++ b/lsp/.gitignore
@@ -0,0 +1,26 @@
+# Built application files
+*.apk
+*.ap_
+
+# Files for the dex VM
+*.dex
+
+# Java class files
+*.class
+
+# Generated files
+bin/
+gen/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# IntelliJ IDEA
+.idea/
+*.iml
+*.iws
+*.ipr
+
+# Gradle
+.gradle
+build/
\ No newline at end of file
diff --git a/lsp/build.gradle b/lsp/build.gradle
new file mode 100644
index 000000000..9aba56a5b
--- /dev/null
+++ b/lsp/build.gradle
@@ -0,0 +1,23 @@
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion rootProject.ext.compileSdk
+ buildToolsVersion rootProject.ext.buildTools
+
+ defaultConfig {
+ minSdkVersion rootProject.ext.minSdk
+ targetSdkVersion rootProject.ext.targetSdk
+ versionCode rootProject.ext.versionCode
+ versionName rootProject.ext.versionName
+ }
+ buildTypes {
+ release {
+ minifyEnabled true
+ }
+ }
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation 'com.google.code.gson:gson:2.8.5'
+}
diff --git a/lsp/gradle.properties b/lsp/gradle.properties
new file mode 100644
index 000000000..18ff56d7a
--- /dev/null
+++ b/lsp/gradle.properties
@@ -0,0 +1,22 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx10248m -XX:MaxPermSize=256m
+# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+
+POM_NAME=AndroidTreeView
+POM_ARTIFACT_ID=atv
+POM_PACKAGING=aar
\ No newline at end of file
diff --git a/lsp/proguard-rules.pro b/lsp/proguard-rules.pro
new file mode 100644
index 000000000..0448f7080
--- /dev/null
+++ b/lsp/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/bogdan/Library/Android/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/lsp/src/main/AndroidManifest.xml b/lsp/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..886f5b021
--- /dev/null
+++ b/lsp/src/main/AndroidManifest.xml
@@ -0,0 +1 @@
+
diff --git a/lsp/src/main/java/com/itsaky/lsp/CancelParams.java b/lsp/src/main/java/com/itsaky/lsp/CancelParams.java
new file mode 100644
index 000000000..82e6cb047
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/CancelParams.java
@@ -0,0 +1,5 @@
+package com.itsaky.lsp;
+
+public class CancelParams {
+ public int id;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/CodeAction.java b/lsp/src/main/java/com/itsaky/lsp/CodeAction.java
new file mode 100644
index 000000000..b4e896a54
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/CodeAction.java
@@ -0,0 +1,11 @@
+package com.itsaky.lsp;
+
+import java.util.List;
+
+public class CodeAction {
+ public String title, kind;
+ public List diagnostics;
+ public WorkspaceEdit edit;
+ public Command command;
+ public static CodeAction NONE;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/CodeActionContext.java b/lsp/src/main/java/com/itsaky/lsp/CodeActionContext.java
new file mode 100644
index 000000000..074122502
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/CodeActionContext.java
@@ -0,0 +1,9 @@
+package com.itsaky.lsp;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CodeActionContext {
+ public List diagnostics = new ArrayList<>();
+ public List only;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/CodeActionKind.java b/lsp/src/main/java/com/itsaky/lsp/CodeActionKind.java
new file mode 100644
index 000000000..328390172
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/CodeActionKind.java
@@ -0,0 +1,11 @@
+package com.itsaky.lsp;
+
+public class CodeActionKind {
+ public static final String QuickFix = "quickfix",
+ Refactor = "refactor",
+ RefactorExtract = "refactor.extract",
+ RefactorInline = "refactor.inline",
+ RefactorRewrite = "refactor.rewrite",
+ Source = "source",
+ SourceOrganizeImports = "source.organizeImports";
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/CodeActionParams.java b/lsp/src/main/java/com/itsaky/lsp/CodeActionParams.java
new file mode 100644
index 000000000..a03e8ebb2
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/CodeActionParams.java
@@ -0,0 +1,7 @@
+package com.itsaky.lsp;
+
+public class CodeActionParams {
+ public TextDocumentIdentifier textDocument;
+ public Range range;
+ public CodeActionContext context = new CodeActionContext();
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/CodeLens.java b/lsp/src/main/java/com/itsaky/lsp/CodeLens.java
new file mode 100644
index 000000000..88b8e83fa
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/CodeLens.java
@@ -0,0 +1,17 @@
+package com.itsaky.lsp;
+
+import com.google.gson.JsonElement;
+
+public class CodeLens {
+ public Range range;
+ public Command command;
+ public JsonElement data;
+
+ public CodeLens() {}
+
+ public CodeLens(Range range, Command command, JsonElement data) {
+ this.range = range;
+ this.command = command;
+ this.data = data;
+ }
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/CodeLensParams.java b/lsp/src/main/java/com/itsaky/lsp/CodeLensParams.java
new file mode 100644
index 000000000..dc9644daf
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/CodeLensParams.java
@@ -0,0 +1,11 @@
+package com.itsaky.lsp;
+
+public class CodeLensParams {
+ public TextDocumentIdentifier textDocument;
+
+ public CodeLensParams() {}
+
+ public CodeLensParams(TextDocumentIdentifier textDocument) {
+ this.textDocument = textDocument;
+ }
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/Command.java b/lsp/src/main/java/com/itsaky/lsp/Command.java
new file mode 100644
index 000000000..bd86072ef
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/Command.java
@@ -0,0 +1,16 @@
+package com.itsaky.lsp;
+
+import com.google.gson.JsonArray;
+
+public class Command {
+ public String title, command;
+ public JsonArray arguments;
+
+ public Command() {}
+
+ public Command(String title, String command, JsonArray arguments) {
+ this.title = title;
+ this.command = command;
+ this.arguments = arguments;
+ }
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/CompletionItem.java b/lsp/src/main/java/com/itsaky/lsp/CompletionItem.java
new file mode 100644
index 000000000..fe2327b41
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/CompletionItem.java
@@ -0,0 +1,19 @@
+package com.itsaky.lsp;
+
+import com.google.gson.JsonElement;
+import java.util.List;
+
+public class CompletionItem {
+ public String label;
+ public int kind;
+ public String detail;
+ public MarkupContent documentation;
+ public Boolean deprecated, preselect;
+ public String sortText, filterText, insertText;
+ public Integer insertTextFormat;
+ public TextEdit textEdit;
+ public List additionalTextEdits;
+ public List commitCharacters;
+ public Command command;
+ public JsonElement data;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/CompletionItemKind.java b/lsp/src/main/java/com/itsaky/lsp/CompletionItemKind.java
new file mode 100644
index 000000000..11967001a
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/CompletionItemKind.java
@@ -0,0 +1,60 @@
+package com.itsaky.lsp;
+
+public class CompletionItemKind {
+ public static final int Text = 1,
+ Method = 2,
+ Function = 3,
+ Constructor = 4,
+ Field = 5,
+ Variable = 6,
+ Class = 7,
+ Interface = 8,
+ Module = 9,
+ Property = 10,
+ Unit = 11,
+ Value = 12,
+ Enum = 13,
+ Keyword = 14,
+ Snippet = 15,
+ Color = 16,
+ File = 17,
+ Reference = 18,
+ Folder = 19,
+ EnumMember = 20,
+ Constant = 21,
+ Struct = 22,
+ Event = 23,
+ Operator = 24,
+ TypeParameter = 25;
+
+ public static String asString(int kind) {
+ switch(kind) {
+ case Text : return "Text";
+ case Method : return "Method";
+ case Function : return "Function";
+ case Constructor : return "Constructo";
+ case Field: return "Field";
+ case Variable : return "Variable";
+ case Class : return "Class";
+ case Interface : return "Interface";
+ case Module : return "Module";
+ case Property : return "Property";
+ case Unit : return "Unit";
+ case Value : return "Value";
+ case Enum : return "Enum";
+ case Keyword : return "Keyword";
+ case Snippet : return "Snippet";
+ case Color : return "Color";
+ case File : return "File";
+ case Reference : return "Reference";
+ case Folder : return "Folder";
+ case EnumMember : return "Enum Member";
+ case Constant : return "Constant";
+ case Struct : return "Struct";
+ case Event : return "Event";
+ case Operator : return "Operator";
+ case TypeParameter : return "Type Parameter";
+ default: return "Unknown";
+ }
+ }
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/CompletionList.java b/lsp/src/main/java/com/itsaky/lsp/CompletionList.java
new file mode 100644
index 000000000..07f3f06c9
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/CompletionList.java
@@ -0,0 +1,25 @@
+package com.itsaky.lsp;
+
+import java.util.ArrayList;
+import java.util.List;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+public class CompletionList {
+ public boolean isIncomplete;
+ public List items;
+
+ public CompletionList() {
+ this.items = new ArrayList<>();
+ }
+
+ public CompletionList(boolean isIncomplete, List items) {
+ this.isIncomplete = isIncomplete;
+ this.items = items;
+ }
+
+ @Override
+ public String toString() {
+ return new GsonBuilder().setPrettyPrinting().create().toJson(this, CompletionList.class);
+ }
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/CompletionTriggerKind.java b/lsp/src/main/java/com/itsaky/lsp/CompletionTriggerKind.java
new file mode 100644
index 000000000..dda106a87
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/CompletionTriggerKind.java
@@ -0,0 +1,18 @@
+package com.itsaky.lsp;
+
+public class CompletionTriggerKind {
+ /**
+ * Completion was triggered by typing an identifier (24x7 code complete), manual invocation (e.g Ctrl+Space) or via
+ * API.
+ */
+ public static final int Invoked = 1;
+
+ /**
+ * Completion was triggered by a trigger character specified by the `triggerCharacters` properties of the
+ * `CompletionRegistrationOptions`.
+ */
+ public static final int TriggerCharacter = 2;
+
+ /** Completion was re-triggered as the current completion list is incomplete. */
+ public static final int TriggerForIncompleteCompletions = 3;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/Diagnostic.java b/lsp/src/main/java/com/itsaky/lsp/Diagnostic.java
new file mode 100644
index 000000000..fa2383c11
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/Diagnostic.java
@@ -0,0 +1,10 @@
+package com.itsaky.lsp;
+
+import java.util.List;
+
+public class Diagnostic {
+ public Range range;
+ public Integer severity;
+ public String code, source, message;
+ public List tags;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/DiagnosticSeverity.java b/lsp/src/main/java/com/itsaky/lsp/DiagnosticSeverity.java
new file mode 100644
index 000000000..b5ed73c57
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/DiagnosticSeverity.java
@@ -0,0 +1,5 @@
+package com.itsaky.lsp;
+
+public class DiagnosticSeverity {
+ public static final int Error = 1, Warning = 2, Information = 3, Hint = 4;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/DiagnosticTag.java b/lsp/src/main/java/com/itsaky/lsp/DiagnosticTag.java
new file mode 100644
index 000000000..b487c2efd
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/DiagnosticTag.java
@@ -0,0 +1,5 @@
+package com.itsaky.lsp;
+
+public class DiagnosticTag {
+ public static final int Unnecessary = 1;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/DidChangeConfigurationParams.java b/lsp/src/main/java/com/itsaky/lsp/DidChangeConfigurationParams.java
new file mode 100644
index 000000000..b8a589749
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/DidChangeConfigurationParams.java
@@ -0,0 +1,7 @@
+package com.itsaky.lsp;
+
+import com.google.gson.JsonElement;
+
+public class DidChangeConfigurationParams {
+ public JsonElement settings;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/DidChangeTextDocumentParams.java b/lsp/src/main/java/com/itsaky/lsp/DidChangeTextDocumentParams.java
new file mode 100644
index 000000000..977d96862
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/DidChangeTextDocumentParams.java
@@ -0,0 +1,9 @@
+package com.itsaky.lsp;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DidChangeTextDocumentParams {
+ public VersionedTextDocumentIdentifier textDocument = new VersionedTextDocumentIdentifier();
+ public List contentChanges = new ArrayList<>();
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/DidChangeWatchedFilesParams.java b/lsp/src/main/java/com/itsaky/lsp/DidChangeWatchedFilesParams.java
new file mode 100644
index 000000000..a14649e1d
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/DidChangeWatchedFilesParams.java
@@ -0,0 +1,7 @@
+package com.itsaky.lsp;
+
+import java.util.List;
+
+public class DidChangeWatchedFilesParams {
+ public List changes;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/DidChangeWorkspaceFoldersParams.java b/lsp/src/main/java/com/itsaky/lsp/DidChangeWorkspaceFoldersParams.java
new file mode 100644
index 000000000..e023c1ce1
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/DidChangeWorkspaceFoldersParams.java
@@ -0,0 +1,5 @@
+package com.itsaky.lsp;
+
+public class DidChangeWorkspaceFoldersParams {
+ public WorkspaceFoldersChangeEvent event;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/DidCloseTextDocumentParams.java b/lsp/src/main/java/com/itsaky/lsp/DidCloseTextDocumentParams.java
new file mode 100644
index 000000000..d22061cba
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/DidCloseTextDocumentParams.java
@@ -0,0 +1,5 @@
+package com.itsaky.lsp;
+
+public class DidCloseTextDocumentParams {
+ public TextDocumentIdentifier textDocument = new TextDocumentIdentifier();
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/DidOpenTextDocument.java b/lsp/src/main/java/com/itsaky/lsp/DidOpenTextDocument.java
new file mode 100644
index 000000000..5e467346d
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/DidOpenTextDocument.java
@@ -0,0 +1,5 @@
+package com.itsaky.lsp;
+
+public class DidOpenTextDocument {
+ public TextDocumentItem textDocument;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/DidOpenTextDocumentParams.java b/lsp/src/main/java/com/itsaky/lsp/DidOpenTextDocumentParams.java
new file mode 100644
index 000000000..f3294afb0
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/DidOpenTextDocumentParams.java
@@ -0,0 +1,11 @@
+package com.itsaky.lsp;
+
+public class DidOpenTextDocumentParams {
+ public TextDocumentItem textDocument = new TextDocumentItem();
+
+ public DidOpenTextDocumentParams() {}
+
+ public DidOpenTextDocumentParams(TextDocumentItem textDocument) {
+ this.textDocument = textDocument;
+ }
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/DidSaveTextDocumentParams.java b/lsp/src/main/java/com/itsaky/lsp/DidSaveTextDocumentParams.java
new file mode 100644
index 000000000..698e1f4c9
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/DidSaveTextDocumentParams.java
@@ -0,0 +1,6 @@
+package com.itsaky.lsp;
+
+public class DidSaveTextDocumentParams {
+ public TextDocumentIdentifier textDocument;
+ public String text;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/DocumentFormattingParams.java b/lsp/src/main/java/com/itsaky/lsp/DocumentFormattingParams.java
new file mode 100644
index 000000000..363df6780
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/DocumentFormattingParams.java
@@ -0,0 +1,6 @@
+package com.itsaky.lsp;
+
+public class DocumentFormattingParams {
+ public TextDocumentIdentifier textDocument;
+ public FormattingOptions options;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/DocumentHighlight.java b/lsp/src/main/java/com/itsaky/lsp/DocumentHighlight.java
new file mode 100644
index 000000000..dd82befd8
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/DocumentHighlight.java
@@ -0,0 +1,6 @@
+package com.itsaky.lsp;
+
+public class DocumentHighlight {
+ public Range range;
+ public int kind;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/DocumentHighlightKind.java b/lsp/src/main/java/com/itsaky/lsp/DocumentHighlightKind.java
new file mode 100644
index 000000000..4a31d8ded
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/DocumentHighlightKind.java
@@ -0,0 +1,5 @@
+package com.itsaky.lsp;
+
+public class DocumentHighlightKind {
+ public static final int Text = 1, Read = 2, Write = 3;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/DocumentLink.java b/lsp/src/main/java/com/itsaky/lsp/DocumentLink.java
new file mode 100644
index 000000000..f1d4e633c
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/DocumentLink.java
@@ -0,0 +1,9 @@
+package com.itsaky.lsp;
+
+import com.google.gson.JsonArray;
+
+public class DocumentLink {
+ public Range range;
+ public String target;
+ public JsonArray data;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/DocumentLinkParams.java b/lsp/src/main/java/com/itsaky/lsp/DocumentLinkParams.java
new file mode 100644
index 000000000..5a8ded5fa
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/DocumentLinkParams.java
@@ -0,0 +1,11 @@
+package com.itsaky.lsp;
+
+public class DocumentLinkParams {
+ public TextDocumentIdentifier textDocument;
+
+ public DocumentLinkParams() {}
+
+ public DocumentLinkParams(TextDocumentIdentifier textDocument) {
+ this.textDocument = textDocument;
+ }
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/DocumentSymbol.java b/lsp/src/main/java/com/itsaky/lsp/DocumentSymbol.java
new file mode 100644
index 000000000..c620abd11
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/DocumentSymbol.java
@@ -0,0 +1,11 @@
+package com.itsaky.lsp;
+
+import java.util.List;
+
+public class DocumentSymbol {
+ public String name, detail;
+ public int kind;
+ public boolean deprecated;
+ public Range range, selectionRange;
+ public List children;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/DocumentSymbolParams.java b/lsp/src/main/java/com/itsaky/lsp/DocumentSymbolParams.java
new file mode 100644
index 000000000..0842990c5
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/DocumentSymbolParams.java
@@ -0,0 +1,11 @@
+package com.itsaky.lsp;
+
+public class DocumentSymbolParams {
+ public TextDocumentIdentifier textDocument;
+
+ public DocumentSymbolParams() {}
+
+ public DocumentSymbolParams(TextDocumentIdentifier textDocument) {
+ this.textDocument = textDocument;
+ }
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/ErrorCodes.java b/lsp/src/main/java/com/itsaky/lsp/ErrorCodes.java
new file mode 100644
index 000000000..38c8554c4
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/ErrorCodes.java
@@ -0,0 +1,18 @@
+package com.itsaky.lsp;
+
+public class ErrorCodes {
+ public static final int ParseError = -32700,
+ InvalidRequest = -32600,
+ MethodNotFound = -32601,
+ InvalidParams = -32602,
+ InternalError = -32603,
+ serverErrorStart = -32099,
+ serverErrorEnd = -32000,
+ ServerNotInitialized = -32002,
+ UnknownErrorCode = -32001,
+
+ // TODO comment doesn't highlight properly
+ // Defined by the protocol.
+ RequestCancelled = -32800,
+ ContentModified = -32801;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/FileChangeType.java b/lsp/src/main/java/com/itsaky/lsp/FileChangeType.java
new file mode 100644
index 000000000..d9393711a
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/FileChangeType.java
@@ -0,0 +1,5 @@
+package com.itsaky.lsp;
+
+public class FileChangeType {
+ public static final int Created = 1, Changed = 2, Deleted = 3;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/FileEvent.java b/lsp/src/main/java/com/itsaky/lsp/FileEvent.java
new file mode 100644
index 000000000..8afb581fe
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/FileEvent.java
@@ -0,0 +1,9 @@
+package com.itsaky.lsp;
+
+import java.net.URI;
+
+public class FileEvent {
+ public URI uri;
+ // FileChangeType
+ public int type;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/FoldingRange.java b/lsp/src/main/java/com/itsaky/lsp/FoldingRange.java
new file mode 100644
index 000000000..848beaf66
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/FoldingRange.java
@@ -0,0 +1,16 @@
+package com.itsaky.lsp;
+
+public class FoldingRange {
+ public int startLine, startCharacter, endLine, endCharacter;
+ public String kind; // FoldingRangeKind
+
+ public FoldingRange() {}
+
+ public FoldingRange(int startLine, int startCharacter, int endLine, int endCharacter, String kind) {
+ this.startLine = startLine;
+ this.startCharacter = startCharacter;
+ this.endLine = endLine;
+ this.endCharacter = endCharacter;
+ this.kind = kind;
+ }
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/FoldingRangeKind.java b/lsp/src/main/java/com/itsaky/lsp/FoldingRangeKind.java
new file mode 100644
index 000000000..eac05a80e
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/FoldingRangeKind.java
@@ -0,0 +1,10 @@
+package com.itsaky.lsp;
+
+public class FoldingRangeKind {
+ /** Folding range for a comment */
+ public static final String Comment = "comment";
+ /** Folding range for a imports or includes */
+ public static final String Imports = "imports";
+ /** Folding range for a region (e.g. `#region`) */
+ public static final String Region = "region";
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/FoldingRangeParams.java b/lsp/src/main/java/com/itsaky/lsp/FoldingRangeParams.java
new file mode 100644
index 000000000..021fd0181
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/FoldingRangeParams.java
@@ -0,0 +1,5 @@
+package com.itsaky.lsp;
+
+public class FoldingRangeParams {
+ public TextDocumentIdentifier textDocument;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/FormattingOptions.java b/lsp/src/main/java/com/itsaky/lsp/FormattingOptions.java
new file mode 100644
index 000000000..1627abff4
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/FormattingOptions.java
@@ -0,0 +1,7 @@
+package com.itsaky.lsp;
+
+public class FormattingOptions {
+ public int tabSize;
+ public boolean insertSpaces;
+ // TODO other properties embedded at same level
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/Hover.java b/lsp/src/main/java/com/itsaky/lsp/Hover.java
new file mode 100644
index 000000000..2af0d4051
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/Hover.java
@@ -0,0 +1,19 @@
+package com.itsaky.lsp;
+
+import java.util.List;
+
+public class Hover {
+ public List contents;
+ public Range range;
+
+ public Hover() {}
+
+ public Hover(List contents) {
+ this.contents = contents;
+ }
+
+ public Hover(List contents, Range range) {
+ this.contents = contents;
+ this.range = range;
+ }
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/InitializeParams.java b/lsp/src/main/java/com/itsaky/lsp/InitializeParams.java
new file mode 100644
index 000000000..cd477504f
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/InitializeParams.java
@@ -0,0 +1,14 @@
+package com.itsaky.lsp;
+
+import com.google.gson.JsonElement;
+import java.net.URI;
+import java.util.List;
+
+public class InitializeParams {
+ public int processId;
+ public String rootPath;
+ public URI rootUri;
+ public JsonElement initializationOptions;
+ public String trace;
+ public List workspaceFolders;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/InitializeResult.java b/lsp/src/main/java/com/itsaky/lsp/InitializeResult.java
new file mode 100644
index 000000000..518a3288b
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/InitializeResult.java
@@ -0,0 +1,13 @@
+package com.itsaky.lsp;
+
+import com.google.gson.JsonObject;
+
+public class InitializeResult {
+ public JsonObject capabilities;
+
+ public InitializeResult() {}
+
+ public InitializeResult(JsonObject capabilities) {
+ this.capabilities = capabilities;
+ }
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/InsertTextFormat.java b/lsp/src/main/java/com/itsaky/lsp/InsertTextFormat.java
new file mode 100644
index 000000000..8cea8e008
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/InsertTextFormat.java
@@ -0,0 +1,5 @@
+package com.itsaky.lsp;
+
+public class InsertTextFormat {
+ public static final int PlainText = 1, Snippet = 2;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/JavaReportProgressParams.java b/lsp/src/main/java/com/itsaky/lsp/JavaReportProgressParams.java
new file mode 100644
index 000000000..e2c442582
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/JavaReportProgressParams.java
@@ -0,0 +1,33 @@
+package com.itsaky.lsp;
+
+public class JavaReportProgressParams {
+ private String message;
+ private int increment = -1;
+
+ public JavaReportProgressParams() {}
+
+ public JavaReportProgressParams(String message) {
+ this.message = message;
+ }
+
+ public JavaReportProgressParams(String message, int increment) {
+ this.message = message;
+ this.increment = increment;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public int getIncrement() {
+ return increment;
+ }
+
+ public void setIncrement(int increment) {
+ this.increment = increment;
+ }
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/JavaStartProgressParams.java b/lsp/src/main/java/com/itsaky/lsp/JavaStartProgressParams.java
new file mode 100644
index 000000000..3eb5c3372
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/JavaStartProgressParams.java
@@ -0,0 +1,19 @@
+package com.itsaky.lsp;
+
+public class JavaStartProgressParams {
+ private String message;
+
+ public JavaStartProgressParams() {}
+
+ public JavaStartProgressParams(String message) {
+ this.message = message;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/LanguageClient.java b/lsp/src/main/java/com/itsaky/lsp/LanguageClient.java
new file mode 100644
index 000000000..344f1756d
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/LanguageClient.java
@@ -0,0 +1,13 @@
+package com.itsaky.lsp;
+
+import com.google.gson.JsonElement;
+
+public interface LanguageClient {
+ void javaProgressStart(JavaStartProgressParams params);
+ void javaProgressReport(JavaReportProgressParams params);
+ void javaProgressEnd();
+ void publishDiagnostics(PublishDiagnosticsParams params);
+ void showMessage(ShowMessageParams params);
+ void registerCapability(String method, JsonElement options);
+ void customNotification(String method, JsonElement params);
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/LanguageServer.java b/lsp/src/main/java/com/itsaky/lsp/LanguageServer.java
new file mode 100644
index 000000000..eff5c5c00
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/LanguageServer.java
@@ -0,0 +1,120 @@
+package com.itsaky.lsp;
+
+import java.util.List;
+import java.util.Optional;
+
+public class LanguageServer {
+ public InitializeResult initialize(InitializeParams params) {
+ throw new RuntimeException("Unimplemented");
+ }
+
+ public void initialized() {
+ throw new RuntimeException("Unimplemented");
+ }
+
+ public void shutdown() {
+ throw new RuntimeException("Unimplemented");
+ }
+
+ public void didChangeConfiguration(DidChangeConfigurationParams params) {
+ throw new RuntimeException("Unimplemented");
+ }
+
+ public void didOpenTextDocument(DidOpenTextDocumentParams params) {
+ throw new RuntimeException("Unimplemented");
+ }
+
+ public void didChangeTextDocument(DidChangeTextDocumentParams params) {
+ throw new RuntimeException("Unimplemented");
+ }
+
+ public void willSaveTextDocument(WillSaveTextDocumentParams params) {
+ throw new RuntimeException("Unimplemented");
+ }
+
+ public List willSaveWaitUntilTextDocument(WillSaveTextDocumentParams params) {
+ throw new RuntimeException("Unimplemented");
+ }
+
+ public void didSaveTextDocument(DidSaveTextDocumentParams params) {
+ throw new RuntimeException("Unimplemented");
+ }
+
+ public void didCloseTextDocument(DidCloseTextDocumentParams params) {
+ throw new RuntimeException("Unimplemented");
+ }
+
+ public void didChangeWatchedFiles(DidChangeWatchedFilesParams params) {
+ throw new RuntimeException("Unimplemented");
+ }
+
+ public Optional completion(TextDocumentPositionParams params) {
+ throw new RuntimeException("Unimplemented");
+ }
+
+ public CompletionItem resolveCompletionItem(CompletionItem params) {
+ throw new RuntimeException("Unimplemented");
+ }
+
+ public Optional hover(TextDocumentPositionParams params) {
+ throw new RuntimeException("Unimplemented");
+ }
+
+ public Optional signatureHelp(TextDocumentPositionParams params) {
+ throw new RuntimeException("Unimplemented");
+ }
+
+ public Optional> gotoDefinition(TextDocumentPositionParams params) {
+ throw new RuntimeException("Unimplemented");
+ }
+
+ public Optional> findReferences(ReferenceParams params) {
+ throw new RuntimeException("Unimplemented");
+ }
+
+ public List documentSymbol(DocumentSymbolParams params) {
+ throw new RuntimeException("Unimplemented");
+ }
+
+ public List workspaceSymbols(WorkspaceSymbolParams params) {
+ throw new RuntimeException("Unimplemented");
+ }
+
+ public List codeAction(CodeActionParams params) {
+ throw new RuntimeException("Unimplemented");
+ }
+
+ public List codeLens(CodeLensParams params) {
+ throw new RuntimeException("Unimplemented");
+ }
+
+ public CodeLens resolveCodeLens(CodeLens params) {
+ throw new RuntimeException("Unimplemented");
+ }
+
+ public Optional prepareRename(TextDocumentPositionParams params) {
+ throw new RuntimeException("Unimplemented");
+ }
+
+ public WorkspaceEdit rename(RenameParams params) {
+ throw new RuntimeException("Unimplemented");
+ }
+
+ public void didChangeWorkspaceFolders(DidChangeWorkspaceFoldersParams params) {
+ throw new RuntimeException("Unimplemented");
+ }
+
+ public List formatting(DocumentFormattingParams params) {
+ throw new RuntimeException("Unimplemented");
+ }
+
+ public List foldingRange(FoldingRangeParams params) {
+ throw new RuntimeException("Unimplemented");
+ }
+
+ public List documentLink(DocumentLinkParams params) {
+ throw new RuntimeException("Unimplemented");
+ }
+
+ public void doAsyncWork() {}
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/Location.java b/lsp/src/main/java/com/itsaky/lsp/Location.java
new file mode 100644
index 000000000..2b7c6bf07
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/Location.java
@@ -0,0 +1,17 @@
+package com.itsaky.lsp;
+
+import java.net.URI;
+
+public class Location {
+ public URI uri;
+ public Range range;
+
+ public Location() {}
+
+ public Location(URI uri, Range range) {
+ this.uri = uri;
+ this.range = range;
+ }
+
+ public static final Location NONE = new Location(null, Range.NONE);
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/MarkedString.java b/lsp/src/main/java/com/itsaky/lsp/MarkedString.java
new file mode 100644
index 000000000..c3c30e6ce
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/MarkedString.java
@@ -0,0 +1,44 @@
+package com.itsaky.lsp;
+
+import com.google.gson.TypeAdapter;
+import com.google.gson.annotations.JsonAdapter;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+
+@JsonAdapter(MarkedString.Adapter.class)
+public class MarkedString {
+ public String language, value;
+
+ public MarkedString() {}
+
+ public MarkedString(String value) {
+ this.value = value;
+ }
+
+ public MarkedString(String language, String value) {
+ this.language = language;
+ this.value = value;
+ }
+
+ public static class Adapter extends TypeAdapter {
+ @Override
+ public void write(JsonWriter out, MarkedString markedString) throws IOException {
+ if (markedString.language == null) {
+ out.value(markedString.value);
+ } else {
+ out.beginObject();
+ out.name("language");
+ out.value(markedString.language);
+ out.name("value");
+ out.value(markedString.value);
+ out.endObject();
+ }
+ }
+
+ @Override
+ public MarkedString read(JsonReader reader) throws IOException {
+ throw new UnsupportedOperationException("Deserializing MarkedString's is unsupported.");
+ }
+ }
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/MarkupContent.java b/lsp/src/main/java/com/itsaky/lsp/MarkupContent.java
new file mode 100644
index 000000000..b1392f638
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/MarkupContent.java
@@ -0,0 +1,12 @@
+package com.itsaky.lsp;
+
+public class MarkupContent {
+ public String kind, value;
+
+ public MarkupContent() {}
+
+ public MarkupContent(String kind, String value) {
+ this.kind = kind;
+ this.value = value;
+ }
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/MarkupKind.java b/lsp/src/main/java/com/itsaky/lsp/MarkupKind.java
new file mode 100644
index 000000000..e1e9bb904
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/MarkupKind.java
@@ -0,0 +1,5 @@
+package com.itsaky.lsp;
+
+public class MarkupKind {
+ public static final String PlainText = "plaintext", Markdown = "markdown";
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/Message.java b/lsp/src/main/java/com/itsaky/lsp/Message.java
new file mode 100644
index 000000000..a23b223a6
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/Message.java
@@ -0,0 +1,10 @@
+package com.itsaky.lsp;
+
+import com.google.gson.JsonElement;
+
+public class Message {
+ public String jsonrpc;
+ public Integer id;
+ public String method;
+ public JsonElement params;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/MessageType.java b/lsp/src/main/java/com/itsaky/lsp/MessageType.java
new file mode 100644
index 000000000..08881ac68
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/MessageType.java
@@ -0,0 +1,12 @@
+package com.itsaky.lsp;
+
+public class MessageType {
+ /** An error message. */
+ public static final int Error = 1;
+ /** A warning message. */
+ public static final int Warning = 2;
+ /** An information message. */
+ public static final int Info = 3;
+ /** A log message. */
+ public static final int Log = 4;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/NotificationMessage.java b/lsp/src/main/java/com/itsaky/lsp/NotificationMessage.java
new file mode 100644
index 000000000..85138e3f8
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/NotificationMessage.java
@@ -0,0 +1,8 @@
+package com.itsaky.lsp;
+
+import com.google.gson.JsonElement;
+
+public class NotificationMessage {
+ public String method;
+ public JsonElement params;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/ParameterInformation.java b/lsp/src/main/java/com/itsaky/lsp/ParameterInformation.java
new file mode 100644
index 000000000..20ce6658d
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/ParameterInformation.java
@@ -0,0 +1,6 @@
+package com.itsaky.lsp;
+
+public class ParameterInformation {
+ public String label;
+ public MarkupContent documentation;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/Position.java b/lsp/src/main/java/com/itsaky/lsp/Position.java
new file mode 100644
index 000000000..1b91e909f
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/Position.java
@@ -0,0 +1,29 @@
+package com.itsaky.lsp;
+
+public class Position {
+ public int line, character;
+
+ public Position() {}
+
+ public Position(int line, int character) {
+ this.line = line;
+ this.character = character;
+ }
+
+ @Override
+ public String toString() {
+ return line + "," + character;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if(obj instanceof Position) {
+ Position that = (Position) obj;
+ return this.line == that.line
+ && this.character == that.character;
+ }
+ return false;
+ }
+
+ public static final Position NONE = new Position(-1, -1);
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/PublishDiagnosticsParams.java b/lsp/src/main/java/com/itsaky/lsp/PublishDiagnosticsParams.java
new file mode 100644
index 000000000..9a07e6248
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/PublishDiagnosticsParams.java
@@ -0,0 +1,19 @@
+package com.itsaky.lsp;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+public class PublishDiagnosticsParams {
+ public URI uri;
+ public List diagnostics;
+
+ public PublishDiagnosticsParams() {
+ diagnostics = new ArrayList<>();
+ }
+
+ public PublishDiagnosticsParams(URI uri, List diagnostics) {
+ this.uri = uri;
+ this.diagnostics = diagnostics;
+ }
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/Range.java b/lsp/src/main/java/com/itsaky/lsp/Range.java
new file mode 100644
index 000000000..96d587287
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/Range.java
@@ -0,0 +1,29 @@
+package com.itsaky.lsp;
+
+public class Range {
+ public Position start, end;
+
+ public Range() {}
+
+ public Range(Position start, Position end) {
+ this.start = start;
+ this.end = end;
+ }
+
+ @Override
+ public String toString() {
+ return start + "-" + end;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if(obj instanceof Range) {
+ Range that = (Range) obj;
+ return this.start.equals(that.start)
+ && this.end.equals(that.end);
+ }
+ return false;
+ }
+
+ public static final Range NONE = new Range(Position.NONE, Position.NONE);
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/ReferenceContext.java b/lsp/src/main/java/com/itsaky/lsp/ReferenceContext.java
new file mode 100644
index 000000000..105a32d38
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/ReferenceContext.java
@@ -0,0 +1,5 @@
+package com.itsaky.lsp;
+
+public class ReferenceContext {
+ public boolean includeDeclaration;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/ReferenceParams.java b/lsp/src/main/java/com/itsaky/lsp/ReferenceParams.java
new file mode 100644
index 000000000..d646b518b
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/ReferenceParams.java
@@ -0,0 +1,5 @@
+package com.itsaky.lsp;
+
+public class ReferenceParams extends TextDocumentPositionParams {
+ public ReferenceContext context;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/RegistrationParams.java b/lsp/src/main/java/com/itsaky/lsp/RegistrationParams.java
new file mode 100644
index 000000000..efa5e61b5
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/RegistrationParams.java
@@ -0,0 +1,8 @@
+package com.itsaky.lsp;
+
+import com.google.gson.JsonElement;
+
+public class RegistrationParams {
+ public String id, method;
+ public JsonElement registerOptions;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/RenameParams.java b/lsp/src/main/java/com/itsaky/lsp/RenameParams.java
new file mode 100644
index 000000000..ba32213a2
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/RenameParams.java
@@ -0,0 +1,7 @@
+package com.itsaky.lsp;
+
+public class RenameParams {
+ public TextDocumentIdentifier textDocument;
+ public Position position;
+ public String newName;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/RenameResponse.java b/lsp/src/main/java/com/itsaky/lsp/RenameResponse.java
new file mode 100644
index 000000000..b974edabd
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/RenameResponse.java
@@ -0,0 +1,6 @@
+package com.itsaky.lsp;
+
+public class RenameResponse {
+ public Range range;
+ public String placeholder;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/RequestMessage.java b/lsp/src/main/java/com/itsaky/lsp/RequestMessage.java
new file mode 100644
index 000000000..e6e568b99
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/RequestMessage.java
@@ -0,0 +1,8 @@
+package com.itsaky.lsp;
+
+import com.google.gson.JsonElement;
+
+public class RequestMessage {
+ public String id, method;
+ public JsonElement params;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/ResponseError.java b/lsp/src/main/java/com/itsaky/lsp/ResponseError.java
new file mode 100644
index 000000000..011f59b1d
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/ResponseError.java
@@ -0,0 +1,15 @@
+package com.itsaky.lsp;
+
+public class ResponseError {
+ public int code;
+ public String message;
+ public Object data;
+
+ public ResponseError() {}
+
+ public ResponseError(int code, String message, Object data) {
+ this.code = code;
+ this.message = message;
+ this.data = data;
+ }
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/ShowMessageParams.java b/lsp/src/main/java/com/itsaky/lsp/ShowMessageParams.java
new file mode 100644
index 000000000..749013ac4
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/ShowMessageParams.java
@@ -0,0 +1,6 @@
+package com.itsaky.lsp;
+
+public class ShowMessageParams {
+ public int type;
+ public String message;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/SignatureHelp.java b/lsp/src/main/java/com/itsaky/lsp/SignatureHelp.java
new file mode 100644
index 000000000..c9cece26b
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/SignatureHelp.java
@@ -0,0 +1,16 @@
+package com.itsaky.lsp;
+
+import java.util.List;
+
+public class SignatureHelp {
+ public List signatures;
+ public Integer activeSignature, activeParameter;
+
+ public SignatureHelp() {}
+
+ public SignatureHelp(List signatures, Integer activeSignature, Integer activeParameter) {
+ this.signatures = signatures;
+ this.activeSignature = activeSignature;
+ this.activeParameter = activeParameter;
+ }
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/SignatureInformation.java b/lsp/src/main/java/com/itsaky/lsp/SignatureInformation.java
new file mode 100644
index 000000000..9da6d97d6
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/SignatureInformation.java
@@ -0,0 +1,17 @@
+package com.itsaky.lsp;
+
+import java.util.List;
+
+public class SignatureInformation {
+ public String label;
+ public MarkupContent documentation;
+ public List parameters;
+
+ public SignatureInformation() {}
+
+ public SignatureInformation(String label, MarkupContent documentation, List parameters) {
+ this.label = label;
+ this.documentation = documentation;
+ this.parameters = parameters;
+ }
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/SymbolInformation.java b/lsp/src/main/java/com/itsaky/lsp/SymbolInformation.java
new file mode 100644
index 000000000..1bf88d5bf
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/SymbolInformation.java
@@ -0,0 +1,9 @@
+package com.itsaky.lsp;
+
+public class SymbolInformation {
+ public String name;
+ public int kind;
+ public boolean deprecated;
+ public Location location;
+ public String containerName;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/SymbolKind.java b/lsp/src/main/java/com/itsaky/lsp/SymbolKind.java
new file mode 100644
index 000000000..2a78ee76a
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/SymbolKind.java
@@ -0,0 +1,30 @@
+package com.itsaky.lsp;
+
+public class SymbolKind {
+ public static final int File = 1,
+ Module = 2,
+ Namespace = 3,
+ Package = 4,
+ Class = 5,
+ Method = 6,
+ Property = 7,
+ Field = 8,
+ Constructor = 9,
+ Enum = 10,
+ Interface = 11,
+ Function = 12,
+ Variable = 13,
+ Constant = 14,
+ String = 15,
+ Number = 16,
+ Boolean = 17,
+ Array = 18,
+ Object = 19,
+ Key = 20,
+ Null = 21,
+ EnumMember = 22,
+ Struct = 23,
+ Event = 24,
+ Operator = 25,
+ TypeParameter = 26;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/TextDocumentContentChangeEvent.java b/lsp/src/main/java/com/itsaky/lsp/TextDocumentContentChangeEvent.java
new file mode 100644
index 000000000..474a4d3ea
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/TextDocumentContentChangeEvent.java
@@ -0,0 +1,7 @@
+package com.itsaky.lsp;
+
+public class TextDocumentContentChangeEvent {
+ public Range range;
+ public Integer rangeLength;
+ public String text;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/TextDocumentEdit.java b/lsp/src/main/java/com/itsaky/lsp/TextDocumentEdit.java
new file mode 100644
index 000000000..48a76f89e
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/TextDocumentEdit.java
@@ -0,0 +1,8 @@
+package com.itsaky.lsp;
+
+import java.util.List;
+
+public class TextDocumentEdit {
+ public VersionedTextDocumentIdentifier textDocument;
+ public List edits;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/TextDocumentIdentifier.java b/lsp/src/main/java/com/itsaky/lsp/TextDocumentIdentifier.java
new file mode 100644
index 000000000..96af196d3
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/TextDocumentIdentifier.java
@@ -0,0 +1,13 @@
+package com.itsaky.lsp;
+
+import java.net.URI;
+
+public class TextDocumentIdentifier {
+ public URI uri;
+
+ public TextDocumentIdentifier() {}
+
+ public TextDocumentIdentifier(URI uri) {
+ this.uri = uri;
+ }
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/TextDocumentItem.java b/lsp/src/main/java/com/itsaky/lsp/TextDocumentItem.java
new file mode 100644
index 000000000..b4068b1be
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/TextDocumentItem.java
@@ -0,0 +1,10 @@
+package com.itsaky.lsp;
+
+import java.net.URI;
+
+public class TextDocumentItem {
+ public URI uri;
+ public String languageId;
+ public int version;
+ public String text;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/TextDocumentPositionParams.java b/lsp/src/main/java/com/itsaky/lsp/TextDocumentPositionParams.java
new file mode 100644
index 000000000..2d7a5d37c
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/TextDocumentPositionParams.java
@@ -0,0 +1,13 @@
+package com.itsaky.lsp;
+
+public class TextDocumentPositionParams {
+ public TextDocumentIdentifier textDocument;
+ public Position position;
+
+ public TextDocumentPositionParams() {}
+
+ public TextDocumentPositionParams(TextDocumentIdentifier textDocument, Position position) {
+ this.textDocument = textDocument;
+ this.position = position;
+ }
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/TextDocumentSaveReason.java b/lsp/src/main/java/com/itsaky/lsp/TextDocumentSaveReason.java
new file mode 100644
index 000000000..fd2c65069
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/TextDocumentSaveReason.java
@@ -0,0 +1,5 @@
+package com.itsaky.lsp;
+
+public class TextDocumentSaveReason {
+ public static final int Manual = 1, AfterDelay = 2, FocusOut = 3;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/TextEdit.java b/lsp/src/main/java/com/itsaky/lsp/TextEdit.java
new file mode 100644
index 000000000..bf6eed66a
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/TextEdit.java
@@ -0,0 +1,20 @@
+package com.itsaky.lsp;
+
+public class TextEdit {
+ public Range range;
+ public String newText;
+
+ public TextEdit() {}
+
+ public TextEdit(Range range, String newText) {
+ this.range = range;
+ this.newText = newText;
+ }
+
+ @Override
+ public String toString() {
+ return range + "/" + newText;
+ }
+
+ public static final TextEdit NONE = new TextEdit(Range.NONE, "");
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/VersionedTextDocumentIdentifier.java b/lsp/src/main/java/com/itsaky/lsp/VersionedTextDocumentIdentifier.java
new file mode 100644
index 000000000..7b21b06ae
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/VersionedTextDocumentIdentifier.java
@@ -0,0 +1,8 @@
+package com.itsaky.lsp;
+
+import java.net.URI;
+
+public class VersionedTextDocumentIdentifier {
+ public URI uri;
+ public int version;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/WillSaveTextDocumentParams.java b/lsp/src/main/java/com/itsaky/lsp/WillSaveTextDocumentParams.java
new file mode 100644
index 000000000..5748d94be
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/WillSaveTextDocumentParams.java
@@ -0,0 +1,6 @@
+package com.itsaky.lsp;
+
+public class WillSaveTextDocumentParams {
+ public TextDocumentIdentifier textDocument;
+ public int reason;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/WorkspaceEdit.java b/lsp/src/main/java/com/itsaky/lsp/WorkspaceEdit.java
new file mode 100644
index 000000000..e80b1380b
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/WorkspaceEdit.java
@@ -0,0 +1,10 @@
+package com.itsaky.lsp;
+
+import java.net.URI;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class WorkspaceEdit {
+ public Map> changes = new HashMap<>();
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/WorkspaceFolder.java b/lsp/src/main/java/com/itsaky/lsp/WorkspaceFolder.java
new file mode 100644
index 000000000..640378771
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/WorkspaceFolder.java
@@ -0,0 +1,8 @@
+package com.itsaky.lsp;
+
+import java.net.URI;
+
+public class WorkspaceFolder {
+ public URI uri;
+ public String name;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/WorkspaceFoldersChangeEvent.java b/lsp/src/main/java/com/itsaky/lsp/WorkspaceFoldersChangeEvent.java
new file mode 100644
index 000000000..e439b25ed
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/WorkspaceFoldersChangeEvent.java
@@ -0,0 +1,7 @@
+package com.itsaky.lsp;
+
+import java.util.List;
+
+public class WorkspaceFoldersChangeEvent {
+ public List added, removed;
+}
diff --git a/lsp/src/main/java/com/itsaky/lsp/WorkspaceSymbolParams.java b/lsp/src/main/java/com/itsaky/lsp/WorkspaceSymbolParams.java
new file mode 100644
index 000000000..1af5a8884
--- /dev/null
+++ b/lsp/src/main/java/com/itsaky/lsp/WorkspaceSymbolParams.java
@@ -0,0 +1,11 @@
+package com.itsaky.lsp;
+
+public class WorkspaceSymbolParams {
+ public String query;
+
+ public WorkspaceSymbolParams() {}
+
+ public WorkspaceSymbolParams(String query) {
+ this.query = query;
+ }
+}
diff --git a/settings.gradle b/settings.gradle
index cee3ae1c0..6f50df8bb 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -3,5 +3,6 @@ include ':treeview'
include ':spinkit'
include ':slidr'
include ':javadocparser'
+include ':lsp'
rootProject.name = "Android IDE"