Added test for 'Add import' action

This commit is contained in:
Akash Yadav
2022-04-03 17:07:55 +05:30
parent 90671aa383
commit 28a437a63c
19 changed files with 240 additions and 208 deletions
@@ -25,10 +25,12 @@ import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.inputmethod.EditorInfo;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.core.content.ContextCompat;
import com.blankj.utilcode.util.ThreadUtils;
import com.itsaky.androidide.R;
import com.itsaky.androidide.app.StudioApp;
@@ -59,12 +61,11 @@ import com.itsaky.lsp.models.ShowDocumentParams;
import com.itsaky.lsp.models.SignatureHelp;
import com.itsaky.lsp.models.SignatureHelpParams;
import com.itsaky.lsp.util.DiagnosticUtil;
import com.itsaky.lsp.util.PathUtils;
import com.itsaky.toaster.Toaster;
import io.github.rosemoe.sora.event.ContentChangeEvent;
import io.github.rosemoe.sora.event.SelectionChangeEvent;
import io.github.rosemoe.sora.event.Unsubscribe;
import io.github.rosemoe.sora.widget.CodeEditor;
import io.github.rosemoe.sora.widget.component.EditorAutoCompletion;
import org.jetbrains.annotations.Contract;
import java.io.File;
import java.util.Collections;
import java.util.List;
@@ -73,7 +74,12 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.jetbrains.annotations.Contract;
import io.github.rosemoe.sora.event.ContentChangeEvent;
import io.github.rosemoe.sora.event.SelectionChangeEvent;
import io.github.rosemoe.sora.event.Unsubscribe;
import io.github.rosemoe.sora.widget.CodeEditor;
import io.github.rosemoe.sora.widget.component.EditorAutoCompletion;
public class IDEEditor extends CodeEditor {
@@ -432,7 +438,8 @@ public class IDEEditor extends CodeEditor {
() -> {
if (locations.size() == 1) {
var location = locations.get(0);
if (location.getFile().equals(getFile().toPath())) {
if (PathUtils.isSameFile(
location.getFile(), getFile().toPath())) {
setSelection(location.getRange());
return;
}
@@ -516,7 +523,7 @@ public class IDEEditor extends CodeEditor {
} else {
if (result.getLocations().size() == 1) {
final var loc = result.getLocations().get(0);
if (loc.getFile().equals(getFile().toPath())) {
if (PathUtils.isSameFile(loc.getFile(), getFile().toPath())) {
setSelection(loc.getRange());
return;
}
@@ -0,0 +1,38 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.lsp.util
import java.io.IOException
import java.nio.file.Files
import java.nio.file.Path
/**
* @author Akash Yadav
*/
class PathUtils {
companion object {
@JvmStatic
fun isSameFile(first: Path, second: Path): Boolean {
return try {
Files.isSameFile(first, second)
} catch (e: IOException) {
false
}
}
}
}
@@ -1,7 +1,9 @@
package com.itsaky.lsp.java;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import com.itsaky.androidide.utils.CharSequenceInputStream;
import com.itsaky.androidide.utils.CharSequenceReader;
import com.itsaky.androidide.utils.Logger;
@@ -9,6 +11,8 @@ import com.itsaky.lsp.java.utils.StringSearch;
import com.itsaky.lsp.models.DocumentChangeEvent;
import com.itsaky.lsp.models.DocumentCloseEvent;
import com.itsaky.lsp.models.DocumentOpenEvent;
import com.itsaky.lsp.util.PathUtils;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
@@ -32,6 +36,7 @@ import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
import javax.lang.model.element.TypeElement;
public class FileStore {
@@ -42,16 +47,7 @@ public class FileStore {
/** javaSources[file] is the javaSources time of a .java source file. */
// TODO organize by package name for speed of list(...)
private static final TreeMap<Path, Info> javaSources = new TreeMap<>();
private static class Info {
final Instant modified;
final String packageName;
Info(Instant modified, String packageName) {
this.modified = modified;
this.packageName = packageName;
}
}
private static final Logger LOG = Logger.newInstance("FileStore");
public static void shutdown() {
workspaceRoots.clear();
@@ -82,16 +78,6 @@ public class FileStore {
}
}
public static class FindJavaSources extends SimpleFileVisitor<Path> {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes _attrs) {
if (isJavaFile(file)) {
readInfoFromDisk(file);
}
return FileVisitResult.CONTINUE;
}
}
public static Collection<Path> all() {
return javaSources.keySet();
}
@@ -137,7 +123,7 @@ public class FileStore {
for (Path dir = file.getParent(); dir != null; dir = dir.getParent()) {
// Try to find a sibling with a package declaration
for (Path sibling : javaSourcesIn(dir)) {
if (sibling.equals(file)) {
if (PathUtils.isSameFile(sibling, file)) {
continue;
}
String packageName = packageName(sibling);
@@ -335,6 +321,26 @@ public class FileStore {
return Optional.empty();
}
private static class Info {
final Instant modified;
final String packageName;
Info(Instant modified, String packageName) {
this.modified = modified;
this.packageName = packageName;
}
}
public static class FindJavaSources extends SimpleFileVisitor<Path> {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes _attrs) {
if (isJavaFile(file)) {
readInfoFromDisk(file);
}
return FileVisitResult.CONTINUE;
}
}
static class VersionedContent {
final CharSequence content;
final int version;
@@ -346,6 +352,4 @@ public class FileStore {
this.version = version;
}
}
private static final Logger LOG = Logger.newInstance("FileStore");
}
@@ -27,6 +27,7 @@ import com.itsaky.lsp.java.utils.ScanClassPath;
import com.itsaky.lsp.java.utils.StringSearch;
import com.itsaky.lsp.java.visitors.FindTypeDeclarations;
import com.sun.source.tree.CompilationUnitTree;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Path;
@@ -43,6 +44,7 @@ import java.util.Set;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import javax.tools.StandardLocation;
@@ -52,8 +54,10 @@ public class JavaCompilerService implements CompilerProvider {
private static final Cache<String, Boolean> cacheContainsWord = new Cache<>();
private static final Cache<Void, List<String>> cacheContainsType = new Cache<>();
private static final Logger LOG = Logger.newInstance("JavaCompilerService");
protected final ScanClassPath classPathScanner = new ScanClassPath();
protected final Set<Path> classPath, docPath;
protected final Set<String> jdkClasses = ScanClassPath.jdkTopLevelClasses(), classPathClasses;
protected final Set<String> jdkClasses = classPathScanner.jdkTopLevelClasses(),
classPathClasses;
protected final ReusableCompiler compiler = new ReusableCompiler();
protected final SynchronizedTask synchronizedTask = new SynchronizedTask();
protected final List<Diagnostic<? extends JavaFileObject>> diagnostics = new ArrayList<>();
@@ -69,7 +73,7 @@ public class JavaCompilerService implements CompilerProvider {
public JavaCompilerService(Set<Path> classPath, Set<Path> docPath) {
this.classPath = Collections.unmodifiableSet(classPath);
this.docPath = Collections.unmodifiableSet(docPath);
this.classPathClasses = ScanClassPath.classPathTopLevelClasses(classPath);
this.classPathClasses = classPathScanner.classPathTopLevelClasses(classPath);
this.fileManager = new SourceFileManager();
}
@@ -18,10 +18,18 @@
package com.itsaky.lsp.java.compiler;
import com.itsaky.lsp.java.FileStore;
import java.io.*;
import com.itsaky.lsp.util.PathUtils;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.Writer;
import java.net.URI;
import java.nio.file.Path;
import java.time.Instant;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.NestingKind;
import javax.tools.JavaFileObject;
@@ -45,11 +53,20 @@ public class SourceFileObject implements JavaFileObject {
this.modified = modified;
}
private static Kind kindFromExtension(String name) {
for (Kind candidate : Kind.values()) {
if (name.endsWith(candidate.extension)) {
return candidate;
}
}
return null;
}
@Override
public boolean equals(Object other) {
if (other.getClass() != SourceFileObject.class) return false;
SourceFileObject that = (SourceFileObject) other;
return this.path.equals(that.path);
return PathUtils.isSameFile(this.path, that.path);
}
@Override
@@ -63,15 +80,6 @@ public class SourceFileObject implements JavaFileObject {
return kindFromExtension(name);
}
private static Kind kindFromExtension(String name) {
for (Kind candidate : Kind.values()) {
if (name.endsWith(candidate.extension)) {
return candidate;
}
}
return null;
}
@Override
public boolean isNameCompatible(String simpleName, Kind kind) {
return path.getFileName().toString().equals(simpleName + kind.extension);
@@ -19,8 +19,10 @@ package com.itsaky.lsp.java.providers;
import android.text.TextUtils;
import android.util.Pair;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.itsaky.androidide.utils.Logger;
import com.itsaky.lsp.java.compiler.CompileTask;
import com.itsaky.lsp.java.compiler.CompilerProvider;
@@ -56,6 +58,9 @@ import com.sun.source.util.TreePath;
import com.sun.source.util.Trees;
import com.sun.tools.javac.api.ClientCodeWrapper;
import com.sun.tools.javac.util.JCDiagnostic;
import org.jetbrains.annotations.Contract;
import java.io.IOException;
import java.nio.file.Path;
import java.time.Duration;
@@ -68,6 +73,7 @@ import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
@@ -79,7 +85,6 @@ import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import org.jetbrains.annotations.Contract;
public class CodeActionProvider {
@@ -322,7 +327,11 @@ public class CodeActionProvider {
final List<String> classes = compiler.publicTopLevelTypes();
for (int i = 0; i < classes.size(); i++) {
final String klass = classes.get(i);
String klass = classes.get(i);
if (klass.contains("/")) {
klass = klass.replace("/", ".");
}
if (!klass.endsWith("." + simpleName)) {
continue;
}
@@ -18,6 +18,7 @@
package com.itsaky.lsp.java.providers;
import androidx.annotation.NonNull;
import com.itsaky.androidide.utils.Logger;
import com.itsaky.lsp.java.compiler.CompileTask;
import com.itsaky.lsp.java.compiler.CompilerProvider;
@@ -28,14 +29,18 @@ import com.itsaky.lsp.java.utils.NavigationHelper;
import com.itsaky.lsp.models.DefinitionParams;
import com.itsaky.lsp.models.DefinitionResult;
import com.itsaky.lsp.models.Location;
import com.itsaky.lsp.util.PathUtils;
import com.sun.source.util.TreePath;
import com.sun.source.util.Trees;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import javax.lang.model.element.Element;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
@@ -100,7 +105,7 @@ public class DefinitionProvider {
return Collections.emptyList();
}
if (otherFile.get().toUri().equals(file.toUri())) {
if (PathUtils.isSameFile(Paths.get(otherFile.get().toUri()), file)) {
return findDefinitions(task, element);
}
task.close();
@@ -132,7 +137,7 @@ public class DefinitionProvider {
SourceFileObject fileAsSource = new SourceFileObject(file);
List<JavaFileObject> sources = Arrays.asList(fileAsSource, otherFile.get());
if (otherFile.get().toUri().equals(file.toUri())) {
if (PathUtils.isSameFile(Paths.get(otherFile.get().toUri()), file)) {
sources = Collections.singletonList(fileAsSource);
}
@@ -18,15 +18,15 @@
package com.itsaky.lsp.java.providers;
import androidx.annotation.NonNull;
import com.itsaky.androidide.app.BaseApplication;
import com.itsaky.lsp.java.FileStore;
import com.itsaky.lsp.java.R;
import com.itsaky.lsp.java.compiler.CompileTask;
import com.itsaky.lsp.java.visitors.DiagnosticVisitor;
import com.itsaky.lsp.models.DiagnosticItem;
import com.itsaky.lsp.models.DiagnosticSeverity;
import com.itsaky.lsp.models.Position;
import com.itsaky.lsp.models.Range;
import com.itsaky.lsp.util.PathUtils;
import com.sun.source.tree.BlockTree;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompilationUnitTree;
@@ -37,6 +37,7 @@ import com.sun.source.tree.VariableTree;
import com.sun.source.util.SourcePositions;
import com.sun.source.util.TreePath;
import com.sun.source.util.Trees;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
@@ -46,6 +47,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.lang.model.element.Element;
import javax.lang.model.element.Name;
import javax.tools.Diagnostic;
@@ -74,7 +76,7 @@ public class DiagnosticsProvider {
CompilationUnitTree root = null;
for (CompilationUnitTree tree : task.roots) {
final Path path = Paths.get(tree.getSourceFile().toUri());
if (path.equals(file)) {
if (PathUtils.isSameFile(path, file)) {
root = tree;
break;
}
@@ -132,7 +134,7 @@ public class DiagnosticsProvider {
long start = pos.getStartPosition(root, path.getLeaf());
long end = pos.getEndPosition(root, path.getLeaf());
final DiagnosticItem d = new DiagnosticItem();
d.setMessage(BaseApplication.getBaseInstance().getString(R.string.msg_not_thrown, name));
d.setMessage(String.format("'%s' is not thrown in the body of the method", name));
d.setRange(new Range(getPosition(start, lines), getPosition(end, lines)));
d.setCode("unused_throws");
d.setSeverity(DiagnosticSeverity.INFO);
@@ -172,7 +174,7 @@ public class DiagnosticsProvider {
end = start + name.length();
}
String message = BaseApplication.getBaseInstance().getString(R.string.msg_not_used, name);
String message = String.format("'%s' is not used", name);
String code;
DiagnosticSeverity severity;
if (leaf instanceof VariableTree) {
@@ -18,38 +18,48 @@
package com.itsaky.lsp.java.utils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.common.reflect.ClassPath;
import com.itsaky.androidide.utils.Logger;
import org.jetbrains.annotations.Contract;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Stream;
import jdk.internal.jrtfs.JrtFileSystemProvider;
public class ScanClassPath {
private static final Logger LOG = Logger.newInstance("ScanClassPath");
public static Set<String> jdkTopLevelClasses() {
public Set<String> jdkTopLevelClasses() {
LOG.info("Searching for top-level classes in the JDK");
Set<String> classes = new TreeSet<>();
try {
final FileSystem fs =
new JrtFileSystemProvider()
.newFileSystem(URI.create("jrt:/"), Collections.emptyMap());
final FileSystem fs = getJRTFileSystem();
if (fs == null) {
LOG.error("Unable to create an instance of JRTFileSystem");
return classes;
}
final Path moduleRoot = fs.getPath("/modules/java.base/");
try (Stream<Path> stream = Files.walk(moduleRoot)) {
final Iterator<Path> it = stream.iterator();
@@ -66,8 +76,8 @@ public class ScanClassPath {
} catch (IOException e) {
LOG.error("An error occurred while indexing module 'java.base'", e);
}
} catch (IOException e) {
LOG.error("Unable to create an instance of JRTFileSystem");
} catch (Throwable e) {
LOG.error("Unable to create an instance of JRTFileSystem", e);
}
LOG.info(
@@ -77,14 +87,14 @@ public class ScanClassPath {
}
@SuppressWarnings("UnstableApiUsage")
public static Set<String> classPathTopLevelClasses(Set<Path> classPath) {
public Set<String> classPathTopLevelClasses(Set<Path> classPath) {
LOG.info(
String.format(
Locale.getDefault(),
"Searching for top-level classes in %d classpath locations",
classPath.size()));
URL[] urls = classPath.stream().map(ScanClassPath::toUrl).toArray(URL[]::new);
URL[] urls = classPath.stream().map(this::toUrl).toArray(URL[]::new);
ClassLoader classLoader = new URLClassLoader(urls, null);
ClassPath scanner;
@@ -104,7 +114,26 @@ public class ScanClassPath {
return classes;
}
private static URL toUrl(@NonNull Path p) {
@Nullable
@Contract(" -> new")
public FileSystem getJRTFileSystem() throws Exception {
final URI uri = URI.create("jrt:/");
try {
// Tests run on the JVM, so we cannot directly access JRTFileSystemProvider
// But this method will work
return FileSystems.getFileSystem(uri);
} catch (Throwable th) {
// When on ART, JRT file system is not available
// So we create an instance of the FS manually
final Class<?> klass = Class.forName("jdk.internal.jrtfs.JrtFileSystemProvider");
final Method newFs = klass.getMethod("newFileSystem", URI.class, Map.class);
newFs.setAccessible(true);
return (FileSystem) newFs.invoke(klass.newInstance(), uri, Collections.emptyMap());
}
}
private URL toUrl(@NonNull Path p) {
try {
return p.toUri().toURL();
} catch (MalformedURLException e) {
@@ -79,7 +79,10 @@ public final class JrtFileSystemProvider extends FileSystemProvider {
}
/** Need RuntimePermission "accessSystemModules" to create or get jrt:/ */
private void checkPermission() {}
private void checkPermission() {
// ANDROIDIDE-CHANGED
// Permission granted...
}
private void checkUri(URI uri) {
if (!uri.getScheme().equalsIgnoreCase(getScheme())) {
@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ This file is part of AndroidIDE.
~
~ AndroidIDE is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ AndroidIDE 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 for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
-->
<resources>
<string name="msg_not_thrown">\'%s\' is not thrown in the body of the method</string>
<string name="msg_not_used">\'%s\' wird nicht genutzt</string>
</resources>
@@ -1,35 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ This file is part of AndroidIDE.
~
~ AndroidIDE is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ AndroidIDE 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 for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
-->
<resources>
<string name="msg_not_thrown">\'%s\' is not thrown in the body of the method</string>
<string name="msg_not_used">\'%s\' n\'est pas utilisé</string>
</resources>
@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ This file is part of AndroidIDE.
~
~ AndroidIDE is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ AndroidIDE 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 for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
-->
<resources>
<string name="msg_not_thrown">\'%s\' मेथड की बॉडी में थ्रो नहीं किया गया है</string>
<string name="msg_not_used">\'%s\' उपयोग नहीं किया गया है</string>
</resources>
@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ This file is part of AndroidIDE.
~
~ AndroidIDE is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ AndroidIDE 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 for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
-->
<resources>
<string name="msg_not_thrown">\'%s\' tidak ditaruh kedalam tubuh metode</string>
<string name="msg_not_used">\'%s\' tidak digunakan</string>
</resources>
@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ This file is part of AndroidIDE.
~
~ AndroidIDE is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ AndroidIDE 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 for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
-->
<resources>
<string name="msg_not_thrown">在方法体中 \'%s\' 不会被抛出</string>
<string name="msg_not_used">\'%s\' 未被使用</string>
</resources>
-21
View File
@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ This file is part of AndroidIDE.
~
~ AndroidIDE is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ AndroidIDE 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 for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
-->
<resources>
<string name="msg_not_thrown">\'%s\' is not thrown in the body of the method</string>
<string name="msg_not_used">\'%s\' is not used</string>
</resources>
@@ -0,0 +1,59 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.lsp.java.providers
import com.google.common.truth.Truth.assertThat
import com.itsaky.lsp.api.ILanguageServer
import com.itsaky.lsp.api.LoggingTest
import com.itsaky.lsp.java.JavaLanguageServerProvider
import com.itsaky.lsp.models.CodeActionParams
import com.itsaky.lsp.models.DiagnosticItem
import com.itsaky.lsp.models.Range
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
/**
* @author Akash Yadav
*/
@RunWith(RobolectricTestRunner::class)
@Config(manifest = Config.NONE)
class JavaCodeActionProviderTest : LoggingTest() {
private val server: ILanguageServer = JavaLanguageServerProvider.INSTANCE.server();
override fun getServer(): ILanguageServer = server
@Test
fun testAddImport() {
openFile("AddImportAction")
val diagnostics = server.analyze(file!!).filter { it.code == "compiler.err.cant.resolve.location" }
val actions = actions(diagnostics)
assertThat(actions).contains("Import 'java.util.stream.Stream'")
}
private fun actions(diagnostics: List<DiagnosticItem>): List<String> {
val params = CodeActionParams(file!!, Range(), diagnostics)
return server.codeActions(params).actions.map { it.title }
}
override fun openFile(fileName: String) {
super.openFile("actions/${fileName}")
}
}
@@ -0,0 +1,7 @@
package com.itsaky.androidide.test;
public class AddImportAction_template {
public static void main(String[] args) {
Stream<String> stream = null;
}
}
@@ -33,6 +33,7 @@ abstract class CursorDependentTest : LoggingTest() {
private val cursorText = "@@cursor@@"
fun requireCursor(): Int {
this.cursor = super.contents!!.indexOf(cursorText)
assertThat(cursor).isGreaterThan(-1)
return cursor
}
@@ -60,9 +61,4 @@ abstract class CursorDependentTest : LoggingTest() {
val pos = Content(contents!!).indexer.getCharPosition(cursor)
return Position(pos.line, pos.column, pos.index)
}
override fun openFile(fileName: String) {
super.openFile(fileName)
this.cursor = super.contents!!.indexOf(cursorText)
}
}