mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
Backup before changing implementation of Terminal
This commit is contained in:
@@ -24,7 +24,7 @@
|
||||
android:hardwareAccelerated="true">
|
||||
<activity
|
||||
android:name=".SplashActivity"
|
||||
android:exported="false">
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
@@ -80,8 +80,5 @@
|
||||
<meta-data
|
||||
android:name="com.google.firebase.messaging.default_notification_channel_id"
|
||||
android:value="@string/cms_channel_id_devs" />
|
||||
<property
|
||||
android:name="android.media.PROPERTY_MEDIA_CAPABILITIES"
|
||||
android:resource="@xml/media_capabilities" />
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
Binary file not shown.
@@ -34,8 +34,10 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java9.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
import java9.util.concurrent.CompletableFuture;
|
||||
import org.eclipse.lsp4j.CodeAction;
|
||||
import org.eclipse.lsp4j.Command;
|
||||
import org.eclipse.lsp4j.Diagnostic;
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.eclipse.lsp4j.LocationLink;
|
||||
@@ -49,6 +51,8 @@ import org.eclipse.lsp4j.ShowDocumentParams;
|
||||
import org.eclipse.lsp4j.ShowDocumentResult;
|
||||
import org.eclipse.lsp4j.SignatureHelp;
|
||||
import org.eclipse.lsp4j.SignatureInformation;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
/**
|
||||
* AndroidIDE specific implementation of the LanguageClient
|
||||
@@ -125,6 +129,29 @@ public abstract class AbstractLanguageClient implements IDELanguageClient {
|
||||
}
|
||||
}
|
||||
|
||||
public void showCodeActions(List<Either<Command, CodeAction>> actions) {
|
||||
final String[] titles = new String[actions.size()];
|
||||
|
||||
int i=0;
|
||||
for(Either<Command, CodeAction> either : actions) {
|
||||
if(either.isLeft()) {
|
||||
titles[i] = "[Command] " + either.getLeft().getTitle();
|
||||
} else if(either.isRight()) {
|
||||
titles[i] = "[Action] " + either.getRight().getTitle();
|
||||
}
|
||||
++i;
|
||||
}
|
||||
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(activity(), R.style.AppTheme_MaterialAlertDialog);
|
||||
builder.setTitle(R.string.msg_code_actions);
|
||||
builder.setItems(titles, null);
|
||||
builder.show();
|
||||
}
|
||||
|
||||
public void hideCodeActions() {
|
||||
if(activity() == null) return;
|
||||
|
||||
}
|
||||
|
||||
public void showDiagnostic(Diagnostic diagnostic, final CodeEditor editor) {
|
||||
if(activity() == null || activity().getDiagnosticBinding() == null) {
|
||||
return;
|
||||
@@ -429,7 +456,7 @@ public abstract class AbstractLanguageClient implements IDELanguageClient {
|
||||
public DiagnosticsAdapter newDiagnosticsAdapter() {
|
||||
return new DiagnosticsAdapter(mapAsGroup(this.diagnostics), activity());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reports connection progress
|
||||
*/
|
||||
|
||||
@@ -7,8 +7,10 @@ import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.channels.AsynchronousServerSocketChannel;
|
||||
import java.nio.channels.AsynchronousSocketChannel;
|
||||
import java.nio.channels.Channels;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.Future;
|
||||
@@ -24,7 +26,7 @@ public class LSPClientLauncher extends Thread {
|
||||
private Future<Void> listeningFuture;
|
||||
private final AbstractLanguageClient languageClient;
|
||||
|
||||
private ServerSocket serverSocket;
|
||||
private AsynchronousServerSocketChannel serverSocket;
|
||||
private IDELanguageServer server;
|
||||
|
||||
public LSPClientLauncher(AbstractLanguageClient client, int port) {
|
||||
@@ -54,13 +56,14 @@ public class LSPClientLauncher extends Thread {
|
||||
public void run() {
|
||||
try {
|
||||
languageClient.connectionReport("Starting server socket");
|
||||
serverSocket = new ServerSocket(PORT);
|
||||
serverSocket = AsynchronousServerSocketChannel.open();
|
||||
serverSocket.bind(new InetSocketAddress(PORT));
|
||||
languageClient.connectionReport("Waiting for server to connect...");
|
||||
languageClient.startServer();
|
||||
|
||||
final Socket server = serverSocket.accept();
|
||||
final OutputStream outWriter = new OutputStreamWrapper(server.getOutputStream());
|
||||
final InputStream inReader = server.getInputStream();
|
||||
final AsynchronousSocketChannel server = serverSocket.accept().get();
|
||||
final OutputStream outWriter = new OutputStreamWrapper(Channels.newOutputStream(server));
|
||||
final InputStream inReader = Channels.newInputStream(server);
|
||||
languageClient.connectionReport("Server connected. Launching client...");
|
||||
|
||||
Launcher<IDELanguageServer> launcher = createClientLauncher(languageClient, inReader, outWriter);
|
||||
@@ -70,13 +73,16 @@ public class LSPClientLauncher extends Thread {
|
||||
languageClient.onServerConnected(this.server);
|
||||
languageClient.connectionReport("Server is now listening.");
|
||||
|
||||
while (!(listeningFuture.isCancelled() || listeningFuture.isDone())) {
|
||||
// Take some rest
|
||||
sleep(50);
|
||||
try {
|
||||
LOG.info("Waiting for server connection result");
|
||||
listeningFuture.get();
|
||||
LOG.info("listeningFuture.get returned");
|
||||
} catch (Throwable th) {
|
||||
LOG.error("listeningFuture.get error", th);
|
||||
}
|
||||
|
||||
// Make sure we close the server. Sockets are limited resources...
|
||||
CloseUtils.closeIOQuietly(inReader, outWriter, server);
|
||||
CloseUtils.closeIOQuietly(serverSocket, server, outWriter, inReader);
|
||||
languageClient.connectionReport("Server disconnected.");
|
||||
languageClient.onServerDisconnected();
|
||||
} catch (Throwable th) {
|
||||
@@ -103,6 +109,9 @@ public class LSPClientLauncher extends Thread {
|
||||
server.exit();
|
||||
}
|
||||
serverSocket.close();
|
||||
if(listeningFuture != null && !listeningFuture.isDone()) {
|
||||
listeningFuture.cancel(true);
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
// Ignored
|
||||
}
|
||||
@@ -113,15 +122,17 @@ public class LSPClientLauncher extends Thread {
|
||||
* Writing to a socket's output stream is considered as network operation
|
||||
* Trying to write to a socket on UI Thread will result in NetworkOnMainThreadExeption
|
||||
*/
|
||||
|
||||
private class OutputStreamWrapper extends OutputStream {
|
||||
|
||||
private final AsyncWriter writer;
|
||||
private final Thread writerThread;
|
||||
|
||||
private final OutputStream actualStream;
|
||||
|
||||
public OutputStreamWrapper(OutputStream actualStream) {
|
||||
this.writer = new AsyncWriter(actualStream);
|
||||
this.writerThread = new Thread(writer);
|
||||
this.actualStream = actualStream;
|
||||
|
||||
this.writerThread.start();
|
||||
}
|
||||
@@ -133,11 +144,11 @@ public class LSPClientLauncher extends Thread {
|
||||
|
||||
|
||||
/**
|
||||
* LSP4J always calls this method to write to server.
|
||||
* LSP4J always calls this method to write to remote proxy.
|
||||
* See https://github.com/eclipse/lsp4j/blob/253aef9a702659f2524ecefaab7e829278c2ffd3/org.eclipse.lsp4j.jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/json/StreamMessageConsumer.java#L67
|
||||
*/
|
||||
@Override
|
||||
public void write(byte[] b) throws IOException {
|
||||
public void write(final byte[] b) throws IOException {
|
||||
// Do not call super.write(b);
|
||||
// If done, it will further call write(int)
|
||||
this.writer.write(b);
|
||||
@@ -177,7 +188,7 @@ public class LSPClientLauncher extends Thread {
|
||||
this.out.write(data.data);
|
||||
this.out.flush();
|
||||
} catch (Throwable e) {
|
||||
LOG.error("Error writing to LanguageServer");
|
||||
LOG.error("Error writing to LanguageServer[closed=" + closed + "]", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,8 +197,8 @@ public class LSPClientLauncher extends Thread {
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
this.closed = true;
|
||||
this.out.close();
|
||||
this.closed = true;
|
||||
}
|
||||
|
||||
class Writable {
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.itsaky.androidide.lsp.providers;
|
||||
|
||||
import com.itsaky.lsp.services.IDELanguageServer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java9.util.concurrent.CompletableFuture;
|
||||
import org.eclipse.lsp4j.CodeAction;
|
||||
import org.eclipse.lsp4j.CodeActionContext;
|
||||
import org.eclipse.lsp4j.CodeActionParams;
|
||||
import org.eclipse.lsp4j.Command;
|
||||
import org.eclipse.lsp4j.Diagnostic;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
|
||||
/**
|
||||
* Provides code actions to editor. Handles cancellation of last incomplete request.
|
||||
*/
|
||||
public class CodeActionProvider {
|
||||
|
||||
private CompletableFuture<List<Either<Command, CodeAction>>> lastRequest;
|
||||
|
||||
public CompletableFuture<List<Either<Command, CodeAction>>> codeActions(IDELanguageServer server, TextDocumentIdentifier documentId, Range cursorRange, List<Diagnostic> diagnostics) {
|
||||
|
||||
if(lastRequest != null && !lastRequest.isDone()) {
|
||||
lastRequest.cancel(true);
|
||||
}
|
||||
|
||||
if(server == null || cursorRange == null || documentId == null)
|
||||
return null;
|
||||
|
||||
final CodeActionParams params = new CodeActionParams();
|
||||
final CodeActionContext context = new CodeActionContext();
|
||||
context.setDiagnostics(diagnostics == null ? new ArrayList<Diagnostic>() : diagnostics);
|
||||
params.setContext(context);
|
||||
params.setTextDocument(documentId);
|
||||
params.setRange(cursorRange);
|
||||
|
||||
return lastRequest = server.getTextDocumentService().codeAction(params);
|
||||
}
|
||||
}
|
||||
@@ -67,6 +67,7 @@ import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.databinding.LayoutDialogTextInputBinding;
|
||||
import com.itsaky.androidide.lsp.AbstractLanguageClient;
|
||||
import com.itsaky.androidide.lsp.LSPProvider;
|
||||
import com.itsaky.androidide.lsp.providers.CodeActionProvider;
|
||||
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
import com.itsaky.androidide.utils.Symbols;
|
||||
@@ -103,7 +104,9 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java9.util.concurrent.CompletableFuture;
|
||||
import org.eclipse.lsp4j.CodeAction;
|
||||
import org.eclipse.lsp4j.CodeActionOptions;
|
||||
import org.eclipse.lsp4j.Command;
|
||||
import org.eclipse.lsp4j.CompletionOptions;
|
||||
import org.eclipse.lsp4j.DefinitionOptions;
|
||||
import org.eclipse.lsp4j.DefinitionParams;
|
||||
@@ -276,6 +279,7 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
|
||||
private EditorLanguage mLanguage;
|
||||
private IDELanguageServer mLanguageServer;
|
||||
private AbstractLanguageClient mLanguageClient;
|
||||
private CodeActionProvider mCodeActionProvider;
|
||||
private long mLastMakeVisible = 0;
|
||||
private EditorAutoCompleteWindow mCompletionWindow;
|
||||
private EditorTouchEventHandler mEventHandler;
|
||||
@@ -560,6 +564,7 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
|
||||
setFocusableInTouchMode(true);
|
||||
mConnection = new EditorInputConnection(this);
|
||||
mCompletionWindow = new EditorAutoCompleteWindow(this);
|
||||
mCodeActionProvider = new CodeActionProvider();
|
||||
|
||||
mVerticalEdgeGlow = new MaterialEdgeEffect();
|
||||
mHorizontalGlow = new MaterialEdgeEffect();
|
||||
@@ -1478,7 +1483,7 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
|
||||
drawWhitespaces(canvas, paintingOffset, row, firstVisibleChar, lastVisibleChar, trailingWhitespaceStart, columnCount, circleRadius);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Draw composing text underline
|
||||
if (line == mConnection.mComposingLine) {
|
||||
int composingStart = mConnection.mComposingStart;
|
||||
@@ -4820,10 +4825,47 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Request code actions from server at the given position containing given diagnostics
|
||||
* <p>
|
||||
* Called by {@link EditorTouchEventHandler}
|
||||
* <p>
|
||||
* TODO The current implementation in JLS does not work. Make it work!
|
||||
*/
|
||||
public void requestCodeActions() {
|
||||
if(mLanguageServer == null && mLanguageClient == null) return;
|
||||
|
||||
final List<Diagnostic> diagnostics = findDiagnosticsContainingLine(getCursor().getLeftLine());
|
||||
final CompletableFuture<List<Either<Command, CodeAction>>> future = mCodeActionProvider.codeActions(mLanguageServer, getDocumentIdentifier(), getCursorRange(), diagnostics);
|
||||
|
||||
if(future == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
future.whenComplete((l, t) -> {
|
||||
final List<Either<Command, CodeAction>> actions = l;
|
||||
final Throwable th = t;
|
||||
|
||||
if(future.isCancelled() || future.isCompletedExceptionally() || actions == null || actions.isEmpty()) {
|
||||
mLanguageClient.hideCodeActions();
|
||||
return;
|
||||
}
|
||||
|
||||
mLanguageClient.showCodeActions(actions);
|
||||
});
|
||||
}
|
||||
|
||||
public Position getCursorAsLSPPosition() {
|
||||
return new Position(getCursor().getLeftLine(), getCursor().getLeftColumn());
|
||||
}
|
||||
|
||||
public Range getCursorRange() {
|
||||
final Cursor cursor = getCursor();
|
||||
final Position start = new Position(cursor.getLeftLine(), cursor.getLeftColumn());
|
||||
final Position end = new Position(cursor.getRightLine(), cursor.getRightColumn());
|
||||
return new Range(start, end);
|
||||
}
|
||||
|
||||
public TextDocumentIdentifier getDocumentIdentifier() {
|
||||
return new TextDocumentIdentifier(getFile().toURI().toString());
|
||||
}
|
||||
|
||||
@@ -48,15 +48,15 @@
|
||||
android:layout_below="@id/item"
|
||||
android:paddingStart="16dp"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
tools:ignore="ScrollViewSize">
|
||||
android:clipToPadding="false">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/items"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"/>
|
||||
android:paddingEnd="8dp"
|
||||
tools:ignore="ScrollViewSize"/>
|
||||
|
||||
</HorizontalScrollView>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user