mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
- Draw diagnostics in editor & send pending messages to server once started
This commit is contained in:
Binary file not shown.
@@ -31,6 +31,9 @@ import com.google.android.material.navigation.NavigationView;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
import com.google.android.material.transition.MaterialArcMotion;
|
||||
import com.google.android.material.transition.MaterialContainerTransform;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.itsaky.androidide.adapters.DiagnosticsAdapter;
|
||||
import com.itsaky.androidide.adapters.EditorPagerAdapter;
|
||||
import com.itsaky.androidide.adapters.viewholders.FileTreeViewHolder;
|
||||
@@ -42,11 +45,11 @@ import com.itsaky.androidide.fragments.EditorFragment;
|
||||
import com.itsaky.androidide.fragments.FileTreeFragment;
|
||||
import com.itsaky.androidide.fragments.sheets.OptionsListFragment;
|
||||
import com.itsaky.androidide.fragments.sheets.TextSheetFragment;
|
||||
import com.itsaky.androidide.interfaces.DexResultListener;
|
||||
import com.itsaky.androidide.interfaces.DiagnosticClickListener;
|
||||
import com.itsaky.androidide.interfaces.JLSRequestor;
|
||||
import com.itsaky.androidide.language.java.server.JavaLanguageServer;
|
||||
import com.itsaky.androidide.language.logs.LogLanguageImpl;
|
||||
import com.itsaky.androidide.models.AndroidProject;
|
||||
import com.itsaky.androidide.models.ConstantsBridge;
|
||||
import com.itsaky.androidide.models.DiagnosticGroup;
|
||||
import com.itsaky.androidide.models.LogLine;
|
||||
import com.itsaky.androidide.models.SheetOption;
|
||||
@@ -62,8 +65,15 @@ import com.itsaky.androidide.utils.Symbols;
|
||||
import com.itsaky.androidide.utils.TypefaceUtils;
|
||||
import com.itsaky.androidide.utils.VersionedFileManager;
|
||||
import com.itsaky.androidide.views.MaterialBanner;
|
||||
import com.itsaky.lsp.Diagnostic;
|
||||
import com.itsaky.lsp.DidCloseTextDocumentParams;
|
||||
import com.itsaky.lsp.DidOpenTextDocumentParams;
|
||||
import com.itsaky.lsp.JavaReportProgressParams;
|
||||
import com.itsaky.lsp.JavaStartProgressParams;
|
||||
import com.itsaky.lsp.LanguageClient;
|
||||
import com.itsaky.lsp.Message;
|
||||
import com.itsaky.lsp.PublishDiagnosticsParams;
|
||||
import com.itsaky.lsp.ShowMessageParams;
|
||||
import com.itsaky.lsp.TextDocumentItem;
|
||||
import com.itsaky.toaster.Toaster;
|
||||
import com.unnamed.b.atv.model.TreeNode;
|
||||
@@ -75,15 +85,13 @@ import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Stack;
|
||||
import java.util.regex.Pattern;
|
||||
import me.piruin.quickaction.ActionItem;
|
||||
import me.piruin.quickaction.QuickAction;
|
||||
import com.itsaky.lsp.JavaReportProgressParams;
|
||||
import com.itsaky.lsp.PublishDiagnosticsParams;
|
||||
import com.itsaky.lsp.JavaStartProgressParams;
|
||||
import com.itsaky.lsp.ShowMessageParams;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.itsaky.lsp.Diagnostic;
|
||||
import com.itsaky.lsp.DidSaveTextDocumentParams;
|
||||
import com.itsaky.lsp.DidChangeTextDocumentParams;
|
||||
import com.itsaky.lsp.TextDocumentIdentifier;
|
||||
|
||||
public class EditorActivity extends StudioActivity implements FileTreeFragment.FileActionListener,
|
||||
IDEService.BuildListener,
|
||||
@@ -91,7 +99,8 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
NavigationView.OnNavigationItemSelectedListener,
|
||||
DiagnosticClickListener,
|
||||
EditorFragment.FileOpenListener,
|
||||
LanguageClient, CodeEditor.CursorChangeListener {
|
||||
LanguageClient, CodeEditor.CursorChangeListener,
|
||||
JLSRequestor {
|
||||
|
||||
private ActivityEditorBinding mBinding;
|
||||
private EditorPagerAdapter mPagerAdapter;
|
||||
@@ -113,13 +122,15 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
private boolean recreateCompletionServices = false;
|
||||
|
||||
private final Map<File, List<Diagnostic>> diagnostics = new HashMap<>();
|
||||
|
||||
private final Stack<Message> pendingMessages = new Stack<>();
|
||||
|
||||
private final String RES_PATH_REGEX = "/.*/src/.*/res";
|
||||
private final String LAYOUTRES_PATH_REGEX = "/.*/src/.*/res/layout";
|
||||
private final String MENURES_PATH_REGEX = "/.*/src/.*/res/menu";
|
||||
private final String DRAWABLERES_PATH_REGEX = "/.*/src/.*/res/drawable";
|
||||
private final String JAVA_PATH_REGEX = "/.*/src/.*/java";
|
||||
|
||||
private static final Gson gson = new Gson();
|
||||
private final IDEService.BuildListener mBuildListener = this;
|
||||
private static final String TAG_FILE_OPTIONS_FRAGMENT = "file_options_fragment";
|
||||
public static final String TAG = "EditorActivity";
|
||||
@@ -652,21 +663,6 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
.setCancelable(false)
|
||||
.create().show();
|
||||
}
|
||||
|
||||
private void provideDiagnosticsToEditors(Map<File, List<Diagnostic>> diagnostics) {
|
||||
if(mPagerAdapter != null && mPagerAdapter.getFragments() != null) {
|
||||
List<Fragment> editors = mPagerAdapter.getFragments();
|
||||
if(editors.size() <= 0) return;
|
||||
for(int i=0;i<editors.size();i++) {
|
||||
try {
|
||||
EditorFragment editor = mPagerAdapter.getFrag(i);
|
||||
if(diagnostics.containsKey(editor.getFile())) {
|
||||
editor.setDiagnostics(diagnostics.get(editor.getFile()));
|
||||
}
|
||||
} catch (Throwable th) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGroupClick(DiagnosticGroup group) {
|
||||
@@ -701,8 +697,12 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
File file = new File(params.uri);
|
||||
if(!(file.exists() && file.isFile())) return;
|
||||
diagnostics.put(file, params.diagnostics);
|
||||
|
||||
getDiagnosticsList().setAdapter(new DiagnosticsAdapter(mapAsGroup(diagnostics), this));
|
||||
|
||||
EditorFragment editor = null;
|
||||
if(mPagerAdapter != null && (editor = mPagerAdapter.findEditorByFile(new File(params.uri))) != null) {
|
||||
editor.setDiagnostics(params.diagnostics);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -716,6 +716,15 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
@Override
|
||||
public void customNotification(String method, JsonElement params) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServerStarted(int currentId) {
|
||||
final JavaLanguageServer server = getApp().getJavaLanguageServer();
|
||||
while(server != null && !pendingMessages.isEmpty()) {
|
||||
Message msg = pendingMessages.pop();
|
||||
server.send(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCursorPositionChange(int leftLine, int leftColumn, int rightLine, int rightColumn) {
|
||||
@@ -725,6 +734,50 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
mBinding.editorToolbar.setSubtitle(String.format("[%d, %d] | [%d, %d]", leftLine, leftColumn, rightLine, rightColumn));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didOpen(DidOpenTextDocumentParams p) {
|
||||
final JavaLanguageServer server = getApp().getJavaLanguageServer();
|
||||
if(server != null)
|
||||
server.didOpen(p);
|
||||
else addPendingMessage(createMessage(JavaLanguageServer.Method.DID_OPEN, gson.toJson(p)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didClose(DidCloseTextDocumentParams p) {
|
||||
final JavaLanguageServer server = getApp().getJavaLanguageServer();
|
||||
if(server != null)
|
||||
server.didClose(p);
|
||||
else addPendingMessage(createMessage(JavaLanguageServer.Method.DID_CLOSE, gson.toJson(p)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didChange(DidChangeTextDocumentParams params) {
|
||||
final JavaLanguageServer server = getApp().getJavaLanguageServer();
|
||||
if(server != null)
|
||||
server.didChange(params);
|
||||
else addPendingMessage(createMessage(JavaLanguageServer.Method.DID_CHANGE, gson.toJson(params)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didSave(DidSaveTextDocumentParams params) {
|
||||
final JavaLanguageServer server = getApp().getJavaLanguageServer();
|
||||
if(server != null)
|
||||
server.didSave(params);
|
||||
else addPendingMessage(createMessage(JavaLanguageServer.Method.DID_SAVE, gson.toJson(params)));
|
||||
}
|
||||
|
||||
private void addPendingMessage(Message msg) {
|
||||
pendingMessages.push(msg);
|
||||
}
|
||||
|
||||
private 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;
|
||||
}
|
||||
|
||||
/*****************************
|
||||
**** Tab Related ************
|
||||
@@ -907,7 +960,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
|
||||
@Override
|
||||
public void openFile(File file) {
|
||||
int i = mPagerAdapter.openFile(file, this, this);
|
||||
int i = mPagerAdapter.openFile(file, this, this, this);
|
||||
if(i >= 0 && !mBinding.tabs.getTabAt(i).isSelected())
|
||||
mBinding.tabs.getTabAt(i).select();
|
||||
mBinding.editorDrawerLayout.closeDrawer(GravityCompat.END);
|
||||
@@ -923,7 +976,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
doc.version = VersionedFileManager.fileOpened(file);
|
||||
DidOpenTextDocumentParams p = new DidOpenTextDocumentParams();
|
||||
p.textDocument = doc;
|
||||
getApp().getJavaLanguageServer().didOpen(p);
|
||||
didOpen(p);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1269,11 +1322,13 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
|
||||
private void closeFile(int index, boolean selectOther) {
|
||||
mBinding.tabs.removeOnTabSelectedListener(this);
|
||||
|
||||
|
||||
int pos = index;
|
||||
final List<Fragment> frags = mPagerAdapter.getFragments();
|
||||
final List<File> files = mPagerAdapter.getOpenedFiles();
|
||||
|
||||
|
||||
final File removed = files.get(index);
|
||||
|
||||
frags.remove(index);
|
||||
files.remove(index);
|
||||
|
||||
@@ -1297,5 +1352,11 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
mBinding.editorViewPager.setCurrentItem(pos, false);
|
||||
}
|
||||
}
|
||||
|
||||
TextDocumentIdentifier id = new TextDocumentIdentifier();
|
||||
id.uri = removed.toURI();
|
||||
DidCloseTextDocumentParams p = new DidCloseTextDocumentParams();
|
||||
p.textDocument = id;
|
||||
didClose(p);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.itsaky.androidide.interfaces.JLSRequestor;
|
||||
|
||||
public class EditorPagerAdapter extends FragmentStatePagerAdapter
|
||||
{
|
||||
@@ -34,8 +35,22 @@ public class EditorPagerAdapter extends FragmentStatePagerAdapter
|
||||
mOpenedFiles.addAll(files);
|
||||
mFragments.addAll(fragments);
|
||||
}
|
||||
|
||||
public EditorFragment findEditorByFile(File file) {
|
||||
for(int i=0;i<mFragments.size();i++) {
|
||||
Fragment frag = mFragments.get(i);
|
||||
if(!(frag instanceof EditorFragment))
|
||||
continue;
|
||||
|
||||
EditorFragment editor = (EditorFragment) frag;
|
||||
if(editor.getFile() != null && editor.getFile().getAbsolutePath().equals(file.getAbsolutePath()))
|
||||
return editor;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public int openFile(File file, EditorFragment.FileOpenListener listener, CodeEditor.CursorChangeListener cursorListener)
|
||||
public int openFile(File file, EditorFragment.FileOpenListener listener, CodeEditor.CursorChangeListener cursorListener, JLSRequestor jlsRequestor)
|
||||
{
|
||||
int openedFileIndex = -1;
|
||||
for(int i=0;i<mOpenedFiles.size();i++)
|
||||
@@ -50,7 +65,11 @@ public class EditorPagerAdapter extends FragmentStatePagerAdapter
|
||||
|
||||
if(openedFileIndex == -1)
|
||||
{
|
||||
mFragments.add(EditorFragment.newInstance(file, project).setFileOpenListener(listener).setCursorChangeListener(cursorListener));
|
||||
mFragments.add(EditorFragment
|
||||
.newInstance(file, project)
|
||||
.setFileOpenListener(listener)
|
||||
.setCursorChangeListener(cursorListener)
|
||||
.setJLSRequestor(jlsRequestor));
|
||||
mOpenedFiles.add(file);
|
||||
notifyDataSetChanged();
|
||||
return mFragments.size() - 1;
|
||||
|
||||
@@ -4,13 +4,13 @@ import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import com.blankj.utilcode.util.FileIOUtils;
|
||||
import com.itsaky.androidide.adapters.CompletionListAdapter;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.databinding.FragmentEditorBinding;
|
||||
import com.itsaky.androidide.fragments.preferences.EditorPreferences;
|
||||
import com.itsaky.androidide.interfaces.JLSRequestor;
|
||||
import com.itsaky.androidide.language.groovy.GroovyLanguage;
|
||||
import com.itsaky.androidide.language.java.JavaLanguage;
|
||||
import com.itsaky.androidide.language.java.server.JavaLanguageServer;
|
||||
import com.itsaky.androidide.language.xml.XMLLanguage;
|
||||
import com.itsaky.androidide.language.xml.lexer.XMLLexer;
|
||||
import com.itsaky.androidide.models.AndroidProject;
|
||||
@@ -18,13 +18,15 @@ import com.itsaky.androidide.models.ConstantsBridge;
|
||||
import com.itsaky.androidide.syntax.colorschemes.SchemeWombat;
|
||||
import com.itsaky.androidide.tasks.TaskExecutor;
|
||||
import com.itsaky.androidide.tasks.callables.ReadFileTask;
|
||||
import com.itsaky.androidide.utils.FileUtil;
|
||||
import com.itsaky.androidide.utils.PreferenceManager;
|
||||
import com.itsaky.androidide.utils.TypefaceUtils;
|
||||
import com.itsaky.androidide.utils.VersionedFileManager;
|
||||
import com.itsaky.lsp.Diagnostic;
|
||||
import com.itsaky.lsp.DidChangeTextDocumentParams;
|
||||
import com.itsaky.lsp.DidSaveTextDocumentParams;
|
||||
import com.itsaky.lsp.Range;
|
||||
import com.itsaky.lsp.TextDocumentContentChangeEvent;
|
||||
import com.itsaky.lsp.TextDocumentIdentifier;
|
||||
import com.itsaky.lsp.VersionedTextDocumentIdentifier;
|
||||
import io.github.rosemoe.editor.interfaces.EditorEventListener;
|
||||
import io.github.rosemoe.editor.langs.EmptyLanguage;
|
||||
@@ -34,7 +36,9 @@ import io.github.rosemoe.editor.widget.schemes.SchemeVS2019;
|
||||
import java.io.File;
|
||||
import java.io.StringReader;
|
||||
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;
|
||||
|
||||
@@ -48,6 +52,7 @@ public class EditorFragment extends BaseFragment implements EditorEventListener,
|
||||
|
||||
private CodeEditor.CursorChangeListener listener;
|
||||
private FileOpenListener openListener;
|
||||
private JLSRequestor jlsRequestor;
|
||||
private static AndroidProject project;
|
||||
|
||||
public static final String KEY_FILE_PATH = "file_path";
|
||||
@@ -65,6 +70,11 @@ public class EditorFragment extends BaseFragment implements EditorEventListener,
|
||||
return this;
|
||||
}
|
||||
|
||||
public EditorFragment setJLSRequestor(JLSRequestor requestor) {
|
||||
this.jlsRequestor = requestor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public static EditorFragment newInstance(File file, AndroidProject project) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(KEY_FILE_PATH, file.getAbsolutePath());
|
||||
@@ -124,8 +134,14 @@ public class EditorFragment extends BaseFragment implements EditorEventListener,
|
||||
}
|
||||
|
||||
public void setDiagnostics(List<Diagnostic> diags) {
|
||||
if(binding.editorCodeEditor != null) {
|
||||
binding.editorCodeEditor.setDiagnostics(diags);
|
||||
if(binding.editorCodeEditor != null && diags != null) {
|
||||
Map<Range, Diagnostic> map = new HashMap<>();
|
||||
for(int i=0;i<diags.size();i++) {
|
||||
final Diagnostic d = diags.get(i);
|
||||
if(d == null) continue;
|
||||
map.put(d.range, d);
|
||||
}
|
||||
binding.editorCodeEditor.setDiagnostics(map);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,10 +202,12 @@ public class EditorFragment extends BaseFragment implements EditorEventListener,
|
||||
}
|
||||
|
||||
public void save() {
|
||||
FileUtil.writeFile(mFile.getAbsolutePath(), binding.editorCodeEditor.getText().toString());
|
||||
final String text = binding.editorCodeEditor.getText().toString();
|
||||
final boolean wrote = FileIOUtils.writeFileFromString(mFile, text);
|
||||
notifySaved(wrote, text);
|
||||
isModified = false;
|
||||
}
|
||||
|
||||
|
||||
public File getFile() {
|
||||
return mFile;
|
||||
}
|
||||
@@ -275,50 +293,19 @@ public class EditorFragment extends BaseFragment implements EditorEventListener,
|
||||
@Override
|
||||
public void onNewTextSet(CodeEditor editor) {
|
||||
isModified = true;
|
||||
notifyChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterDelete(CodeEditor editor, CharSequence content, int startLine, int startColumn, int endLine, int endColumn, CharSequence deletedContent) {
|
||||
isModified = true;
|
||||
JavaLanguageServer server = StudioApp.getInstance().getJavaLanguageServer();
|
||||
if(server != null) {
|
||||
try {
|
||||
TextDocumentContentChangeEvent event = new TextDocumentContentChangeEvent();
|
||||
VersionedTextDocumentIdentifier id = new VersionedTextDocumentIdentifier();
|
||||
DidChangeTextDocumentParams p = new DidChangeTextDocumentParams();
|
||||
List<TextDocumentContentChangeEvent> changes = new ArrayList<>();
|
||||
event.text = getText();
|
||||
event.range = null;
|
||||
changes.add(event);
|
||||
id.uri = getFile().toURI();
|
||||
id.version = VersionedFileManager.incrementVersion(getFile());
|
||||
p.textDocument = id;
|
||||
p.contentChanges = changes;
|
||||
server.didChange(p);
|
||||
} catch (Throwable th) {}
|
||||
}
|
||||
notifyChanged();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void afterInsert(CodeEditor editor, CharSequence content, int startLine, int startColumn, int endLine, int endColumn, CharSequence insertedContent) {
|
||||
isModified = true;
|
||||
JavaLanguageServer server = StudioApp.getInstance().getJavaLanguageServer();
|
||||
if(server != null) {
|
||||
try {
|
||||
TextDocumentContentChangeEvent event = new TextDocumentContentChangeEvent();
|
||||
VersionedTextDocumentIdentifier id = new VersionedTextDocumentIdentifier();
|
||||
DidChangeTextDocumentParams p = new DidChangeTextDocumentParams();
|
||||
List<TextDocumentContentChangeEvent> changes = new ArrayList<>();
|
||||
event.text = getText();
|
||||
event.range = null;
|
||||
changes.add(event);
|
||||
id.uri = getFile().toURI();
|
||||
id.version = VersionedFileManager.incrementVersion(getFile());
|
||||
p.textDocument = id;
|
||||
p.contentChanges = changes;
|
||||
server.didChange(p);
|
||||
} catch (Throwable th) {}
|
||||
}
|
||||
notifyChanged();
|
||||
|
||||
boolean isOpen = false;
|
||||
try {
|
||||
@@ -333,6 +320,36 @@ public class EditorFragment extends BaseFragment implements EditorEventListener,
|
||||
public void beforeReplace(CodeEditor editor, CharSequence content) {
|
||||
}
|
||||
|
||||
private void notifyChanged() {
|
||||
if (getFile() != null && getFile().getName().endsWith(EXT_JAVA) && jlsRequestor != null) {
|
||||
try {
|
||||
TextDocumentContentChangeEvent event = new TextDocumentContentChangeEvent();
|
||||
VersionedTextDocumentIdentifier id = new VersionedTextDocumentIdentifier();
|
||||
DidChangeTextDocumentParams p = new DidChangeTextDocumentParams();
|
||||
List<TextDocumentContentChangeEvent> changes = new ArrayList<>();
|
||||
event.text = getText();
|
||||
event.range = null;
|
||||
changes.add(event);
|
||||
id.uri = getFile().toURI();
|
||||
id.version = VersionedFileManager.incrementVersion(getFile());
|
||||
p.textDocument = id;
|
||||
p.contentChanges = changes;
|
||||
jlsRequestor.didChange(p);
|
||||
} catch (Throwable th) {}
|
||||
}
|
||||
}
|
||||
|
||||
private void notifySaved(boolean wrote, String text) {
|
||||
if (wrote && jlsRequestor != null) {
|
||||
TextDocumentIdentifier id = new TextDocumentIdentifier();
|
||||
id.uri = mFile.toURI();
|
||||
DidSaveTextDocumentParams p = new DidSaveTextDocumentParams();
|
||||
p.text = text;
|
||||
p.textDocument = id;
|
||||
jlsRequestor.didSave(p);
|
||||
}
|
||||
}
|
||||
|
||||
public static interface FileOpenListener {
|
||||
public void onOpenSuccessful(File file, String text);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.itsaky.androidide.interfaces;
|
||||
|
||||
import com.itsaky.lsp.DidChangeTextDocumentParams;
|
||||
import com.itsaky.lsp.DidCloseTextDocumentParams;
|
||||
import com.itsaky.lsp.DidOpenTextDocumentParams;
|
||||
import com.itsaky.lsp.DidSaveTextDocumentParams;
|
||||
|
||||
public interface JLSRequestor {
|
||||
void didOpen(DidOpenTextDocumentParams params);
|
||||
void didClose(DidCloseTextDocumentParams params);
|
||||
void didChange(DidChangeTextDocumentParams params);
|
||||
void didSave(DidSaveTextDocumentParams params);
|
||||
}
|
||||
+28
-8
@@ -16,7 +16,9 @@ import com.itsaky.lsp.CancelParams;
|
||||
import com.itsaky.lsp.CompletionList;
|
||||
import com.itsaky.lsp.DidChangeConfigurationParams;
|
||||
import com.itsaky.lsp.DidChangeTextDocumentParams;
|
||||
import com.itsaky.lsp.DidCloseTextDocumentParams;
|
||||
import com.itsaky.lsp.DidOpenTextDocumentParams;
|
||||
import com.itsaky.lsp.DidSaveTextDocumentParams;
|
||||
import com.itsaky.lsp.InitializeParams;
|
||||
import com.itsaky.lsp.JavaReportProgressParams;
|
||||
import com.itsaky.lsp.JavaStartProgressParams;
|
||||
@@ -128,6 +130,11 @@ public class JavaLanguageServer implements ShellServer.Callback {
|
||||
DidChangeConfigurationParams p2 = new DidChangeConfigurationParams();
|
||||
p2.settings = settings;
|
||||
configurationChanged(p2);
|
||||
|
||||
ThreadUtils.runOnUiThread(() -> {
|
||||
if(client != null)
|
||||
client.onServerStarted(UNIVERSION_ID);
|
||||
});
|
||||
}
|
||||
|
||||
public void initialize(InitializeParams p) {
|
||||
@@ -146,10 +153,18 @@ public class JavaLanguageServer implements ShellServer.Callback {
|
||||
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 Pair<Integer, CompletionList> completion(TextDocumentPositionParams p) {
|
||||
int id = write(Method.COMPLETION, gson.toJson(p));
|
||||
completionRequests.put(id, id);
|
||||
@@ -166,19 +181,15 @@ public class JavaLanguageServer implements ShellServer.Callback {
|
||||
int id = write(Method.CANCEL_REQUEST, gson.toJson(p));
|
||||
}
|
||||
|
||||
private int write(String method, String data) {
|
||||
public int send(Message msg) {
|
||||
final int id = UNIVERSION_ID++;
|
||||
Message msg = new Message();
|
||||
msg.id = id;
|
||||
msg.jsonrpc = "2.0";
|
||||
msg.method = method;
|
||||
msg.params = new JsonParser().parse(data);
|
||||
|
||||
if(this.send != null) {
|
||||
final String body = gson.toJson(msg);
|
||||
final String head = String.format("Content-Length: %d\r\n\r\n", body.length());
|
||||
new Thread(() -> {
|
||||
try {
|
||||
logger.i(body);
|
||||
send.write(head.concat(body).getBytes(StandardCharsets.UTF_8));
|
||||
} catch (Throwable e) {
|
||||
logger.e("Error writing to server: " + ThrowableUtils.getFullStackTrace(e));
|
||||
@@ -189,6 +200,14 @@ public class JavaLanguageServer implements ShellServer.Callback {
|
||||
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) {
|
||||
logger.e(ThrowableUtils.getFullStackTrace(th));
|
||||
}
|
||||
@@ -204,6 +223,7 @@ public class JavaLanguageServer implements ShellServer.Callback {
|
||||
private void onServerOut(String line) {
|
||||
if(line == null || line.trim().length() <= 0) return;
|
||||
if(line.contains(CONTENT_LENGTH)) line = line.substring(0, line.lastIndexOf(CONTENT_LENGTH));
|
||||
logger.v(line);
|
||||
if(this.client == null) return;
|
||||
try {
|
||||
JsonObject obj = new JsonParser().parse(line.trim()).getAsJsonObject();
|
||||
@@ -233,7 +253,7 @@ public class JavaLanguageServer implements ShellServer.Callback {
|
||||
}
|
||||
}
|
||||
|
||||
private class Method {
|
||||
public static class Method {
|
||||
public static final String INITIALIZE = "initialize";
|
||||
public static final String INITIALIZED = "initialized";
|
||||
public static final String SHUTDOWN = "shutdown";
|
||||
@@ -270,7 +290,7 @@ public class JavaLanguageServer implements ShellServer.Callback {
|
||||
public static final String JAVA_COLORS = "java/colors";
|
||||
}
|
||||
|
||||
private class Key {
|
||||
public static class Key {
|
||||
public static final String METHOD = "method";
|
||||
public static final String PARAMS = "params";
|
||||
public static final String ID = "id";
|
||||
|
||||
@@ -58,9 +58,11 @@ import android.widget.Toast;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.Px;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
import com.itsaky.lsp.Diagnostic;
|
||||
import com.itsaky.lsp.DiagnosticSeverity;
|
||||
import com.itsaky.lsp.Position;
|
||||
import com.itsaky.lsp.Range;
|
||||
import io.github.rosemoe.editor.interfaces.EditorEventListener;
|
||||
@@ -88,6 +90,9 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import com.itsaky.lsp.DiagnosticSeverity;
|
||||
|
||||
/**
|
||||
* CodeEditor is a editor that can highlight text regions by doing basic syntax analyzing
|
||||
@@ -251,8 +256,8 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
|
||||
private CharPosition mLockedSelection;
|
||||
KeyMetaStates mKeyMetaStates = new KeyMetaStates(this);
|
||||
|
||||
private HashMap<HexColor, Integer> mLineColors;
|
||||
private HashMap<Range, List<Diagnostic>> mDiagnostics;
|
||||
private Map<HexColor, Integer> mLineColors;
|
||||
private Map<Range, Diagnostic> mDiagnostics;
|
||||
|
||||
private File currentFile;
|
||||
|
||||
@@ -524,54 +529,66 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
|
||||
return mPinLineNumber;
|
||||
}
|
||||
|
||||
public void showOrHideDiagnosticWindow() {
|
||||
private Diagnostic findDiagnosticInCursorRange() {
|
||||
if(mDiagnostics == null)
|
||||
return null;
|
||||
int leftLine = getCursor().getLeftLine();
|
||||
int leftCol = getCursor().getLeftColumn();
|
||||
int rightLine = getCursor().getRightLine();
|
||||
int rightCol = getCursor().getRightColumn();
|
||||
final Range thisRange = new Range(new Position(leftLine, leftCol), new Position(rightLine, rightCol));
|
||||
boolean hide = false;
|
||||
boolean has = mDiagnostics.containsKey(thisRange);
|
||||
if(has) {
|
||||
List<Diagnostic> diags = mDiagnostics.get(thisRange);
|
||||
if(diags != null && diags.size() > 0) {
|
||||
Diagnostic d = diags.get(0);
|
||||
mDiagnosticWindow.setDiagnostic(d);
|
||||
if(!mDiagnosticWindow.isShowing())
|
||||
mDiagnosticWindow.show();
|
||||
else updateDiagnosticWindowPosition();
|
||||
} else hide = true;
|
||||
} else hide = true;
|
||||
final Range range = new Range(new Position(leftLine, leftCol), new Position(rightLine, rightCol));
|
||||
final Set<Range> keys = mDiagnostics.keySet();
|
||||
Range found = null;
|
||||
for(Range r : keys) {
|
||||
if(r.start.line <= range.start.line
|
||||
&& r.start.character <= range.start.character
|
||||
&& r.end.line >= range.end.line
|
||||
&& r.end.character >= range.end.character) {
|
||||
found = r;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(found != null) {
|
||||
return mDiagnostics.get(found);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<Diagnostic> findDiagnosticsContainingLine(int line) {
|
||||
final List<Diagnostic> diags = new ArrayList<>();
|
||||
final Set<Range> keys = mDiagnostics.keySet();
|
||||
|
||||
for(Range r : keys) {
|
||||
if(r == null) continue;
|
||||
if(r.start.line <= line && line <= r.end.line)
|
||||
diags.add(mDiagnostics.get(r));
|
||||
}
|
||||
|
||||
return diags;
|
||||
}
|
||||
|
||||
public void showOrHideDiagnosticWindow() {
|
||||
if(mDiagnostics == null) return;
|
||||
boolean hide = true;
|
||||
Diagnostic d = findDiagnosticInCursorRange();
|
||||
if(d != null) {
|
||||
hide = false;
|
||||
mDiagnosticWindow.setDiagnostic(d);
|
||||
if(!mDiagnosticWindow.isShowing())
|
||||
mDiagnosticWindow.show();
|
||||
else updateDiagnosticWindowPosition();
|
||||
}
|
||||
if(hide && mDiagnosticWindow.isShowing()) {
|
||||
mDiagnosticWindow.hide();
|
||||
}
|
||||
}
|
||||
|
||||
public void setDiagnostics(List<Diagnostic> diags) {
|
||||
this.mDiagnostics = new HashMap<>();
|
||||
mapDiagnostics(diags);
|
||||
}
|
||||
|
||||
private void mapDiagnostics(List<Diagnostic> diags) {
|
||||
public void setDiagnostics(Map<Range, Diagnostic> diags) {
|
||||
if(diags == null)
|
||||
return;
|
||||
for(int i=0;i<diags.size();i++) {
|
||||
Diagnostic d = diags.get(i);
|
||||
if(d == null)
|
||||
continue;
|
||||
if(mDiagnostics.containsKey(d.range)) {
|
||||
List<Diagnostic> ds = mDiagnostics.get(d.range);
|
||||
if(ds == null)
|
||||
ds = new ArrayList<>();
|
||||
if(!ds.contains(d))
|
||||
ds.add(d);
|
||||
mDiagnostics.put(d.range, ds);
|
||||
} else {
|
||||
List<Diagnostic> ds = new ArrayList<>();
|
||||
ds.add(d);
|
||||
mDiagnostics.put(d.range, ds);
|
||||
}
|
||||
}
|
||||
diags = new HashMap<>();
|
||||
this.mDiagnostics = diags;
|
||||
}
|
||||
|
||||
public CodeEditor setLineColors(HashMap<HexColor, Integer> map) {
|
||||
@@ -1318,30 +1335,27 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
|
||||
}
|
||||
}
|
||||
|
||||
// Draw errors and warnings
|
||||
int leftLine = getCursor().getLeftLine();
|
||||
int leftCol = getCursor().getLeftColumn();
|
||||
int rightLine = getCursor().getRightLine();
|
||||
int rightCol = getCursor().getRightColumn();
|
||||
final Range thisRange = new Range(new Position(leftLine, leftCol), new Position(rightLine, rightCol));
|
||||
if(mDiagnostics != null && mDiagnostics.containsKey(thisRange)) {
|
||||
List<Diagnostic> diags = mDiagnostics.get(thisRange);
|
||||
if(diags != null && diags.size() > 0) {
|
||||
for(int i=0;i<diags.size();i++) {
|
||||
Diagnostic d = diags.get(i);
|
||||
if(d == null) continue;
|
||||
final int paintStart = Math.max(firstVisibleChar, d.range.start.character);
|
||||
final int paintEnd = Math.min(lastVisibleChar, d.range.end.character);
|
||||
final float width = measureText(mBuffer, paintStart, paintEnd - paintStart);
|
||||
final RectF r = new RectF();
|
||||
r.bottom = getRowBottom(line) - getOffsetY() - mDpUnit * 1;
|
||||
r.top = r.bottom - getRowHeight() * 0.2f;
|
||||
r.left = paintingOffset + measureText(mBuffer, firstVisibleChar, paintStart - firstVisibleChar);
|
||||
r.right = r.left + width;
|
||||
mPaint.setColor(getDiagnosticIconId(d));
|
||||
drawZigzagUnderline(r, canvas, mPaint);
|
||||
}
|
||||
}
|
||||
// Draw diagnostics
|
||||
final List<Diagnostic> diags = findDiagnosticsContainingLine(line);
|
||||
if(diags.size() > 0) {
|
||||
for(int i=0;i<diags.size();i++) {
|
||||
Diagnostic d = diags.get(i);
|
||||
if(d == null) continue;
|
||||
int startCol = d.range.start.character;
|
||||
int endCol = d.range.end.character;
|
||||
if(line > d.range.start.line) startCol = 0;
|
||||
if(line > d.range.end.line) endCol = 0;
|
||||
final int paintStart = Math.max(firstVisibleChar, startCol);
|
||||
final int paintEnd = Math.min(lastVisibleChar, endCol);
|
||||
final float width = measureText(mBuffer, paintStart, paintEnd - paintStart);
|
||||
final RectF r = new RectF();
|
||||
r.bottom = getRowBottom(line) - getOffsetY() - mDpUnit * 1;
|
||||
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));
|
||||
drawZigzagUnderline(r, canvas, mPaint);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw cursors
|
||||
@@ -1452,10 +1466,19 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
|
||||
}
|
||||
}
|
||||
|
||||
private int getDiagnosticIconId(Diagnostic diagnostic) {
|
||||
if(diagnostic.severity == DiagnosticSeverity.Error)
|
||||
return R.drawable.ic_compilation_error;
|
||||
return R.drawable.ic_info;
|
||||
private int diagnosticColor(int severity) {
|
||||
int id = R.color.diagnostic_warning;
|
||||
switch(id) {
|
||||
case DiagnosticSeverity.Error:
|
||||
id = R.color.diagnostic_error;
|
||||
break;
|
||||
default:
|
||||
id = R.color.diagnostic_warning;
|
||||
break;
|
||||
}
|
||||
|
||||
final Context ctx = StudioApp.getInstance();
|
||||
return ContextCompat.getColor(ctx, id);
|
||||
}
|
||||
|
||||
protected void showTextActionPopup() {
|
||||
|
||||
@@ -46,10 +46,10 @@ public class DiagnosticWindow extends EditorBasePopupWindow {
|
||||
binding.errorPosition.setText(
|
||||
editor.getContext().getString(
|
||||
com.itsaky.androidide.R.string.diagnostic_position,
|
||||
""+ diagnostic.range.start.line,
|
||||
""+ diagnostic.range.start.character,
|
||||
""+ diagnostic.range.end.line,
|
||||
""+ diagnostic.range.end.character
|
||||
""+ diagnostic.range.start.line + 1,
|
||||
""+ diagnostic.range.start.character + 1,
|
||||
""+ diagnostic.range.end.line + 1,
|
||||
""+ diagnostic.range.end.character + 1
|
||||
)
|
||||
);
|
||||
|
||||
@@ -74,8 +74,10 @@ public class DiagnosticWindow extends EditorBasePopupWindow {
|
||||
return editor.getContext().getString(com.itsaky.androidide.R.string.diagnostic_error);
|
||||
} else if(diagnostic.severity == DiagnosticSeverity.Warning) {
|
||||
return editor.getContext().getString(com.itsaky.androidide.R.string.diagnostic_warning);
|
||||
} else if(diagnostic.severity == DiagnosticSeverity.Hint) {
|
||||
return editor.getContext().getString(com.itsaky.androidide.R.string.diagnostic_hint);
|
||||
}
|
||||
return editor.getContext().getString(com.itsaky.androidide.R.string.diagnostic_note);
|
||||
return editor.getContext().getString(com.itsaky.androidide.R.string.diagnostic_info);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -35,6 +35,8 @@
|
||||
<color name="log_normal">@color/primaryTextColor</color>
|
||||
<color name="diagnostic_error">@color/secondaryDarkColor</color>
|
||||
<color name="diagnostic_warning">@color/log_warning</color>
|
||||
<color name="diagnostic_info">@color/log_info</color>
|
||||
<color name="diagnostic_hint">@color/primaryTextColor</color>
|
||||
<color name="content_background">#212121</color>
|
||||
<color name="bg_green">#4CAF50</color>
|
||||
<color name="transform_scrim">#70000000</color>
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
<string name="diagnostic_position">Start: %s, %s End: %s, %s</string>
|
||||
<string name="diagnostic_error">ERROR</string>
|
||||
<string name="diagnostic_warning">WARNING</string>
|
||||
<string name="diagnostic_note">NOTE</string>
|
||||
<string name="diagnostic_hint">INFO</string>
|
||||
<string name="diagnostic_info">HINT</string>
|
||||
<string name="msg_project_deleted">Project deleted</string>
|
||||
<string name="msg_project_delete_failed">Failed to delete project!</string>
|
||||
<string name="msg_failed_load_icon_path">Failed to load icon path!</string>
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
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;
|
||||
public static final int
|
||||
|
||||
Error = 1,
|
||||
Warning = 2,
|
||||
Information = 3,
|
||||
Hint = 4;
|
||||
}
|
||||
|
||||
@@ -10,4 +10,6 @@ public interface LanguageClient {
|
||||
void showMessage(ShowMessageParams params);
|
||||
void registerCapability(String method, JsonElement options);
|
||||
void customNotification(String method, JsonElement params);
|
||||
|
||||
void onServerStarted(int currentId);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user