diff --git a/app/build.gradle b/app/build.gradle index 5b98e2e27..113054a1f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -145,6 +145,9 @@ dependencies { implementation project(':javapoet') implementation project(':lsp') + // LSP4J + implementation 'org.eclipse.lsp4j:org.eclipse.lsp4j:0.12.0' + implementation fileTree(dir: 'libs', include: ['*.jar']) coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' diff --git a/app/libs/android-retrostreams-and-retrofuture.jar b/app/libs/android-retrostreams-and-retrofuture.jar deleted file mode 100644 index 6eb00c60e..000000000 Binary files a/app/libs/android-retrostreams-and-retrofuture.jar and /dev/null differ diff --git a/app/libs/lsp4j-android-desugared.jar b/app/libs/lsp4j-android-desugared.jar deleted file mode 100644 index 9f2352ae5..000000000 Binary files a/app/libs/lsp4j-android-desugared.jar and /dev/null differ diff --git a/app/src/main/assets/data/jls.jar b/app/src/main/assets/data/jls.jar index 1e2bb1f22..3d4ab85bc 100644 Binary files a/app/src/main/assets/data/jls.jar and b/app/src/main/assets/data/jls.jar differ diff --git a/app/src/main/java/com/itsaky/androidide/language/java/JavaAutoComplete.java b/app/src/main/java/com/itsaky/androidide/language/java/JavaAutoComplete.java index 6b03fb83c..9bedc33c7 100644 --- a/app/src/main/java/com/itsaky/androidide/language/java/JavaAutoComplete.java +++ b/app/src/main/java/com/itsaky/androidide/language/java/JavaAutoComplete.java @@ -9,7 +9,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; -import java9.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletableFuture; import org.eclipse.lsp4j.CompletionItem; import org.eclipse.lsp4j.CompletionList; import org.eclipse.lsp4j.CompletionParams; diff --git a/app/src/main/java/com/itsaky/androidide/lsp/AbstractLanguageClient.java b/app/src/main/java/com/itsaky/androidide/lsp/AbstractLanguageClient.java index 297f54665..291c6b15b 100644 --- a/app/src/main/java/com/itsaky/androidide/lsp/AbstractLanguageClient.java +++ b/app/src/main/java/com/itsaky/androidide/lsp/AbstractLanguageClient.java @@ -35,7 +35,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import java9.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletableFuture; import org.eclipse.lsp4j.CodeAction; import org.eclipse.lsp4j.Command; import org.eclipse.lsp4j.Diagnostic; diff --git a/app/src/main/java/com/itsaky/androidide/lsp/LSPClientLauncher.java b/app/src/main/java/com/itsaky/androidide/lsp/LSPClientLauncher.java index 1beb24543..1aa133d5e 100644 --- a/app/src/main/java/com/itsaky/androidide/lsp/LSPClientLauncher.java +++ b/app/src/main/java/com/itsaky/androidide/lsp/LSPClientLauncher.java @@ -1,21 +1,15 @@ package com.itsaky.androidide.lsp; -import com.blankj.utilcode.util.CloseUtils; import com.itsaky.androidide.utils.Logger; import com.itsaky.lsp.services.IDELanguageServer; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.net.InetSocketAddress; -import java.nio.channels.AsynchronousServerSocketChannel; -import java.nio.channels.AsynchronousSocketChannel; -import java.nio.channels.Channels; +import java.util.Objects; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; -import java.util.concurrent.Future; import org.eclipse.lsp4j.jsonrpc.Launcher; -import java.util.Objects; /** * Launches a client for a Language server and handles connection. @@ -167,5 +161,5 @@ public abstract class LSPClientLauncher extends Thread { } } - private static final Logger LOG = Logger.instance("LSPClientLauncher"); + protected static final Logger LOG = Logger.instance("LSPClientLauncher"); } diff --git a/app/src/main/java/com/itsaky/androidide/lsp/LanguageServerWrapper.java b/app/src/main/java/com/itsaky/androidide/lsp/LanguageServerWrapper.java index 23f484861..ea93bef04 100644 --- a/app/src/main/java/com/itsaky/androidide/lsp/LanguageServerWrapper.java +++ b/app/src/main/java/com/itsaky/androidide/lsp/LanguageServerWrapper.java @@ -7,7 +7,7 @@ import com.itsaky.lsp.services.IDETextDocumentService; import com.itsaky.lsp.services.IDEWorkspaceService; import java.util.Collections; import java.util.List; -import java9.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletableFuture; import org.eclipse.lsp4j.CallHierarchyIncomingCall; import org.eclipse.lsp4j.CallHierarchyIncomingCallsParams; import org.eclipse.lsp4j.CallHierarchyItem; diff --git a/app/src/main/java/com/itsaky/androidide/lsp/StandardStreamsLauncher.java b/app/src/main/java/com/itsaky/androidide/lsp/StandardStreamsLauncher.java index 5f6b5b4e6..dfaee41db 100644 --- a/app/src/main/java/com/itsaky/androidide/lsp/StandardStreamsLauncher.java +++ b/app/src/main/java/com/itsaky/androidide/lsp/StandardStreamsLauncher.java @@ -2,6 +2,8 @@ package com.itsaky.androidide.lsp; import com.blankj.utilcode.util.CloseUtils; import com.itsaky.lsp.services.IDELanguageServer; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.util.concurrent.Future; @@ -16,8 +18,8 @@ public class StandardStreamsLauncher extends LSPClientLauncher { public StandardStreamsLauncher(AbstractLanguageClient client, InputStream in, OutputStream out) { super(client); - this.in = in; - this.out = out; + this.in = new BufferedInputStream( in ); + this.out = new BufferedOutputStream( out ); } @Override @@ -32,6 +34,7 @@ public class StandardStreamsLauncher extends LSPClientLauncher { try { listening.get(); } catch (Throwable th) { + LOG.error("listening.get error", th); } shutdown(); diff --git a/app/src/main/java/com/itsaky/androidide/lsp/client/java/JavaLanguageClient.java b/app/src/main/java/com/itsaky/androidide/lsp/client/java/JavaLanguageClient.java index 12c59e864..3a432fb87 100644 --- a/app/src/main/java/com/itsaky/androidide/lsp/client/java/JavaLanguageClient.java +++ b/app/src/main/java/com/itsaky/androidide/lsp/client/java/JavaLanguageClient.java @@ -2,7 +2,7 @@ package com.itsaky.androidide.lsp.client.java; import com.itsaky.androidide.lsp.AbstractLanguageClient; import java.util.List; -import java9.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletableFuture; import org.eclipse.lsp4j.ApplyWorkspaceEditParams; import org.eclipse.lsp4j.ApplyWorkspaceEditResponse; import org.eclipse.lsp4j.ConfigurationParams; diff --git a/app/src/main/java/com/itsaky/androidide/lsp/handlers/JLSHandler.java b/app/src/main/java/com/itsaky/androidide/lsp/handlers/JLSHandler.java index 815ba6814..8da5894c7 100644 --- a/app/src/main/java/com/itsaky/androidide/lsp/handlers/JLSHandler.java +++ b/app/src/main/java/com/itsaky/androidide/lsp/handlers/JLSHandler.java @@ -6,16 +6,14 @@ import com.itsaky.androidide.lsp.LSPClientLauncher; import com.itsaky.androidide.lsp.LSPProvider; import com.itsaky.androidide.lsp.StandardStreamsLauncher; import com.itsaky.androidide.lsp.client.java.JavaLanguageClient; -import com.itsaky.androidide.shell.ShellServer; import com.itsaky.androidide.utils.Environment; import com.itsaky.androidide.utils.Logger; import com.itsaky.lsp.services.IDELanguageServer; import java.io.File; import java.util.ArrayList; import java.util.List; -import java.util.Locale; import java.util.Optional; -import java9.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletableFuture; import org.eclipse.lsp4j.InitializeParams; import org.eclipse.lsp4j.InitializeResult; import org.eclipse.lsp4j.InitializedParams; @@ -23,11 +21,22 @@ import org.eclipse.lsp4j.WorkspaceFolder; public class JLSHandler implements LSPHandler { - private static ShellServer mShell; + /** + * Why not use {@link com.itsaky.androidide.shell.ShellServer ShellServer} instead? + * Because it will try to read from the process's input stream + * and at the same time, LSP4J will try to do the same. But, + * as {@code ShellServer} will be started first, LSP4J will not he able to read at all. + *

+ * That's why we use a ProcessBuilder without redirecting {@code System.err} of the process + */ + private static ProcessBuilder mShellBuilder; + private static Process mShell; + private static JavaLanguageClient javaClient; private static LSPClientLauncher mLauncher; - private static final Logger LOG = Logger.instance("LSP"); + private static final Logger LOG = Logger.instance("JLSHandler"); + private static final boolean QUIET = !StudioApp.DEBUG; @Override public void start(Runnable onStarted) { @@ -35,21 +44,36 @@ public class JLSHandler implements LSPHandler { return; } - final boolean quiet = false; - mShell = StudioApp.getInstance().newShell((t -> LOG.verbose(t)), false); - mShell.append(String.format(Locale.US, "java -jar %s%s", Environment.JLS_JAR.getAbsolutePath(), quiet ? /* IMPORTANT: Do not forget the leading space */ " --quiet" : ""), false); - - javaClient = new JavaLanguageClient(null, server -> storeServerInfoAndNotify(server, onStarted)); - javaClient.setActivityProvider(LSP.PROVIDER); - mLauncher = new StandardStreamsLauncher(javaClient, mShell.getProcessInputStream(), mShell.getProcessOutputStream()); - mLauncher.start(); + try { + // Why not directly use 'java' command? + // I tried doing the same, but it ends up throwing unexpected errors. + // Something related to FileSystem... + mShellBuilder = new ProcessBuilder("/system/bin/sh"); + mShellBuilder.directory(Environment.HOME); + mShellBuilder.redirectErrorStream(false); + + if(StudioApp.DEBUG) { + mShellBuilder.redirectError(new File("/sdcard/ide_xlog/process_error.txt")); + } + + mShellBuilder.environment().putAll(Environment.getEnvironment(false)); + mShell = mShellBuilder.start(); + + mShell.getOutputStream().write(("java -jar " + Environment.JLS_JAR.getAbsolutePath() + (QUIET ? " --quiet" : "") + "\n").getBytes()); + mShell.getOutputStream().flush(); + + javaClient = new JavaLanguageClient(null, server -> storeServerInfoAndNotify(server, onStarted)); + javaClient.setActivityProvider(LSP.PROVIDER); + mLauncher = new StandardStreamsLauncher(javaClient, mShell.getInputStream(), mShell.getOutputStream()); + mLauncher.start(); + } catch (Throwable th) {} } @Override public Optional init(String rootPath) { final IDELanguageServer server = LSPProvider.getServerForLanguage(LSPProvider.LANGUAGE_JAVA); - LOG.info("SERVER JLS: " + server); + if (server == null) return Optional.empty(); @@ -73,15 +97,12 @@ public class JLSHandler implements LSPHandler { CompletableFuture initResult = server.initialize(params); try { - LOG.info("init future: " + initResult); InitializeResult result = initResult.get(); - LOG.info("Initialize result", result); if(result.getCapabilities() != null) { LSPProvider.setServerCapabilitesForLanguage(LSPProvider.LANGUAGE_JAVA, result.getCapabilities()); } return Optional.of(result); } catch (Throwable e) { - LOG.error("Initialize error", e); return Optional.empty(); } } @@ -103,6 +124,9 @@ public class JLSHandler implements LSPHandler { if(mLauncher != null) { mLauncher.shutdown(); } + if(mShell != null) { + mShell.destroy(); + } } /** diff --git a/app/src/main/java/com/itsaky/androidide/lsp/providers/CodeActionProvider.java b/app/src/main/java/com/itsaky/androidide/lsp/providers/CodeActionProvider.java index e6e2b70ea..346e874d9 100644 --- a/app/src/main/java/com/itsaky/androidide/lsp/providers/CodeActionProvider.java +++ b/app/src/main/java/com/itsaky/androidide/lsp/providers/CodeActionProvider.java @@ -3,7 +3,7 @@ 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 java.util.concurrent.CompletableFuture; import org.eclipse.lsp4j.CodeAction; import org.eclipse.lsp4j.CodeActionContext; import org.eclipse.lsp4j.CodeActionParams; diff --git a/app/src/main/java/com/itsaky/androidide/shell/ShellServer.java b/app/src/main/java/com/itsaky/androidide/shell/ShellServer.java index 884c5e384..4094c3184 100644 --- a/app/src/main/java/com/itsaky/androidide/shell/ShellServer.java +++ b/app/src/main/java/com/itsaky/androidide/shell/ShellServer.java @@ -36,10 +36,6 @@ public class ShellServer extends Thread { ProcessBuilder processBuilder = new ProcessBuilder(new String[]{command}); processBuilder.directory(new File(dirPath)); processBuilder.redirectErrorStream(redirectErrors); - - if(!redirectErrors) - processBuilder.redirectError(new File("/sdcard/ide_xlog/process_error.txt")); - processBuilder.environment().putAll(Environment.getEnvironment(false)); try { this.process = processBuilder.start(); diff --git a/app/src/main/java/io/github/rosemoe/editor/widget/CodeEditor.java b/app/src/main/java/io/github/rosemoe/editor/widget/CodeEditor.java index 2a48376b6..a05c850c9 100644 --- a/app/src/main/java/io/github/rosemoe/editor/widget/CodeEditor.java +++ b/app/src/main/java/io/github/rosemoe/editor/widget/CodeEditor.java @@ -103,7 +103,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; -import java9.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletableFuture; import org.eclipse.lsp4j.CodeAction; import org.eclipse.lsp4j.CodeActionOptions; import org.eclipse.lsp4j.Command; diff --git a/lsp/build.gradle b/lsp/build.gradle index 0ac2a6cc1..4bb21d874 100644 --- a/lsp/build.gradle +++ b/lsp/build.gradle @@ -26,5 +26,6 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.google.code.gson:gson:2.8.7' - implementation 'org.eclipse.xtend:org.eclipse.xtend.core:2.24.0' + implementation 'org.eclipse.lsp4j:org.eclipse.lsp4j:0.12.0' + implementation 'org.eclipse.lsp4j:org.eclipse.lsp4j.jsonrpc:0.12.0' } diff --git a/lsp/libs/android-retrostreams-and-retrofuture.jar b/lsp/libs/android-retrostreams-and-retrofuture.jar deleted file mode 100644 index 6eb00c60e..000000000 Binary files a/lsp/libs/android-retrostreams-and-retrofuture.jar and /dev/null differ diff --git a/lsp/libs/lsp4j-android-desugared.jar b/lsp/libs/lsp4j-android-desugared.jar deleted file mode 100644 index 9f2352ae5..000000000 Binary files a/lsp/libs/lsp4j-android-desugared.jar and /dev/null differ diff --git a/lsp/libs/lsp4j-android-not-desugared.jar.backup b/lsp/libs/lsp4j-android-not-desugared.jar.backup deleted file mode 100644 index 6ec7da8de..000000000 Binary files a/lsp/libs/lsp4j-android-not-desugared.jar.backup and /dev/null differ diff --git a/lsp/src/main/java/com/itsaky/lsp/services/IDELanguageServer.java b/lsp/src/main/java/com/itsaky/lsp/services/IDELanguageServer.java index 0bb71e664..71e9c89fc 100644 --- a/lsp/src/main/java/com/itsaky/lsp/services/IDELanguageServer.java +++ b/lsp/src/main/java/com/itsaky/lsp/services/IDELanguageServer.java @@ -1,6 +1,6 @@ package com.itsaky.lsp.services; -import java9.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletableFuture; import org.eclipse.lsp4j.InitializeParams; import org.eclipse.lsp4j.InitializeResult; diff --git a/lsp/src/main/java/com/itsaky/lsp/services/IDETextDocumentService.java b/lsp/src/main/java/com/itsaky/lsp/services/IDETextDocumentService.java index 5ba7dd690..998974dc6 100644 --- a/lsp/src/main/java/com/itsaky/lsp/services/IDETextDocumentService.java +++ b/lsp/src/main/java/com/itsaky/lsp/services/IDETextDocumentService.java @@ -1,7 +1,7 @@ package com.itsaky.lsp.services; import java.util.List; -import java9.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletableFuture; import com.itsaky.lsp.SemanticHighlight; import com.itsaky.lsp.SemanticHighlightsParams; diff --git a/lsp/src/main/java/com/itsaky/lsp/services/IDEWorkspaceService.java b/lsp/src/main/java/com/itsaky/lsp/services/IDEWorkspaceService.java index f2b4cc807..edcc973f5 100644 --- a/lsp/src/main/java/com/itsaky/lsp/services/IDEWorkspaceService.java +++ b/lsp/src/main/java/com/itsaky/lsp/services/IDEWorkspaceService.java @@ -1,7 +1,7 @@ package com.itsaky.lsp.services; import java.util.List; -import java9.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletableFuture; import org.eclipse.lsp4j.CreateFilesParams; import org.eclipse.lsp4j.DeleteFilesParams; diff --git a/lsp/src/main/java/java9/util/function/BiConsumer.java b/lsp/src/main/java/java9/util/function/BiConsumer.java deleted file mode 100644 index b8d12fa42..000000000 --- a/lsp/src/main/java/java9/util/function/BiConsumer.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package java9.util.function; - -import java9.util.function.Consumer; -import java9.util.Objects; - -/** - * Represents an operation that accepts two input arguments and returns no - * result. This is the two-arity specialization of {@link Consumer}. - * Unlike most other functional interfaces, {@code BiConsumer} is expected - * to operate via side-effects. - * - *

This is a functional interface - * whose functional method is {@link #accept(Object, Object)}. - * - * @param the type of the first argument to the operation - * @param the type of the second argument to the operation - * - * @see Consumer - * @since 1.8 - */ -public interface BiConsumer { - - /** - * Performs this operation on the given arguments. - * - * @param t the first input argument - * @param u the second input argument - */ - void accept(T t, U u); - - /** - * Returns a composed {@code BiConsumer} that performs, in sequence, this - * operation followed by the {@code after} operation. If performing either - * operation throws an exception, it is relayed to the caller of the - * composed operation. If performing this operation throws an exception, - * the {@code after} operation will not be performed. - * - * @param after the operation to perform after this operation - * @return a composed {@code BiConsumer} that performs in sequence this - * operation followed by the {@code after} operation - * @throws NullPointerException if {@code after} is null - */ - default BiConsumer andThen(BiConsumer after) { - Objects.requireNonNull(after); - - return (l, r) -> { - accept(l, r); - after.accept(l, r); - }; - } -}