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 listener; - - public CompilerRequestor(DiagnosticListener 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 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 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 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 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 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 someDiagnosticListener, Iterable options, Iterable classes, Iterable 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 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 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 compilationUnits; - public JavaFileManager fileManager; - protected Processor[] processors; - public DiagnosticListener 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 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 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 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"