Implemented references and definition

Removed usage of custom LSP data classes
This commit is contained in:
Akash Yadav
2021-10-02 19:02:10 +05:30
committed by Akash Yadav
parent a4cab1e7e3
commit afafab5a16
122 changed files with 530 additions and 3820 deletions
-1
View File
@@ -131,7 +131,6 @@ dependencies {
// Local projects
implementation project(':api-info')
implementation project(':treeview')
implementation project(':lsp')
implementation project(':emulator')
implementation project(':emulatorview')
implementation project(':javapoet')
Binary file not shown.
@@ -33,7 +33,7 @@ import com.google.android.material.tabs.TabLayout;
import com.google.android.material.transition.MaterialContainerTransform;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.itsaky.androidide.adapters.DiagnosticsAdapter;
import com.itsaky.androidide.adapters.EditorPagerAdapter;
import com.itsaky.androidide.app.StudioActivity;
import com.itsaky.androidide.databinding.ActivityEditorBinding;
@@ -47,8 +47,6 @@ import com.itsaky.androidide.fragments.sheets.TextSheetFragment;
import com.itsaky.androidide.handlers.BuildServiceHandler;
import com.itsaky.androidide.handlers.FileOptionsHandler;
import com.itsaky.androidide.handlers.IDEHandler;
import com.itsaky.androidide.handlers.jls.JLSHandler;
import com.itsaky.androidide.handlers.jls.LanguageClientHandler;
import com.itsaky.androidide.interfaces.DiagnosticClickListener;
import com.itsaky.androidide.interfaces.EditorActivityProvider;
import com.itsaky.androidide.language.buildout.BuildOutputLanguage;
@@ -56,7 +54,6 @@ import com.itsaky.androidide.language.logs.LogLanguageImpl;
import com.itsaky.androidide.lsp.LSP;
import com.itsaky.androidide.lsp.LSPProvider;
import com.itsaky.androidide.managers.PreferenceManager;
import com.itsaky.androidide.managers.VersionedFileManager;
import com.itsaky.androidide.models.AndroidProject;
import com.itsaky.androidide.models.DiagnosticGroup;
import com.itsaky.androidide.models.LogLine;
@@ -69,6 +66,7 @@ import com.itsaky.androidide.services.IDEService;
import com.itsaky.androidide.shell.ShellServer;
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE;
import com.itsaky.androidide.tasks.TaskExecutor;
import com.itsaky.androidide.utils.LSPUtils;
import com.itsaky.androidide.utils.Logger;
import com.itsaky.androidide.utils.RecursiveFileSearcher;
import com.itsaky.androidide.utils.Symbols;
@@ -76,13 +74,6 @@ import com.itsaky.androidide.utils.TransformUtils;
import com.itsaky.androidide.utils.TypefaceUtils;
import com.itsaky.androidide.views.MaterialBanner;
import com.itsaky.androidide.views.SymbolInputView;
import com.itsaky.lsp.Diagnostic;
import com.itsaky.lsp.DidCloseTextDocumentParams;
import com.itsaky.lsp.DidOpenTextDocumentParams;
import com.itsaky.lsp.Message;
import com.itsaky.lsp.Range;
import com.itsaky.lsp.TextDocumentIdentifier;
import com.itsaky.lsp.TextDocumentItem;
import com.itsaky.toaster.Toaster;
import com.unnamed.b.atv.model.TreeNode;
import io.github.rosemoe.editor.widget.CodeEditor;
@@ -91,15 +82,14 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Stack;
import java.util.regex.Pattern;
import me.piruin.quickaction.ActionItem;
import me.piruin.quickaction.QuickAction;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.DidChangeConfigurationParams;
import org.eclipse.lsp4j.InitializeResult;
import org.eclipse.lsp4j.services.LanguageServer;
import org.eclipse.lsp4j.Position;
import com.itsaky.androidide.utils.LSPUtils;
import org.eclipse.lsp4j.services.LanguageServer;
public class EditorActivity extends StudioActivity implements FileTreeFragment.FileActionListener,
TabLayout.OnTabSelectedListener,
@@ -125,8 +115,6 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
private static AndroidProject mProject;
private IDEProject mIDEProject;
private JLSHandler mJLSHandler;
private LanguageClientHandler mLanguageClientHandler;
private BuildServiceHandler mBuildServiceHandler;
private FileOptionsHandler mFileOptionsHandler;
@@ -139,8 +127,6 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
private ProjectInfoSheet mProjectInfoSheet;
private AlertDialog mFindInProjectDialog;
public final Stack<Message> pendingMessages = new Stack<>();
private static final String TAG_FILE_OPTIONS_FRAGMENT = "file_options_fragment";
public static final String TAG = "EditorActivity";
@@ -206,9 +192,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
createQuickActions();
mLanguageClientHandler = new LanguageClientHandler(this);
mBuildServiceHandler = new BuildServiceHandler(this);
mJLSHandler = new JLSHandler(mLanguageClientHandler, this);
mFileOptionsHandler = new FileOptionsHandler(this);
getApp().checkAndUpdateGradle();
@@ -417,11 +401,6 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
return mIDEProject;
}
@Override
public JLSHandler provideJLSHandler() {
return mJLSHandler;
}
@Override
public void editorStateChanged() {
for(int i=0;i<mBinding.tabs.getTabCount();i++) {
@@ -613,7 +592,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
@Override
public void onDiagnosticClick(File file, Diagnostic diagnostic) {
// openFileAndSelect(file, diagnostic.getRange());
openFileAndSelect(file, diagnostic.getRange());
hideDiagnostics();
}
@@ -675,18 +654,6 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
}
}
public void addPendingMessage(Message msg) {
pendingMessages.push(msg);
}
public Message createMessage(String method, String data) {
Message msg = new Message();
msg.jsonrpc = "2.0";
msg.method = method;
msg.params = new JsonParser().parse(data);
return msg;
}
@Override
public void onTabSelected(TabLayout.Tab tab) {
EditorFragment current = mPagerAdapter.getFrag(tab.getPosition());
@@ -865,7 +832,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
public RecyclerView createDiagnosticsList() {
diagnosticList = new RecyclerView(this);
diagnosticList.setLayoutManager(new LinearLayoutManager(this));
diagnosticList.setAdapter(mLanguageClientHandler.newDiagnosticsAdapter());
diagnosticList.setAdapter(new DiagnosticsAdapter(new ArrayList<DiagnosticGroup>(), this));
return diagnosticList;
}
@@ -890,7 +857,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
public EditorFragment openFile(File file, org.eclipse.lsp4j.Range selection) {
if(selection == null) selection = Range_ofZero;
int i = mPagerAdapter.openFile(file, selection, this, mJLSHandler);
int i = mPagerAdapter.openFile(file, selection, this);
if(i >= 0 && !mBinding.tabs.getTabAt(i).isSelected())
mBinding.tabs.getTabAt(i).select();
mBinding.editorDrawerLayout.closeDrawer(GravityCompat.END);
@@ -904,16 +871,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
@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;
mJLSHandler.didOpen(p);
}
// textDocument/didOpen is now handled by CodeEditor
}
@Override
@@ -1020,12 +978,6 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
}
}
TextDocumentIdentifier id = new TextDocumentIdentifier();
id.uri = removed.toURI();
DidCloseTextDocumentParams p = new DidCloseTextDocumentParams();
p.textDocument = id;
mJLSHandler.didClose(p);
if(mPagerAdapter.getCount() <= 0) {
mCurrentFragment = null;
mCurrentFile = null;
@@ -1042,13 +994,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
LSP.setActivityProvider(this);
LSP.Java.start(() -> {
Optional<InitializeResult> result = LSP.Java.init(mProject.getProjectPath());
if(result.isPresent()) {
// TODO Handle initialize result
// TODO Register server capablities
} else if(true) {
// TODO Show user that we cant initialize Java language server
}
LSP.Java.initialized();
});
}
@@ -1169,8 +1115,8 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
getApp().getPrefManager().setOpenedProject(PreferenceManager.NO_OPENED_PROJECT);
mJLSHandler.stop();
LSP.shutdownAll();
getApp().stopAllDaemons();
if(manualFinish)
@@ -16,9 +16,6 @@ import com.google.gson.JsonObject;
import com.itsaky.androidide.R;
import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.databinding.LayoutCompletionItemBinding;
import com.itsaky.androidide.models.CompletionItemWrapper;
import com.itsaky.androidide.models.CompletionListItem;
import com.itsaky.androidide.models.SuggestItem;
import com.itsaky.androidide.utils.Logger;
import com.itsaky.androidide.utils.TypefaceUtils;
import com.itsaky.apiinfo.ApiInfo;
@@ -27,7 +24,6 @@ import com.itsaky.apiinfo.models.FieldInfo;
import com.itsaky.apiinfo.models.Info;
import com.itsaky.apiinfo.models.MethodInfo;
import io.github.rosemoe.editor.widget.EditorCompletionAdapter;
import java.util.Locale;
import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.CompletionItemKind;
@@ -8,10 +8,10 @@ import androidx.recyclerview.widget.RecyclerView;
import com.itsaky.androidide.R;
import com.itsaky.androidide.databinding.LayoutDiagnosticItemBinding;
import com.itsaky.androidide.interfaces.DiagnosticClickListener;
import com.itsaky.lsp.Diagnostic;
import com.itsaky.lsp.DiagnosticSeverity;
import java.util.List;
import java.io.File;
import java.util.List;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.DiagnosticSeverity;
public class DiagnosticItemAdpater extends RecyclerView.Adapter<DiagnosticItemAdpater.VH> {
@@ -36,8 +36,8 @@ public class DiagnosticItemAdpater extends RecyclerView.Adapter<DiagnosticItemAd
final LayoutDiagnosticItemBinding binding = p1.binding;
binding.icon.setImageResource(getDiagnosticIconId(diagnostic));
binding.icon.setColorFilter(ContextCompat.getColor(binding.icon.getContext(), diagnostic.severity == DiagnosticSeverity.Error ? R.color.diagnostic_error : R.color.diagnostic_warning), PorterDuff.Mode.SRC_ATOP);
binding.title.setText(diagnostic.message);
binding.icon.setColorFilter(ContextCompat.getColor(binding.icon.getContext(), diagnostic.getSeverity() == DiagnosticSeverity.Error ? R.color.diagnostic_error : R.color.diagnostic_warning), PorterDuff.Mode.SRC_ATOP);
binding.title.setText(diagnostic.getMessage());
binding.getRoot().setOnClickListener(v -> {
if(listener != null) {
@@ -52,7 +52,7 @@ public class DiagnosticItemAdpater extends RecyclerView.Adapter<DiagnosticItemAd
}
private int getDiagnosticIconId(Diagnostic diagnostic) {
if(diagnostic.severity == DiagnosticSeverity.Error)
if(diagnostic.getSeverity() == DiagnosticSeverity.Error)
return R.drawable.ic_compilation_error;
return R.drawable.ic_info;
}
@@ -4,11 +4,9 @@ import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter;
import com.itsaky.androidide.fragments.EditorFragment;
import com.itsaky.androidide.interfaces.JLSRequestor;
import com.itsaky.androidide.models.AndroidProject;
import com.itsaky.androidide.models.SaveResult;
import com.itsaky.androidide.utils.Logger;
import com.itsaky.lsp.Range;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
@@ -66,7 +64,7 @@ public class EditorPagerAdapter extends FragmentStatePagerAdapter implements Edi
return null;
}
public int openFile(File file, org.eclipse.lsp4j.Range selection, EditorFragment.FileOpenListener listener, JLSRequestor jlsRequestor) {
public int openFile(File file, org.eclipse.lsp4j.Range selection, EditorFragment.FileOpenListener listener) {
int openedFileIndex = -1;
for (int i=0;i < mOpenedFiles.size();i++) {
File f = mOpenedFiles.get(i);
@@ -80,7 +78,6 @@ public class EditorPagerAdapter extends FragmentStatePagerAdapter implements Edi
mFragments.add(EditorFragment
.newInstance(file, project, selection)
.setFileOpenListener(listener)
.setJLSRequestor(jlsRequestor)
.setModificationStateListener(this));
mOpenedFiles.add(file);
notifyDataSetChanged();
@@ -12,43 +12,31 @@ import androidx.core.app.NotificationManagerCompat;
import androidx.multidex.MultiDexApplication;
import com.blankj.utilcode.util.EncodeUtils;
import com.blankj.utilcode.util.ResourceUtils;
import com.blankj.utilcode.util.SPStaticUtils;
import com.blankj.utilcode.util.ThrowableUtils;
import com.google.firebase.messaging.FirebaseMessaging;
import com.itsaky.androidide.R;
import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.interfaces.LanguageServerListener;
import com.itsaky.androidide.language.java.manager.JavaCharacter;
import com.itsaky.androidide.language.java.server.JavaLanguageServer;
import com.itsaky.androidide.language.xml.completion.XMLCompletionService;
import com.itsaky.androidide.managers.PreferenceManager;
import com.itsaky.androidide.managers.ToolsManager;
import com.itsaky.androidide.models.AndroidProject;
import com.itsaky.androidide.services.IDEService;
import com.itsaky.androidide.services.MessagingService;
import com.itsaky.androidide.shell.ShellServer;
import com.itsaky.androidide.utils.Environment;
import com.itsaky.androidide.utils.FileUtil;
import com.itsaky.androidide.utils.Logger;
import com.itsaky.androidide.utils.StudioUtils;
import com.itsaky.lsp.LanguageClient;
import com.itsaky.apiinfo.ApiInfo;
import com.itsaky.toaster.Toaster;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import com.blankj.utilcode.util.FileUtils;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import com.itsaky.apiinfo.ApiInfo;
public class StudioApp extends MultiDexApplication
{
private static StudioApp instance;
private StudioUtils mUtils;
private JavaLanguageServer languageServer;
private XMLCompletionService mXmlCompletionService;
private PreferenceManager mPrefsManager;
private NotificationManager mNotificationManager;
@@ -58,8 +46,6 @@ public class StudioApp extends MultiDexApplication
private ApiInfo mApiInfo;
private List<LanguageServerListener> serveralisteners = new ArrayList<>();
public static final boolean DEBUG = com.itsaky.androidide.BuildConfig.DEBUG;
public static final long BUSYBOX_VERSION = 1;
@@ -169,14 +155,6 @@ public class StudioApp extends MultiDexApplication
public void createCompletionService() {
this.mXmlCompletionService = new XMLCompletionService();
}
public void setJavaLanguageServer(JavaLanguageServer server) {
this.languageServer = server;
}
public JavaLanguageServer getJavaLanguageServer() {
return languageServer;
}
public XMLCompletionService getXmlCompletionService() {
return mXmlCompletionService;
@@ -209,18 +187,6 @@ public class StudioApp extends MultiDexApplication
return shellServer;
}
public void addServerListener(LanguageServerListener listener) {
if(serveralisteners == null) serveralisteners = new ArrayList<>();
if(!serveralisteners.contains(listener))
serveralisteners.add(listener);
}
public void removeServerListener(LanguageServerListener listener) {
if(serveralisteners == null) serveralisteners = new ArrayList<>();
if(serveralisteners.contains(listener))
serveralisteners.remove(listener);
}
public void writeException(Throwable th) {
FileUtil.writeFile(new File(FileUtil.getExternalStorageDir(), "idelog.txt").getAbsolutePath(), ThrowableUtils.getFullStackTrace(th));
}
@@ -9,10 +9,8 @@ import com.blankj.utilcode.util.SizeUtils;
import com.itsaky.androidide.adapters.CompletionListAdapter;
import com.itsaky.androidide.databinding.FragmentEditorBinding;
import com.itsaky.androidide.fragments.preferences.EditorPreferences;
import com.itsaky.androidide.interfaces.JLSRequestor;
import com.itsaky.androidide.language.groovy.GroovyLanguage;
import com.itsaky.androidide.language.java.JavaLanguage;
import com.itsaky.androidide.language.java.JavaLanguageAnalyzer;
import com.itsaky.androidide.language.xml.XMLLanguage;
import com.itsaky.androidide.language.xml.lexer.XMLLexer;
import com.itsaky.androidide.managers.PreferenceManager;
@@ -21,30 +19,21 @@ import com.itsaky.androidide.models.ConstantsBridge;
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE;
import com.itsaky.androidide.tasks.TaskExecutor;
import com.itsaky.androidide.tasks.callables.ReadFileTask;
import com.itsaky.androidide.utils.LSPUtils;
import com.itsaky.androidide.utils.TypefaceUtils;
import com.itsaky.lsp.CodeAction;
import com.itsaky.lsp.Diagnostic;
import com.itsaky.lsp.DidSaveTextDocumentParams;
import com.itsaky.lsp.JavaColors;
import com.itsaky.lsp.Position;
import com.itsaky.lsp.Range;
import com.itsaky.lsp.TextDocumentIdentifier;
import com.itsaky.lsp.TextDocumentPositionParams;
import com.itsaky.lsp.TextEdit;
import io.github.rosemoe.editor.interfaces.EditorEventListener;
import io.github.rosemoe.editor.langs.EmptyLanguage;
import io.github.rosemoe.editor.widget.CodeEditor;
import java.io.File;
import java.io.StringReader;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.Token;
import io.github.rosemoe.editor.text.Content;
import com.itsaky.androidide.utils.LSPUtils;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.Range;
public class EditorFragment extends BaseFragment implements EditorEventListener {
@@ -56,11 +45,8 @@ public class EditorFragment extends BaseFragment implements EditorEventListener
private ModificationStateListener mModificationStateListener;
private FileOpenListener mOpenListener;
private JLSRequestor mJLSRequestor;
private static AndroidProject mProject;
private JavaLanguage mJavaLanguage;
public static final String KEY_FILE_PATH = "file_path";
public static final String KEY_PROJECT = "project";
public static final String KEY_LINE_START = "line_start";
@@ -76,11 +62,6 @@ public class EditorFragment extends BaseFragment implements EditorEventListener
public static final String EXT_KOTLIN = ".kt";
public static final String EXT_JSON = ".json";
public EditorFragment setJLSRequestor(JLSRequestor requestor) {
this.mJLSRequestor = requestor;
return this;
}
public EditorFragment setModificationStateListener(ModificationStateListener listener) {
this.mModificationStateListener = listener;
return this;
@@ -97,14 +78,6 @@ public class EditorFragment extends BaseFragment implements EditorEventListener
return "";
}
public void setJavaColors(JavaColors colors) {
if(mJavaLanguage != null) {
JavaLanguageAnalyzer analyzer = (JavaLanguageAnalyzer) mJavaLanguage.getAnalyzer();
analyzer.setJavaColors(colors);
mBinding.editor.notifySpansChanged();
}
}
public static EditorFragment newInstance(File file, AndroidProject project, org.eclipse.lsp4j.Range selection) {
Bundle bundle = new Bundle();
bundle.putString(KEY_FILE_PATH, file.getAbsolutePath());
@@ -161,7 +134,6 @@ public class EditorFragment extends BaseFragment implements EditorEventListener
mBinding.editor.setAutoCompletionItemAdapter(new CompletionListAdapter());
mBinding.editor.setLineColorsEnabled(true);
mBinding.editor.setDividerWidth(SizeUtils.dp2px(1));
mBinding.editor.setJLSRequestor(mJLSRequestor);
configureEditorIfNeeded();
@@ -202,7 +174,7 @@ public class EditorFragment extends BaseFragment implements EditorEventListener
for(int i=0;i<diags.size();i++) {
final Diagnostic d = diags.get(i);
if(d == null) continue;
map.put(d.range, d);
map.put(d.getRange(), d);
}
mBinding.editor.setDiagnostics(map);
}
@@ -304,7 +276,7 @@ public class EditorFragment extends BaseFragment implements EditorEventListener
private void postRead() {
if (mFile.isFile() && mFile.getName().endsWith(EXT_JAVA)) {
mBinding.editor.setEditorLanguage(mJavaLanguage = new JavaLanguage(mProject));
mBinding.editor.setEditorLanguage(new JavaLanguage());
} else if (mFile.isFile() && mFile.getName().endsWith(EXT_XML)) {
mBinding.editor.setEditorLanguage(new XMLLanguage());
} else if (mFile.isFile() && mFile.getName().endsWith(EXT_GRADLE)) {
@@ -381,9 +353,7 @@ public class EditorFragment extends BaseFragment implements EditorEventListener
@Override
public void onSetSelection(int startLine, int startCol, int endLine, int endCol) {
if(mJLSRequestor != null) {
mJLSRequestor.hideSignature();
}
}
@Override
@@ -407,26 +377,8 @@ public class EditorFragment extends BaseFragment implements EditorEventListener
}
notifyModified();
if(insertedContent.length() == 1) {
char i = insertedContent.charAt(0);
if(i == '(' || i == ',') {
requestSignature();
}
}
}
private void requestSignature() {
try {
if (mJLSRequestor != null) {
TextDocumentPositionParams p = new TextDocumentPositionParams();
p.textDocument = new TextDocumentIdentifier(getFile().toURI());
p.position = new Position(mBinding.editor.getCursor().getLeftLine(), mBinding.editor.getCursor().getLeftColumn());
mJLSRequestor.signatureHelp(p, getFile());
}
} catch (Throwable th) {}
}
@Override
public void beforeReplace(CodeEditor editor, CharSequence content) {
}
@@ -11,6 +11,7 @@ import com.itsaky.androidide.databinding.LayoutCreateFileJavaBinding;
import com.itsaky.androidide.databinding.LayoutDialogTextInputBinding;
import com.itsaky.androidide.fragments.EditorFragment;
import com.itsaky.androidide.fragments.sheets.OptionsListFragment;
import com.itsaky.androidide.lsp.LSP;
import com.itsaky.androidide.models.SheetOption;
import com.itsaky.androidide.utils.Environment;
import com.itsaky.androidide.utils.ProjectWriter;
@@ -201,7 +202,7 @@ public class FileOptionsHandler extends IDEHandler implements OptionsListFragmen
activity().getApp().toast(R.string.msg_file_exists, Toaster.Type.ERROR);
} else {
if(FileIOUtils.writeFileFromString(file, content)) {
jls().notifyFileCreated(file);
LSP.notifyFileCreated(file);
activity(). getApp().toast(R.string.msg_file_created, Toaster.Type.SUCCESS);
if(activity().getLastHoldTreeNode() != null) {
TreeNode node = new TreeNode(file);
@@ -269,7 +270,7 @@ public class FileOptionsHandler extends IDEHandler implements OptionsListFragmen
final boolean deleted = FileUtils.delete(f);
activity(). getApp().toast(deleted ? R.string.deleted : R.string.delete_failed, deleted ? Toaster.Type.SUCCESS : Toaster.Type.ERROR);
if(deleted) {
jls().notifyFileDeleted(f);
LSP.notifyFileDeleted(f);
if(activity().getLastHoldTreeNode() != null) {
TreeNode parent = activity().getLastHoldTreeNode().getParent();
parent.deleteChild(activity().getLastHoldTreeNode());
@@ -306,6 +307,7 @@ public class FileOptionsHandler extends IDEHandler implements OptionsListFragmen
boolean renamed = name != null && name.length() > 0 && name.length() <= 40 && FileUtils.rename(f, name);
activity(). getApp().toast(renamed ? R.string.renamed : R.string.rename_failed, renamed ? Toaster.Type.SUCCESS : Toaster.Type.ERROR);
if(renamed) {
LSP.notifyFileRenamed(f, name);
if(activity().getLastHoldTreeNode() != null) {
TreeNode parent = activity().getLastHoldTreeNode().getParent();
parent.deleteChild(activity().getLastHoldTreeNode());
@@ -4,7 +4,6 @@ import com.itsaky.androidide.EditorActivity;
import com.itsaky.androidide.models.AndroidProject;
import com.itsaky.androidide.models.project.IDEProject;
import com.itsaky.androidide.utils.Logger;
import com.itsaky.androidide.handlers.jls.JLSHandler;
/**
* A handler is an implementation that handles a specific feature, data or anything else in AndroidIDE
@@ -27,10 +26,6 @@ public abstract class IDEHandler {
return provider.provideEditorActivity();
}
protected JLSHandler jls() {
return provider.provideJLSHandler();
}
protected AndroidProject androidProject() {
return provider.provideAndroidProject();
}
@@ -68,12 +63,5 @@ public abstract class IDEHandler {
* @throws NullPointerException is this is required
*/
IDEProject provideIDEProject();
/**
* Called by handler to get a reference of the current {@link IDEProject}
*
* @throws NullPointerException is this is required
*/
JLSHandler provideJLSHandler();
}
}
@@ -1,322 +0,0 @@
package com.itsaky.androidide.handlers.jls;
import android.text.TextUtils;
import android.view.View;
import com.blankj.utilcode.util.FileIOUtils;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.gson.Gson;
import com.itsaky.androidide.EditorActivity;
import com.itsaky.androidide.R;
import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.fragments.EditorFragment;
import com.itsaky.androidide.handlers.IDEHandler;
import com.itsaky.androidide.interfaces.JLSRequestor;
import com.itsaky.androidide.language.java.server.JavaLanguageServer;
import com.itsaky.androidide.models.AndroidProject;
import com.itsaky.lsp.CodeAction;
import com.itsaky.lsp.DidChangeTextDocumentParams;
import com.itsaky.lsp.DidChangeWatchedFilesParams;
import com.itsaky.lsp.DidCloseTextDocumentParams;
import com.itsaky.lsp.DidOpenTextDocumentParams;
import com.itsaky.lsp.DidSaveTextDocumentParams;
import com.itsaky.lsp.FileChangeType;
import com.itsaky.lsp.FileEvent;
import com.itsaky.lsp.LanguageClient;
import com.itsaky.lsp.Range;
import com.itsaky.lsp.ReferenceParams;
import com.itsaky.lsp.TextDocumentPositionParams;
import com.itsaky.lsp.TextEdit;
import io.github.rosemoe.editor.text.Content;
import java.io.File;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
public class JLSHandler extends IDEHandler implements JLSRequestor {
private JavaLanguageServer languageServer;
private final LanguageClientHandler client;
private static final Gson gson = new Gson();
public JLSHandler(LanguageClientHandler client, Provider provider) {
super(provider);
this.client = client;
}
public JavaLanguageServer getJLS() {
return languageServer;
}
@Override
public void start() {
if(activity() == null) {
throw new NullPointerException("EditorActivity should not be null");
}
AndroidProject project = provider.provideAndroidProject();
if(project == null) {
throw new NullPointerException("Project is null");
}
this.client.start();
this.languageServer = new JavaLanguageServer(project, this.client);
// this.languageServer.startServer();
StudioApp.getInstance().setJavaLanguageServer(languageServer);
}
@Override
public void stop() {
if(languageServer != null)
languageServer.exit();
}
@Override
public void hideSignature() {
activity().getBinding().symbolText.setVisibility(View.GONE);
}
@Override
public void didOpen(DidOpenTextDocumentParams p) {
if(languageServer != null)
languageServer.didOpen(p);
else activity().addPendingMessage(activity().createMessage(JavaLanguageServer.Method.DID_OPEN, gson.toJson(p)));
}
@Override
public void didClose(DidCloseTextDocumentParams p) {
if(languageServer != null)
languageServer.didClose(p);
else activity().addPendingMessage(activity().createMessage(JavaLanguageServer.Method.DID_CLOSE, gson.toJson(p)));
}
@Override
public void didChange(DidChangeTextDocumentParams params) {
if(languageServer != null)
languageServer.didChange(params);
else activity().addPendingMessage(activity().createMessage(JavaLanguageServer.Method.DID_CHANGE, gson.toJson(params)));
}
@Override
public void didSave(DidSaveTextDocumentParams params) {
if(languageServer != null)
languageServer.didSave(params);
else activity().addPendingMessage(activity().createMessage(JavaLanguageServer.Method.DID_SAVE, gson.toJson(params)));
}
@Override
public void didChangeWatchedFiles(DidChangeWatchedFilesParams params) {
if(languageServer != null)
languageServer.didChangeWatchedFiles(params);
else activity().addPendingMessage(activity().createMessage(JavaLanguageServer.Method.DID_SAVE, gson.toJson(params)));
}
@Override
public void signatureHelp(TextDocumentPositionParams params, File file) {
if(languageServer != null)
languageServer.signatureHelp(params, file);
else activity().addPendingMessage(activity().createMessage(JavaLanguageServer.Method.DID_SAVE, gson.toJson(params)));
}
@Override
public void findDefinition(TextDocumentPositionParams params) {
if(languageServer == null || !languageServer.isStarted()) return;
languageServer.findDefinition(params);
activity().getProgressSheet(R.string.msg_finding_definition).show(activity().getSupportFragmentManager(), "definition_progress");
}
@Override
public void findReferences(ReferenceParams params) {
if(languageServer == null || !languageServer.isStarted()) return;
languageServer.findReferences(params);
activity().getProgressSheet(R.string.msg_finding_references).show(activity().getSupportFragmentManager(), "references_progress");
}
@Override
public void performCodeActions(final File file, List<CodeAction> actions) {
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(activity(), R.style.AppTheme_MaterialAlertDialog);
builder.setTitle(R.string.msg_code_actions);
builder.setItems(mapActionsAsArray(removeDuplicates(actions)), (d, w) -> {
d.dismiss();
int index = w;
confirmActionIfNecessary(file, actions.get(index));
});
builder.setCancelable(true);
builder.show();
}
/**
* Shows a dialog to user to get confirmation from user to modify files other than this one
* If the code action is only in the current file, directly performs the actions
*/
private void confirmActionIfNecessary(File file, CodeAction action) {
if(isInSameFile(file, action.edit.changes)) {
performCodeAction(file, action);
} else {
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(activity(), R.style.AppTheme_MaterialAlertDialog);
builder.setTitle(R.string.title_confirm_changes);
builder.setMessage(activity().getString(R.string.msg_confirm_changes, getFileNamesOfEdits(action.edit.changes.keySet())));
builder.setPositiveButton(android.R.string.yes, (d, w) -> performCodeAction(file, action));
builder.setNegativeButton(android.R.string.no, null);
builder.show();
}
}
private String getFileNamesOfEdits(Set<URI> names) {
return TextUtils.join("\n", names.stream().filter(n -> n != null).map(File::new).collect(Collectors.toList()));
}
/**
* Some code actions may contain duplicate items <br>
* Removes duplicate CodeActions from the given list
*
* @param actions CodeActions to filter
*/
private List<CodeAction> removeDuplicates(List<CodeAction> actions) {
List<CodeAction> filtered = new ArrayList<>();
for(CodeAction action : actions) {
if(filtered.contains(action)) continue;
filtered.add(action);
}
return filtered;
}
private CharSequence[] mapActionsAsArray(List<CodeAction> actions) {
String[] result = new String[actions.size()];
for(int i=0;i<actions.size();i++) {
CodeAction action = actions.get(i);
if(action == null) continue;
result[i] = actions.get(i).title;
}
return result;
}
/**
* Performs given code action
*
* @param file Currently opened file. This is provided by current CodeEditor when invoking JLSRequestor#performCodeActions
*/
private void performCodeAction(File file, CodeAction action) {
if(action != null && action.edit != null && action.edit.changes != null) {
Map<URI, List<TextEdit>> changes = action.edit.changes;
/**
* TODO: Ask for confirmation before writing file other than currently opened file
*/
for(Map.Entry<URI, List<TextEdit>> entry : changes.entrySet()) {
if(entry == null && entry.getKey() == null || entry.getValue() == null) continue;
performEdits(new File(entry.getKey()), entry.getValue());
}
}
}
/**
* Performs edits in specified file. If the file is already opened,
* it will find the EditorFragment containing this file and perform the edits in its Content
* <br><br>
* If the file is not opened, it will read that file, edit it accordingly and will save it.
*
* @param file The file to edit
* @param edits The edits to perform
*/
public void performEdits(File file, List<TextEdit> edits) {
if(edits == null || edits.size() <= 0) return;
boolean wroteInFragment = false;
if(activity().getPagerAdapter() != null) {
EditorFragment frag = activity().getPagerAdapter().findEditorByFile(file);
if(frag != null && frag.getEditor() != null && frag.getEditor().getText() != null) {
performEdits(frag.getEditor().getText(), edits);
wroteInFragment = true;
}
}
if(!wroteInFragment) {
readAndPerformEdits(file, edits);
}
}
/**
* Performs given text edits in provided content
*
* @param text Content to edit
* @param edits The edits to perform
*/
private void performEdits(Content text, List<TextEdit> edits) {
for(TextEdit edit : edits) {
if(edit == null) continue;
if(edit.newText == null || edit.range == null) continue;
final Range range = edit.range;
text.replace(
range.start.line,
range.start.column,
range.end.line,
range.end.column,
edit.newText
);
}
}
/**
* Reads the provided file and performs the given edits
*
* @param file The file to read
* @param edits Edits to perform
*/
private void readAndPerformEdits(File file, List<TextEdit> edits) {
String text = FileIOUtils.readFile2String(file);
if(text == null || text.length() <= 0) return;
Content content = new Content(null, text);
performEdits(content, edits);
FileIOUtils.writeFileFromString(file, content.toString());
notifyFileChanged(file);
}
/**
* Checks if all the changes are in the same (currently opened) file
*
* @param current File to check
* @param changes The changes to check
* @return Whether all changes are in the currently opened file
*/
private boolean isInSameFile(File current, Map<URI, List<TextEdit>> changes) {
for(URI uri : changes.keySet()) {
File file = new File(uri);
if(file.exists()
&& !file.getAbsolutePath().equals(current.getAbsolutePath())) {
return false;
}
}
return true;
}
public void notifyFileCreated(File file) {
notifyExternalFileChange(file, FileChangeType.Created);
activity().openFile(file);
}
public void notifyFileDeleted(File file) {
notifyExternalFileChange(file, FileChangeType.Deleted);
}
public void notifyFileChanged(File file) {
notifyExternalFileChange(file, FileChangeType.Changed);
}
private void notifyExternalFileChange(File file, int changeType) {
if(!file.getName().endsWith(".java")) return;
DidChangeWatchedFilesParams p = new DidChangeWatchedFilesParams();
p.changes.add(new FileEvent(file.toURI(), changeType));
didChangeWatchedFiles(p);
}
}
@@ -1,254 +0,0 @@
package com.itsaky.androidide.handlers.jls;
import android.text.SpannableStringBuilder;
import android.text.style.ForegroundColorSpan;
import android.view.View;
import com.blankj.utilcode.util.FileIOUtils;
import com.blankj.utilcode.util.ThrowableUtils;
import com.google.gson.JsonElement;
import com.itsaky.androidide.R;
import com.itsaky.androidide.adapters.DiagnosticsAdapter;
import com.itsaky.androidide.adapters.SearchListAdapter;
import com.itsaky.androidide.fragments.EditorFragment;
import com.itsaky.androidide.handlers.IDEHandler;
import com.itsaky.androidide.language.java.server.JavaLanguageServer;
import com.itsaky.androidide.models.DiagnosticGroup;
import com.itsaky.androidide.models.SearchResult;
import com.itsaky.lsp.Diagnostic;
import com.itsaky.lsp.JavaColors;
import com.itsaky.lsp.JavaReportProgressParams;
import com.itsaky.lsp.JavaStartProgressParams;
import com.itsaky.lsp.LanguageClient;
import com.itsaky.lsp.Location;
import com.itsaky.lsp.Message;
import com.itsaky.lsp.ParameterInformation;
import com.itsaky.lsp.PublishDiagnosticsParams;
import com.itsaky.lsp.ShowMessageParams;
import com.itsaky.lsp.SignatureHelp;
import com.itsaky.lsp.SignatureInformation;
import com.itsaky.toaster.Toaster;
import io.github.rosemoe.editor.text.Content;
import io.github.rosemoe.editor.widget.CodeEditor;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class LanguageClientHandler extends IDEHandler implements LanguageClient {
private final Map<File, List<Diagnostic>> diagnostics = new HashMap<>();
public LanguageClientHandler(Provider provider) {
super(provider);
}
@Override
public void start() {
// Unimplemented
}
@Override
public void stop() {
// Unimplemented
}
@Override
public void publishDiagnostics(PublishDiagnosticsParams params) {
boolean error = params == null || params.diagnostics == null || params.diagnostics.size() <= 0;
activity().handleDiagnosticsResultVisibility(error);
if(error) return;
File file = new File(params.uri);
if(!(file.exists() && file.isFile())) return;
diagnostics.put(file, params.diagnostics);
activity().getDiagnosticsList().setAdapter(newDiagnosticsAdapter());
EditorFragment editor = null;
if(activity().getPagerAdapter() != null && (editor = activity().getPagerAdapter().findEditorByFile(new File(params.uri))) != null) {
editor.setDiagnostics(params.diagnostics);
}
}
@Override
public void gotoDefinition(List<Location> locations) {
if(activity().getProgressSheet() != null && activity().getProgressSheet().isShowing())
activity().getProgressSheet().dismiss();
if(locations == null || locations.size() <= 0) {
activity().getApp().toast(R.string.msg_no_definition, Toaster.Type.ERROR);
return;
}
}
@Override
public void references(List<Location> references) {
if(activity().getProgressSheet() != null && activity().getProgressSheet().isShowing())
activity().getProgressSheet().dismiss();
boolean error = references == null || references.size() <= 0;
activity().handleSearchResultVisibility(error);
if(error) {
activity().getApp().toast(R.string.msg_no_references, Toaster.Type.INFO);
activity().getSearchResultList().setAdapter(new SearchListAdapter(null, null, null));
return;
}
final Map<File, List<SearchResult>> results = new HashMap<>();
// for(int i=0;i<references.size();i++) {
// try {
// final Location loc = references.get(i);
// if(loc == null || loc.uri == null || loc.range == null) continue;
// final File file = new File(loc.uri);
// if(!file.exists() || !file.isFile()) continue;
// EditorFragment frag = activity().getPagerAdapter().findEditorByFile(file);
// Content content;
// if(frag != null && frag.getEditor() != null)
// content = frag.getEditor().getText();
// else content = new Content(null, FileIOUtils.readFile2String(file));
// final List<SearchResult> matches = results.containsKey(file) ? results.get(file) : new ArrayList<>();
// matches.add(
// new SearchResult(
// loc.range,
// file,
// content.getLineString(loc.range.start.line),
// content.subContent(
// loc.range.start.line,
// loc.range.start.column,
// loc.range.end.line,
// loc.range.end.column
// ).toString()
// )
// );
// results.put(file, matches);
// } catch (Throwable th) {
// LOG.error(ThrowableUtils.getFullStackTrace(th));
// }
// }
activity().handleSearchResults(results);
}
@Override
public void signatureHelp(SignatureHelp signature, File file) {
if(signature == null || signature.signatures == null) {
activity().getBinding().symbolText.setVisibility(View.GONE);
return;
}
SignatureInformation info = signature.signatures.get(signature.activeSignature);
activity().getBinding().symbolText.setText(formatSignature(info, signature.activeParameter));
final EditorFragment frag = activity().getPagerAdapter().findEditorByFile(file);
if(frag != null) {
final CodeEditor editor = frag.getEditor();
final float[] cursor = editor.getCursorPosition();
float x = editor.updateCursorAnchor() - (activity().getBinding().symbolText.getWidth() / 2);
float y = activity().getBinding().editorAppBarLayout.getHeight() + (cursor[0] - editor.getRowHeight() - editor.getOffsetY() - activity().getBinding().symbolText.getHeight());
activity().getBinding().symbolText.setVisibility(View.VISIBLE);
activity().positionViewWithinScreen(activity().getBinding().symbolText, x, y);
}
}
@Override
public void javaColors(JavaColors colors) {
final File file = new File(colors.uri);
final EditorFragment editor = this.activity().getPagerAdapter().findEditorByFile(file);
if(editor != null)
editor.setJavaColors(colors);
}
@Override
public void onServerStarted(int currentId) {
new Thread(() -> {
final JavaLanguageServer server = provider.provideJLSHandler().getJLS();
while(server != null && !activity().pendingMessages.isEmpty()) {
Message msg = activity().pendingMessages.pop();
server.send(msg);
}
}).start();
}
public DiagnosticsAdapter newDiagnosticsAdapter() {
return new DiagnosticsAdapter(mapAsGroup(this.diagnostics), activity());
}
/**
* Formats (highlights) a method signature
*
* @param signature Signature information
* @param paramIndex Currently active parameter index
*/
private CharSequence formatSignature(SignatureInformation signature, int paramIndex) {
String name = signature.label;
name = name.substring(0, name.indexOf("("));
SpannableStringBuilder sb = new SpannableStringBuilder();
sb.append(name, new ForegroundColorSpan(0xffffffff), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
sb.append("(", new ForegroundColorSpan(0xff4fc3f7), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
List<ParameterInformation> params = signature.parameters;
for(int i=0;i<params.size();i++) {
int color = i == paramIndex ? 0xffff6060 : 0xffffffff;
final ParameterInformation info = params.get(i);
if(i == params.size() - 1) {
sb.append(info.label, new ForegroundColorSpan(color), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
} else {
sb.append(info.label, new ForegroundColorSpan(color), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
sb.append(",", new ForegroundColorSpan(0xff4fc3f7), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
sb.append(" ");
}
}
sb.append(")", new ForegroundColorSpan(0xff4fc3f7), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
return sb;
}
private List<DiagnosticGroup> mapAsGroup(Map<File, List<Diagnostic>> diags) {
List<DiagnosticGroup> groups = new ArrayList<>();
if(diags == null || diags.size() <= 0)
return groups;
for(File file : diags.keySet()) {
List<Diagnostic> fileDiags = diags.get(file);
if(fileDiags == null || fileDiags.size() <= 0)
continue;
DiagnosticGroup group = new DiagnosticGroup(R.drawable.ic_language_java, file, fileDiags);
groups.add(group);
}
return groups;
}
/**
* NOT IMPLEMENTED
*/
@Override
public void javaProgressStart(JavaStartProgressParams params) {
}
@Override
public void javaProgressReport(JavaReportProgressParams params) {
}
@Override
public void javaProgressEnd() {
}
@Override
public void showMessage(ShowMessageParams params) {
}
@Override
public void customNotification(String method, JsonElement params) {
}
@Override
public void registerCapability(String method, JsonElement options) {
}
}
@@ -1,8 +1,8 @@
package com.itsaky.androidide.interfaces;
import com.itsaky.androidide.models.DiagnosticGroup;
import com.itsaky.lsp.Diagnostic;
import java.io.File;
import org.eclipse.lsp4j.Diagnostic;
public interface DiagnosticClickListener {
void onGroupClick(DiagnosticGroup group);
@@ -1,28 +0,0 @@
package com.itsaky.androidide.interfaces;
import com.itsaky.lsp.CodeAction;
import com.itsaky.lsp.DidChangeTextDocumentParams;
import com.itsaky.lsp.DidChangeWatchedFilesParams;
import com.itsaky.lsp.DidCloseTextDocumentParams;
import com.itsaky.lsp.DidOpenTextDocumentParams;
import com.itsaky.lsp.DidSaveTextDocumentParams;
import com.itsaky.lsp.ReferenceParams;
import com.itsaky.lsp.TextDocumentPositionParams;
import java.io.File;
import java.util.List;
public interface JLSRequestor {
void hideSignature();
void didOpen(DidOpenTextDocumentParams params);
void didClose(DidCloseTextDocumentParams params);
void didChange(DidChangeTextDocumentParams params);
void didSave(DidSaveTextDocumentParams params);
void didChangeWatchedFiles(DidChangeWatchedFilesParams params);
void signatureHelp(TextDocumentPositionParams params, File file);
void findDefinition(TextDocumentPositionParams params);
void findReferences(ReferenceParams params);
void performCodeActions(File file, List<CodeAction> actions);
}
@@ -1,5 +0,0 @@
package com.itsaky.androidide.interfaces;
public interface LanguageServerListener
{
}
@@ -1,17 +1,13 @@
package com.itsaky.androidide.language.groovy;
import com.itsaky.androidide.models.GradleSuggestItem;
import com.itsaky.androidide.models.SuggestItem;
import io.github.rosemoe.editor.interfaces.AutoCompleteProvider;
import io.github.rosemoe.editor.text.TextAnalyzeResult;
import io.github.rosemoe.editor.widget.CodeEditor;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.eclipse.lsp4j.InsertTextFormat;
import org.eclipse.lsp4j.CompletionItemKind;
import org.eclipse.lsp4j.InsertTextFormat;
public class GroovyAutoComplete implements AutoCompleteProvider {
@@ -3,7 +3,6 @@ package com.itsaky.androidide.language.java;
import com.itsaky.androidide.utils.Logger;
import io.github.rosemoe.editor.interfaces.AutoCompleteProvider;
import io.github.rosemoe.editor.text.TextAnalyzeResult;
import io.github.rosemoe.editor.widget.CodeEditor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -16,20 +15,15 @@ import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.eclipse.lsp4j.services.LanguageServer;
import com.itsaky.androidide.utils.JSONUtility;
import com.itsaky.androidide.lsp.LSPProvider;
public class JavaAutoComplete implements AutoCompleteProvider {
private JavaLanguage language;
private CompletableFuture<Either<List<CompletionItem>, CompletionList>> future;
public JavaAutoComplete(JavaLanguage lang) {
this.language = lang;
}
@Override
public List<CompletionItem> getAutoCompleteItems(String fileUri, String prefix, boolean isInCodeBlock, TextAnalyzeResult colors, int index, int line, int column) throws Exception {
LanguageServer languageServer = language.getLanguageServer();
LanguageServer languageServer = LSPProvider.getServerForLanguage(LSPProvider.LANGUAGE_JAVA);
if(languageServer != null && fileUri != null) {
if(future != null && !future.isDone()) future.cancel(true);
@@ -50,7 +44,6 @@ public class JavaAutoComplete implements AutoCompleteProvider {
return sort(either.getRight().getItems());
} catch (Throwable th) {
LOG.error("Java completion error: ", th);
// Ignore this exception. An empty list will be sent after this
// This exeption is thrown probably because the request was cancelled or interrupted
}
@@ -5,7 +5,6 @@ import com.itsaky.androidide.language.java.manager.JavaCharacter;
import com.itsaky.androidide.language.java.parser.JavaLexer;
import com.itsaky.androidide.language.java.parser.JavaParser;
import com.itsaky.androidide.lsp.LSPProvider;
import com.itsaky.androidide.models.AndroidProject;
import io.github.rosemoe.editor.interfaces.AutoCompleteProvider;
import io.github.rosemoe.editor.interfaces.CodeAnalyzer;
import io.github.rosemoe.editor.interfaces.NewlineHandler;
@@ -18,14 +17,12 @@ import org.eclipse.lsp4j.services.LanguageServer;
public class JavaLanguage extends BaseLanguage {
private JavaLanguageAnalyzer analyzer;
private JavaAutoComplete complete;
private AndroidProject project;
private final JavaLanguageAnalyzer analyzer;
private final JavaAutoComplete complete;
public JavaLanguage(AndroidProject project) {
public JavaLanguage() {
this.analyzer = new JavaLanguageAnalyzer();
this.complete = new JavaAutoComplete(this);
this.project = project;
this.complete = new JavaAutoComplete();
}
@Override
@@ -1,22 +1,16 @@
package com.itsaky.androidide.language.java;
import com.itsaky.androidide.syntax.lexer.impls.java.JavaLexerImpl;
import com.itsaky.lsp.Range;
import io.github.rosemoe.editor.interfaces.CodeAnalyzer;
import io.github.rosemoe.editor.text.TextAnalyzeResult;
import io.github.rosemoe.editor.text.TextAnalyzer;
import java.util.List;
import com.itsaky.lsp.JavaColors;
public class JavaLanguageAnalyzer implements CodeAnalyzer {
private JavaColors colors;
@Override
public void analyze(CharSequence content, TextAnalyzeResult colors, TextAnalyzer.AnalyzeThread.Delegate delegate) {
try {
JavaLexerImpl lexer = new JavaLexerImpl(content.toString(), colors);
lexer.setJavaColors(this.colors);
lexer.init();
// Adding spans, hex colors, block lines and all is handled by JavaLexerImpl
@@ -34,8 +28,4 @@ public class JavaLanguageAnalyzer implements CodeAnalyzer {
} catch (Throwable e) {
}
}
public void setJavaColors(JavaColors colors) {
this.colors = colors;
}
}
@@ -1,429 +0,0 @@
package com.itsaky.androidide.language.java.server;
import android.annotation.SuppressLint;
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.google.gson.reflect.TypeToken;
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.CodeActionParams;
import com.itsaky.lsp.CompletionItem;
import com.itsaky.lsp.CompletionList;
import com.itsaky.lsp.DidChangeConfigurationParams;
import com.itsaky.lsp.DidChangeTextDocumentParams;
import com.itsaky.lsp.DidChangeWatchedFilesParams;
import com.itsaky.lsp.DidCloseTextDocumentParams;
import com.itsaky.lsp.DidOpenTextDocumentParams;
import com.itsaky.lsp.DidSaveTextDocumentParams;
import com.itsaky.lsp.DocumentFormattingParams;
import com.itsaky.lsp.InitializeParams;
import com.itsaky.lsp.JavaColors;
import com.itsaky.lsp.JavaReportProgressParams;
import com.itsaky.lsp.JavaStartProgressParams;
import com.itsaky.lsp.LanguageClient;
import com.itsaky.lsp.Location;
import com.itsaky.lsp.Message;
import com.itsaky.lsp.PublishDiagnosticsParams;
import com.itsaky.lsp.ReferenceParams;
import com.itsaky.lsp.SignatureHelp;
import com.itsaky.lsp.TextDocumentPositionParams;
import com.itsaky.lsp.TextEdit;
import java.io.BufferedOutputStream;
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 BufferedOutputStream send;
private boolean started;
private final ConcurrentHashMap<Integer, Integer> completionRequests = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Integer, Integer> formattingRequests = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Integer, Integer> definitionRequests = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Integer, Integer> referencesRequests = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Integer, Integer> codeActionRequests = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Integer, File> signatureRequests = new ConcurrentHashMap<>();
private final BlockingQueue<CompletionList> completionResponseQueue = new ArrayBlockingQueue<>(20);
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 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();
sendInit();
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line;
while((line = reader.readLine()) != null) {
final String temp = line;
if(temp == null || temp.trim().length() <= 0) continue;
ThreadUtils.runOnUiThread(() -> {
onServerOut(temp);
});
}
socket.close();
LOG.info("Socket disconnected");
} catch (Throwable e) {
logEx(e);
}
}).start();
}
public void initialize(AndroidProject project) {
this.project = project;
sendInit();
}
private Socket connectAndGet() {
while(server == null || send == null || !server.isConnected()) {
try {
server = new Socket(SOCKET_HOST, SOCKET_PORT);
OutputStream o = server.getOutputStream();
if(o != null)
send = new BufferedOutputStream(o);
} catch (IOException e) {
try {
Thread.sleep(100);
} catch (InterruptedException e2) {}
}
}
return server;
}
@SuppressLint("NewApi")
protected void sendInit() {
InitializeParams p = new InitializeParams();
p.rootUri = new File(project.getProjectPath()).toURI();
p.roots = java.util.Set.of(p.rootUri, StudioApp.getInstance().getLogSenderDir().toURI());
initialize(p);
initialized();
final List<String> 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<cps.size();i++) classpaths.add(cps.get(i));
java.add("classPath", classpaths);
settings.add("java", java);
DidChangeConfigurationParams p2 = new DidChangeConfigurationParams();
p2.settings = settings;
configurationChanged(p2);
ThreadUtils.runOnUiThread(() -> {
if(client != null)
client.onServerStarted(UNIVERSION_ID);
});
}
public void exit() {
write(Method.EXIT, "");
try {
server.close();
} catch (Throwable th) {}
}
public boolean isStarted() {
return started;
}
public void initialize(InitializeParams p) {
write(Method.INITIALIZE, gson.toJson(p));
}
public void initialized() {
write(Method.INITIALIZED, "{}");
}
public void configurationChanged(DidChangeConfigurationParams p) {
write(Method.CHANGED_CONFIGURATION, gson.toJson(p));
}
public void didOpen(DidOpenTextDocumentParams p) {
write(Method.DID_OPEN, gson.toJson(p));
}
public void didClose(DidCloseTextDocumentParams p) {
write(Method.DID_CLOSE, gson.toJson(p));
}
public void didChange(DidChangeTextDocumentParams p) {
write(Method.DID_CHANGE, gson.toJson(p));
}
public void didSave(DidSaveTextDocumentParams p) {
write(Method.DID_SAVE, gson.toJson(p));
}
public void didChangeWatchedFiles(DidChangeWatchedFilesParams params) {
write(Method.CHANGED_WATCHED_FILES, gson.toJson(params));
}
public void buildModified() {
write(Method.BUILD_MODIFIED, "");
}
public Pair<Integer, CompletionList> completion(TextDocumentPositionParams p) {
int id = write(Method.COMPLETION, gson.toJson(p));
completionRequests.put(id, id);
CompletionList result = null;
try {
result = completionResponseQueue.take();
} catch (InterruptedException e) {}
return Pair.create(id, result);
}
public void resolveCompletion(CompletionItem item) {
write(Method.RESOLVE_COMPLETION, gson.toJson(item));
}
public List<TextEdit> formatting(DocumentFormattingParams params) {
int id = write(Method.FORMATTING, gson.toJson(params));
formattingRequests.put(id, id);
List<TextEdit> edits = null;
return edits;
}
public void codeActions(CodeActionParams p) {
int id = write(Method.CODE_ACTION, gson.toJson(p));
codeActionRequests.put(id, id);
}
public void signatureHelp(TextDocumentPositionParams params, File file) {
int id = write(Method.SIGNATURE_HELP, gson.toJson(params));
signatureRequests.put(id, file);
}
public void findDefinition(TextDocumentPositionParams params) {
int id = write(Method.DEFINITION, gson.toJson(params));
definitionRequests.put(id, id);
}
public void findReferences(ReferenceParams params) {
int id = write(Method.REFERENCES, gson.toJson(params));
referencesRequests.put(id, id);
}
public void cancelRequest(int requestId) {
CancelParams p = new CancelParams();
p.id = requestId;
write(Method.CANCEL_REQUEST, gson.toJson(p));
}
public int send(Message msg) {
final int id = UNIVERSION_ID++;
msg.id = id;
if(this.send != null) {
// Message must end with a \n so that server can read it correctly
final String body = gson.toJson(msg) + "\n";
final byte[] bodyBytes = body.getBytes(StandardCharsets.UTF_8);
new Thread(() -> {
try {
send.write(bodyBytes);
send.flush();
} catch (Throwable e) {
LOG.error("Error writing to server: " + ThrowableUtils.getFullStackTrace(e));
}
}).start();
}
return id;
}
private int write(String method, String data) {
Message msg = new Message();
msg.jsonrpc = "2.0";
msg.method = method;
msg.params = new JsonParser().parse(data);
return send(msg);
}
private void logEx(Throwable th) {
LOG.error(ThrowableUtils.getFullStackTrace(th));
}
@Override
public void output(CharSequence charSequence) {
LOG.verbose(charSequence);
}
/**
* This method is called on UI Thread, do not perform time consuming or networking tasks
*/
private void onServerOut(String line) {
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.REGISTER_CAPABILITY)) {
started = true;
} else if(method.equals(Method.PUBLISH_DIAGNOSTICS)) {
LOG.debug(line);
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);
} else if(method.equals(Method.JLS_PROGRESS_REPORT)) {
JavaReportProgressParams params = gson.fromJson(obj.get(Key.PARAMS).toString(), JavaReportProgressParams.class);
client.javaProgressReport(params);
} else if(method.equals(Method.JLS_PROGRESS_END)) {
client.javaProgressEnd();
} else if(method.equals(Method.JAVA_COLORS)) {
JavaColors colors = gson.fromJson(obj.get(Key.PARAMS).toString(), JavaColors.class);
client.javaColors(colors);
}
} else if(obj.has(Key.ID)) {
if(obj.has(Key.ERROR)) {
/**
* We got an error
* TODO Handle this error
*/
return;
}
final int id = obj.get(Key.ID).getAsInt();
if(completionRequests.containsKey(id)) {
final CompletionList list = gson.fromJson(obj.get(Key.RESULT).getAsJsonObject(), CompletionList.class);
completionResponseQueue.put(list);
} else if(formattingRequests.containsKey(id)) {
// TODO: Implement this
} else if(signatureRequests.containsKey(id)) {
try {
SignatureHelp signature = gson.fromJson(obj.get(Key.RESULT).getAsJsonObject(), SignatureHelp.class);
client.signatureHelp(signature, signatureRequests.get(id));
} catch ( Throwable th) {
client.signatureHelp(null, null);
}
} else if(definitionRequests.containsKey(id)) {
try {
final List<Location> locations = gson.fromJson(obj.get(Key.RESULT).getAsJsonArray(), new TypeToken<List<Location>>(){}.getType());
client.gotoDefinition(locations);
} catch (Throwable th) {
client.gotoDefinition(null);
}
} else if(referencesRequests.containsKey(id)) {
try {
final List<Location> locations = gson.fromJson(obj.get(Key.RESULT).getAsJsonArray(), new TypeToken<List<Location>>(){}.getType());
client.references(locations);
} catch (Throwable th) {
client.references(null);
}
} else if(codeActionRequests.containsKey(id)) {
// TODO: Implement code actions
}
}
} catch (Throwable th) {}
}
private static final Logger LOG = Logger.instance("JavaLanguageServer");
public static 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 BUILD_MODIFIED = "project/buildModified";
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 REFERENCES = "textDocument/references";
public static final String PREPARE_RENAME = "textDocument/prepareRename";
public static final String RENAME = "textDocument/rename";
public static final String FORMATTING = "textDocument/formatting";
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";
public static final String REGISTER_CAPABILITY = "client/registerCapability";
}
public static 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";
public static final String STATICS = "statics";
public static final String FIELDS = "fields";
public static final String ERROR = "error";
}
}
@@ -2,18 +2,12 @@ package com.itsaky.androidide.language.xml;
import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.language.xml.completion.XMLCompletionService;
import com.itsaky.androidide.models.CompletionListItem;
import com.itsaky.androidide.models.SuggestItem;
import com.itsaky.androidide.utils.EitherOfResult;
import io.github.rosemoe.editor.interfaces.AutoCompleteProvider;
import io.github.rosemoe.editor.text.TextAnalyzeResult;
import io.github.rosemoe.editor.widget.CodeEditor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
public class XMLAutoComplete implements AutoCompleteProvider {
@@ -1,24 +1,44 @@
package com.itsaky.androidide.lsp;
import android.text.SpannableStringBuilder;
import android.text.style.ForegroundColorSpan;
import android.view.View;
import com.blankj.utilcode.util.FileIOUtils;
import com.blankj.utilcode.util.FileUtils;
import com.blankj.utilcode.util.ThrowableUtils;
import com.google.gson.Gson;
import com.itsaky.androidide.EditorActivity;
import com.itsaky.androidide.R;
import com.itsaky.androidide.adapters.DiagnosticsAdapter;
import com.itsaky.androidide.adapters.SearchListAdapter;
import com.itsaky.androidide.fragments.EditorFragment;
import com.itsaky.androidide.interfaces.EditorActivityProvider;
import com.itsaky.androidide.models.DiagnosticGroup;
import com.itsaky.androidide.models.SearchResult;
import com.itsaky.androidide.utils.LSPUtils;
import com.itsaky.androidide.utils.Logger;
import io.github.rosemoe.editor.text.Content;
import io.github.rosemoe.editor.widget.CodeEditor;
import java.io.File;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.LocationLink;
import org.eclipse.lsp4j.ParameterInformation;
import org.eclipse.lsp4j.PublishDiagnosticsParams;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.ShowDocumentParams;
import org.eclipse.lsp4j.ShowDocumentResult;
import org.eclipse.lsp4j.SignatureHelp;
import org.eclipse.lsp4j.SignatureInformation;
import org.eclipse.lsp4j.services.LanguageClient;
import org.eclipse.lsp4j.services.LanguageServer;
import com.itsaky.androidide.utils.JSONUtility;
/**
* AndroidIDE specific implementation of the LanguageClient
@@ -29,7 +49,8 @@ public abstract class IDELanguageClient implements LanguageClient {
protected static final Logger LOG = Logger.instance("IDELanguageClient");
protected EditorActivityProvider activityProvider;
private final Map<File, List<Diagnostic>> diagnostics = new HashMap<>();
private final StarterListener starterListener;
private final OnConnectedListener onConnectedListener;
@@ -82,18 +103,118 @@ public abstract class IDELanguageClient implements LanguageClient {
return activityProvider.provide();
}
@Override
public void publishDiagnostics(PublishDiagnosticsParams params) {
boolean error = params == null || params.getDiagnostics() == null || params.getDiagnostics().isEmpty();
activity().handleDiagnosticsResultVisibility(error);
if(error) return;
File file = new File(URI.create(params.getUri()));
if(!file.exists() || !file.isFile()) return;
diagnostics.put(file, params.getDiagnostics());
activity().getDiagnosticsList().setAdapter(newDiagnosticsAdapter());
EditorFragment editor = null;
if(activity().getPagerAdapter() != null && (editor = activity().getPagerAdapter().findEditorByFile(new File(URI.create(params.getUri())))) != null) {
editor.setDiagnostics(params.getDiagnostics());
}
}
/**
* Called by {@link io.github.rosemoe.editor.widget.CodeEditor CodeEditor} to show locations in EditorActivity
* Called by {@link io.github.rosemoe.editor.widget.CodeEditor CodeEditor} to show signature help in EditorActivity
*/
public void showLocations(List<? extends Location> locations) {
LOG.info("showLocations", JSONUtility.prettyPrinter.toJson(locations));
public void showSignatureHelp(SignatureHelp signature, File file) {
if(signature == null || signature.getSignatures() == null) {
activity().getBinding().symbolText.setVisibility(View.GONE);
return;
}
SignatureInformation info = signature.getSignatures().get(signature.getActiveSignature());
activity().getBinding().symbolText.setText(formatSignature(info, signature.getActiveParameter()));
final EditorFragment frag = activity().getPagerAdapter().findEditorByFile(file);
if(frag != null) {
final CodeEditor editor = frag.getEditor();
final float[] cursor = editor.getCursorPosition();
float x = editor.updateCursorAnchor() - (activity().getBinding().symbolText.getWidth() / 2);
float y = activity().getBinding().editorAppBarLayout.getHeight() + (cursor[0] - editor.getRowHeight() - editor.getOffsetY() - activity().getBinding().symbolText.getHeight());
activity().getBinding().symbolText.setVisibility(View.VISIBLE);
activity().positionViewWithinScreen(activity().getBinding().symbolText, x, y);
}
}
/**
* Called by {@link io.github.rosemoe.editor.widget.CodeEditor CodeEditor} to show locations in EditorActivity
*/
public void showLocations(List<? extends Location> locations) {
// Cannot show anything if the activity() is null
if(activity() == null) {
return;
}
boolean error = locations == null || locations.isEmpty();
activity().handleSearchResultVisibility(error);
if(error) {
activity().getSearchResultList().setAdapter(new SearchListAdapter(null, null, null));
return;
}
final Map<File, List<SearchResult>> results = new HashMap<>();
for(int i=0;i<locations.size();i++) {
try {
final Location loc = locations.get(i);
if(loc == null || loc.getUri() == null || loc.getRange() == null) continue;
final File file = new File(URI.create(loc.getUri()));
if(!file.exists() || !file.isFile()) continue;
EditorFragment frag = activity().getPagerAdapter().findEditorByFile(file);
Content content;
if(frag != null && frag.getEditor() != null)
content = frag.getEditor().getText();
else content = new Content(null, FileIOUtils.readFile2String(file));
final List<SearchResult> matches = results.containsKey(file) ? results.get(file) : new ArrayList<>();
matches.add(
new SearchResult(
loc.getRange(),
file,
content.getLineString(loc.getRange().getStart().getLine()),
content.subContent(
loc.getRange().getStart().getLine(),
loc.getRange().getStart().getCharacter(),
loc.getRange().getEnd().getLine(),
loc.getRange().getEnd().getCharacter()
).toString()
)
);
results.put(file, matches);
} catch (Throwable th) {
LOG.error(ThrowableUtils.getFullStackTrace(th));
}
}
activity().handleSearchResults(results);
}
/**
* Called by {@link io.github.rosemoe.editor.widget.CodeEditor CodeEditor} to show location links in EditorActivity.
* These location links are mapped as {@link org.eclipse.lsp4j.Location Location} and then {@link #showLocations(List) } is called.
*/
public void showLocationLinks(List<? extends LocationLink> locations) {
LOG.info("showLocationLinks", JSONUtility.prettyPrinter.toJson(locations));
if(locations == null || locations.size() <= 0) {
return;
}
showLocations(locations
.stream()
.filter(l -> l != null)
.map(l -> asLocation(l))
.filter(l -> l != null)
.collect(Collectors.toList())
);
}
/**
@@ -138,6 +259,62 @@ public abstract class IDELanguageClient implements LanguageClient {
public void telemetryEvent(Object p1) {
LOG.info("telemetryEvent: ", gson.toJson(p1));
}
private Location asLocation(LocationLink link) {
if(link == null || link.getTargetRange() == null || link.getTargetUri() == null) return null;
final Location location = new Location();
location.setUri(link.getTargetUri());
location.setRange(link.getTargetRange());
return location;
}
/**
* Formats (highlights) a method signature
*
* @param signature Signature information
* @param paramIndex Currently active parameter index
*/
private CharSequence formatSignature(SignatureInformation signature, int paramIndex) {
String name = signature.getLabel();
name = name.substring(0, name.indexOf("("));
SpannableStringBuilder sb = new SpannableStringBuilder();
sb.append(name, new ForegroundColorSpan(0xffffffff), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
sb.append("(", new ForegroundColorSpan(0xff4fc3f7), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
List<ParameterInformation> params = signature.getParameters();
for(int i=0;i<params.size();i++) {
int color = i == paramIndex ? 0xffff6060 : 0xffffffff;
final ParameterInformation info = params.get(i);
if(i == params.size() - 1) {
sb.append(info.getLabel().getLeft() + "", new ForegroundColorSpan(color), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
} else {
sb.append(info.getLabel().getLeft() + "", new ForegroundColorSpan(color), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
sb.append(",", new ForegroundColorSpan(0xff4fc3f7), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
sb.append(" ");
}
}
sb.append(")", new ForegroundColorSpan(0xff4fc3f7), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
return sb;
}
private List<DiagnosticGroup> mapAsGroup(Map<File, List<Diagnostic>> diags) {
List<DiagnosticGroup> groups = new ArrayList<>();
if(diags == null || diags.size() <= 0)
return groups;
for(File file : diags.keySet()) {
List<Diagnostic> fileDiags = diags.get(file);
if(fileDiags == null || fileDiags.size() <= 0)
continue;
DiagnosticGroup group = new DiagnosticGroup(R.drawable.ic_language_java, file, fileDiags);
groups.add(group);
}
return groups;
}
public DiagnosticsAdapter newDiagnosticsAdapter() {
return new DiagnosticsAdapter(mapAsGroup(this.diagnostics), activity());
}
/**
* Reports connection progress
@@ -1,7 +1,7 @@
package com.itsaky.androidide.lsp;
import com.google.gson.Gson;
import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.interfaces.EditorActivityProvider;
import com.itsaky.androidide.lsp.client.java.JavaLanguageClient;
import com.itsaky.androidide.shell.ShellServer;
import com.itsaky.androidide.utils.Environment;
@@ -9,32 +9,34 @@ import com.itsaky.androidide.utils.Logger;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import org.eclipse.lsp4j.ClientCapabilities;
import org.eclipse.lsp4j.ClientInfo;
import org.eclipse.lsp4j.ColorProviderCapabilities;
import org.eclipse.lsp4j.CompletionCapabilities;
import org.eclipse.lsp4j.CreateFilesParams;
import org.eclipse.lsp4j.DeleteFilesParams;
import org.eclipse.lsp4j.DidChangeConfigurationCapabilities;
import org.eclipse.lsp4j.DidChangeWatchedFilesCapabilities;
import org.eclipse.lsp4j.DidChangeWatchedFilesParams;
import org.eclipse.lsp4j.ExecuteCommandCapabilities;
import org.eclipse.lsp4j.FileCreate;
import org.eclipse.lsp4j.FileDelete;
import org.eclipse.lsp4j.FileOperationsWorkspaceCapabilities;
import org.eclipse.lsp4j.GeneralClientCapabilities;
import org.eclipse.lsp4j.InitializeParams;
import org.eclipse.lsp4j.InitializeResult;
import org.eclipse.lsp4j.InitializedParams;
import org.eclipse.lsp4j.SynchronizationCapabilities;
import org.eclipse.lsp4j.TextDocumentClientCapabilities;
import org.eclipse.lsp4j.WindowClientCapabilities;
import org.eclipse.lsp4j.WorkspaceClientCapabilities;
import org.eclipse.lsp4j.WorkspaceFolder;
import org.eclipse.lsp4j.services.LanguageServer;
import java.util.concurrent.ExecutionException;
import java.util.Optional;
import org.eclipse.lsp4j.CallHierarchyCapabilities;
import org.eclipse.lsp4j.CodeActionCapabilities;
import org.eclipse.lsp4j.DidChangeConfigurationCapabilities;
import org.eclipse.lsp4j.DidChangeWatchedFilesCapabilities;
import org.eclipse.lsp4j.ExecuteCommandCapabilities;
import org.eclipse.lsp4j.FileOperationsWorkspaceCapabilities;
import org.eclipse.lsp4j.SemanticTokensCapabilities;
import org.eclipse.lsp4j.SynchronizationCapabilities;
import org.eclipse.lsp4j.ColorProviderCapabilities;
import org.eclipse.lsp4j.CompletionCapabilities;
import org.eclipse.lsp4j.CompletionItemCapabilities;
import com.itsaky.androidide.interfaces.EditorActivityProvider;
import org.eclipse.lsp4j.RenameFilesParams;
import org.eclipse.lsp4j.FileRename;
/**
* A manager class to manage Language Server Protocol implementations
@@ -108,6 +110,20 @@ public class LSP {
}
}
/**
* Send initialized notification to Language server
*/
public static void initialized() {
final LanguageServer server = LSPProvider.getServerForLanguage(LSPProvider.LANGUAGE_JAVA);
if (server == null)
return;
// Initialized params do not have any parameters
server.initialized(new InitializedParams());
}
/**
* Sends a shutdown request to the java language server
*/
@@ -142,6 +158,83 @@ public class LSP {
PROVIDER = provider;
}
public static void notifyFileCreated(File file) {
if(!shouldSendFileActionNotification(file)) return;
final CreateFilesParams params =
new CreateFilesParams(
List.of(
new FileCreate(
file.toURI().toString()
)
)
);
for(Map.Entry<String, LanguageServer> entry : LSPProvider.getAvailableServers().entrySet()) {
final LanguageServer server = entry.getValue();
if(server == null) {
server.getWorkspaceService().didCreateFiles(params);
}
}
}
public static void notifyFileDeleted(File file) {
if(!shouldSendFileActionNotification(file)) return;
final DeleteFilesParams params =
new DeleteFilesParams(
List.of(
new FileDelete(
file.toURI().toString()
)
)
);
for(Map.Entry<String, LanguageServer> entry : LSPProvider.getAvailableServers().entrySet()) {
final LanguageServer server = entry.getValue();
if(server == null) {
server.getWorkspaceService().didDeleteFiles(params);
}
}
}
public static void notifyFileRenamed(File file, String newName) {
if(!shouldSendFileActionNotification(file)) return;
final File newFile = new File(file.getParentFile(), newName);
final RenameFilesParams params =
new RenameFilesParams(
List.of(
new FileRename(
file.toURI().toString(), // Old uri
newFile.toURI().toString() // New uri
)
)
);
for(Map.Entry<String, LanguageServer> entry : LSPProvider.getAvailableServers().entrySet()) {
final LanguageServer server = entry.getValue();
if(server == null) {
server.getWorkspaceService().didRenameFiles(params);
}
}
}
public static void notifyWatchedFilesChanged(final DidChangeWatchedFilesParams params) {
for(Map.Entry<String, LanguageServer> entry : LSPProvider.getAvailableServers().entrySet()) {
final LanguageServer server = entry.getValue();
if(server == null) {
server.getWorkspaceService().didChangeWatchedFiles(params);
}
}
}
public static boolean shouldSendFileActionNotification(File file) {
// TODO Check if we should send notification to any language server
// which handles this type of files
return true;
}
/**
* Shuts down all known language server
*/
@@ -66,10 +66,6 @@ public class JavaLanguageClient extends IDELanguageClient {
LOG.info("notifyProgress: ", gson.toJson(p1));
}
@Override
public void publishDiagnostics(PublishDiagnosticsParams p1) {
}
@Override
public CompletableFuture<Void> refreshCodeLenses() {
return null;
@@ -1,25 +0,0 @@
package com.itsaky.androidide.models;
public class CompletionEdit {
public String insertText;
public InsertPosition position;
public CompletionEdit(String insertText, InsertPosition position) {
this.insertText = insertText;
this.position = position;
}
public static class InsertPosition {
public int line;
public int column;
public InsertPosition(int line) {
this(line, -1);
}
public InsertPosition(int line, int column) {
this.line = line;
this.column = column;
}
}
}
@@ -1,135 +0,0 @@
package com.itsaky.androidide.models;
import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.language.java.manager.JavaCharacter;
import com.itsaky.androidide.language.java.server.JavaLanguageServer;
import com.itsaky.lsp.CompletionItem;
import com.itsaky.lsp.CompletionItemKind;
import com.itsaky.lsp.Position;
import com.itsaky.lsp.Range;
import com.itsaky.lsp.TextDocumentIdentifier;
import com.itsaky.lsp.TextDocumentPositionParams;
import com.itsaky.lsp.TextEdit;
import io.github.rosemoe.editor.text.Content;
import io.github.rosemoe.editor.text.Cursor;
import io.github.rosemoe.editor.widget.CodeEditor;
import java.util.List;
import io.github.rosemoe.editor.text.CharPosition;
import com.itsaky.androidide.utils.Logger;
public class CompletionItemWrapper implements SuggestItem, Comparable {
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 String getSortText() {
return item.kind + getName();
}
@Override
public void onSelectThis(CodeEditor editor) {
try {
final Cursor cursor = editor.getCursor();
if(cursor.isSelected()) return;
Range range = getIdentifierRange(cursor.getLeft(), editor.getText());
String text = getInsertText();
final boolean shiftLeft = text.contains("$0");
final boolean atEnd = text.endsWith("$0");
text = text.replace("$0", "");
editor.getText().delete(range.start.line, range.start.column, range.end.line, range.end.column);
cursor.onCommitText(text);
if(shiftLeft && !atEnd) {
editor.moveSelectionLeft();
}
if(item.command != null
&& item.command.command != null
&& item.command.command.equals(CompletionItem.COMMAND_TRIGGER_PARAMETER_HINTS)) {
requestSignature(editor);
}
final List<TextEdit> edits = item.additionalTextEdits;
if(edits != null && edits.size() > 0) {
for(int i=0;i<edits.size();i++) {
final TextEdit edit = edits.get(i);
if(edit == null || edit.range == null || edit.newText == null) continue;
final Position start = edit.range.start;
final Position end = edit.range.end;
if(start == null || end == null) continue;
if(start.equals(end)) {
editor.getText().insert(start.line, start.column, edit.newText);
} else {
editor.getText().replace(start.line, start.column, end.line, end.column, edit.newText);
}
}
}
} catch (Throwable th) {}
}
private Range getIdentifierRange(int end, Content content) {
int start = end;
while(start > 0 && JavaCharacter.isJavaIdentifierPart(content.charAt(start - 1))) {
start--;
}
CharPosition startPos = content.getIndexer().getCharPosition(start);
CharPosition endPos = content.getIndexer().getCharPosition(end);
return new Range(new Position(startPos.line, startPos.column), new Position(endPos.line, endPos.column));
}
private void requestSignature(CodeEditor editor) {
final JavaLanguageServer server = StudioApp.getInstance().getJavaLanguageServer();
if (server != null) {
TextDocumentPositionParams p = new TextDocumentPositionParams();
p.textDocument = new TextDocumentIdentifier(editor.getFile().toURI());
p.position = new Position(editor.getCursor().getLeftLine(), editor.getCursor().getLeftColumn());
server.signatureHelp(p, editor.getFile());
}
}
@Override
public int compareTo(Object p1) {
if(p1 instanceof CompletionItemWrapper) {
CompletionItemWrapper that = (CompletionItemWrapper) p1;
return this.getSortText().compareTo(that.getSortText());
}
return 1;
}
public CompletionItem getItem() {
return item;
}
}
@@ -1,214 +0,0 @@
package com.itsaky.androidide.models;
import android.graphics.drawable.Drawable;
import io.github.rosemoe.editor.struct.CompletionItem;
import java.util.ArrayList;
import java.util.List;
public class CompletionListItem extends CompletionItem {
private String detail;
private String itemType;
private Type type;
private List<CompletionEdit> additionalEdits;
public CompletionListItem() {
super("", "");
}
public CompletionListItem setLabel(String label) {
this.label = label;
return this;
}
public CompletionListItem setCommit(String commit) {
this.commit = commit;
cursorOffset(commit.length());
return this;
}
public CompletionListItem setDesc(String desc) {
this.desc = desc;
return this;
}
public CompletionListItem setIcon(Drawable icon) {
this.icon = icon;
return this;
}
public String getLabel() {
return this.label;
}
public String getCommit() {
return this.commit;
}
public String getDesc() {
return this.desc;
}
public Drawable getIcon() {
return this.icon;
}
public CompletionListItem setType(Type type) {
this.type = type;
return this;
}
public Type getType() {
return type;
}
public CompletionListItem setItemType(String type) {
this.itemType = type;
return this;
}
public String getItemType() {
return itemType;
}
public CompletionListItem setDetail(String detail) {
this.detail = detail;
return this;
}
public String getDetail() {
return detail;
}
public CompletionListItem setAdditionalEdits(List<CompletionEdit> edits) {
this.additionalEdits = edits;
return this;
}
public List<CompletionEdit> getAdditionalEdits() {
if (this.additionalEdits == null)
this.additionalEdits = new ArrayList<>();
return additionalEdits;
}
public CompletionListItem addAdditionalEdit(CompletionEdit edit) {
if (edit != null) {
getAdditionalEdits().add(edit);
}
return this;
}
public boolean hasAdditionalEdits() {
return getAdditionalEdits() != null && getAdditionalEdits().size() > 0;
}
public String getSortText() {
if (getType() == Type.LOCAL_VARIABLE)
return "0" + getLabel();
if (getType() == Type.FIELD)
return "1" + getLabel();
if (getType() == Type.KEYWORD)
return "2" + getLabel();
if (getType() == Type.METHOD)
return "3" + getLabel();
if (getType() == Type.ENUM)
return "4" + getLabel();
if (getType() == Type.INTERFACE)
return "5" + getLabel();
if (getType() == Type.CLASS)
return "6" + getLabel();
if (getType() == Type.NOT_IMPORTED_CLASS)
return "7" + getLabel();
if (getType() == Type.ATTRIBUTE)
return "8" + getLabel();
if (getType() == Type.WIDGET)
return "9" + getLabel();
if (getType() == Type.OBJECT)
return "10" + getLabel();
return getLabel();
}
public static enum Type {
ATTRIBUTE('A'),
CLASS('C'),
ENUM('E'),
FIELD('F'),
INTERFACE('I'),
KEYWORD('K'),
LOCAL_VARIABLE('V'),
METHOD('M'),
NOT_IMPORTED_CLASS(' '),
OBJECT('O'),
WIDGET('W');
private char c;
private Type(char c) {
this.c = c;
}
public String getAsString() {
return String.valueOf(c);
}
public char get() {
return c;
}
}
public int compareTo(CompletionListItem obj) {
return getSortText().compareTo(obj.getSortText());
}
public CompletionListItem clone() {
CompletionListItem item = new CompletionListItem();
item.setCommit(getCommit());
item.setDesc(getDesc());
item.setDetail(getDetail());
item.setIcon(getIcon());
item.setItemType(getItemType());
item.setLabel(getLabel());
item.setType(getType());
return item;
}
@Override
public boolean equals(Object obj) {
if(obj != null && obj instanceof CompletionListItem) {
CompletionListItem that = (CompletionListItem) obj;
return this.getLabel().equals(that.getLabel())
&& this.getDesc().equals(that.getDesc())
&& this.getDetail().equals(that.getDetail())
&& this.getCommit().equals(that.getCommit())
&& this.getItemType() == that.getItemType()
&& this.hasAdditionalEdits() == that.hasAdditionalEdits()
&& this.getAdditionalEdits().size() == that.getAdditionalEdits().size()
&& this.getAdditionalEdits() == that.getAdditionalEdits();
}
return false;
}
@Override
public String toString() {
return
"["
+ "\ncommit = " + getCommit()
+ "\ndesc = " + getDesc()
+ "\ndetail = " + getDetail()
+ "\nicon = " + getIcon()
+ "\nitemType = " + getItemType()
+ "\nlabel = " + getLabel()
+ "\ntype = " + getType()
+ "\n]";
}
}
@@ -1,8 +1,8 @@
package com.itsaky.androidide.models;
import com.itsaky.lsp.Diagnostic;
import java.io.File;
import java.util.List;
import org.eclipse.lsp4j.Diagnostic;
public class DiagnosticGroup {
public int icon;
@@ -1,58 +0,0 @@
package com.itsaky.androidide.models;
import io.github.rosemoe.editor.text.CharPosition;
import io.github.rosemoe.editor.text.Content;
import io.github.rosemoe.editor.widget.CodeEditor;
public class GradleSuggestItem implements SuggestItem {
private String label;
private String desc;
private String prefix;
private boolean isArtifact;
public GradleSuggestItem(String label, String desc, String prefix, boolean isArtifact) {
this.label = label;
this.desc = desc;
this.prefix = prefix;
this.isArtifact = isArtifact;
}
@Override
public String getName() {
return label;
}
@Override
public String getDescription() {
return desc;
}
@Override
public String getReturnType() {
return "";
}
@Override
public char getTypeHeader() {
return ' ';
}
@Override
public String getSortText() {
return label;
}
@Override
public void onSelectThis(CodeEditor editor) {
try {
final int length = prefix.length();
final int start = editor.getCursor().getLeft() - length;
final int end = editor.getCursor().getLeft();
final CharPosition s = editor.getText().getIndexer().getCharPosition(start);
Content editable = editor.getText();
editable.delete(start, end);
editable.insert(s.line, s.column, getName());
} catch (Throwable ignored) {}
}
}
@@ -7,9 +7,8 @@ import com.blankj.utilcode.util.FileUtils;
import com.google.gson.Gson;
import com.itsaky.androidide.R;
import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.language.java.server.JavaLanguageServer;
import com.itsaky.androidide.lsp.LSP;
import com.itsaky.androidide.managers.PreferenceManager;
import com.itsaky.androidide.models.AndroidProject;
import com.itsaky.androidide.models.project.IDEModule;
import com.itsaky.androidide.models.project.IDEProject;
import com.itsaky.androidide.shell.ShellServer;
@@ -19,9 +18,6 @@ import com.itsaky.androidide.tasks.gradle.build.AssembleDebug;
import com.itsaky.androidide.tasks.gradle.resources.UpdateResourceClassesTask;
import com.itsaky.androidide.utils.Environment;
import com.itsaky.androidide.utils.Logger;
import com.itsaky.lsp.DidChangeWatchedFilesParams;
import com.itsaky.lsp.FileChangeType;
import com.itsaky.lsp.FileEvent;
import com.itsaky.toaster.Toaster;
import java.io.File;
import java.io.FileFilter;
@@ -33,6 +29,9 @@ import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import org.eclipse.lsp4j.DidChangeWatchedFilesParams;
import org.eclipse.lsp4j.FileChangeType;
import org.eclipse.lsp4j.FileEvent;
public class IDEService implements ShellServer.Callback {
@@ -201,11 +200,7 @@ public class IDEService implements ShellServer.Callback {
* get completions from updated classpaths
*/
private void notifyExternalSourceChange() {
JavaLanguageServer server = app.getJavaLanguageServer();
if(mIDEProject == null || server == null) return;
server.didChangeWatchedFiles(new DidChangeWatchedFilesParams(createSourceChangeEvents()));
server.buildModified();
LSP.notifyWatchedFilesChanged(new DidChangeWatchedFilesParams(createSourceChangeEvents()));
if(listener != null) {
listener.onBuildModified();
@@ -232,7 +227,7 @@ public class IDEService implements ShellServer.Callback {
final List<File> sources = FileUtils.listFilesInDirWithFilter(generated, JAVA_FILTER, true);
if(sources == null) continue;
for(File source : sources) {
events.add(new FileEvent(source.toURI(), FileChangeType.Changed));
events.add(new FileEvent(source.toURI().toString(), FileChangeType.Changed));
}
}
return events;
@@ -5,24 +5,17 @@ import com.itsaky.androidide.syntax.lexer.Lexer;
import com.itsaky.androidide.syntax.lexer.impls.BaseJavaLexer;
import com.itsaky.androidide.syntax.lexer.tokens.Token;
import com.itsaky.androidide.syntax.lexer.tokens.TokenType;
import com.itsaky.lsp.JavaColors;
import com.itsaky.lsp.Range;
import io.github.rosemoe.editor.struct.BlockLine;
import io.github.rosemoe.editor.text.TextAnalyzeResult;
import io.github.rosemoe.editor.widget.EditorColorScheme;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import org.antlr.v4.runtime.CharStreams;
import java.util.Map;
import java.util.HashMap;
import com.itsaky.lsp.Position;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
public class JavaLexerImpl extends BaseJavaLexer implements Lexer {
private JavaColors javaColors;
public JavaLexerImpl(String content, TextAnalyzeResult colors) {
this.content = content;
this.colors = colors;
@@ -33,10 +26,6 @@ public class JavaLexerImpl extends BaseJavaLexer implements Lexer {
this.isFirst = true;
}
public void setJavaColors(JavaColors colors) {
this.javaColors = colors;
}
@Override
public void init() throws IOException {
lexer = new JavaLexer(CharStreams.fromReader(new StringReader(content)));
@@ -227,92 +216,6 @@ public class JavaLexerImpl extends BaseJavaLexer implements Lexer {
break;
case JavaLexer.IDENTIFIER :
type = TokenType.IDENTIFIER;
if(isPackageName(line, column)) {
colors.addIfNeeded(line, column, EditorColorScheme.PACKAGE_NAME);
break;
}
if(isEnumType(line, column)) {
colors.addIfNeeded(line, column, EditorColorScheme.ENUM_TYPE);
break;
}
if(isClassName(line, column)) {
colors.addIfNeeded(line, column, EditorColorScheme.TYPE_NAME);
break;
}
if(isAnnotationType(line, column)) {
colors.addIfNeeded(line, column, EditorColorScheme.ANNOTATION);
break;
}
if(isInterface(line, column)) {
colors.addIfNeeded(line, column, EditorColorScheme.INTERFACE);
break;
}
if(isEnum(line, column)) {
colors.addIfNeeded(line, column, EditorColorScheme.ENUM);
break;
}
if(isStaticField(line, column)) {
colors.addIfNeeded(line, column, EditorColorScheme.STATIC_FIELD);
break;
}
if(isField(line, column)) {
colors.addIfNeeded(line, column, EditorColorScheme.FIELD);
break;
}
if(isParameter(line, column)) {
colors.addIfNeeded(line, column, EditorColorScheme.PARAMETER);
break;
}
if(isLocal(line, column)) {
colors.addIfNeeded(line, column, EditorColorScheme.LOCAL_VARIABLE);
break;
}
if(isExceptionParam(line, column)) {
colors.addIfNeeded(line, column, EditorColorScheme.EXCEPTION_PARAM);
break;
}
if(isMethod(line, column)) {
colors.addIfNeeded(line, column, EditorColorScheme.METHOD);
break;
}
if(isConstructor(line, column)) {
colors.addIfNeeded(line, column, EditorColorScheme.CONSTRUCTOR);
break;
}
if(isStaticInit(line, column)) {
colors.addIfNeeded(line, column, EditorColorScheme.STATIC_INIT);
break;
}
if(isInstanceInit(line, column)) {
colors.addIfNeeded(line, column, EditorColorScheme.INSTANCE_INIT);
break;
}
if(isTypeParam(line, column)) {
colors.addIfNeeded(line, column, EditorColorScheme.TYPE_PARAM);
break;
}
if(isResourceVariable(line, column)) {
colors.addIfNeeded(line, column, EditorColorScheme.RESOURCE_VARIABLE);
break;
}
colors.addIfNeeded(line, column, EditorColorScheme.TEXT_NORMAL);
break;
case JavaLexer.LBRACE :
@@ -360,101 +263,4 @@ public class JavaLexerImpl extends BaseJavaLexer implements Lexer {
return type;
}
private boolean isEnumType(int line, int column) {
if(javaColors == null || javaColors.enumTypes == null) return false;
return isInRange(javaColors.enumTypes, line, column);
}
private boolean isAnnotationType(int line, int column) {
if(javaColors == null || javaColors.annotationTypes == null) return false;
return isInRange(javaColors.annotationTypes, line, column);
}
private boolean isInterface(int line, int column) {
if(javaColors == null || javaColors.interfaces == null) return false;
return isInRange(javaColors.interfaces, line, column);
}
private boolean isEnum(int line, int column) {
if(javaColors == null || javaColors.enums == null) return false;
return isInRange(javaColors.enums, line, column);
}
private boolean isParameter(int line, int column) {
if(javaColors == null || javaColors.parameters == null) return false;
return isInRange(javaColors.parameters, line, column);
}
private boolean isExceptionParam(int line, int column) {
if(javaColors == null || javaColors.exceptionParams == null) return false;
return isInRange(javaColors.exceptionParams, line, column);
}
private boolean isConstructor(int line, int column) {
if(javaColors == null || javaColors.constructors == null) return false;
return isInRange(javaColors.constructors, line, column);
}
private boolean isStaticInit(int line, int column) {
if(javaColors == null || javaColors.staticInits == null) return false;
return isInRange(javaColors.staticInits, line, column);
}
private boolean isInstanceInit(int line, int column) {
if(javaColors == null || javaColors.instanceInits == null) return false;
return isInRange(javaColors.instanceInits, line, column);
}
private boolean isTypeParam(int line, int column) {
if(javaColors == null || javaColors.typeParams == null) return false;
return isInRange(javaColors.typeParams, line, column);
}
private boolean isResourceVariable(int line, int column) {
if(javaColors == null || javaColors.resourceVariables == null) return false;
return isInRange(javaColors.resourceVariables, line, column);
}
private boolean isPackageName(int line, int column) {
if(javaColors == null || javaColors.packages == null) return false;
return isInRange(javaColors.packages, line, column);
}
private boolean isClassName(int line, int column) {
if(javaColors == null || javaColors.classNames == null) return false;
return isInRange(javaColors.classNames, line, column);
}
private boolean isField(int line, int column) {
if(javaColors == null || javaColors.fields == null) return false;
return isInRange(javaColors.fields, line, column);
}
private boolean isStaticField(int line, int column) {
if(javaColors == null || javaColors.statics == null) return false;
return isInRange(javaColors.statics, line, column);
}
private boolean isMethod(int line, int column) {
if(javaColors == null || javaColors.methods == null) return false;
return isInRange(javaColors.methods, line, column);
}
private boolean isLocal(int line, int column) {
if(javaColors == null || javaColors.locals == null) return false;
return isInRange(javaColors.locals, line, column);
}
private boolean isInRange(List<Range> ranges, int line, int column) {
if(ranges != null && ranges.size() > 0) {
for(int i=0;i<ranges.size();i++) {
final Range range = ranges.get(i);
if(range == null) continue;
if(range.start.line == line && range.start.column == column)
return true;
}
}
return false;
}
}
@@ -15,17 +15,16 @@
*/
package io.github.rosemoe.editor.text;
import java.util.ArrayList;
import java.util.List;
import io.github.rosemoe.editor.struct.BlockLine;
import io.github.rosemoe.editor.struct.HexColor;
import io.github.rosemoe.editor.struct.NavigationItem;
import io.github.rosemoe.editor.struct.Span;
import io.github.rosemoe.editor.widget.EditorColorScheme;
import java.util.ArrayList;
import java.util.HashMap;
import io.github.rosemoe.editor.struct.HexColor;
import java.util.List;
import java.util.Map;
import com.itsaky.lsp.Range;
import org.eclipse.lsp4j.Range;
/**
* The result of analysis
@@ -16,6 +16,8 @@
package io.github.rosemoe.editor.widget;
import android.annotation.SuppressLint;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
@@ -58,20 +60,17 @@ import androidx.annotation.Px;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.view.ActionMode;
import androidx.appcompat.view.menu.MenuBuilder;
import com.blankj.utilcode.util.ThreadUtils;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.itsaky.androidide.R;
import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.databinding.LayoutDialogTextInputBinding;
import com.itsaky.androidide.interfaces.JLSRequestor;
import com.itsaky.androidide.lsp.IDELanguageClient;
import com.itsaky.androidide.lsp.LSPProvider;
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE;
import com.itsaky.androidide.utils.Logger;
import com.itsaky.androidide.utils.Symbols;
import com.itsaky.androidide.utils.TypefaceUtils;
import com.itsaky.lsp.CodeAction;
import com.itsaky.lsp.Diagnostic;
import com.itsaky.lsp.DiagnosticSeverity;
import com.itsaky.lsp.Range;
import com.itsaky.toaster.Toaster;
import io.github.rosemoe.editor.interfaces.EditorEventListener;
import io.github.rosemoe.editor.interfaces.EditorLanguage;
@@ -101,7 +100,11 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import org.eclipse.lsp4j.CodeActionOptions;
import org.eclipse.lsp4j.DefinitionOptions;
import org.eclipse.lsp4j.DefinitionParams;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.DiagnosticSeverity;
import org.eclipse.lsp4j.DidChangeTextDocumentParams;
import org.eclipse.lsp4j.DidCloseTextDocumentParams;
import org.eclipse.lsp4j.DidOpenTextDocumentParams;
@@ -109,24 +112,23 @@ import org.eclipse.lsp4j.DidSaveTextDocumentParams;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.LocationLink;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.ReferenceContext;
import org.eclipse.lsp4j.ReferenceOptions;
import org.eclipse.lsp4j.ReferenceParams;
import org.eclipse.lsp4j.ServerCapabilities;
import org.eclipse.lsp4j.ShowDocumentParams;
import org.eclipse.lsp4j.SignatureHelp;
import org.eclipse.lsp4j.SignatureHelpContext;
import org.eclipse.lsp4j.SignatureHelpOptions;
import org.eclipse.lsp4j.SignatureHelpParams;
import org.eclipse.lsp4j.SignatureHelpTriggerKind;
import org.eclipse.lsp4j.TextDocumentContentChangeEvent;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.eclipse.lsp4j.TextDocumentItem;
import org.eclipse.lsp4j.VersionedTextDocumentIdentifier;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.eclipse.lsp4j.services.LanguageServer;
import android.app.ProgressDialog;
import com.blankj.utilcode.util.ThreadUtils;
import com.itsaky.androidide.lsp.IDELanguageClient;
import org.eclipse.lsp4j.ShowDocumentParams;
import org.eclipse.lsp4j.CodeActionOptions;
import org.eclipse.lsp4j.DefinitionOptions;
import org.eclipse.lsp4j.ReferenceOptions;
import com.itsaky.androidide.tasks.TaskExecutor;
import android.app.Dialog;
import com.itsaky.androidide.utils.JSONUtility;
import com.itsaky.androidide.lsp.LSP;
/**
* CodeEditor is a editor that can highlight text regions by doing basic syntax analyzing
@@ -236,6 +238,7 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
private boolean mFindReferencesEnabled;
private boolean mGotoDefinitionEnabled;
private boolean mCodeActionsEnabled;
private boolean mSignatureHelpEnabled;
private boolean mCompletionOnComposing;
private boolean mHighlightSelectedText;
private boolean mHighlightCurrentBlock;
@@ -295,6 +298,8 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
EditorInputConnection mConnection;
KeyMetaStates mKeyMetaStates = new KeyMetaStates(this);
private List<String> mSignatureHelpTriggerChars = new ArrayList<>();
private Map<HexColor, Integer> mLineColors;
private Map<Range, Diagnostic> mDiagnostics;
private Map<Integer, List<Range>> stringMap;
@@ -617,10 +622,10 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
final Set<Range> keys = mDiagnostics.keySet();
Range found = null;
for(Range r : keys) {
if(r.start.line <= range.getStart().getLine()
&& r.start.column <= range.getStart().getCharacter()
&& r.end.line >= range.getEnd().getLine()
&& r.end.column >= range.getEnd().getCharacter()) {
if(r.getStart().getLine() <= range.getStart().getLine()
&& r.getStart().getCharacter() <= range.getStart().getCharacter()
&& r.getEnd().getLine() >= range.getEnd().getLine()
&& r.getEnd().getCharacter() >= range.getEnd().getCharacter()) {
found = r;
break;
}
@@ -641,7 +646,7 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
for(Range r : keys) {
if(r == null) continue;
if(r.start.line <= line && line <= r.end.line)
if(r.getStart().getLine() <= line && line <= r.getEnd().getLine())
diags.add(mDiagnostics.get(r));
}
@@ -681,9 +686,7 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
public CodeEditor putLineColor(HexColor index, int color) {
createMapIfNull();
if(this.mLineColors != null) {
mLineColors.put(index, color);
}
mLineColors.put(index, color);
return this;
}
@@ -873,27 +876,47 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
}
Either<Boolean, CodeActionOptions> codeActionOptions = c.getCodeActionProvider();
if(codeActionOptions.isLeft()) {
mCodeActionsEnabled = codeActionOptions.getLeft();
if(codeActionOptions == null) {
setCodeActionsEnabled(false);
} else {
CodeActionOptions options = codeActionOptions.getRight();
mCodeActionsEnabled = options != null;
if(codeActionOptions.isLeft()) {
setCodeActionsEnabled(codeActionOptions.getLeft());
} else {
CodeActionOptions options = codeActionOptions.getRight();
setCodeActionsEnabled(options != null);
}
}
Either<Boolean, DefinitionOptions> definitionOptions = c.getDefinitionProvider();
if(definitionOptions.isLeft()) {
mGotoDefinitionEnabled = definitionOptions.getLeft();
if(definitionOptions == null) {
setGotoDefinitionEnabled(false);
} else {
DefinitionOptions options = definitionOptions.getRight();
mGotoDefinitionEnabled = options != null;
if(definitionOptions.isLeft()) {
setGotoDefinitionEnabled(definitionOptions.getLeft());
} else {
DefinitionOptions options = definitionOptions.getRight();
setGotoDefinitionEnabled(options != null);
}
}
Either<Boolean, ReferenceOptions> referenceOptions = c.getReferencesProvider();
if(definitionOptions.isLeft()) {
mFindReferencesEnabled = referenceOptions.getLeft();
if(referenceOptions == null) {
setFindReferencesEnabled(false);
} else {
ReferenceOptions options = referenceOptions.getRight();
mFindReferencesEnabled = options != null;
if(definitionOptions.isLeft()) {
setFindReferencesEnabled(referenceOptions.getLeft());
} else {
ReferenceOptions options = referenceOptions.getRight();
setFindReferencesEnabled(options != null);
}
}
SignatureHelpOptions signatureOptions = c.getSignatureHelpProvider();
if(signatureOptions == null) {
setSignatureHelpEnabled(false);
} else {
setSignatureHelpEnabled(true);
mSignatureHelpTriggerChars = new ArrayList<String>(signatureOptions.getTriggerCharacters());
}
}
@@ -1485,10 +1508,10 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
for(int i=0;i<diags.size();i++) {
Diagnostic d = diags.get(i);
if(d == null) continue;
int startCol = d.range.start.column;
int endCol = d.range.end.column;
if(line > d.range.start.line) startCol = 0;
if(line > d.range.end.line) endCol = 0;
int startCol = d.getRange().getStart().getCharacter();
int endCol = d.getRange().getEnd().getCharacter();
if(line > d.getRange().getStart().getCharacter()) startCol = 0;
if(line > d.getRange().getEnd().getCharacter()) endCol = 0;
final int paintStart = Math.max(firstVisibleChar, startCol);
final int paintEnd = Math.min(lastVisibleChar, endCol);
final float width = measureText(mBuffer, paintStart, paintEnd - paintStart);
@@ -1497,7 +1520,7 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
r.top = r.bottom - getRowHeight() * 0.2f;
r.left = paintingOffset + measureText(mBuffer, firstVisibleChar, paintStart - firstVisibleChar);
r.right = r.left + width;
mPaint.setColor(diagnosticColor(d.severity));
mPaint.setColor(diagnosticColor(d.getSeverity()));
drawZigzagUnderline(r, canvas, mPaint);
}
}
@@ -1610,12 +1633,12 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
}
}
private int diagnosticColor(int severity) {
private int diagnosticColor(DiagnosticSeverity severity) {
int id = EditorColorScheme.DIAGNOSTIC_HINT;
switch(severity) {
case DiagnosticSeverity.Error : id = EditorColorScheme.DIAGNOSTIC_ERROR; break;
case DiagnosticSeverity.Warning : id = EditorColorScheme.DIAGNOSTIC_WARNING; break;
case DiagnosticSeverity.Information : id = EditorColorScheme.DIAGNOSTIC_INFO; break;
case Error : id = EditorColorScheme.DIAGNOSTIC_ERROR; break;
case Warning : id = EditorColorScheme.DIAGNOSTIC_WARNING; break;
case Information : id = EditorColorScheme.DIAGNOSTIC_INFO; break;
default : id = EditorColorScheme.DIAGNOSTIC_ERROR; break;
}
return mColors.getColor(id);
@@ -2706,6 +2729,10 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
public void setFindReferencesEnabled(boolean findReferencesEnabled) {
mFindReferencesEnabled = findReferencesEnabled;
}
public void setSignatureHelpEnabled(boolean enabled) {
this.mSignatureHelpEnabled = enabled;
}
/**
* @see CodeEditor#setAutoCompletionEnabled(boolean)
@@ -2725,6 +2752,10 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
public boolean isFindReferencesEnabled() {
return mFindReferencesEnabled && mLanguageServer != null;
}
public boolean isSignatureHelpEnabled() {
return mSignatureHelpEnabled && mLanguageServer != null;
}
/**
* Sets non-printable painting flags.
@@ -4571,12 +4602,6 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
}
}
private JLSRequestor jlsRequestor;
public void setJLSRequestor(JLSRequestor requestor) {
this.jlsRequestor = requestor;
}
public void commentLine() {
if(getFile() == null) return;
@@ -4642,7 +4667,7 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
try {
final DefinitionParams params = new DefinitionParams();
params.setTextDocument(new TextDocumentIdentifier(getFile().toURI().toString()));
params.setTextDocument(getDocumentIdentifier());
params.setPosition(getCursorAsLSPPosition());
final CompletableFuture<Either<List<? extends Location>, List<? extends LocationLink>>> future = mLanguageServer.getTextDocumentService().definition(params);
future.whenComplete((e, t) -> {
@@ -4650,39 +4675,43 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
final Throwable th = t;
final IDELanguageClient client = LSPProvider.getClientForLanguage(mLanguage.getLanguageCode());
if(client == null || future.isCancelled() || future.isCompletedExceptionally()) {
if(either == null || client == null || future.isCancelled() || future.isCompletedExceptionally()) {
showDefinitionNotFound(pd);
return;
}
if(either.isLeft()) {
List<? extends Location> locations = either.getLeft();
final List<? extends Location> locations = either.getLeft();
if(locations == null || locations.size() <= 0) {
showDefinitionNotFound(pd);
return;
}
if(locations.size() == 1) {
Location location = locations.get(0);
showDocument(client, location.getUri(), location.getRange());
} else {
client.showLocations(locations);
}
ThreadUtils.runOnUiThread(() -> {
if(locations.size() == 1) {
Location location = locations.get(0);
showDocument(client, location.getUri(), location.getRange());
} else {
client.showLocations(locations);
}
});
dismissOnUiThread(pd);
} else if(either.isRight()) {
List<? extends LocationLink> locations = either.getRight();
final List<? extends LocationLink> locations = either.getRight();
if(locations == null || locations.size() <= 0) {
showDefinitionNotFound(pd);
return;
}
if(locations.size() == 1) {
LocationLink location = locations.get(0);
showDocument(client, location.getTargetUri(), location.getTargetSelectionRange());
} else {
client.showLocationLinks(locations);
}
ThreadUtils.runOnUiThread(() -> {
if(locations.size() == 1) {
LocationLink location = locations.get(0);
showDocument(client, location.getTargetUri(), location.getTargetSelectionRange());
} else {
client.showLocationLinks(locations);
}
});
dismissOnUiThread(pd);
}
@@ -4699,19 +4728,41 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
});
}
private void dismissOnUiThread(final Dialog dialog) {
ThreadUtils.runOnUiThread(() -> {
dialog.dismiss();
});
}
public void findReferences() {
if(!isFindReferencesEnabled()) return;
if(!isFindReferencesEnabled() || getFile() == null) return;
final ProgressDialog pd = ProgressDialog.show(getContext(), null, getContext().getString(R.string.msg_finding_definition));
final ProgressDialog pd = ProgressDialog.show(getContext(), null, getContext().getString(R.string.msg_finding_references));
try {
ReferenceParams params = new ReferenceParams();
params.setTextDocument(getDocumentIdentifier());
params.setPosition(getCursorAsLSPPosition());
params.setContext(new ReferenceContext(true));
final CompletableFuture<List<? extends Location>> future = mLanguageServer.getTextDocumentService().references(params);
future.whenComplete((l, t) -> {
final List<? extends Location> locations = l;
final Throwable th = t;
final IDELanguageClient client = LSPProvider.getClientForLanguage(mLanguage.getLanguageCode());
if(locations == null || client == null || future.isCancelled() || future.isCompletedExceptionally()) {
showReferencesNotFound(pd);
return;
}
if(locations.isEmpty()) {
showReferencesNotFound(pd);
return;
} else {
ThreadUtils.runOnUiThread(() -> {
client.showLocations(locations);
});
}
dismissOnUiThread(pd);
});
} catch (Throwable th) {
showReferencesNotFound(pd);
}
@@ -4724,18 +4775,56 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
});
}
public void signatureHelp(String insertedContent) {
if(mSignatureHelpEnabled
&& mLanguageServer != null
&& getFile() != null
&& mSignatureHelpTriggerChars != null
&& mSignatureHelpTriggerChars.size() > 0
&& mSignatureHelpTriggerChars.contains(insertedContent)) {
SignatureHelpContext context = new SignatureHelpContext();
context.setTriggerCharacter(insertedContent);
context.setTriggerKind(SignatureHelpTriggerKind.ContentChange);
SignatureHelpParams params = new SignatureHelpParams();
params.setPosition(getCursorAsLSPPosition());
params.setTextDocument(getDocumentIdentifier());
params.setContext(context);
CompletableFuture<SignatureHelp> future = mLanguageServer.getTextDocumentService().signatureHelp(params);
future.whenComplete((h, t) -> {
final SignatureHelp help = h;
final Throwable th = t;
final IDELanguageClient client = LSPProvider.getClientForLanguage(mLanguage.getLanguageCode());
if(help == null || client == null || future.isCancelled() || future.isCompletedExceptionally()) {
return;
}
ThreadUtils.runOnUiThread(() -> {
client.showSignatureHelp(help, getFile());
});
});
}
}
private void dismissOnUiThread(final Dialog dialog) {
ThreadUtils.runOnUiThread(() -> {
dialog.dismiss();
});
}
/**
* Request the client to show the specific document in an editor and select the provided range
*/
private void showDocument(IDELanguageClient client, String uri, org.eclipse.lsp4j.Range range) {
LOG.info("showDocument called", "client = " + client, "uri" + uri, "range" + range);
ShowDocumentParams params = new ShowDocumentParams();
params.setExternal(false);
params.setSelection(range);
params.setUri(uri);
LOG.info("ShowDocumentParams", JSONUtility.prettyPrinter.toJson(params));
client.showDocument(params);
LOG.info("sent showDocumentRequest");
}
public void didSave () {
@@ -4758,6 +4847,10 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
return new Position(getCursor().getLeftLine(), getCursor().getLeftColumn());
}
public TextDocumentIdentifier getDocumentIdentifier() {
return new TextDocumentIdentifier(getFile().toURI().toString());
}
private boolean isInString() {
final int line = getCursor().getLeftLine();
final int column = getCursor().getLeftColumn();
@@ -4766,7 +4859,7 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
if(strings == null || strings.isEmpty()) return false;
for(int i=0;i<strings.size();i++) {
Range r = strings.get(i);
if(r.start.column <= column && column <= r.end.column)
if(r.getStart().getCharacter() <= column && column <= r.getEnd().getCharacter())
return true;
}
return false;
@@ -4845,6 +4938,8 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
if (mListener != null) {
mListener.afterInsert(this, mText, startLine, startColumn, endLine, endColumn, insertedContent);
}
signatureHelp(insertedContent.toString());
}
@Override
@@ -5,8 +5,8 @@ import android.view.LayoutInflater;
import androidx.core.content.ContextCompat;
import com.itsaky.androidide.R;
import com.itsaky.androidide.databinding.LayoutDiagnosticInfoBinding;
import com.itsaky.lsp.Diagnostic;
import com.itsaky.lsp.DiagnosticSeverity;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.DiagnosticSeverity;
public class DiagnosticWindow extends EditorBasePopupWindow {
@@ -46,14 +46,14 @@ public class DiagnosticWindow extends EditorBasePopupWindow {
binding.errorPosition.setText(
editor.getContext().getString(
com.itsaky.androidide.R.string.diagnostic_position,
""+ (diagnostic.range.start.line + 1),
""+ (diagnostic.range.start.column + 1),
""+ (diagnostic.range.end.line + 1),
""+ (diagnostic.range.end.column + 1)
""+ (diagnostic.getRange().getStart().getLine() + 1),
""+ (diagnostic.getRange().getStart().getCharacter() + 1),
""+ (diagnostic.getRange().getEnd().getLine() + 1),
""+ (diagnostic.getRange().getEnd().getCharacter() + 1)
)
);
binding.msg.setText(diagnostic.message);
binding.msg.setText(diagnostic.getMessage());
binding.msg.setTextIsSelectable(true);
}
}
@@ -64,17 +64,17 @@ public class DiagnosticWindow extends EditorBasePopupWindow {
}
private int getDiagnosticIconId() {
if(diagnostic.severity == DiagnosticSeverity.Error)
if(diagnostic.getSeverity() == DiagnosticSeverity.Error)
return R.drawable.ic_compilation_error;
return R.drawable.ic_info;
}
private String getDiagnosticTypeString() {
if(diagnostic.severity == DiagnosticSeverity.Error) {
if(diagnostic.getSeverity() == DiagnosticSeverity.Error) {
return editor.getContext().getString(com.itsaky.androidide.R.string.diagnostic_error);
} else if(diagnostic.severity == DiagnosticSeverity.Warning) {
} else if(diagnostic.getSeverity() == DiagnosticSeverity.Warning) {
return editor.getContext().getString(com.itsaky.androidide.R.string.diagnostic_warning);
} else if(diagnostic.severity == DiagnosticSeverity.Hint) {
} else if(diagnostic.getSeverity() == DiagnosticSeverity.Hint) {
return editor.getContext().getString(com.itsaky.androidide.R.string.diagnostic_hint);
}
return editor.getContext().getString(com.itsaky.androidide.R.string.diagnostic_info);
@@ -26,11 +26,7 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.view.ActionMode;
import androidx.appcompat.view.menu.MenuBuilder;
import androidx.core.content.ContextCompat;
import com.itsaky.lsp.CodeAction;
import com.itsaky.lsp.Diagnostic;
import io.github.rosemoe.editor.util.IntPair;
import java.util.List;
import java.util.ArrayList;
/**
* Action Mode style text action panel for editor
@@ -76,22 +72,22 @@ class EditorTextActionModeStarter implements CodeEditor.EditorTextActionPresente
}
menu.add(0, 0, 0, mEditor.getContext().getString(android.R.string.selectAll))
.setShowAsActionFlags(1)
.setShowAsActionFlags(2)
.setIcon(array.getDrawable(0));
if(mEditor.isEditable()) {
menu.add(0, 1, 0, mEditor.getContext().getString(android.R.string.cut))
.setShowAsActionFlags(1)
.setShowAsActionFlags(2)
.setIcon(array.getDrawable(1));
}
menu.add(0, 2, 0, mEditor.getContext().getString(android.R.string.copy))
.setShowAsActionFlags(1)
.setShowAsActionFlags(2)
.setIcon(array.getDrawable(2));
if(mEditor.isEditable()) {
menu.add(0, 3, 0, mEditor.getContext().getString(android.R.string.paste))
.setShowAsActionFlags(1)
.setShowAsActionFlags(2)
.setIcon(array.getDrawable(3));
}
-85
View File
@@ -1,85 +0,0 @@
# Built application files
*.apk
*.aar
*.ap_
*.aab
# Files for the ART/Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
out/
# Uncomment the following line in case you need and you don't have the release build type files in your app
# release/
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
# Android Studio Navigation editor temp files
.navigation/
# Android Studio captures folder
captures/
# IntelliJ
*.iml
.idea/workspace.xml
.idea/tasks.xml
.idea/gradle.xml
.idea/assetWizardSettings.xml
.idea/dictionaries
.idea/libraries
# Android Studio 3 in .gitignore file.
.idea/caches
.idea/modules.xml
# Comment next line if keeping position of elements in Navigation Editor is relevant for you
.idea/navEditor.xml
# Keystore files
# Uncomment the following lines if you do not want to check your keystore files in.
#*.jks
#*.keystore
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
.cxx/
# Google Services (e.g. APIs or Firebase)
# google-services.json
# Freeline
freeline.py
freeline/
freeline_project_description.json
# fastlane
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
fastlane/readme.md
# Version control
vcs.xml
# lint
lint/intermediates/
lint/generated/
lint/outputs/
lint/tmp/
# lint/reports/
-23
View File
@@ -1,23 +0,0 @@
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'
}
-17
View File
@@ -1,17 +0,0 @@
# 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 *;
#}
-1
View File
@@ -1 +0,0 @@
<manifest package="com.itsaky.lsp"/>
@@ -1,8 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.annotations.SerializedName;
public class CancelParams {
@SerializedName("id")
public int id;
}
@@ -1,44 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.GsonBuilder;
import com.google.gson.annotations.SerializedName;
import java.util.List;
public class CodeAction {
@SerializedName("title")
public String title;
@SerializedName("kind")
public String kind;
@SerializedName("diagnostics")
public List<Diagnostic> diagnostics;
@SerializedName("edit")
public WorkspaceEdit edit;
@SerializedName("command")
public Command command;
public static CodeAction NONE;
@Override
public String toString() {
return new GsonBuilder().setPrettyPrinting().create().toJson(this);
}
@Override
public boolean equals(Object obj) {
if(obj instanceof CodeAction) {
CodeAction that = (CodeAction) obj;
return this.title.equals(that.title)
&& this.kind.equals(that.kind);
}
return false;
}
@Override
public int hashCode() {
return title.hashCode();
}
}
@@ -1,13 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.annotations.SerializedName;
import java.util.ArrayList;
import java.util.List;
public class CodeActionContext {
@SerializedName("diagnostics")
public List<Diagnostic> diagnostics = new ArrayList<>();
@SerializedName("only")
public List<String> only;
}
@@ -1,11 +0,0 @@
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";
}
@@ -1,14 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.annotations.SerializedName;
public class CodeActionParams {
@SerializedName("textDocument")
public TextDocumentIdentifier textDocument;
@SerializedName("range")
public Range range;
@SerializedName("context")
public CodeActionContext context = new CodeActionContext();
}
@@ -1,23 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.JsonElement;
import com.google.gson.annotations.SerializedName;
public class CodeLens {
@SerializedName("range")
public Range range;
@SerializedName("command")
public Command command;
@SerializedName("data")
public JsonElement data;
public CodeLens() {}
public CodeLens(Range range, Command command, JsonElement data) {
this.range = range;
this.command = command;
this.data = data;
}
}
@@ -1,14 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.annotations.SerializedName;
public class CodeLensParams {
@SerializedName("textDocument")
public TextDocumentIdentifier textDocument;
public CodeLensParams() {}
public CodeLensParams(TextDocumentIdentifier textDocument) {
this.textDocument = textDocument;
}
}
@@ -1,24 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.JsonArray;
import com.google.gson.annotations.SerializedName;
public class Command {
@SerializedName("title")
public String title;
@SerializedName("command")
public String command;
@SerializedName("arguments")
public JsonArray arguments;
public Command() {}
public Command(String title, String command, JsonArray arguments) {
this.title = title;
this.command = command;
this.arguments = arguments;
}
}
@@ -1,54 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.JsonElement;
import com.google.gson.annotations.SerializedName;
import java.util.List;
public class CompletionItem {
@SerializedName("label")
public String label;
@SerializedName("kind")
public int kind;
@SerializedName("detail")
public String detail;
@SerializedName("documentation")
public MarkupContent documentation;
@SerializedName("deprecated")
public Boolean deprecated;
@SerializedName("preselect")
public Boolean preselect;
@SerializedName("sortText")
public String sortText;
@SerializedName("filterText")
public String filterText;
@SerializedName("insertText")
public String insertText;
@SerializedName("insertTextFormat")
public Integer insertTextFormat;
@SerializedName("textEdit")
public TextEdit textEdit;
@SerializedName("additionalTextEdits")
public List<TextEdit> additionalTextEdits;
@SerializedName("commitCharacters")
public List<Character> commitCharacters;
@SerializedName("command")
public Command command;
@SerializedName("data")
public JsonElement data;
public static final String COMMAND_TRIGGER_PARAMETER_HINTS = "editor.action.triggerParameterHints";
}
@@ -1,60 +0,0 @@
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";
}
}
}
@@ -1,29 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.GsonBuilder;
import com.google.gson.annotations.SerializedName;
import java.util.ArrayList;
import java.util.List;
public class CompletionList {
@SerializedName("isIncomplete")
public boolean isIncomplete;
@SerializedName("items")
public List<CompletionItem> items;
public CompletionList() {
this.items = new ArrayList<>();
}
public CompletionList(boolean isIncomplete, List<CompletionItem> items) {
this.isIncomplete = isIncomplete;
this.items = items;
}
@Override
public String toString() {
return new GsonBuilder().setPrettyPrinting().create().toJson(this, CompletionList.class);
}
}
@@ -1,18 +0,0 @@
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;
}
@@ -1,25 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.annotations.SerializedName;
import java.util.ArrayList;
import java.util.List;
public class Diagnostic {
@SerializedName("range")
public Range range;
@SerializedName("severity")
public Integer severity;
@SerializedName("code")
public String code;
@SerializedName("source")
public String source;
@SerializedName("message")
public String message;
@SerializedName("tags")
public List<Integer> tags;
}
@@ -1,13 +0,0 @@
package com.itsaky.lsp;
import android.R;
import android.content.Context;
public class DiagnosticSeverity {
public static final int
Error = 1,
Warning = 2,
Information = 3,
Hint = 4;
}
@@ -1,5 +0,0 @@
package com.itsaky.lsp;
public class DiagnosticTag {
public static final int Unnecessary = 1;
}
@@ -1,9 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.JsonElement;
import com.google.gson.annotations.SerializedName;
public class DidChangeConfigurationParams {
@SerializedName("settings")
public JsonElement settings;
}
@@ -1,13 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.annotations.SerializedName;
import java.util.ArrayList;
import java.util.List;
public class DidChangeTextDocumentParams {
@SerializedName("textDocument")
public VersionedTextDocumentIdentifier textDocument = new VersionedTextDocumentIdentifier();
@SerializedName("contentChanges")
public List<TextDocumentContentChangeEvent> contentChanges = new ArrayList<>();
}
@@ -1,19 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.annotations.SerializedName;
import java.util.List;
import java.util.ArrayList;
public class DidChangeWatchedFilesParams {
@SerializedName("changes")
public List<FileEvent> changes = new ArrayList<>();
public DidChangeWatchedFilesParams(List<FileEvent> changes) {
this.changes = changes;
}
public DidChangeWatchedFilesParams() {
changes = new ArrayList<>();
}
}
@@ -1,8 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.annotations.SerializedName;
public class DidChangeWorkspaceFoldersParams {
@SerializedName("event")
public WorkspaceFoldersChangeEvent event;
}
@@ -1,9 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.annotations.SerializedName;
public class DidCloseTextDocumentParams {
@SerializedName("textDocument")
public TextDocumentIdentifier textDocument = new TextDocumentIdentifier();
}
@@ -1,8 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.annotations.SerializedName;
public class DidOpenTextDocument {
@SerializedName("textDocument")
public TextDocumentItem textDocument;
}
@@ -1,15 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.annotations.SerializedName;
public class DidOpenTextDocumentParams {
@SerializedName("textDocument")
public TextDocumentItem textDocument = new TextDocumentItem();
public DidOpenTextDocumentParams() {}
public DidOpenTextDocumentParams(TextDocumentItem textDocument) {
this.textDocument = textDocument;
}
}
@@ -1,12 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.annotations.SerializedName;
public class DidSaveTextDocumentParams {
@SerializedName("textDocument")
public TextDocumentIdentifier textDocument;
@SerializedName("text")
public String text;
}
@@ -1,12 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.annotations.SerializedName;
public class DocumentFormattingParams {
@SerializedName("textDocument")
public TextDocumentIdentifier textDocument;
@SerializedName("options")
public FormattingOptions options;
}
@@ -1,12 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.annotations.SerializedName;
public class DocumentHighlight {
@SerializedName("range")
public Range range;
@SerializedName("kind")
public int kind;
}
@@ -1,5 +0,0 @@
package com.itsaky.lsp;
public class DocumentHighlightKind {
public static final int Text = 1, Read = 2, Write = 3;
}
@@ -1,16 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.JsonArray;
import com.google.gson.annotations.SerializedName;
public class DocumentLink {
@SerializedName("range")
public Range range;
@SerializedName("target")
public String target;
@SerializedName("data")
public JsonArray data;
}
@@ -1,15 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.annotations.SerializedName;
public class DocumentLinkParams {
@SerializedName("textDocument")
public TextDocumentIdentifier textDocument;
public DocumentLinkParams() {}
public DocumentLinkParams(TextDocumentIdentifier textDocument) {
this.textDocument = textDocument;
}
}
@@ -1,27 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.annotations.SerializedName;
import java.util.List;
public class DocumentSymbol {
@SerializedName("name")
public String name;
@SerializedName("detail")
public String detail;
@SerializedName("kind")
public int kind;
@SerializedName("deprecated")
public boolean deprecated;
@SerializedName("range")
public Range range;
@SerializedName("selectionRange")
public Range selectionRange;
@SerializedName("children")
public List<DocumentSymbol> children;
}
@@ -1,15 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.annotations.SerializedName;
public class DocumentSymbolParams {
@SerializedName("textDocument")
public TextDocumentIdentifier textDocument;
public DocumentSymbolParams() {}
public DocumentSymbolParams(TextDocumentIdentifier textDocument) {
this.textDocument = textDocument;
}
}
@@ -1,18 +0,0 @@
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;
}
@@ -1,5 +0,0 @@
package com.itsaky.lsp;
public class FileChangeType {
public static final int Created = 1, Changed = 2, Deleted = 3;
}
@@ -1,18 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.annotations.SerializedName;
import java.net.URI;
public class FileEvent {
@SerializedName("uri")
public URI uri;
@SerializedName("type")
public int type;
public FileEvent(URI uri, int type) {
this.uri = uri;
this.type = type;
}
}
@@ -1,30 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.annotations.SerializedName;
public class FoldingRange {
@SerializedName("startLine")
public int startLine;
@SerializedName("startCharacter")
public int startCharacter;
@SerializedName("endLine")
public int endLine;
@SerializedName("endCharacter")
public int endCharacter;
@SerializedName("kind")
public String kind;
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;
}
}
@@ -1,10 +0,0 @@
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";
}
@@ -1,9 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.annotations.SerializedName;
public class FoldingRangeParams {
@SerializedName("textDocument")
public TextDocumentIdentifier textDocument;
}
@@ -1,19 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.annotations.SerializedName;
public class FormattingOptions {
@SerializedName("tabSize")
public int tabSize;
@SerializedName("insertSpaces")
public boolean insertSpaces;
public FormattingOptions(){}
public FormattingOptions(int tabSize, boolean insertSpaces) {
this.tabSize = tabSize;
this.insertSpaces = insertSpaces;
}
}
@@ -1,23 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.annotations.SerializedName;
import java.util.List;
public class Hover {
@SerializedName("contents")
public List<MarkedString> contents;
@SerializedName("range")
public Range range;
public Hover() {}
public Hover(List<MarkedString> contents) {
this.contents = contents;
}
public Hover(List<MarkedString> contents, Range range) {
this.contents = contents;
this.range = range;
}
}
@@ -1,30 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.JsonElement;
import com.google.gson.annotations.SerializedName;
import java.net.URI;
import java.util.List;
import java.util.Set;
public class InitializeParams {
@SerializedName("processId")
public int processId;
@SerializedName("rootPath")
public String rootPath;
@SerializedName("rootUri")
public URI rootUri;
@SerializedName("roots")
public Set<URI> roots;
@SerializedName("initializationOptions")
public JsonElement initializationOptions;
@SerializedName("trace")
public String trace;
@SerializedName("workspaceFolders")
public List<WorkspaceFolder> workspaceFolders;
}
@@ -1,16 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.JsonObject;
import com.google.gson.annotations.SerializedName;
public class InitializeResult {
@SerializedName("capabilities")
public JsonObject capabilities;
public InitializeResult() {}
public InitializeResult(JsonObject capabilities) {
this.capabilities = capabilities;
}
}
@@ -1,5 +0,0 @@
package com.itsaky.lsp;
public class InsertTextFormat {
public static final int PlainText = 1, Snippet = 2;
}
@@ -1,71 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.GsonBuilder;
import com.google.gson.annotations.SerializedName;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
/**
* A class which stores the syntax highlighting colors obtained from JLS
*/
public class JavaColors {
@SerializedName("uri")
public URI uri;
@SerializedName("packages")
public List<Range> packages;
@SerializedName("enumTypes")
public List<Range> enumTypes;
@SerializedName("classNames")
public List<Range> classNames;
@SerializedName("annotationTypes")
public List<Range> annotationTypes;
@SerializedName("interfaces")
public List<Range> interfaces;
@SerializedName("enums")
public List<Range> enums;
@SerializedName("statics")
public List<Range> statics;
@SerializedName("fields")
public List<Range> fields;
@SerializedName("parameters")
public List<Range> parameters;
@SerializedName("locals")
public List<Range> locals;
@SerializedName("exceptionParams")
public List<Range> exceptionParams;
@SerializedName("methods")
public List<Range> methods;
@SerializedName("constructors")
public List<Range> constructors;
@SerializedName("staticInits")
public List<Range> staticInits;
@SerializedName("instanceInits")
public List<Range> instanceInits;
@SerializedName("typeParams")
public List<Range> typeParams;
@SerializedName("resourceVariables")
public List<Range> resourceVariables;
@Override
public String toString() {
return new GsonBuilder().setPrettyPrinting().create().toJson(this);
}
}
@@ -1,38 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.annotations.SerializedName;
public class JavaReportProgressParams {
@SerializedName("message")
private String message;
@SerializedName("increment")
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;
}
}
@@ -1,23 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.annotations.SerializedName;
public class JavaStartProgressParams {
@SerializedName("message")
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;
}
}
@@ -1,25 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.JsonElement;
import java.io.File;
import java.util.List;
public interface LanguageClient {
void javaProgressStart(JavaStartProgressParams params);
void javaProgressReport(JavaReportProgressParams params);
void javaProgressEnd();
void publishDiagnostics(PublishDiagnosticsParams params);
void gotoDefinition(List<Location> locations);
void references(List<Location> references);
void signatureHelp(SignatureHelp signature, File file);
void javaColors(JavaColors colors);
void showMessage(ShowMessageParams params);
void customNotification(String method, JsonElement params);
void registerCapability(String method, JsonElement options);
void onServerStarted(int currentId);
}
@@ -1,120 +0,0 @@
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<TextEdit> 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<CompletionList> completion(TextDocumentPositionParams params) {
throw new RuntimeException("Unimplemented");
}
public CompletionItem resolveCompletionItem(CompletionItem params) {
throw new RuntimeException("Unimplemented");
}
public Optional<Hover> hover(TextDocumentPositionParams params) {
throw new RuntimeException("Unimplemented");
}
public Optional<SignatureHelp> signatureHelp(TextDocumentPositionParams params) {
throw new RuntimeException("Unimplemented");
}
public Optional<List<Location>> gotoDefinition(TextDocumentPositionParams params) {
throw new RuntimeException("Unimplemented");
}
public Optional<List<Location>> findReferences(ReferenceParams params) {
throw new RuntimeException("Unimplemented");
}
public List<SymbolInformation> documentSymbol(DocumentSymbolParams params) {
throw new RuntimeException("Unimplemented");
}
public List<SymbolInformation> workspaceSymbols(WorkspaceSymbolParams params) {
throw new RuntimeException("Unimplemented");
}
public List<CodeAction> codeAction(CodeActionParams params) {
throw new RuntimeException("Unimplemented");
}
public List<CodeLens> codeLens(CodeLensParams params) {
throw new RuntimeException("Unimplemented");
}
public CodeLens resolveCodeLens(CodeLens params) {
throw new RuntimeException("Unimplemented");
}
public Optional<RenameResponse> 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<TextEdit> formatting(DocumentFormattingParams params) {
throw new RuntimeException("Unimplemented");
}
public List<FoldingRange> foldingRange(FoldingRangeParams params) {
throw new RuntimeException("Unimplemented");
}
public List<DocumentLink> documentLink(DocumentLinkParams params) {
throw new RuntimeException("Unimplemented");
}
public void doAsyncWork() {}
}
@@ -1,27 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.annotations.SerializedName;
import java.net.URI;
import com.google.gson.GsonBuilder;
public class Location {
@SerializedName("uri")
public URI uri;
@SerializedName("range")
public Range range;
public Location() {}
public Location(URI uri, Range range) {
this.uri = uri;
this.range = range;
}
@Override
public String toString() {
return new GsonBuilder().setPrettyPrinting().create().toJson(this);
}
public static final Location NONE = new Location(null, Range.NONE);
}
@@ -1,49 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.TypeAdapter;
import com.google.gson.annotations.JsonAdapter;
import com.google.gson.annotations.SerializedName;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
@JsonAdapter(MarkedString.Adapter.class)
public class MarkedString {
@SerializedName("language")
public String language;
@SerializedName("value")
public String 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<MarkedString> {
@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.");
}
}
}
@@ -1,18 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.annotations.SerializedName;
public class MarkupContent {
@SerializedName("kind")
public String kind;
@SerializedName("value")
public String value;
public MarkupContent() {}
public MarkupContent(String kind, String value) {
this.kind = kind;
this.value = value;
}
}
@@ -1,5 +0,0 @@
package com.itsaky.lsp;
public class MarkupKind {
public static final String PlainText = "plaintext", Markdown = "markdown";
}
@@ -1,18 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.JsonElement;
import com.google.gson.annotations.SerializedName;
public class Message {
@SerializedName("jsonrpc")
public String jsonrpc;
@SerializedName("id")
public Integer id;
@SerializedName("method")
public String method;
@SerializedName("params")
public JsonElement params;
}
@@ -1,12 +0,0 @@
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;
}
@@ -1,13 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.JsonElement;
import com.google.gson.annotations.SerializedName;
public class NotificationMessage {
@SerializedName("method")
public String method;
@SerializedName("params")
public JsonElement params;
}
@@ -1,11 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.annotations.SerializedName;
public class ParameterInformation {
@SerializedName("label")
public String label;
@SerializedName("documentation")
public MarkupContent documentation;
}
@@ -1,37 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.GsonBuilder;
import com.google.gson.annotations.SerializedName;
public class Position {
@SerializedName("line")
public int line;
@SerializedName("character")
public int column;
public Position() {}
public Position(int line, int character) {
this.line = line;
this.column = character;
}
@Override
public String toString() {
return new GsonBuilder().setPrettyPrinting().create().toJson(this);
}
@Override
public boolean equals(Object obj) {
if(obj instanceof Position) {
Position that = (Position) obj;
return this.line == that.line
&& this.column == that.column;
}
return false;
}
public static final Position NONE = new Position(-1, -1);
}
@@ -1,24 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.annotations.SerializedName;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
public class PublishDiagnosticsParams {
@SerializedName("uri")
public URI uri;
@SerializedName("diagnostics")
public List<Diagnostic> diagnostics;
public PublishDiagnosticsParams() {
diagnostics = new ArrayList<>();
}
public PublishDiagnosticsParams(URI uri, List<Diagnostic> diagnostics) {
this.uri = uri;
this.diagnostics = diagnostics;
}
}
@@ -1,40 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.GsonBuilder;
import com.google.gson.annotations.SerializedName;
public class Range {
@SerializedName("start")
public Position start;
@SerializedName("end")
public Position end;
public Range() {}
public Range(Position start, Position end) {
this.start = start;
this.end = end;
}
@Override
public String toString() {
return new GsonBuilder().setPrettyPrinting().create().toJson(this);
}
@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 Range ofZero() {
return new Range(new Position(0, 0), new Position(0, 0));
}
public static final Range NONE = new Range(Position.NONE, Position.NONE);
}
@@ -1,15 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.annotations.SerializedName;
public class ReferenceContext {
@SerializedName("includeDeclaration")
public boolean includeDeclaration;
public ReferenceContext(){}
public ReferenceContext(boolean includeDeclaration) {
this.includeDeclaration = includeDeclaration;
}
}
@@ -1,9 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.annotations.SerializedName;
public class ReferenceParams extends TextDocumentPositionParams {
@SerializedName("context")
public ReferenceContext context;
}
@@ -1,16 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.JsonElement;
import com.google.gson.annotations.SerializedName;
public class RegistrationParams {
@SerializedName("id")
public String id;
@SerializedName("method")
public String method;
@SerializedName("registerOptions")
public JsonElement registerOptions;
}
@@ -1,16 +0,0 @@
package com.itsaky.lsp;
import com.google.gson.annotations.SerializedName;
public class RenameParams {
@SerializedName("textDocument")
public TextDocumentIdentifier textDocument;
@SerializedName("position")
public Position position;
@SerializedName("newName")
public String newName;
}

Some files were not shown because too many files have changed in this diff Show More