mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
Implemented references and definition
Removed usage of custom LSP data classes
This commit is contained in:
@@ -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;
|
||||
|
||||
+2
-196
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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/
|
||||
@@ -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'
|
||||
}
|
||||
Vendored
-17
@@ -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 +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
Reference in New Issue
Block a user