Remove as31 bunch files
This commit is contained in:
@@ -1,8 +0,0 @@
|
||||
org.gradle.jvmargs=-Duser.country=US -Dkotlin.daemon.jvm.options=-Xmx1600m -Dfile.encoding=UTF-8
|
||||
|
||||
cacheRedirectorEnabled=true
|
||||
|
||||
#buildSrc.kotlin.repo=https://jcenter.bintray.com
|
||||
#buildSrc.kotlin.version=1.1.50
|
||||
|
||||
intellijUltimateEnabled=false
|
||||
-133
@@ -1,133 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.load.java.structure.impl
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.PsiTypeParameter
|
||||
import com.intellij.psi.search.SearchScope
|
||||
import org.jetbrains.kotlin.asJava.KtLightClassMarker
|
||||
import org.jetbrains.kotlin.descriptors.Visibility
|
||||
import org.jetbrains.kotlin.load.java.structure.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtPsiUtil
|
||||
import org.jetbrains.kotlin.psi.psiUtil.contains
|
||||
|
||||
class JavaClassImpl(psiClass: PsiClass) : JavaClassifierImpl<PsiClass>(psiClass), VirtualFileBoundJavaClass, JavaAnnotationOwnerImpl, JavaModifierListOwnerImpl {
|
||||
init {
|
||||
assert(psiClass !is PsiTypeParameter) { "PsiTypeParameter should be wrapped in JavaTypeParameter, not JavaClass: use JavaClassifier.create()" }
|
||||
}
|
||||
|
||||
override val innerClassNames: Collection<Name>
|
||||
get() = psi.innerClasses.mapNotNull { it.name?.takeIf(Name::isValidIdentifier)?.let(Name::identifier) }
|
||||
|
||||
override fun findInnerClass(name: Name): JavaClass? {
|
||||
return psi.findInnerClassByName(name.asString(), false)?.let(::JavaClassImpl)
|
||||
}
|
||||
|
||||
override val fqName: FqName?
|
||||
get() {
|
||||
val qualifiedName = psi.qualifiedName
|
||||
return if (qualifiedName == null) null else FqName(qualifiedName)
|
||||
}
|
||||
|
||||
override val name: Name
|
||||
get() = KtPsiUtil.safeName(psi.name)
|
||||
|
||||
override val isInterface: Boolean
|
||||
get() = psi.isInterface
|
||||
|
||||
override val isAnnotationType: Boolean
|
||||
get() = psi.isAnnotationType
|
||||
|
||||
override val isEnum: Boolean
|
||||
get() = psi.isEnum
|
||||
|
||||
override val outerClass: JavaClassImpl?
|
||||
get() {
|
||||
val outer = psi.containingClass
|
||||
return if (outer == null) null else JavaClassImpl(outer)
|
||||
}
|
||||
|
||||
override val typeParameters: List<JavaTypeParameter>
|
||||
get() = typeParameters(psi.typeParameters)
|
||||
|
||||
override val supertypes: Collection<JavaClassifierType>
|
||||
get() = classifierTypes(psi.superTypes)
|
||||
|
||||
override val methods: Collection<JavaMethod>
|
||||
get() {
|
||||
assertNotLightClass()
|
||||
// We apply distinct here because PsiClass#getMethods() can return duplicate PSI methods, for example in Lombok (see KT-11778)
|
||||
// Return type seems to be null for example for the 'clone' Groovy method generated by @AutoClone (see EA-73795)
|
||||
return methods(psi.methods.filter { method -> !method.isConstructor && method.returnType != null }).distinct()
|
||||
}
|
||||
|
||||
override val fields: Collection<JavaField>
|
||||
get() {
|
||||
assertNotLightClass()
|
||||
return fields(psi.fields.filter { field ->
|
||||
val name = field.name
|
||||
// ex. Android plugin generates LightFields for resources started from '.' (.DS_Store file etc)
|
||||
name != null && Name.isValidIdentifier(name)
|
||||
})
|
||||
}
|
||||
|
||||
override val constructors: Collection<JavaConstructor>
|
||||
get() {
|
||||
assertNotLightClass()
|
||||
// See for example org.jetbrains.plugins.scala.lang.psi.light.ScFunctionWrapper,
|
||||
// which is present in getConstructors(), but its isConstructor() returns false
|
||||
return constructors(psi.constructors.filter { method -> method.isConstructor })
|
||||
}
|
||||
|
||||
override val isAbstract: Boolean
|
||||
get() = JavaElementUtil.isAbstract(this)
|
||||
|
||||
override val isStatic: Boolean
|
||||
get() = JavaElementUtil.isStatic(this)
|
||||
|
||||
override val isFinal: Boolean
|
||||
get() = JavaElementUtil.isFinal(this)
|
||||
|
||||
override val visibility: Visibility
|
||||
get() = JavaElementUtil.getVisibility(this)
|
||||
|
||||
override val lightClassOriginKind: LightClassOriginKind?
|
||||
get() = (psi as? KtLightClassMarker)?.originKind
|
||||
|
||||
override val virtualFile: VirtualFile?
|
||||
get() = psi.containingFile?.virtualFile
|
||||
|
||||
override fun isFromSourceCodeInScope(scope: SearchScope): Boolean = psi.containingFile in scope
|
||||
|
||||
override fun getAnnotationOwnerPsi() = psi.modifierList
|
||||
|
||||
private fun assertNotLightClass() {
|
||||
val psiClass = psi
|
||||
if (psiClass !is KtLightClassMarker) return
|
||||
|
||||
val message = "Querying members of JavaClass created for $psiClass of type ${psiClass::class.java} defined in file ${psiClass.containingFile?.virtualFile?.canonicalPath}"
|
||||
LOGGER.error(message)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOGGER = Logger.getInstance(JavaClassImpl::class.java)
|
||||
}
|
||||
}
|
||||
-78
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.load.kotlin
|
||||
|
||||
import com.intellij.ide.highlighter.JavaClassFileType
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.openapi.util.Computable
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
|
||||
class KotlinBinaryClassCache : Disposable {
|
||||
private class RequestCache {
|
||||
internal var virtualFile: VirtualFile? = null
|
||||
internal var modificationStamp: Long = 0
|
||||
internal var result: KotlinClassFinder.Result? = null
|
||||
|
||||
fun cache(
|
||||
file: VirtualFile,
|
||||
result: KotlinClassFinder.Result?
|
||||
): KotlinClassFinder.Result? {
|
||||
virtualFile = file
|
||||
this.result = result
|
||||
modificationStamp = file.modificationStamp
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
private val cache = object : ThreadLocal<RequestCache>() {
|
||||
override fun initialValue(): RequestCache {
|
||||
return RequestCache()
|
||||
}
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
// This is only relevant for tests. We create a new instance of Application for each test, and so a new instance of this service is
|
||||
// also created for each test. However all tests share the same event dispatch thread, which would collect all instances of this
|
||||
// thread-local if they're not removed properly. Each instance would transitively retain VFS resulting in OutOfMemoryError
|
||||
cache.remove()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getKotlinBinaryClassOrClassFileContent(
|
||||
file: VirtualFile, fileContent: ByteArray? = null
|
||||
): KotlinClassFinder.Result? {
|
||||
if (file.fileType !== JavaClassFileType.INSTANCE) return null
|
||||
|
||||
val service = ServiceManager.getService(KotlinBinaryClassCache::class.java)
|
||||
val requestCache = service.cache.get()
|
||||
|
||||
if (file.modificationStamp == requestCache.modificationStamp && file == requestCache.virtualFile) {
|
||||
return requestCache.result
|
||||
}
|
||||
|
||||
val aClass = ApplicationManager.getApplication().runReadAction(Computable {
|
||||
@Suppress("DEPRECATION")
|
||||
VirtualFileKotlinClass.create(file, fileContent)
|
||||
})
|
||||
|
||||
return requestCache.cache(file, aClass)
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
-337
@@ -1,337 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test.testFramework;
|
||||
|
||||
import com.intellij.core.CoreASTFactory;
|
||||
import com.intellij.lang.*;
|
||||
import com.intellij.lang.impl.PsiBuilderFactoryImpl;
|
||||
import com.intellij.mock.*;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.PathManager;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.EditorFactory;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.extensions.Extensions;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.fileEditor.impl.LoadTextUtil;
|
||||
import com.intellij.openapi.fileTypes.FileTypeFactory;
|
||||
import com.intellij.openapi.fileTypes.FileTypeManager;
|
||||
import com.intellij.openapi.progress.EmptyProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.progress.impl.CoreProgressManager;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.CharsetToolkit;
|
||||
import com.intellij.pom.PomModel;
|
||||
import com.intellij.pom.core.impl.PomModelImpl;
|
||||
import com.intellij.pom.tree.TreeAspect;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.*;
|
||||
import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry;
|
||||
import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistryImpl;
|
||||
import com.intellij.psi.impl.source.text.BlockSupportImpl;
|
||||
import com.intellij.psi.impl.source.text.DiffLog;
|
||||
import com.intellij.psi.util.CachedValuesManager;
|
||||
import com.intellij.testFramework.LightVirtualFile;
|
||||
import com.intellij.testFramework.TestDataFile;
|
||||
import com.intellij.util.CachedValuesManagerImpl;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.messages.MessageBus;
|
||||
import com.intellij.util.messages.MessageBusFactory;
|
||||
import junit.framework.TestCase;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.picocontainer.ComponentAdapter;
|
||||
import org.picocontainer.MutablePicoContainer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
|
||||
@SuppressWarnings("ALL")
|
||||
public abstract class KtParsingTestCase extends KtPlatformLiteFixture {
|
||||
public static final Key<Document> HARD_REF_TO_DOCUMENT_KEY = Key.create("HARD_REF_TO_DOCUMENT_KEY");
|
||||
protected String myFilePrefix = "";
|
||||
protected String myFileExt;
|
||||
protected final String myFullDataPath;
|
||||
protected PsiFile myFile;
|
||||
private MockPsiManager myPsiManager;
|
||||
private PsiFileFactoryImpl myFileFactory;
|
||||
protected Language myLanguage;
|
||||
private final ParserDefinition[] myDefinitions;
|
||||
private final boolean myLowercaseFirstLetter;
|
||||
|
||||
protected KtParsingTestCase(@NonNls @NotNull String dataPath, @NotNull String fileExt, @NotNull ParserDefinition... definitions) {
|
||||
this(dataPath, fileExt, false, definitions);
|
||||
}
|
||||
|
||||
protected KtParsingTestCase(@NonNls @NotNull String dataPath, @NotNull String fileExt, boolean lowercaseFirstLetter, @NotNull ParserDefinition... definitions) {
|
||||
myDefinitions = definitions;
|
||||
myFullDataPath = getTestDataPath() + "/" + dataPath;
|
||||
myFileExt = fileExt;
|
||||
myLowercaseFirstLetter = lowercaseFirstLetter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
initApplication();
|
||||
ComponentAdapter component = getApplication().getPicoContainer().getComponentAdapter(ProgressManager.class.getName());
|
||||
|
||||
Extensions.registerAreaClass("IDEA_PROJECT", null);
|
||||
myProject = new MockProjectEx(getTestRootDisposable());
|
||||
myPsiManager = new MockPsiManager(myProject);
|
||||
myFileFactory = new PsiFileFactoryImpl(myPsiManager);
|
||||
MutablePicoContainer appContainer = getApplication().getPicoContainer();
|
||||
registerComponentInstance(appContainer, MessageBus.class, MessageBusFactory.newMessageBus(getApplication()));
|
||||
final MockEditorFactory editorFactory = new MockEditorFactory();
|
||||
registerComponentInstance(appContainer, EditorFactory.class, editorFactory);
|
||||
registerComponentInstance(appContainer, FileDocumentManager.class, new MockFileDocumentManagerImpl(new Function<CharSequence, Document>() {
|
||||
@Override
|
||||
public Document fun(CharSequence charSequence) {
|
||||
return editorFactory.createDocument(charSequence);
|
||||
}
|
||||
}, HARD_REF_TO_DOCUMENT_KEY));
|
||||
registerComponentInstance(appContainer, PsiDocumentManager.class, new MockPsiDocumentManager());
|
||||
registerApplicationService(PsiBuilderFactory.class, new PsiBuilderFactoryImpl());
|
||||
registerApplicationService(DefaultASTFactory.class, new CoreASTFactory());
|
||||
registerApplicationService(ReferenceProvidersRegistry.class, new ReferenceProvidersRegistryImpl());
|
||||
|
||||
registerApplicationService(ProgressManager.class, new CoreProgressManager());
|
||||
|
||||
myProject.registerService(CachedValuesManager.class, new CachedValuesManagerImpl(myProject, new PsiCachedValuesFactory(myPsiManager)));
|
||||
myProject.registerService(PsiManager.class, myPsiManager);
|
||||
|
||||
this.registerExtensionPoint(FileTypeFactory.FILE_TYPE_FACTORY_EP, FileTypeFactory.class);
|
||||
registerExtensionPoint(MetaLanguage.EP_NAME, MetaLanguage.class);
|
||||
|
||||
for (ParserDefinition definition : myDefinitions) {
|
||||
addExplicitExtension(LanguageParserDefinitions.INSTANCE, definition.getFileNodeType().getLanguage(), definition);
|
||||
}
|
||||
if (myDefinitions.length > 0) {
|
||||
configureFromParserDefinition(myDefinitions[0], myFileExt);
|
||||
}
|
||||
|
||||
// That's for reparse routines
|
||||
final PomModelImpl pomModel = new PomModelImpl(myProject);
|
||||
myProject.registerService(PomModel.class, pomModel);
|
||||
new TreeAspect(pomModel);
|
||||
}
|
||||
|
||||
public void configureFromParserDefinition(ParserDefinition definition, String extension) {
|
||||
myLanguage = definition.getFileNodeType().getLanguage();
|
||||
myFileExt = extension;
|
||||
addExplicitExtension(LanguageParserDefinitions.INSTANCE, this.myLanguage, definition);
|
||||
registerComponentInstance(
|
||||
getApplication().getPicoContainer(), FileTypeManager.class,
|
||||
new MockFileTypeManager(new MockLanguageFileType(myLanguage, myFileExt)));
|
||||
}
|
||||
|
||||
protected <T> void addExplicitExtension(final LanguageExtension<T> instance, final Language language, final T object) {
|
||||
instance.addExplicitExtension(language, object);
|
||||
Disposer.register(myProject, new Disposable() {
|
||||
@Override
|
||||
public void dispose() {
|
||||
instance.removeExplicitExtension(language, object);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> void registerExtensionPoint(final ExtensionPointName<T> extensionPointName, Class<T> aClass) {
|
||||
super.registerExtensionPoint(extensionPointName, aClass);
|
||||
Disposer.register(myProject, new Disposable() {
|
||||
@Override
|
||||
public void dispose() {
|
||||
Extensions.getRootArea().unregisterExtensionPoint(extensionPointName.getName());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected <T> void registerApplicationService(final Class<T> aClass, T object) {
|
||||
getApplication().registerService(aClass, object);
|
||||
Disposer.register(myProject, new Disposable() {
|
||||
@Override
|
||||
public void dispose() {
|
||||
getApplication().getPicoContainer().unregisterComponent(aClass.getName());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public MockProjectEx getProject() {
|
||||
return myProject;
|
||||
}
|
||||
|
||||
public MockPsiManager getPsiManager() {
|
||||
return myPsiManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
myFile = null;
|
||||
myProject = null;
|
||||
myPsiManager = null;
|
||||
}
|
||||
|
||||
protected String getTestDataPath() {
|
||||
return PathManager.getHomePath();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public final String getTestName() {
|
||||
return getTestName(myLowercaseFirstLetter);
|
||||
}
|
||||
|
||||
protected boolean includeRanges() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean skipSpaces() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean checkAllPsiRoots() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void doTest(boolean checkResult) {
|
||||
String name = getTestName();
|
||||
try {
|
||||
String text = loadFile(name + "." + myFileExt);
|
||||
myFile = createPsiFile(name, text);
|
||||
ensureParsed(myFile);
|
||||
assertEquals("light virtual file text mismatch", text, ((LightVirtualFile)myFile.getVirtualFile()).getContent().toString());
|
||||
assertEquals("virtual file text mismatch", text, LoadTextUtil.loadText(myFile.getVirtualFile()));
|
||||
assertEquals("doc text mismatch", text, myFile.getViewProvider().getDocument().getText());
|
||||
assertEquals("psi text mismatch", text, myFile.getText());
|
||||
ensureCorrectReparse(myFile);
|
||||
if (checkResult){
|
||||
checkResult(name, myFile);
|
||||
}
|
||||
else{
|
||||
toParseTreeText(myFile, skipSpaces(), includeRanges());
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void doTest(String suffix) throws IOException {
|
||||
String name = getTestName();
|
||||
String text = loadFile(name + "." + myFileExt);
|
||||
myFile = createPsiFile(name, text);
|
||||
ensureParsed(myFile);
|
||||
assertEquals(text, myFile.getText());
|
||||
checkResult(name + suffix, myFile);
|
||||
}
|
||||
|
||||
protected void doCodeTest(String code) throws IOException {
|
||||
String name = getTestName();
|
||||
myFile = createPsiFile("a", code);
|
||||
ensureParsed(myFile);
|
||||
assertEquals(code, myFile.getText());
|
||||
checkResult(myFilePrefix + name, myFile);
|
||||
}
|
||||
|
||||
protected PsiFile createPsiFile(String name, String text) {
|
||||
return createFile(name + "." + myFileExt, text);
|
||||
}
|
||||
|
||||
protected PsiFile createFile(@NonNls String name, String text) {
|
||||
LightVirtualFile virtualFile = new LightVirtualFile(name, myLanguage, text);
|
||||
virtualFile.setCharset(CharsetToolkit.UTF8_CHARSET);
|
||||
return createFile(virtualFile);
|
||||
}
|
||||
|
||||
protected PsiFile createFile(LightVirtualFile virtualFile) {
|
||||
return myFileFactory.trySetupPsiForFile(virtualFile, myLanguage, true, false);
|
||||
}
|
||||
|
||||
protected void checkResult(@NonNls @TestDataFile String targetDataName, final PsiFile file) throws IOException {
|
||||
doCheckResult(myFullDataPath, file, checkAllPsiRoots(), targetDataName, skipSpaces(), includeRanges());
|
||||
}
|
||||
|
||||
public static void doCheckResult(String testDataDir,
|
||||
PsiFile file,
|
||||
boolean checkAllPsiRoots,
|
||||
String targetDataName,
|
||||
boolean skipSpaces,
|
||||
boolean printRanges) throws IOException {
|
||||
FileViewProvider provider = file.getViewProvider();
|
||||
Set<Language> languages = provider.getLanguages();
|
||||
|
||||
if (!checkAllPsiRoots || languages.size() == 1) {
|
||||
doCheckResult(testDataDir, targetDataName + ".txt", toParseTreeText(file, skipSpaces, printRanges).trim());
|
||||
return;
|
||||
}
|
||||
|
||||
for (Language language : languages) {
|
||||
PsiFile root = provider.getPsi(language);
|
||||
String expectedName = targetDataName + "." + language.getID() + ".txt";
|
||||
doCheckResult(testDataDir, expectedName, toParseTreeText(root, skipSpaces, printRanges).trim());
|
||||
}
|
||||
}
|
||||
|
||||
protected void checkResult(String actual) throws IOException {
|
||||
String name = getTestName();
|
||||
doCheckResult(myFullDataPath, myFilePrefix + name + ".txt", actual);
|
||||
}
|
||||
|
||||
protected void checkResult(@TestDataFile @NonNls String targetDataName, String actual) throws IOException {
|
||||
doCheckResult(myFullDataPath, targetDataName, actual);
|
||||
}
|
||||
|
||||
public static void doCheckResult(String fullPath, String targetDataName, String actual) throws IOException {
|
||||
String expectedFileName = fullPath + File.separatorChar + targetDataName;
|
||||
KtUsefulTestCase.assertSameLinesWithFile(expectedFileName, actual);
|
||||
}
|
||||
|
||||
protected static String toParseTreeText(PsiElement file, boolean skipSpaces, boolean printRanges) {
|
||||
return DebugUtil.psiToString(file, skipSpaces, printRanges);
|
||||
}
|
||||
|
||||
protected String loadFile(@NonNls @TestDataFile String name) throws IOException {
|
||||
return loadFileDefault(myFullDataPath, name);
|
||||
}
|
||||
|
||||
public static String loadFileDefault(String dir, String name) throws IOException {
|
||||
return FileUtil.loadFile(new File(dir, name), CharsetToolkit.UTF8, true).trim();
|
||||
}
|
||||
|
||||
public static void ensureParsed(PsiFile file) {
|
||||
file.accept(new PsiElementVisitor() {
|
||||
@Override
|
||||
public void visitElement(PsiElement element) {
|
||||
element.acceptChildren(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void ensureCorrectReparse(@NotNull PsiFile file) {
|
||||
String psiToStringDefault = DebugUtil.psiToString(file, false, false);
|
||||
String fileText = file.getText();
|
||||
DiffLog diffLog = (new BlockSupportImpl(file.getProject())).reparseRange(
|
||||
file, file.getNode(), TextRange.allOf(fileText), fileText, new EmptyProgressIndicator(), fileText);
|
||||
diffLog.performActualPsiChange(file);
|
||||
|
||||
TestCase.assertEquals(psiToStringDefault, DebugUtil.psiToString(file, false, false));
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,13 +0,0 @@
|
||||
org.gradle.jvmargs=-Duser.country=US -Dkotlin.daemon.jvm.options=-Xmx1600m -Dfile.encoding=UTF-8
|
||||
|
||||
cacheRedirectorEnabled=true
|
||||
|
||||
kotlin.compiler.effectSystemEnabled=true
|
||||
kotlin.compiler.newInferenceEnabled=true
|
||||
#maven.repository.mirror=http://repository.jetbrains.com/remote-repos/
|
||||
#bootstrap.kotlin.repo=https://dl.bintray.com/kotlin/kotlin-dev
|
||||
#bootstrap.kotlin.version=1.1.50-dev-1451
|
||||
#signingRequired=true
|
||||
|
||||
intellijUltimateEnabled=false
|
||||
intellijEnforceCommunitySdk=true
|
||||
@@ -1,15 +0,0 @@
|
||||
versions.intellijSdk=173.3727.127
|
||||
versions.androidBuildTools=r23.0.1
|
||||
versions.idea.NodeJS=172.3757.32
|
||||
versions.androidStudioRelease=3.1.3.0
|
||||
versions.androidStudioBuild=173.4819257
|
||||
versions.jar.guava=21.0
|
||||
versions.jar.groovy-all=2.4.12
|
||||
versions.jar.swingx-core=1.6.2
|
||||
versions.jar.kxml2=2.3.0
|
||||
versions.jar.streamex=0.6.5
|
||||
versions.jar.gson=2.8.2
|
||||
versions.jar.snappy-in-java=0.5.1
|
||||
ignore.jar.common=true
|
||||
ignore.jar.lombok-ast=true
|
||||
versions.gradle-api=4.0
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android
|
||||
|
||||
import com.android.tools.idea.npw.template.ConvertJavaToKotlinProvider
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiJavaFile
|
||||
import org.jetbrains.kotlin.android.configure.KotlinAndroidGradleModuleConfigurator
|
||||
import org.jetbrains.kotlin.idea.actions.JavaToKotlinAction
|
||||
import org.jetbrains.kotlin.idea.configuration.KotlinProjectConfigurator
|
||||
import org.jetbrains.kotlin.idea.configuration.getCanBeConfiguredModules
|
||||
import org.jetbrains.kotlin.idea.versions.bundledRuntimeVersion
|
||||
|
||||
class ConvertJavaToKotlinProviderImpl : ConvertJavaToKotlinProvider {
|
||||
override fun configureKotlin(project: Project) {
|
||||
val configurator = KotlinProjectConfigurator.EP_NAME.findExtension(KotlinAndroidGradleModuleConfigurator::class.java)
|
||||
val nonConfiguredModules = getCanBeConfiguredModules(project, configurator)
|
||||
configurator.configureSilently(project, nonConfiguredModules, bundledRuntimeVersion())
|
||||
}
|
||||
|
||||
override fun getKotlinVersion(): String {
|
||||
return bundledRuntimeVersion()
|
||||
}
|
||||
|
||||
override fun convertToKotlin(project: Project, files: List<PsiJavaFile>): List<PsiFile> {
|
||||
return JavaToKotlinAction.convertFiles(files, project, askExternalCodeProcessing = false)
|
||||
}
|
||||
}
|
||||
-137
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android
|
||||
|
||||
import com.android.SdkConstants
|
||||
import com.android.resources.ResourceType
|
||||
import com.intellij.codeHighlighting.Pass
|
||||
import com.intellij.codeInsight.daemon.GutterIconNavigationHandler
|
||||
import com.intellij.codeInsight.daemon.LineMarkerInfo
|
||||
import com.intellij.codeInsight.daemon.LineMarkerProvider
|
||||
import com.intellij.codeInsight.navigation.NavigationUtil
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.ide.highlighter.XmlFileType
|
||||
import com.intellij.navigation.GotoRelatedItem
|
||||
import com.intellij.openapi.editor.markup.GutterIconRenderer
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.xml.XmlAttributeValue
|
||||
import com.intellij.ui.awt.RelativePoint
|
||||
import com.intellij.util.Function
|
||||
import org.jetbrains.android.dom.manifest.Manifest
|
||||
import org.jetbrains.android.facet.AndroidFacet
|
||||
import org.jetbrains.android.resourceManagers.ModuleResourceManagers
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import java.awt.event.MouseEvent
|
||||
import javax.swing.Icon
|
||||
|
||||
|
||||
class KotlinAndroidLineMarkerProvider : LineMarkerProvider {
|
||||
override fun getLineMarkerInfo(element: PsiElement): LineMarkerInfo<*>? = null
|
||||
|
||||
override fun collectSlowLineMarkers(elements: List<PsiElement>, result: MutableCollection<LineMarkerInfo<PsiElement>>) {
|
||||
elements.forEach {
|
||||
(it as? KtClass)?.getLineMarkerInfo()?.let { marker -> result.add(marker) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtClass.getLineMarkerInfo(): LineMarkerInfo<PsiElement>? {
|
||||
val nameIdentifier = nameIdentifier ?: return null
|
||||
val androidFacet = AndroidFacet.getInstance(this) ?: return null
|
||||
val manifest = androidFacet.manifest ?: return null
|
||||
val manifestItems = collectGoToRelatedManifestItems(manifest)
|
||||
if (manifestItems.isEmpty() && !isClassWithLayoutXml()) {
|
||||
return null
|
||||
}
|
||||
|
||||
return LineMarkerInfo(
|
||||
nameIdentifier,
|
||||
nameIdentifier.textRange,
|
||||
AllIcons.FileTypes.Xml,
|
||||
Pass.LINE_MARKERS,
|
||||
Function { "Related XML file" },
|
||||
GutterIconNavigationHandler { e: MouseEvent, _: PsiElement ->
|
||||
NavigationUtil
|
||||
.getRelatedItemsPopup(
|
||||
manifestItems + collectGoToRelatedLayoutItems(androidFacet),
|
||||
"Go to Related Files")
|
||||
.show(RelativePoint(e))
|
||||
},
|
||||
GutterIconRenderer.Alignment.RIGHT)
|
||||
}
|
||||
|
||||
private fun KtClass.collectGoToRelatedLayoutItems(androidFacet: AndroidFacet): List<GotoRelatedItem> {
|
||||
val resources = mutableSetOf<PsiFile>()
|
||||
accept(object: KtVisitorVoid() {
|
||||
override fun visitKtElement(element: KtElement) {
|
||||
element.acceptChildren(this)
|
||||
}
|
||||
|
||||
override fun visitReferenceExpression(expression: KtReferenceExpression) {
|
||||
super.visitReferenceExpression(expression)
|
||||
|
||||
val resClassName = ResourceType.LAYOUT.getName()
|
||||
val info = (expression as? KtSimpleNameExpression)?.let {
|
||||
getReferredResourceOrManifestField(androidFacet, it, resClassName, true)
|
||||
}
|
||||
|
||||
if (info == null || info.isFromManifest) {
|
||||
return
|
||||
}
|
||||
|
||||
val files = ModuleResourceManagers
|
||||
.getInstance(androidFacet)
|
||||
.localResourceManager
|
||||
.findResourcesByFieldName(resClassName, info.fieldName)
|
||||
.filterIsInstance<PsiFile>()
|
||||
|
||||
resources.addAll(files)
|
||||
}
|
||||
})
|
||||
|
||||
return resources.map { GotoRelatedLayoutItem(it) }
|
||||
}
|
||||
|
||||
private fun KtClass.collectGoToRelatedManifestItems(manifest: Manifest): List<GotoRelatedItem> =
|
||||
findComponentDeclarationInManifest(manifest)?.xmlAttributeValue?.let { listOf(GotoManifestItem(it)) } ?: emptyList()
|
||||
|
||||
private class GotoManifestItem(attributeValue: XmlAttributeValue) : GotoRelatedItem(attributeValue) {
|
||||
override fun getCustomName(): String? = "AndroidManifest.xml"
|
||||
override fun getCustomContainerName(): String? = ""
|
||||
override fun getCustomIcon(): Icon? = XmlFileType.INSTANCE.icon
|
||||
}
|
||||
|
||||
private class GotoRelatedLayoutItem(private val file: PsiFile) : GotoRelatedItem(file, "Layout Files") {
|
||||
override fun getCustomContainerName(): String? = "(${file.containingDirectory.name})"
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val CLASSES_WITH_LAYOUT_XML = arrayOf(
|
||||
SdkConstants.CLASS_ACTIVITY,
|
||||
SdkConstants.CLASS_FRAGMENT,
|
||||
SdkConstants.CLASS_V4_FRAGMENT,
|
||||
"android.widget.Adapter")
|
||||
|
||||
private fun KtClass.isClassWithLayoutXml(): Boolean {
|
||||
val type = (unsafeResolveToDescriptor(BodyResolveMode.PARTIAL) as? ClassDescriptor)?.defaultType ?: return false
|
||||
return CLASSES_WITH_LAYOUT_XML.any { type.isSubclassOf(it, true) }
|
||||
}
|
||||
}
|
||||
}
|
||||
-270
@@ -1,270 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android;
|
||||
|
||||
|
||||
import com.android.ide.common.rendering.api.ResourceValue;
|
||||
import com.android.ide.common.resources.ResourceItem;
|
||||
import com.android.ide.common.resources.ResourceRepository;
|
||||
import com.android.ide.common.resources.ResourceResolver;
|
||||
import com.android.resources.ResourceType;
|
||||
import com.android.tools.idea.configurations.Configuration;
|
||||
import com.android.tools.idea.configurations.ConfigurationManager;
|
||||
import com.android.tools.idea.res.AppResourceRepository;
|
||||
import com.android.tools.idea.res.ResourceHelper;
|
||||
import com.android.tools.idea.ui.resourcechooser.ColorPicker;
|
||||
import com.android.utils.XmlUtils;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.io.Files;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.markup.GutterIconRenderer;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.xml.XmlAttribute;
|
||||
import com.intellij.psi.xml.XmlAttributeValue;
|
||||
import com.intellij.psi.xml.XmlTag;
|
||||
import com.intellij.util.ui.ColorIcon;
|
||||
import com.intellij.util.ui.EmptyIcon;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import org.jetbrains.android.facet.AndroidFacet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
|
||||
import static com.android.SdkConstants.*;
|
||||
|
||||
/**
|
||||
* Contains copied privates from AndroidColorAnnotator, so we could use them for Kotlin AndroidResourceReferenceAnnotator
|
||||
*/
|
||||
public class ResourceReferenceAnnotatorUtil {
|
||||
|
||||
private static final int ICON_SIZE = 8;
|
||||
|
||||
@Nullable
|
||||
public static File pickBitmapFromXml(@NotNull File file, @NotNull ResourceResolver resourceResolver, @NotNull Project project) {
|
||||
try {
|
||||
String xml = Files.toString(file, Charsets.UTF_8);
|
||||
Document document = XmlUtils.parseDocumentSilently(xml, true);
|
||||
if (document != null && document.getDocumentElement() != null) {
|
||||
Element root = document.getDocumentElement();
|
||||
String tag = root.getTagName();
|
||||
Element target = null;
|
||||
String attribute = null;
|
||||
if ("vector".equals(tag)) {
|
||||
// Vectors are handled in the icon cache
|
||||
return file;
|
||||
}
|
||||
else if ("bitmap".equals(tag) || "nine-patch".equals(tag)) {
|
||||
target = root;
|
||||
attribute = ATTR_SRC;
|
||||
}
|
||||
else if ("selector".equals(tag) ||
|
||||
"level-list".equals(tag) ||
|
||||
"layer-list".equals(tag) ||
|
||||
"transition".equals(tag)) {
|
||||
NodeList children = root.getChildNodes();
|
||||
for (int i = children.getLength() - 1; i >= 0; i--) {
|
||||
Node item = children.item(i);
|
||||
if (item.getNodeType() == Node.ELEMENT_NODE && TAG_ITEM.equals(item.getNodeName())) {
|
||||
target = (Element)item;
|
||||
if (target.hasAttributeNS(ANDROID_URI, ATTR_DRAWABLE)) {
|
||||
attribute = ATTR_DRAWABLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ("clip".equals(tag) || "inset".equals(tag) || "scale".equals(tag)) {
|
||||
target = root;
|
||||
attribute = ATTR_DRAWABLE;
|
||||
} else {
|
||||
// <shape> etc - no bitmap to be found
|
||||
return null;
|
||||
}
|
||||
if (attribute != null && target.hasAttributeNS(ANDROID_URI, attribute)) {
|
||||
String src = target.getAttributeNS(ANDROID_URI, attribute);
|
||||
ResourceValue value = resourceResolver.findResValue(src, false);
|
||||
if (value != null) {
|
||||
return ResourceHelper.resolveDrawable(resourceResolver, value, project);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable ignore) {
|
||||
// Not logging for now; afraid to risk unexpected crashes in upcoming preview. TODO: Re-enable.
|
||||
//Logger.getInstance(AndroidColorAnnotator.class).warn(String.format("Could not read/render icon image %1$s", file), e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Looks up the resource item of the given type and name for the given configuration, if any */
|
||||
@Nullable
|
||||
public static ResourceValue findResourceValue(ResourceType type,
|
||||
String name,
|
||||
boolean isFramework,
|
||||
Module module,
|
||||
Configuration configuration) {
|
||||
if (isFramework) {
|
||||
ResourceRepository frameworkResources = configuration.getFrameworkResources();
|
||||
if (frameworkResources == null) {
|
||||
return null;
|
||||
}
|
||||
if (!frameworkResources.hasResourceItem(type, name)) {
|
||||
return null;
|
||||
}
|
||||
ResourceItem item = frameworkResources.getResourceItem(type, name);
|
||||
return item.getResourceValue(type, configuration.getFullConfig(), false);
|
||||
} else {
|
||||
AppResourceRepository appResources = AppResourceRepository.getOrCreateInstance(module);
|
||||
if (appResources == null) {
|
||||
return null;
|
||||
}
|
||||
if (!appResources.hasResourceItem(type, name)) {
|
||||
return null;
|
||||
}
|
||||
return appResources.getConfiguredValue(type, name, configuration.getFullConfig());
|
||||
}
|
||||
}
|
||||
|
||||
/** Picks a suitable configuration to use for resource resolution */
|
||||
@Nullable
|
||||
public static Configuration pickConfiguration(AndroidFacet facet, Module module, PsiFile file) {
|
||||
VirtualFile virtualFile = file.getVirtualFile();
|
||||
if(virtualFile == null) {
|
||||
return null;
|
||||
} else {
|
||||
VirtualFile parent = virtualFile.getParent();
|
||||
if(parent == null) {
|
||||
return null;
|
||||
} else {
|
||||
String parentName = parent.getName();
|
||||
VirtualFile layout;
|
||||
if(!parentName.startsWith("layout")) {
|
||||
layout = ResourceHelper.pickAnyLayoutFile(module, facet);
|
||||
if(layout == null) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
layout = virtualFile;
|
||||
}
|
||||
|
||||
return ConfigurationManager.getOrCreateInstance(module).getConfiguration(layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ColorRenderer extends GutterIconRenderer {
|
||||
private final PsiElement myElement;
|
||||
private final Color myColor;
|
||||
|
||||
ColorRenderer(@NotNull PsiElement element, @Nullable Color color) {
|
||||
myElement = element;
|
||||
myColor = color;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Icon getIcon() {
|
||||
Color color = getCurrentColor();
|
||||
return JBUI.scale(color == null ? EmptyIcon.create(ICON_SIZE) : new ColorIcon(ICON_SIZE, color));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Color getCurrentColor() {
|
||||
if (myColor != null) {
|
||||
return myColor;
|
||||
} else if (myElement instanceof XmlTag) {
|
||||
return ResourceHelper.parseColor(((XmlTag)myElement).getValue().getText());
|
||||
} else if (myElement instanceof XmlAttributeValue) {
|
||||
return ResourceHelper.parseColor(((XmlAttributeValue)myElement).getValue());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnAction getClickAction() {
|
||||
if (myColor != null) { // Cannot set colors that were derived
|
||||
return null;
|
||||
}
|
||||
return new AnAction() {
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
final Editor editor = CommonDataKeys.EDITOR.getData(e.getDataContext());
|
||||
if (editor != null) {
|
||||
// Need ARGB support in platform color chooser; see
|
||||
// https://youtrack.jetbrains.com/issue/IDEA-123498
|
||||
//final Color color =
|
||||
// ColorChooser.chooseColor(editor.getComponent(), AndroidBundle.message("android.choose.color"), getCurrentColor());
|
||||
final Color color = ColorPicker.showDialog(editor.getComponent(), "Choose Color", getCurrentColor(), true, null, false);
|
||||
if (color != null) {
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (myElement instanceof XmlTag) {
|
||||
((XmlTag)myElement).getValue().setText(ResourceHelper.colorToString(color));
|
||||
} else if (myElement instanceof XmlAttributeValue) {
|
||||
XmlAttribute attribute = PsiTreeUtil.getParentOfType(myElement, XmlAttribute.class);
|
||||
if (attribute != null) {
|
||||
attribute.setValue(ResourceHelper.colorToString(color));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
ColorRenderer that = (ColorRenderer)o;
|
||||
// TODO: Compare with modification count in app resources (if not framework)
|
||||
if (myColor != null ? !myColor.equals(that.myColor) : that.myColor != null) return false;
|
||||
if (!myElement.equals(that.myElement)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = myElement.hashCode();
|
||||
result = 31 * result + (myColor != null ? myColor.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android.configure
|
||||
|
||||
import com.android.tools.idea.gradle.project.model.JavaModuleModel
|
||||
import com.android.tools.idea.gradle.project.sync.idea.data.service.AndroidProjectKeys
|
||||
import com.android.tools.idea.io.FilePaths
|
||||
import com.intellij.openapi.externalSystem.model.DataNode
|
||||
import com.intellij.openapi.externalSystem.model.project.ModuleData
|
||||
import com.intellij.openapi.externalSystem.model.project.ProjectData
|
||||
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider
|
||||
import com.intellij.openapi.externalSystem.service.project.manage.AbstractProjectDataService
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.OrderRootType
|
||||
import com.intellij.openapi.roots.impl.libraries.LibraryEx
|
||||
import com.intellij.openapi.roots.libraries.Library
|
||||
import org.jetbrains.kotlin.idea.configuration.detectPlatformKindByPlugin
|
||||
import org.jetbrains.kotlin.idea.framework.detectLibraryKind
|
||||
import org.jetbrains.kotlin.idea.platform.tooling
|
||||
import java.io.File
|
||||
|
||||
class KotlinAndroidGradleLibraryDataService : AbstractProjectDataService<JavaModuleModel, Void>() {
|
||||
override fun getTargetDataKey() = AndroidProjectKeys.JAVA_MODULE_MODEL
|
||||
|
||||
override fun postProcess(
|
||||
toImport: MutableCollection<DataNode<JavaModuleModel>>,
|
||||
projectData: ProjectData?,
|
||||
project: Project,
|
||||
modelsProvider: IdeModifiableModelsProvider
|
||||
) {
|
||||
for (dataNode in toImport) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val targetLibraryKind = detectPlatformKindByPlugin(dataNode.parent as DataNode<ModuleData>)?.tooling?.libraryKind
|
||||
if (targetLibraryKind != null) {
|
||||
for (dep in dataNode.data.jarLibraryDependencies) {
|
||||
val library = modelsProvider.findLibraryByBinaryPath(dep.binaryPath) as LibraryEx? ?: continue
|
||||
if (library.kind == null) {
|
||||
val model = modelsProvider.getModifiableLibraryModel(library) as LibraryEx.ModifiableModelEx
|
||||
detectLibraryKind(model.getFiles(OrderRootType.CLASSES))?.let { model.kind = it }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun IdeModifiableModelsProvider.findLibraryByBinaryPath(path: File?): Library? {
|
||||
if (path == null) return null
|
||||
val url = FilePaths.pathToIdeaUrl(path)
|
||||
return allLibraries.firstOrNull { url in getModifiableLibraryModel(it).getUrls(OrderRootType.CLASSES) }
|
||||
}
|
||||
}
|
||||
-236
@@ -1,236 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android.configure
|
||||
|
||||
import com.android.tools.idea.gradle.project.sync.idea.data.service.AndroidProjectKeys
|
||||
import com.android.tools.idea.gradle.util.ContentEntries.findParentContentEntry
|
||||
import com.android.tools.idea.io.FilePaths
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.externalSystem.ExternalSystemModulePropertyManager
|
||||
import com.intellij.openapi.externalSystem.model.DataNode
|
||||
import com.intellij.openapi.externalSystem.model.ProjectKeys
|
||||
import com.intellij.openapi.externalSystem.model.project.ContentRootData
|
||||
import com.intellij.openapi.externalSystem.model.project.ModuleData
|
||||
import com.intellij.openapi.externalSystem.model.project.ProjectData
|
||||
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider
|
||||
import com.intellij.openapi.externalSystem.service.project.manage.AbstractProjectDataService
|
||||
import com.intellij.openapi.externalSystem.service.project.manage.ContentRootDataService
|
||||
import com.intellij.openapi.externalSystem.service.project.manage.ContentRootDataService.CREATE_EMPTY_DIRECTORIES
|
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.DependencyScope
|
||||
import com.intellij.openapi.roots.ModifiableRootModel
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
import com.intellij.util.SmartList
|
||||
import com.intellij.util.containers.stream
|
||||
import org.jetbrains.jps.model.java.JavaResourceRootType
|
||||
import org.jetbrains.jps.model.java.JavaSourceRootType
|
||||
import org.jetbrains.jps.model.module.JpsModuleSourceRootType
|
||||
import org.jetbrains.kotlin.gradle.KotlinCompilation
|
||||
import org.jetbrains.kotlin.gradle.KotlinPlatform
|
||||
import org.jetbrains.kotlin.gradle.KotlinSourceSet
|
||||
import org.jetbrains.kotlin.idea.addModuleDependencyIfNeeded
|
||||
import org.jetbrains.kotlin.idea.configuration.*
|
||||
import org.jetbrains.kotlin.idea.facet.KotlinFacet
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
||||
class KotlinAndroidGradleMPPModuleDataService : AbstractProjectDataService<ModuleData, Void>() {
|
||||
override fun getTargetDataKey() = ProjectKeys.MODULE
|
||||
|
||||
private fun shouldCreateEmptySourceRoots(
|
||||
moduleDataNode: DataNode<ModuleData>,
|
||||
module: Module
|
||||
): Boolean {
|
||||
val projectDataNode = ExternalSystemApiUtil.findParent(moduleDataNode, ProjectKeys.PROJECT) ?: return false
|
||||
if (projectDataNode.getUserData<Boolean>(CREATE_EMPTY_DIRECTORIES) == true) return true
|
||||
|
||||
val projectSystemId = projectDataNode.data.owner
|
||||
val externalSystemSettings = ExternalSystemApiUtil.getSettings(module.project, projectSystemId)
|
||||
|
||||
val path = ExternalSystemModulePropertyManager.getInstance(module).getRootProjectPath() ?: return false
|
||||
return externalSystemSettings.getLinkedProjectSettings(path)?.isCreateEmptyContentRootDirectories ?: false
|
||||
}
|
||||
|
||||
override fun postProcess(
|
||||
toImport: MutableCollection<DataNode<ModuleData>>,
|
||||
projectData: ProjectData?,
|
||||
project: Project,
|
||||
modelsProvider: IdeModifiableModelsProvider
|
||||
) {
|
||||
val contentRootsToImport = ArrayList<DataNode<ContentRootData>>()
|
||||
|
||||
for (nodeToImport in toImport) {
|
||||
val projectNode = ExternalSystemApiUtil.findParent(nodeToImport, ProjectKeys.PROJECT) ?: continue
|
||||
|
||||
for (childNode in ExternalSystemApiUtil.getChildren(nodeToImport, GradleSourceSetData.KEY)) {
|
||||
val sourceSet = childNode.kotlinSourceSet?.kotlinModule ?: continue
|
||||
if (sourceSet.platform == KotlinPlatform.ANDROID) continue
|
||||
contentRootsToImport += ExternalSystemApiUtil.getChildren(childNode, ProjectKeys.CONTENT_ROOT)
|
||||
}
|
||||
|
||||
val moduleData = nodeToImport.data
|
||||
val module = modelsProvider.findIdeModule(moduleData) ?: continue
|
||||
val shouldCreateEmptySourceRoots = shouldCreateEmptySourceRoots(nodeToImport, module)
|
||||
val rootModel = modelsProvider.getModifiableRootModel(module)
|
||||
val kotlinAndroidSourceSets = nodeToImport.kotlinAndroidSourceSets ?: emptyList()
|
||||
for (sourceSetInfo in kotlinAndroidSourceSets) {
|
||||
val compilation = sourceSetInfo.kotlinModule as? KotlinCompilation ?: continue
|
||||
for (sourceSet in compilation.sourceSets) {
|
||||
if (sourceSet.platform == KotlinPlatform.ANDROID) {
|
||||
val sourceType = if (sourceSet.isTestModule) JavaSourceRootType.TEST_SOURCE else JavaSourceRootType.SOURCE
|
||||
val resourceType = if (sourceSet.isTestModule) JavaResourceRootType.TEST_RESOURCE else JavaResourceRootType.RESOURCE
|
||||
sourceSet.sourceDirs.forEach { addSourceRoot(it, sourceType, rootModel, shouldCreateEmptySourceRoots) }
|
||||
sourceSet.resourceDirs.forEach { addSourceRoot(it, resourceType, rootModel, shouldCreateEmptySourceRoots) }
|
||||
}
|
||||
}
|
||||
}
|
||||
addExtraDependeeModules(nodeToImport, projectNode, modelsProvider, rootModel, false)
|
||||
addExtraDependeeModules(nodeToImport, projectNode, modelsProvider, rootModel, true)
|
||||
|
||||
if (nodeToImport.kotlinAndroidSourceSets == null) {
|
||||
continue
|
||||
}
|
||||
|
||||
val androidModel = getAndroidModuleModel(nodeToImport) ?: continue
|
||||
val variantName = androidModel.selectedVariant.name
|
||||
val activeSourceSetInfos = nodeToImport.kotlinAndroidSourceSets?.filter { it.kotlinModule.name.startsWith(variantName) } ?: emptyList()
|
||||
for (activeSourceSetInfo in activeSourceSetInfos) {
|
||||
val activeCompilation = activeSourceSetInfo.kotlinModule as? KotlinCompilation ?: continue
|
||||
for (sourceSet in activeCompilation.sourceSets) {
|
||||
if (sourceSet.platform != KotlinPlatform.ANDROID) {
|
||||
val sourceSetId = activeSourceSetInfo.sourceSetIdsByName[sourceSet.name] ?: continue
|
||||
val sourceSetData = ExternalSystemApiUtil.findFirstRecursively(projectNode) {
|
||||
(it.data as? ModuleData)?.id == sourceSetId
|
||||
}?.data as? ModuleData ?: continue
|
||||
val sourceSetModule = modelsProvider.findIdeModule(sourceSetData) ?: continue
|
||||
addModuleDependencyIfNeeded(rootModel, sourceSetModule, activeSourceSetInfo.isTestModule)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val mainSourceSetInfo = activeSourceSetInfos.firstOrNull { it.kotlinModule.name == variantName }
|
||||
if (mainSourceSetInfo != null) {
|
||||
KotlinSourceSetDataService.configureFacet(moduleData, mainSourceSetInfo, nodeToImport, module, modelsProvider)
|
||||
}
|
||||
|
||||
val kotlinFacet = KotlinFacet.get(module)
|
||||
if (kotlinFacet != null) {
|
||||
GradleProjectImportHandler.getInstances(project).forEach { it.importByModule(kotlinFacet, nodeToImport) }
|
||||
}
|
||||
}
|
||||
|
||||
ContentRootDataService().importData(contentRootsToImport, projectData, project, modelsProvider)
|
||||
}
|
||||
|
||||
private fun getDependeeModuleNodes(
|
||||
moduleNode: DataNode<ModuleData>,
|
||||
projectNode: DataNode<ProjectData>,
|
||||
modelsProvider: IdeModifiableModelsProvider,
|
||||
testScope: Boolean
|
||||
): List<DataNode<out ModuleData>> {
|
||||
val androidModel = getAndroidModuleModel(moduleNode)
|
||||
if (androidModel != null) {
|
||||
val dependencies = if (testScope) {
|
||||
androidModel.selectedAndroidTestCompileDependencies
|
||||
} else {
|
||||
androidModel.selectedMainCompileLevel2Dependencies
|
||||
} ?: return emptyList()
|
||||
return dependencies
|
||||
.moduleDependencies
|
||||
.mapNotNull { projectNode.findChildModuleById(it.projectPath) }
|
||||
}
|
||||
|
||||
val javaModel = getJavaModuleModel(moduleNode)
|
||||
if (javaModel != null) {
|
||||
val scope = if (testScope) DependencyScope.TEST.name else DependencyScope.COMPILE.name
|
||||
val moduleNames = javaModel
|
||||
.javaModuleDependencies
|
||||
.filter { scope == it.scope ?: DependencyScope.COMPILE.name }
|
||||
.mapTo(HashSet()) { it.moduleName }
|
||||
return ExternalSystemApiUtil
|
||||
.getChildren(projectNode, ProjectKeys.MODULE)
|
||||
.filter { modelsProvider.findIdeModule(it.data)?.name in moduleNames }
|
||||
}
|
||||
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
private fun List<DataNode<GradleSourceSetData>>.firstByPlatformOrNull(platform: KotlinPlatform) = firstOrNull {
|
||||
it.kotlinSourceSet?.platform == platform
|
||||
}
|
||||
|
||||
private fun addExtraDependeeModules(
|
||||
moduleNode: DataNode<ModuleData>,
|
||||
projectNode: DataNode<ProjectData>,
|
||||
modelsProvider: IdeModifiableModelsProvider,
|
||||
rootModel: ModifiableRootModel,
|
||||
testScope: Boolean
|
||||
) {
|
||||
val dependeeModuleNodes = getDependeeModuleNodes(moduleNode, projectNode, modelsProvider, testScope)
|
||||
val relevantNodes = dependeeModuleNodes
|
||||
.flatMap { ExternalSystemApiUtil.getChildren(it, GradleSourceSetData.KEY) }
|
||||
.filter {
|
||||
val ktModule = it.kotlinSourceSet?.kotlinModule
|
||||
ktModule != null && ktModule.isTestModule == testScope
|
||||
}
|
||||
val commonSourceSetName = KotlinSourceSet.commonName(testScope)
|
||||
val isAndroidModule = getAndroidModuleModel(moduleNode) != null
|
||||
SmartList<DataNode<GradleSourceSetData>>()
|
||||
.apply {
|
||||
addIfNotNull(
|
||||
(if (isAndroidModule) relevantNodes.firstByPlatformOrNull(KotlinPlatform.ANDROID) else null)
|
||||
?: relevantNodes.firstByPlatformOrNull(KotlinPlatform.JVM)
|
||||
)
|
||||
addIfNotNull(
|
||||
relevantNodes.firstOrNull {
|
||||
it.kotlinSourceSet?.platform == KotlinPlatform.COMMON && it.kotlinSourceSet?.kotlinModule?.name == commonSourceSetName
|
||||
}
|
||||
)
|
||||
}
|
||||
.mapNotNull { modelsProvider.findIdeModule(it.data) }
|
||||
.forEach {
|
||||
addModuleDependencyIfNeeded(rootModel, it, testScope)
|
||||
val dependeeRootModel = modelsProvider.getModifiableRootModel(it)
|
||||
dependeeRootModel.getModuleDependencies(testScope).forEach { transitiveDependee ->
|
||||
addModuleDependencyIfNeeded(rootModel, transitiveDependee, testScope)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAndroidModuleModel(moduleNode: DataNode<ModuleData>) =
|
||||
ExternalSystemApiUtil.getChildren(moduleNode, AndroidProjectKeys.ANDROID_MODEL).firstOrNull()?.data
|
||||
|
||||
private fun getJavaModuleModel(moduleNode: DataNode<ModuleData>) =
|
||||
ExternalSystemApiUtil.getChildren(moduleNode, AndroidProjectKeys.JAVA_MODULE_MODEL).firstOrNull()?.data
|
||||
|
||||
private fun addSourceRoot(
|
||||
sourceRoot: File,
|
||||
type: JpsModuleSourceRootType<*>,
|
||||
rootModel: ModifiableRootModel,
|
||||
shouldCreateEmptySourceRoots: Boolean
|
||||
) {
|
||||
val parent = findParentContentEntry(sourceRoot, rootModel.contentEntries.stream()) ?: return
|
||||
val url = FilePaths.pathToIdeaUrl(sourceRoot)
|
||||
parent.addSourceFolder(url, type)
|
||||
if (shouldCreateEmptySourceRoots) {
|
||||
ExternalSystemApiUtil.doWriteAction {
|
||||
try {
|
||||
VfsUtil.createDirectoryIfMissing(sourceRoot.path)
|
||||
} catch (e: IOException) {
|
||||
LOG.warn(String.format("Unable to create directory for the path: %s", sourceRoot.path), e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOG = Logger.getInstance(KotlinAndroidGradleMPPModuleDataService::class.java)
|
||||
}
|
||||
}
|
||||
-98
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android.configure
|
||||
|
||||
import com.android.builder.model.AndroidProject
|
||||
import com.android.builder.model.NativeAndroidProject
|
||||
import com.android.tools.idea.IdeInfo
|
||||
import com.android.tools.idea.gradle.project.sync.idea.data.model.ImportedModule
|
||||
import com.android.tools.idea.gradle.project.sync.idea.data.service.AndroidProjectKeys.IMPORTED_MODULE
|
||||
import com.intellij.openapi.externalSystem.model.DataNode
|
||||
import com.intellij.openapi.externalSystem.model.project.ModuleData
|
||||
import com.intellij.openapi.externalSystem.model.project.ProjectData
|
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
|
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemConstants
|
||||
import com.intellij.openapi.externalSystem.util.Order
|
||||
import org.gradle.tooling.model.idea.IdeaModule
|
||||
import org.jetbrains.kotlin.gradle.KotlinMPPGradleModel
|
||||
import org.jetbrains.kotlin.gradle.KotlinMPPGradleModelBuilder
|
||||
import org.jetbrains.kotlin.gradle.KotlinPlatform
|
||||
import org.jetbrains.kotlin.idea.configuration.KotlinAndroidSourceSetData
|
||||
import org.jetbrains.kotlin.idea.configuration.KotlinMPPGradleProjectResolver
|
||||
import org.jetbrains.kotlin.idea.configuration.kotlinSourceSet
|
||||
import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData
|
||||
import org.jetbrains.plugins.gradle.service.project.AbstractProjectResolverExtension
|
||||
|
||||
@Order(ExternalSystemConstants.UNORDERED - 1)
|
||||
class KotlinAndroidMPPGradleProjectResolver : AbstractProjectResolverExtension() {
|
||||
private val isAndroidProject by lazy {
|
||||
resolverCtx.hasModulesWithModel(AndroidProject::class.java)
|
||||
|| resolverCtx.hasModulesWithModel(NativeAndroidProject::class.java)
|
||||
}
|
||||
|
||||
override fun getToolingExtensionsClasses(): Set<Class<out Any>> {
|
||||
return setOf(KotlinMPPGradleModelBuilder::class.java, Unit::class.java)
|
||||
}
|
||||
|
||||
override fun getExtraProjectModelClasses(): Set<Class<out Any>> {
|
||||
return setOf(KotlinMPPGradleModel::class.java)
|
||||
}
|
||||
|
||||
override fun createModule(gradleModule: IdeaModule, projectDataNode: DataNode<ProjectData>): DataNode<ModuleData> {
|
||||
return super.createModule(gradleModule, projectDataNode).also {
|
||||
initializeModuleData(gradleModule, it, projectDataNode)
|
||||
}
|
||||
}
|
||||
|
||||
override fun populateModuleContentRoots(gradleModule: IdeaModule, ideModule: DataNode<ModuleData>) {
|
||||
super.populateModuleContentRoots(gradleModule, ideModule)
|
||||
if (IdeInfo.getInstance().isAndroidStudio || isAndroidProject) {
|
||||
KotlinMPPGradleProjectResolver.populateContentRoots(gradleModule, ideModule, resolverCtx)
|
||||
// Work around module disposal service which discards modules without accompanying ImportedModule instance
|
||||
for (childNode in ExternalSystemApiUtil.getChildren(ideModule, GradleSourceSetData.KEY)) {
|
||||
if (childNode.kotlinSourceSet == null) continue
|
||||
val moduleName = childNode.data.internalName
|
||||
val importedModule = ImportedModule(gradleModule)
|
||||
importedModuleNameField.set(importedModule, moduleName)
|
||||
ideModule.createChild(IMPORTED_MODULE, importedModule)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun populateModuleDependencies(gradleModule: IdeaModule, ideModule: DataNode<ModuleData>, ideProject: DataNode<ProjectData>) {
|
||||
super.populateModuleDependencies(gradleModule, ideModule, ideProject)
|
||||
if (isAndroidProject) {
|
||||
KotlinMPPGradleProjectResolver.populateModuleDependencies(gradleModule, ideProject, ideModule, resolverCtx)
|
||||
}
|
||||
}
|
||||
|
||||
private fun initializeModuleData(
|
||||
gradleModule: IdeaModule,
|
||||
mainModuleData: DataNode<ModuleData>,
|
||||
projectDataNode: DataNode<ProjectData>
|
||||
) {
|
||||
if (!isAndroidProject) return
|
||||
|
||||
KotlinMPPGradleProjectResolver.initializeModuleData(gradleModule, mainModuleData, projectDataNode, resolverCtx)
|
||||
|
||||
val mppModel = resolverCtx.getExtraProject(gradleModule, KotlinMPPGradleModel::class.java) ?: return
|
||||
|
||||
val androidSourceSets = mppModel
|
||||
.targets
|
||||
.asSequence()
|
||||
.flatMap { it.compilations.asSequence() }
|
||||
.filter { it.platform == KotlinPlatform.ANDROID }
|
||||
.mapNotNull { KotlinMPPGradleProjectResolver.createSourceSetInfo(it, gradleModule, resolverCtx) }
|
||||
.toList()
|
||||
mainModuleData.createChild(KotlinAndroidSourceSetData.KEY, KotlinAndroidSourceSetData(androidSourceSets))
|
||||
}
|
||||
|
||||
companion object {
|
||||
val importedModuleNameField by lazy {
|
||||
ImportedModule::class.java.getDeclaredField("myName").apply { isAccessible = true }
|
||||
}
|
||||
}
|
||||
}
|
||||
-40
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android.configure
|
||||
|
||||
import com.android.tools.idea.gradle.project.model.AndroidModuleModel
|
||||
import com.android.tools.idea.gradle.project.sync.setup.post.ModuleSetupStep
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.progress.ProgressIndicator
|
||||
import org.jetbrains.android.facet.AndroidFacet
|
||||
import org.jetbrains.kotlin.idea.configuration.compilerArgumentsBySourceSet
|
||||
import org.jetbrains.kotlin.idea.configuration.configureFacetByCompilerArguments
|
||||
import org.jetbrains.kotlin.idea.configuration.sourceSetName
|
||||
import org.jetbrains.kotlin.idea.facet.KotlinFacet
|
||||
|
||||
class KotlinAndroidModuleSetupStep : ModuleSetupStep() {
|
||||
override fun setUpModule(module: Module, progressIndicator: ProgressIndicator?) {
|
||||
val facet = AndroidFacet.getInstance(module) ?: return
|
||||
val androidModel = AndroidModuleModel.get(facet) ?: return
|
||||
val sourceSetName = androidModel.selectedVariant.name
|
||||
if (module.sourceSetName == sourceSetName) return
|
||||
val argsInfo = module.compilerArgumentsBySourceSet?.get(sourceSetName) ?: return
|
||||
val kotlinFacet = KotlinFacet.get(module) ?: return
|
||||
module.sourceSetName = sourceSetName
|
||||
configureFacetByCompilerArguments(kotlinFacet, argsInfo, null)
|
||||
}
|
||||
}
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android.configure
|
||||
|
||||
import com.android.tools.idea.gradle.project.model.AndroidModuleModel
|
||||
import com.intellij.openapi.externalSystem.model.DataNode
|
||||
import com.intellij.openapi.externalSystem.model.Key
|
||||
import com.intellij.openapi.externalSystem.model.ProjectKeys
|
||||
import com.intellij.openapi.externalSystem.model.project.ProjectData
|
||||
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider
|
||||
import com.intellij.openapi.externalSystem.service.project.manage.AbstractProjectDataService
|
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.idea.configuration.GradleProjectImportHandler
|
||||
import org.jetbrains.kotlin.idea.configuration.configureFacetByGradleModule
|
||||
|
||||
class KotlinGradleAndroidModuleModelProjectDataService : AbstractProjectDataService<AndroidModuleModel, Void>() {
|
||||
companion object {
|
||||
val KEY = Key<AndroidModuleModel>(AndroidModuleModel::class.qualifiedName!!, 0)
|
||||
}
|
||||
|
||||
override fun getTargetDataKey() = KEY
|
||||
|
||||
override fun postProcess(
|
||||
toImport: MutableCollection<DataNode<AndroidModuleModel>>,
|
||||
projectData: ProjectData?,
|
||||
project: Project,
|
||||
modelsProvider: IdeModifiableModelsProvider
|
||||
) {
|
||||
super.postProcess(toImport, projectData, project, modelsProvider)
|
||||
for (moduleModelNode in toImport) {
|
||||
val moduleNode = ExternalSystemApiUtil.findParent(moduleModelNode, ProjectKeys.MODULE) ?: continue
|
||||
val moduleData = moduleNode.data
|
||||
val ideModule = modelsProvider.findIdeModule(moduleData) ?: continue
|
||||
val sourceSetName = moduleModelNode.data.selectedVariant.name
|
||||
val kotlinFacet = configureFacetByGradleModule(moduleNode, sourceSetName, ideModule, modelsProvider) ?: continue
|
||||
GradleProjectImportHandler.getInstances(project).forEach { it.importByModule(kotlinFacet, moduleNode) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android.debugger
|
||||
|
||||
import com.intellij.openapi.module.ModuleManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.ProjectRootModificationTracker
|
||||
import com.intellij.psi.util.CachedValueProvider
|
||||
import com.intellij.psi.util.CachedValuesManager
|
||||
import org.jetbrains.android.facet.AndroidFacet
|
||||
import org.jetbrains.android.sdk.AndroidSdkData
|
||||
import org.jetbrains.kotlin.idea.debugger.evaluate.classLoading.AndroidDexer
|
||||
import org.jetbrains.kotlin.idea.debugger.evaluate.classLoading.ClassToLoad
|
||||
import java.io.File
|
||||
import java.net.URLClassLoader
|
||||
import java.security.ProtectionDomain
|
||||
|
||||
class AndroidDexerImpl(val project: Project) : AndroidDexer {
|
||||
private val cachedDexWrapper = CachedValuesManager.getManager(project).createCachedValue({
|
||||
val dexWrapper = doGetAndroidDexFile()?.let { dexJarFile ->
|
||||
val androidDexWrapperName = AndroidDexWrapper::class.java.canonicalName
|
||||
val classBytes = this.javaClass.classLoader.getResource(
|
||||
androidDexWrapperName.replace('.', '/') + ".class").readBytes()
|
||||
|
||||
val dexClassLoader = object : URLClassLoader(arrayOf(dexJarFile.toURI().toURL()), this::class.java.classLoader) {
|
||||
init {
|
||||
defineClass(androidDexWrapperName, classBytes, 0, classBytes.size, null as ProtectionDomain?)
|
||||
}
|
||||
}
|
||||
|
||||
Class.forName(androidDexWrapperName, true, dexClassLoader).newInstance()
|
||||
}
|
||||
|
||||
CachedValueProvider.Result.createSingleDependency(dexWrapper, ProjectRootModificationTracker.getInstance(project))
|
||||
}, /* trackValue = */ false)
|
||||
|
||||
override fun dex(classes: Collection<ClassToLoad>): ByteArray? {
|
||||
val dexWrapper = cachedDexWrapper.value
|
||||
val dexMethod = dexWrapper::class.java.methods.firstOrNull { it.name == "dex" } ?: return null
|
||||
return dexMethod.invoke(dexWrapper, classes) as? ByteArray ?: return null
|
||||
}
|
||||
|
||||
private fun doGetAndroidDexFile(): File? {
|
||||
for (module in ModuleManager.getInstance(project).modules) {
|
||||
val androidFacet = AndroidFacet.getInstance(module) ?: continue
|
||||
val sdkData = AndroidSdkData.getSdkData(androidFacet) ?: continue
|
||||
val latestBuildTool = sdkData.getLatestBuildTool(/* allowPreview = */ false)
|
||||
?: sdkData.getLatestBuildTool(/* allowPreview = */ true)
|
||||
?: continue
|
||||
|
||||
val dxJar = File(latestBuildTool.location, "lib/dx.jar")
|
||||
if (dxJar.exists()) {
|
||||
return dxJar
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
-258
@@ -1,258 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android.folding
|
||||
|
||||
import com.android.SdkConstants.*
|
||||
import com.android.ide.common.resources.configuration.FolderConfiguration
|
||||
import com.android.ide.common.resources.configuration.LocaleQualifier
|
||||
import com.android.resources.ResourceType
|
||||
import com.android.tools.idea.folding.AndroidFoldingSettings
|
||||
import com.android.tools.idea.res.AppResourceRepository
|
||||
import com.android.tools.idea.res.LocalResourceRepository
|
||||
import com.intellij.lang.ASTNode
|
||||
import com.intellij.lang.folding.FoldingBuilderEx
|
||||
import com.intellij.lang.folding.FoldingDescriptor
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.openapi.editor.Document
|
||||
import com.intellij.openapi.module.ModuleUtilCore
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.impl.source.SourceTreeToPsiMap
|
||||
import org.jetbrains.android.facet.AndroidFacet
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.uast.*
|
||||
import org.jetbrains.uast.visitor.AbstractUastVisitor
|
||||
import java.util.regex.Pattern
|
||||
|
||||
|
||||
class ResourceFoldingBuilder : FoldingBuilderEx() {
|
||||
|
||||
companion object {
|
||||
// See lint's StringFormatDetector
|
||||
private val FORMAT = Pattern.compile("%(\\d+\\$)?([-+#, 0(<]*)?(\\d+)?(\\.\\d+)?([tT])?([a-zA-Z%])")
|
||||
private val FOLD_MAX_LENGTH = 60
|
||||
private val UNIT_TEST_MODE: Boolean = ApplicationManager.getApplication().isUnitTestMode
|
||||
private val RESOURCE_TYPES = listOf(ResourceType.STRING,
|
||||
ResourceType.DIMEN,
|
||||
ResourceType.INTEGER,
|
||||
ResourceType.PLURALS)
|
||||
}
|
||||
|
||||
private val isFoldingEnabled = AndroidFoldingSettings.getInstance().isCollapseAndroidStrings
|
||||
|
||||
override fun getPlaceholderText(node: ASTNode): String? {
|
||||
|
||||
tailrec fun UElement.unwrapReferenceAndGetValue(resources: LocalResourceRepository): String? = when (this) {
|
||||
is UQualifiedReferenceExpression -> selector.unwrapReferenceAndGetValue(resources)
|
||||
is UCallExpression -> (valueArguments.firstOrNull() as? UReferenceExpression)?.getAndroidResourceValue(resources, this)
|
||||
else -> (this as? UReferenceExpression)?.getAndroidResourceValue(resources)
|
||||
}
|
||||
|
||||
val element = SourceTreeToPsiMap.treeElementToPsi(node) ?: return null
|
||||
val appResources = getAppResources(element) ?: return null
|
||||
val uastContext = ServiceManager.getService(element.project, UastContext::class.java) ?: return null
|
||||
return uastContext.convertElement(element, null, null)?.unwrapReferenceAndGetValue(appResources)
|
||||
}
|
||||
|
||||
override fun buildFoldRegions(root: PsiElement, document: Document, quick: Boolean): Array<FoldingDescriptor> {
|
||||
if (root !is KtFile || quick && !UNIT_TEST_MODE || !isFoldingEnabled || AndroidFacet.getInstance(root) == null) {
|
||||
return emptyArray()
|
||||
}
|
||||
|
||||
val file = root.toUElement()
|
||||
val result = arrayListOf<FoldingDescriptor>()
|
||||
file?.accept(object : AbstractUastVisitor() {
|
||||
override fun visitSimpleNameReferenceExpression(node: USimpleNameReferenceExpression): Boolean {
|
||||
node.getFoldingDescriptor()?.let { result.add(it) }
|
||||
return super.visitSimpleNameReferenceExpression(node)
|
||||
}
|
||||
})
|
||||
|
||||
return result.toTypedArray()
|
||||
}
|
||||
|
||||
override fun isCollapsedByDefault(node: ASTNode): Boolean = isFoldingEnabled
|
||||
|
||||
private fun UReferenceExpression.getFoldingDescriptor(): FoldingDescriptor? {
|
||||
val resolved = resolve() ?: return null
|
||||
val resourceType = resolved.getAndroidResourceType() ?: return null
|
||||
if (resourceType !in RESOURCE_TYPES) return null
|
||||
|
||||
fun UElement.createFoldingDescriptor() = psi?.let { psi ->
|
||||
val dependencies: Set<PsiElement> = setOf(psi)
|
||||
FoldingDescriptor(psi.node, psi.textRange, null, dependencies)
|
||||
}
|
||||
|
||||
val element = uastParent as? UQualifiedReferenceExpression ?: this
|
||||
val getResourceValueCall = (element.uastParent as? UCallExpression)?.takeIf { it.isFoldableGetResourceValueCall() }
|
||||
if (getResourceValueCall != null) {
|
||||
val qualifiedCall = getResourceValueCall.uastParent as? UQualifiedReferenceExpression
|
||||
if (qualifiedCall?.selector == getResourceValueCall) {
|
||||
return qualifiedCall.createFoldingDescriptor()
|
||||
}
|
||||
|
||||
return getResourceValueCall.createFoldingDescriptor()
|
||||
}
|
||||
|
||||
return element.createFoldingDescriptor()
|
||||
}
|
||||
|
||||
private fun UCallExpression.isFoldableGetResourceValueCall(): Boolean {
|
||||
return methodName == "getString" ||
|
||||
methodName == "getText" ||
|
||||
methodName == "getInteger" ||
|
||||
methodName?.startsWith("getDimension") ?: false ||
|
||||
methodName?.startsWith("getQuantityString") ?: false
|
||||
}
|
||||
|
||||
private fun PsiElement.getAndroidResourceType(): ResourceType? {
|
||||
val elementType = parent as? PsiClass ?: return null
|
||||
val elementPackage = elementType.parent as? PsiClass ?: return null
|
||||
if (R_CLASS != elementPackage.name) return null
|
||||
if (elementPackage.qualifiedName != "$ANDROID_PKG.$R_CLASS") {
|
||||
return ResourceType.getEnum(elementType.name)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun UReferenceExpression.getAndroidResourceValue(resources: LocalResourceRepository, call: UCallExpression? = null): String? {
|
||||
val resourceType = resolve()?.getAndroidResourceType() ?: return null
|
||||
val referenceConfig = FolderConfiguration().apply { localeQualifier = LocaleQualifier("xx") }
|
||||
val key = resolvedName ?: return null
|
||||
val resourceValue = resources.getResourceValue(resourceType, key, referenceConfig) ?: return null
|
||||
val text = if (call != null) formatArguments(call, resourceValue) else resourceValue
|
||||
|
||||
if (resourceType == ResourceType.STRING || resourceType == ResourceType.PLURALS) {
|
||||
return '"' + StringUtil.shortenTextWithEllipsis(text, FOLD_MAX_LENGTH - 2, 0) + '"'
|
||||
}
|
||||
else if (text.length <= 1) {
|
||||
// Don't just inline empty or one-character replacements: they can't be expanded by a mouse click
|
||||
// so are hard to use without knowing about the folding keyboard shortcut to toggle folding.
|
||||
// This is similar to how IntelliJ 14 handles call parameters
|
||||
return key + ": " + text
|
||||
}
|
||||
|
||||
return StringUtil.shortenTextWithEllipsis(text, FOLD_MAX_LENGTH, 0)
|
||||
}
|
||||
|
||||
private tailrec fun LocalResourceRepository.getResourceValue(
|
||||
type: ResourceType,
|
||||
name: String,
|
||||
referenceConfig: FolderConfiguration): String? {
|
||||
val value = getConfiguredValue(type, name, referenceConfig)?.value ?: return null
|
||||
if (!value.startsWith('@')) {
|
||||
return value
|
||||
}
|
||||
|
||||
val (referencedTypeName, referencedName) = value.substring(1).split('/').takeIf { it.size == 2 } ?: return value
|
||||
val referencedType = ResourceType.getEnum(referencedTypeName) ?: return value
|
||||
return getResourceValue(referencedType, referencedName, referenceConfig)
|
||||
}
|
||||
|
||||
// Converted from com.android.tools.idea.folding.InlinedResource#insertArguments
|
||||
private fun formatArguments(callExpression: UCallExpression, formatString: String): String {
|
||||
if (!formatString.contains('%')) {
|
||||
return formatString
|
||||
}
|
||||
|
||||
val args = callExpression.valueArguments
|
||||
if (args.isEmpty() || !args.first().isPsiValid) {
|
||||
return formatString
|
||||
}
|
||||
|
||||
val matcher = FORMAT.matcher(formatString)
|
||||
var index = 0
|
||||
var prevIndex = 0
|
||||
var nextNumber = 1
|
||||
var start = 0
|
||||
val sb = StringBuilder(2 * formatString.length)
|
||||
while (true) {
|
||||
if (matcher.find(index)) {
|
||||
if ("%" == matcher.group(6)) {
|
||||
index = matcher.end()
|
||||
continue
|
||||
}
|
||||
val matchStart = matcher.start()
|
||||
// Make sure this is not an escaped '%'
|
||||
while (prevIndex < matchStart) {
|
||||
val c = formatString[prevIndex]
|
||||
if (c == '\\') {
|
||||
prevIndex++
|
||||
}
|
||||
prevIndex++
|
||||
}
|
||||
if (prevIndex > matchStart) {
|
||||
// We're in an escape, ignore this result
|
||||
index = prevIndex
|
||||
continue
|
||||
}
|
||||
|
||||
index = matcher.end()
|
||||
|
||||
// Shouldn't throw a number format exception since we've already
|
||||
// matched the pattern in the regexp
|
||||
val number: Int
|
||||
var numberString: String? = matcher.group(1)
|
||||
if (numberString != null) {
|
||||
// Strip off trailing $
|
||||
numberString = numberString.substring(0, numberString.length - 1)
|
||||
number = Integer.parseInt(numberString)
|
||||
nextNumber = number + 1
|
||||
}
|
||||
else {
|
||||
number = nextNumber++
|
||||
}
|
||||
|
||||
if (number > 0 && number < args.size) {
|
||||
val argExpression = args[number]
|
||||
var value: Any? = argExpression.evaluate()
|
||||
|
||||
if (value == null) {
|
||||
value = args[number].asSourceString()
|
||||
}
|
||||
|
||||
for (i in start..matchStart - 1) {
|
||||
sb.append(formatString[i])
|
||||
}
|
||||
|
||||
sb.append("{")
|
||||
sb.append(value)
|
||||
sb.append('}')
|
||||
start = index
|
||||
}
|
||||
}
|
||||
else {
|
||||
var i = start
|
||||
val n = formatString.length
|
||||
while (i < n) {
|
||||
sb.append(formatString[i])
|
||||
i++
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
private fun getAppResources(element: PsiElement): LocalResourceRepository? = ModuleUtilCore.findModuleForPsiElement(element)?.let {
|
||||
AppResourceRepository.findExistingInstance(it)
|
||||
}
|
||||
}
|
||||
-83
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android.inspection
|
||||
|
||||
import com.android.tools.idea.model.AndroidModuleInfo
|
||||
import com.intellij.codeInspection.*
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElementVisitor
|
||||
import org.jetbrains.android.facet.AndroidFacet
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
||||
import org.jetbrains.kotlin.idea.inspections.AbstractKotlinInspection
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.KtPsiUtil.isUnsafeCast
|
||||
import org.jetbrains.kotlin.psi.psiUtil.addTypeArgument
|
||||
|
||||
class TypeParameterFindViewByIdInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
|
||||
val compileSdk = AndroidFacet.getInstance(session.file)
|
||||
?.let { facet -> AndroidModuleInfo.getInstance(facet) }
|
||||
?.buildSdkVersion
|
||||
?.apiLevel
|
||||
|
||||
if (compileSdk == null || compileSdk < 26) {
|
||||
return KtVisitorVoid()
|
||||
}
|
||||
|
||||
return object : KtVisitorVoid() {
|
||||
override fun visitCallExpression(expression: KtCallExpression) {
|
||||
super.visitCallExpression(expression)
|
||||
if (expression.calleeExpression?.text != "findViewById" || expression.typeArguments.isNotEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
val parentCast = (expression.parent as? KtBinaryExpressionWithTypeRHS)?.takeIf { isUnsafeCast(it) } ?: return
|
||||
val typeText = parentCast.right?.getTypeTextWithoutQuestionMark() ?: return
|
||||
val callableDescriptor = expression.resolveToCall()?.resultingDescriptor ?: return
|
||||
if (callableDescriptor.name.asString() != "findViewById" || callableDescriptor.typeParameters.size != 1) {
|
||||
return
|
||||
}
|
||||
|
||||
holder.registerProblem(
|
||||
parentCast,
|
||||
"Can be converted to findViewById<$typeText>(...)",
|
||||
ConvertCastToFindViewByIdWithTypeParameter())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ConvertCastToFindViewByIdWithTypeParameter : LocalQuickFix {
|
||||
override fun getFamilyName(): String = "Convert cast to findViewById with type parameter"
|
||||
|
||||
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
|
||||
val cast = descriptor.psiElement as? KtBinaryExpressionWithTypeRHS ?: return
|
||||
val typeText = cast.right?.getTypeTextWithoutQuestionMark() ?: return
|
||||
val call = cast.left as? KtCallExpression ?: return
|
||||
|
||||
val newCall = call.copy() as KtCallExpression
|
||||
val typeArgument = KtPsiFactory(call).createTypeArgument(typeText)
|
||||
newCall.addTypeArgument(typeArgument)
|
||||
|
||||
cast.replace(newCall)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun KtTypeReference.getTypeTextWithoutQuestionMark(): String? =
|
||||
(typeElement as? KtNullableType)?.innerType?.text ?: typeElement?.text
|
||||
}
|
||||
}
|
||||
-149
@@ -1,149 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android.navigation;
|
||||
|
||||
import com.android.resources.ResourceType;
|
||||
import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.xml.XmlElement;
|
||||
import org.jetbrains.android.dom.AndroidAttributeValue;
|
||||
import org.jetbrains.android.dom.manifest.Manifest;
|
||||
import org.jetbrains.android.dom.manifest.ManifestElementWithRequiredName;
|
||||
import org.jetbrains.android.dom.resources.Attr;
|
||||
import org.jetbrains.android.dom.resources.DeclareStyleable;
|
||||
import org.jetbrains.android.facet.AndroidFacet;
|
||||
import org.jetbrains.android.resourceManagers.LocalResourceManager;
|
||||
import org.jetbrains.android.resourceManagers.ModuleResourceManagers;
|
||||
import org.jetbrains.android.resourceManagers.ResourceManager;
|
||||
import org.jetbrains.android.util.AndroidResourceUtil;
|
||||
import org.jetbrains.android.util.AndroidUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.android.AndroidUtilKt;
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
// TODO: ask for extension point
|
||||
// this class is mostly copied from org.jetbrains.android.AndroidGotoDeclarationHandler
|
||||
public class KotlinAndroidGotoDeclarationHandler implements GotoDeclarationHandler {
|
||||
@Override
|
||||
public PsiElement[] getGotoDeclarationTargets(@Nullable PsiElement sourceElement, int offset, Editor editor) {
|
||||
KtSimpleNameExpression referenceExpression = GotoResourceHelperKt.getReferenceExpression(sourceElement);
|
||||
if (referenceExpression == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
AndroidFacet facet = AndroidUtilKt.getAndroidFacetForFile(referenceExpression);
|
||||
if (facet == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
AndroidResourceUtil.MyReferredResourceFieldInfo info = GotoResourceHelperKt.getInfo(referenceExpression, facet);
|
||||
|
||||
if (info == null) return null;
|
||||
|
||||
String nestedClassName = info.getClassName();
|
||||
String fieldName = info.getFieldName();
|
||||
List<PsiElement> resourceList = new ArrayList<PsiElement>();
|
||||
|
||||
if (info.isFromManifest()) {
|
||||
collectManifestElements(nestedClassName, fieldName, facet, resourceList);
|
||||
}
|
||||
else {
|
||||
ModuleResourceManagers managers = ModuleResourceManagers.getInstance(facet);
|
||||
ResourceManager manager = info.isSystem()
|
||||
? managers.getSystemResourceManager(false)
|
||||
: managers.getLocalResourceManager();
|
||||
if (manager == null) {
|
||||
return null;
|
||||
}
|
||||
manager.collectLazyResourceElements(nestedClassName, fieldName, false, referenceExpression, resourceList);
|
||||
|
||||
if (manager instanceof LocalResourceManager) {
|
||||
LocalResourceManager lrm = (LocalResourceManager) manager;
|
||||
|
||||
if (nestedClassName.equals(ResourceType.ATTR.getName())) {
|
||||
for (Attr attr : lrm.findAttrs(fieldName)) {
|
||||
resourceList.add(attr.getName().getXmlAttributeValue());
|
||||
}
|
||||
}
|
||||
else if (nestedClassName.equals(ResourceType.STYLEABLE.getName())) {
|
||||
for (DeclareStyleable styleable : lrm.findStyleables(fieldName)) {
|
||||
resourceList.add(styleable.getName().getXmlAttributeValue());
|
||||
}
|
||||
|
||||
for (Attr styleable : lrm.findStyleableAttributesByFieldName(fieldName)) {
|
||||
resourceList.add(styleable.getName().getXmlAttributeValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (resourceList.size() > 1) {
|
||||
// Sort to ensure the output is stable, and to prefer the base folders
|
||||
Collections.sort(resourceList, AndroidResourceUtil.RESOURCE_ELEMENT_COMPARATOR);
|
||||
}
|
||||
|
||||
return resourceList.toArray(new PsiElement[resourceList.size()]);
|
||||
}
|
||||
|
||||
private static void collectManifestElements(
|
||||
@NotNull String nestedClassName,
|
||||
@NotNull String fieldName,
|
||||
@NotNull AndroidFacet facet,
|
||||
@NotNull List<PsiElement> result
|
||||
) {
|
||||
Manifest manifest = facet.getManifest();
|
||||
|
||||
if (manifest == null) {
|
||||
return;
|
||||
}
|
||||
List<? extends ManifestElementWithRequiredName> list;
|
||||
|
||||
if ("permission".equals(nestedClassName)) {
|
||||
list = manifest.getPermissions();
|
||||
}
|
||||
else if ("permission_group".equals(nestedClassName)) {
|
||||
list = manifest.getPermissionGroups();
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
for (ManifestElementWithRequiredName domElement : list) {
|
||||
AndroidAttributeValue<String> nameAttribute = domElement.getName();
|
||||
String name = nameAttribute.getValue();
|
||||
|
||||
if (AndroidUtils.equal(name, fieldName, false)) {
|
||||
XmlElement psiElement = nameAttribute.getXmlAttributeValue();
|
||||
|
||||
if (psiElement != null) {
|
||||
result.add(psiElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getActionText(DataContext context) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
-111
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android.navigation
|
||||
|
||||
import org.jetbrains.android.util.AndroidResourceUtil
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.android.facet.AndroidFacet
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.psi.PsiClass
|
||||
import org.jetbrains.android.util.AndroidUtils
|
||||
import com.android.SdkConstants
|
||||
import org.jetbrains.android.augment.AndroidPsiElementFinder
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
|
||||
fun getReferenceExpression(element: PsiElement?): KtSimpleNameExpression? {
|
||||
return PsiTreeUtil.getParentOfType<KtSimpleNameExpression>(element, KtSimpleNameExpression::class.java)
|
||||
}
|
||||
|
||||
// given 'R.a.b' returns info for all three parts of the expression 'a', 'b', 'R'
|
||||
fun getInfo(
|
||||
referenceExpression: KtSimpleNameExpression,
|
||||
facet: AndroidFacet
|
||||
): AndroidResourceUtil.MyReferredResourceFieldInfo? {
|
||||
val info = getReferredInfo(referenceExpression, facet)
|
||||
if (info != null) return info
|
||||
|
||||
val topMostQualified = referenceExpression.getParentQualified().getParentQualified() ?: return null
|
||||
val selectorCandidate = topMostQualified.selectorExpression as? KtSimpleNameExpression ?: return null
|
||||
return getReferredInfo(selectorCandidate, facet)
|
||||
}
|
||||
|
||||
private fun KtExpression?.getParentQualified(): KtDotQualifiedExpression? {
|
||||
return this?.parent as? KtDotQualifiedExpression
|
||||
}
|
||||
|
||||
// returns info if passed expression is 'b' in 'R.a.b'
|
||||
private fun getReferredInfo(
|
||||
lastPart: KtSimpleNameExpression,
|
||||
facet: AndroidFacet
|
||||
): AndroidResourceUtil.MyReferredResourceFieldInfo? {
|
||||
val resFieldName = lastPart.getReferencedName()
|
||||
if (resFieldName.isEmpty()) return null
|
||||
|
||||
val middlePart = getReceiverAsSimpleNameExpression(lastPart) ?: return null
|
||||
|
||||
val resClassName = middlePart.getReferencedName()
|
||||
if (resClassName.isEmpty()) return null
|
||||
|
||||
val firstPart = getReceiverAsSimpleNameExpression(middlePart) ?: return null
|
||||
|
||||
val resolvedClass = firstPart.mainReference.resolve() as? PsiClass ?: return null
|
||||
|
||||
//the following code is copied from
|
||||
// org.jetbrains.android.util.AndroidResourceUtil.getReferredResourceOrManifestField
|
||||
// (org.jetbrains.android.facet.AndroidFacet, com.intellij.psi.PsiReferenceExpression, java.lang.String, boolean)
|
||||
val classShortName = resolvedClass.name
|
||||
|
||||
val fromManifest = AndroidUtils.MANIFEST_CLASS_NAME == classShortName
|
||||
|
||||
if (!fromManifest && AndroidUtils.R_CLASS_NAME != classShortName) {
|
||||
return null
|
||||
}
|
||||
val qName = resolvedClass.qualifiedName
|
||||
|
||||
if (SdkConstants.CLASS_R == qName || AndroidPsiElementFinder.INTERNAL_R_CLASS_QNAME == qName) {
|
||||
return AndroidResourceUtil.MyReferredResourceFieldInfo(resClassName, resFieldName, facet.module, true, false)
|
||||
}
|
||||
val containingFile = resolvedClass.containingFile ?: return null
|
||||
|
||||
val isFromCorrectFile =
|
||||
if (fromManifest) AndroidResourceUtil.isManifestJavaFile(facet, containingFile)
|
||||
else AndroidResourceUtil.isRJavaFile(facet, containingFile)
|
||||
|
||||
if (!isFromCorrectFile) {
|
||||
return null
|
||||
}
|
||||
|
||||
return AndroidResourceUtil.MyReferredResourceFieldInfo(resClassName, resFieldName, facet.module, false, fromManifest)
|
||||
}
|
||||
|
||||
private fun getReceiverAsSimpleNameExpression(exp: KtSimpleNameExpression): KtSimpleNameExpression? {
|
||||
val receiver = exp.getReceiverExpression()
|
||||
return when (receiver) {
|
||||
is KtSimpleNameExpression -> {
|
||||
receiver
|
||||
}
|
||||
is KtDotQualifiedExpression -> {
|
||||
receiver.selectorExpression as? KtSimpleNameExpression
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
||||
}
|
||||
-95
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android.quickfix
|
||||
|
||||
import com.android.SdkConstants
|
||||
import com.android.tools.lint.checks.ApiDetector.REQUIRES_API_ANNOTATION
|
||||
import com.intellij.codeInsight.FileModificationService
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.android.inspections.lint.AndroidLintQuickFix
|
||||
import org.jetbrains.android.inspections.lint.AndroidQuickfixContexts
|
||||
import org.jetbrains.android.util.AndroidBundle
|
||||
import org.jetbrains.kotlin.android.hasBackingField
|
||||
import org.jetbrains.kotlin.idea.util.addAnnotation
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
|
||||
|
||||
class AddTargetApiQuickFix(
|
||||
val api: Int,
|
||||
val useRequiresApi: Boolean
|
||||
) : AndroidLintQuickFix {
|
||||
|
||||
private companion object {
|
||||
val FQNAME_TARGET_API = FqName(SdkConstants.FQCN_TARGET_API)
|
||||
val FQNAME_REQUIRES_API = FqName(REQUIRES_API_ANNOTATION)
|
||||
}
|
||||
|
||||
override fun isApplicable(startElement: PsiElement, endElement: PsiElement, contextType: AndroidQuickfixContexts.ContextType): Boolean =
|
||||
getAnnotationContainer(startElement, useRequiresApi) != null
|
||||
|
||||
override fun getName(): String = getAnnotationValue(false).let {
|
||||
if (useRequiresApi) {
|
||||
// Not Available in Android plugin 2.0
|
||||
// AndroidBundle.message("android.lint.fix.add.requires.api", it)
|
||||
"Add @RequiresApi($it) Annotation"
|
||||
} else {
|
||||
AndroidBundle.message("android.lint.fix.add.target.api", it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun apply(startElement: PsiElement, endElement: PsiElement, context: AndroidQuickfixContexts.Context) {
|
||||
val annotationContainer = getAnnotationContainer(startElement, useRequiresApi) ?: return
|
||||
if (!FileModificationService.getInstance().preparePsiElementForWrite(annotationContainer)) {
|
||||
return
|
||||
}
|
||||
|
||||
if (annotationContainer is KtModifierListOwner) {
|
||||
annotationContainer.addAnnotation(
|
||||
if (useRequiresApi) FQNAME_REQUIRES_API else FQNAME_TARGET_API,
|
||||
getAnnotationValue(true),
|
||||
whiteSpaceText = if (annotationContainer.isNewLineNeededForAnnotation()) "\n" else " ")
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtElement.isNewLineNeededForAnnotation() = !(this is KtParameter || this is KtTypeParameter || this is KtPropertyAccessor)
|
||||
|
||||
private fun getAnnotationValue(fullyQualified: Boolean) = getVersionField(api, fullyQualified)
|
||||
|
||||
private fun getAnnotationContainer(element: PsiElement, useRequiresApi: Boolean): PsiElement? {
|
||||
return PsiTreeUtil.findFirstParent(element) {
|
||||
if (useRequiresApi)
|
||||
it.isRequiresApiAnnotationValidTarget()
|
||||
else
|
||||
it.isTargetApiAnnotationValidTarget()
|
||||
}
|
||||
}
|
||||
|
||||
private fun PsiElement.isRequiresApiAnnotationValidTarget(): Boolean {
|
||||
return this is KtClassOrObject ||
|
||||
(this is KtFunction && this !is KtFunctionLiteral) ||
|
||||
(this is KtProperty && !isLocal && hasBackingField()) ||
|
||||
this is KtPropertyAccessor
|
||||
}
|
||||
|
||||
private fun PsiElement.isTargetApiAnnotationValidTarget(): Boolean {
|
||||
return this is KtClassOrObject ||
|
||||
(this is KtFunction && this !is KtFunctionLiteral) ||
|
||||
this is KtPropertyAccessor
|
||||
}
|
||||
}
|
||||
-94
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android.quickfix
|
||||
|
||||
import com.intellij.codeInsight.FileModificationService
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.android.inspections.lint.AndroidLintQuickFix
|
||||
import org.jetbrains.android.inspections.lint.AndroidQuickfixContexts
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.codeInsight.surroundWith.statement.KotlinIfSurrounder
|
||||
import org.jetbrains.kotlin.idea.core.ShortenReferences
|
||||
import org.jetbrains.kotlin.idea.inspections.findExistingEditor
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
|
||||
|
||||
class AddTargetVersionCheckQuickFix(val api: Int) : AndroidLintQuickFix {
|
||||
|
||||
override fun apply(startElement: PsiElement, endElement: PsiElement, context: AndroidQuickfixContexts.Context) {
|
||||
val targetExpression = getTargetExpression(startElement)
|
||||
val project = targetExpression?.project ?: return
|
||||
val editor = targetExpression.findExistingEditor() ?: return
|
||||
|
||||
val file = targetExpression.containingFile
|
||||
val documentManager = PsiDocumentManager.getInstance(project)
|
||||
val document = documentManager.getDocument(file) ?: return
|
||||
|
||||
if (!FileModificationService.getInstance().prepareFileForWrite(file)) {
|
||||
return
|
||||
}
|
||||
|
||||
val surrounder = getSurrounder(targetExpression, "\"VERSION.SDK_INT < ${getVersionField(api, false)}\"")
|
||||
val conditionRange = surrounder.surroundElements(project, editor, arrayOf(targetExpression)) ?: return
|
||||
val conditionText = "android.os.Build.VERSION.SDK_INT >= ${getVersionField(api, true)}"
|
||||
document.replaceString(conditionRange.startOffset, conditionRange.endOffset, conditionText)
|
||||
documentManager.commitDocument(document)
|
||||
|
||||
ShortenReferences.DEFAULT.process(documentManager.getPsiFile(document) as KtFile,
|
||||
conditionRange.startOffset,
|
||||
conditionRange.startOffset + conditionText.length)
|
||||
}
|
||||
|
||||
override fun isApplicable(startElement: PsiElement, endElement: PsiElement, contextType: AndroidQuickfixContexts.ContextType): Boolean =
|
||||
getTargetExpression(startElement) != null
|
||||
|
||||
override fun getName(): String = "Surround with if (VERSION.SDK_INT >= VERSION_CODES.${getVersionField(api, false)}) { ... }"
|
||||
|
||||
private fun getTargetExpression(element: PsiElement): KtElement? {
|
||||
var current = PsiTreeUtil.getParentOfType(element, KtExpression::class.java)
|
||||
while (current != null) {
|
||||
if (current.parent is KtBlockExpression ||
|
||||
current.parent is KtContainerNode ||
|
||||
current.parent is KtWhenEntry ||
|
||||
current.parent is KtFunction ||
|
||||
current.parent is KtPropertyAccessor ||
|
||||
current.parent is KtProperty ||
|
||||
current.parent is KtReturnExpression ||
|
||||
current.parent is KtDestructuringDeclaration) {
|
||||
break
|
||||
}
|
||||
current = PsiTreeUtil.getParentOfType(current, KtExpression::class.java, true)
|
||||
}
|
||||
|
||||
return current
|
||||
}
|
||||
|
||||
private fun getSurrounder(element: KtElement, todoText: String?): KotlinIfSurrounder {
|
||||
val used = element.analyze(BodyResolveMode.PARTIAL_WITH_CFA)[BindingContext.USED_AS_EXPRESSION, element] ?: false
|
||||
return if (used) {
|
||||
object : KotlinIfSurrounder() {
|
||||
override fun getCodeTemplate(): String = "if (a) { \n} else {\nTODO(${todoText ?: ""})\n}"
|
||||
}
|
||||
} else {
|
||||
KotlinIfSurrounder()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android.quickfix
|
||||
|
||||
import com.android.sdklib.SdkVersionInfo.*
|
||||
|
||||
|
||||
fun getVersionField(api: Int, fullyQualified: Boolean): String = getBuildCode(api)?.let {
|
||||
if (fullyQualified) "android.os.Build.VERSION_CODES.$it" else it
|
||||
} ?: api.toString()
|
||||
-80
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android.quickfix
|
||||
|
||||
import com.android.SdkConstants.SUPPORT_ANNOTATIONS_PREFIX
|
||||
import com.android.tools.lint.checks.ApiDetector
|
||||
import com.android.tools.lint.checks.CommentDetector
|
||||
import com.android.tools.lint.checks.ParcelDetector
|
||||
import com.android.tools.lint.detector.api.Issue
|
||||
import com.intellij.psi.JavaPsiFacade
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.android.inspections.lint.AndroidLintQuickFix
|
||||
import org.jetbrains.android.inspections.lint.AndroidLintQuickFixProvider
|
||||
import java.util.regex.Pattern
|
||||
|
||||
|
||||
class KotlinAndroidQuickFixProvider : AndroidLintQuickFixProvider {
|
||||
override fun getQuickFixes(
|
||||
issue: Issue,
|
||||
startElement: PsiElement,
|
||||
endElement: PsiElement,
|
||||
message: String,
|
||||
data: Any?
|
||||
): Array<AndroidLintQuickFix> {
|
||||
val fixes: Array<AndroidLintQuickFix> = when (issue) {
|
||||
ApiDetector.UNSUPPORTED, ApiDetector.INLINED -> getApiQuickFixes(issue, startElement, message)
|
||||
ParcelDetector.ISSUE -> arrayOf(ParcelableQuickFix())
|
||||
else -> emptyArray()
|
||||
}
|
||||
|
||||
if (issue != CommentDetector.STOP_SHIP) {
|
||||
return fixes + SuppressLintQuickFix(issue.id)
|
||||
}
|
||||
|
||||
return fixes
|
||||
}
|
||||
|
||||
fun getApiQuickFixes(issue: Issue, element: PsiElement, message: String): Array<AndroidLintQuickFix> {
|
||||
val api = getRequiredVersion(message)
|
||||
if (api == -1) {
|
||||
return AndroidLintQuickFix.EMPTY_ARRAY
|
||||
}
|
||||
|
||||
val project = element.project
|
||||
if (JavaPsiFacade.getInstance(project).findClass(REQUIRES_API_ANNOTATION, GlobalSearchScope.allScope(project)) != null) {
|
||||
return arrayOf(AddTargetApiQuickFix(api, true), AddTargetApiQuickFix(api, false), AddTargetVersionCheckQuickFix(api))
|
||||
}
|
||||
|
||||
return arrayOf(AddTargetApiQuickFix(api, false), AddTargetVersionCheckQuickFix(api))
|
||||
}
|
||||
|
||||
private fun getRequiredVersion(errorMessage: String): Int {
|
||||
val pattern = Pattern.compile("\\s(\\d+)\\s")
|
||||
val matcher = pattern.matcher(errorMessage)
|
||||
if (matcher.find()) {
|
||||
return Integer.parseInt(matcher.group(1))
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
companion object {
|
||||
val REQUIRES_API_ANNOTATION = SUPPORT_ANNOTATIONS_PREFIX + "RequiresApi"
|
||||
}
|
||||
}
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android.quickfix
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.android.inspections.lint.AndroidLintQuickFix
|
||||
import org.jetbrains.android.inspections.lint.AndroidQuickfixContexts
|
||||
import org.jetbrains.android.util.AndroidBundle
|
||||
import org.jetbrains.kotlin.android.canAddParcelable
|
||||
import org.jetbrains.kotlin.android.implementParcelable
|
||||
import org.jetbrains.kotlin.android.isParcelize
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
|
||||
|
||||
class ParcelableQuickFix : AndroidLintQuickFix {
|
||||
override fun apply(startElement: PsiElement, endElement: PsiElement, context: AndroidQuickfixContexts.Context) {
|
||||
startElement.getTargetClass()?.implementParcelable()
|
||||
}
|
||||
|
||||
override fun isApplicable(startElement: PsiElement, endElement: PsiElement, contextType: AndroidQuickfixContexts.ContextType): Boolean {
|
||||
val targetClass = startElement.getTargetClass() ?: return false
|
||||
return targetClass.canAddParcelable() && !targetClass.isParcelize()
|
||||
}
|
||||
|
||||
override fun getName(): String = AndroidBundle.message("implement.parcelable.intention.text")
|
||||
|
||||
private fun PsiElement.getTargetClass(): KtClass? = PsiTreeUtil.getParentOfType(this, KtClass::class.java, false)
|
||||
}
|
||||
-99
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android.quickfix
|
||||
|
||||
import com.android.SdkConstants
|
||||
import com.intellij.codeInsight.FileModificationService
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.android.inspections.lint.AndroidLintQuickFix
|
||||
import org.jetbrains.android.inspections.lint.AndroidQuickfixContexts
|
||||
import org.jetbrains.android.util.AndroidBundle
|
||||
import org.jetbrains.kotlin.android.hasBackingField
|
||||
import org.jetbrains.kotlin.idea.util.addAnnotation
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
|
||||
|
||||
class SuppressLintQuickFix(id: String) : AndroidLintQuickFix {
|
||||
private val lintId = getLintId(id)
|
||||
|
||||
override fun apply(startElement: PsiElement, endElement: PsiElement, context: AndroidQuickfixContexts.Context) {
|
||||
val annotationContainer = PsiTreeUtil.findFirstParent(startElement, true) { it.isSuppressLintTarget() } ?: return
|
||||
if (!FileModificationService.getInstance().preparePsiElementForWrite(annotationContainer)) {
|
||||
return
|
||||
}
|
||||
|
||||
val argument = "\"$lintId\""
|
||||
|
||||
when (annotationContainer) {
|
||||
is KtModifierListOwner -> {
|
||||
annotationContainer.addAnnotation(
|
||||
FQNAME_SUPPRESS_LINT,
|
||||
argument,
|
||||
whiteSpaceText = if (annotationContainer.isNewLineNeededForAnnotation()) "\n" else " ",
|
||||
addToExistingAnnotation = { entry -> addArgumentToAnnotation(entry, argument) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getName(): String = AndroidBundle.message(SUPPRESS_LINT_MESSAGE, lintId)
|
||||
|
||||
override fun isApplicable(
|
||||
startElement: PsiElement,
|
||||
endElement: PsiElement,
|
||||
contextType: AndroidQuickfixContexts.ContextType
|
||||
): Boolean = true
|
||||
|
||||
private fun addArgumentToAnnotation(entry: KtAnnotationEntry, argument: String): Boolean {
|
||||
// add new arguments to an existing entry
|
||||
val args = entry.valueArgumentList
|
||||
val psiFactory = KtPsiFactory(entry)
|
||||
val newArgList = psiFactory.createCallArguments("($argument)")
|
||||
when {
|
||||
args == null -> // new argument list
|
||||
entry.addAfter(newArgList, entry.lastChild)
|
||||
args.arguments.isEmpty() -> // replace '()' with a new argument list
|
||||
args.replace(newArgList)
|
||||
args.arguments.none { it.textMatches(argument) } ->
|
||||
args.addArgument(newArgList.arguments[0])
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun getLintId(intentionId: String) =
|
||||
if (intentionId.startsWith(INTENTION_NAME_PREFIX)) intentionId.substring(INTENTION_NAME_PREFIX.length) else intentionId
|
||||
|
||||
private fun KtElement.isNewLineNeededForAnnotation(): Boolean {
|
||||
return !(this is KtParameter ||
|
||||
this is KtTypeParameter ||
|
||||
this is KtPropertyAccessor)
|
||||
}
|
||||
|
||||
private fun PsiElement.isSuppressLintTarget(): Boolean {
|
||||
return this is KtDeclaration &&
|
||||
(this as? KtProperty)?.hasBackingField() ?: true &&
|
||||
this !is KtFunctionLiteral &&
|
||||
this !is KtDestructuringDeclaration
|
||||
}
|
||||
private companion object {
|
||||
val INTENTION_NAME_PREFIX = "AndroidLint"
|
||||
val SUPPRESS_LINT_MESSAGE = "android.lint.fix.suppress.lint.api.annotation"
|
||||
val FQNAME_SUPPRESS_LINT = FqName(SdkConstants.FQCN_SUPPRESS_LINT)
|
||||
}
|
||||
}
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android.quickfix
|
||||
|
||||
import com.intellij.codeInspection.InspectionProfileEntry
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.util.PathUtil
|
||||
import org.jetbrains.android.inspections.lint.AndroidLintInspectionBase
|
||||
import org.jetbrains.kotlin.android.KotlinAndroidTestCase
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
import java.io.File
|
||||
|
||||
|
||||
abstract class AbstractAndroidLintQuickfixTest : KotlinAndroidTestCase() {
|
||||
|
||||
override fun setUp() {
|
||||
AndroidLintInspectionBase.invalidateInspectionShortName2IssueMap()
|
||||
super.setUp()
|
||||
}
|
||||
|
||||
fun doTest(path: String) {
|
||||
val fileText = FileUtil.loadFile(File(path), true)
|
||||
val intentionText = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// INTENTION_TEXT: ") ?: error("Empty intention text")
|
||||
val mainInspectionClassName = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// INSPECTION_CLASS: ") ?: error("Empty inspection class name")
|
||||
val dependency = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// DEPENDENCY: ")
|
||||
val intentionAvailable = !InTextDirectivesUtils.isDirectiveDefined(fileText, "// INTENTION_NOT_AVAILABLE")
|
||||
|
||||
val inspection = Class.forName(mainInspectionClassName).newInstance() as InspectionProfileEntry
|
||||
myFixture.enableInspections(inspection)
|
||||
|
||||
val sourceFile = myFixture.copyFileToProject(path, "src/${PathUtil.getFileName(path)}")
|
||||
myFixture.configureFromExistingVirtualFile(sourceFile)
|
||||
|
||||
if (dependency != null) {
|
||||
val (dependencyFile, dependencyTargetPath) = dependency.split(" -> ").map(String::trim)
|
||||
myFixture.copyFileToProject("${PathUtil.getParentPath(path)}/$dependencyFile", "src/$dependencyTargetPath")
|
||||
}
|
||||
|
||||
if (intentionAvailable) {
|
||||
val intention = myFixture.getAvailableIntention(intentionText) ?: error("Failed to find intention")
|
||||
myFixture.launchAction(intention)
|
||||
myFixture.checkResultByFile(path + ".expected")
|
||||
}
|
||||
else {
|
||||
assertNull("Intention should not be available", myFixture.availableIntentions.find { it.text == intentionText })
|
||||
}
|
||||
}
|
||||
}
|
||||
-2254
File diff suppressed because it is too large
Load Diff
-51
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.framework;
|
||||
|
||||
import com.intellij.framework.library.LibraryVersionProperties;
|
||||
import com.intellij.openapi.roots.libraries.LibraryPresentationProvider;
|
||||
import com.intellij.openapi.roots.libraries.LibraryProperties;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.idea.KotlinIcons;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.List;
|
||||
|
||||
public class JavaRuntimePresentationProvider extends LibraryPresentationProvider<LibraryVersionProperties> {
|
||||
public static JavaRuntimePresentationProvider getInstance() {
|
||||
return LibraryPresentationProvider.EP_NAME.findExtension(JavaRuntimePresentationProvider.class);
|
||||
}
|
||||
|
||||
protected JavaRuntimePresentationProvider() {
|
||||
super(JavaRuntimeLibraryDescription.Companion.getKOTLIN_JAVA_RUNTIME_KIND());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Icon getIcon(@Nullable LibraryVersionProperties properties) {
|
||||
return KotlinIcons.SMALL_LOGO;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public LibraryVersionProperties detect(@NotNull List<VirtualFile> classesRoots) {
|
||||
String version = JavaRuntimeDetectionUtil.getJavaRuntimeVersion(classesRoots);
|
||||
return version == null ? null : new LibraryVersionProperties(version);
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile(project(":core:util.runtime"))
|
||||
compile(project(":compiler:frontend"))
|
||||
compile(project(":compiler:frontend.java"))
|
||||
compile(project(":compiler:util"))
|
||||
compile(project(":compiler:cli-common"))
|
||||
compile(project(":kotlin-build-common"))
|
||||
|
||||
compile(project(":js:js.frontend"))
|
||||
|
||||
compile(project(":idea"))
|
||||
compile(project(":idea:idea-jvm"))
|
||||
compile(project(":idea:idea-jps-common"))
|
||||
|
||||
compileOnly(intellijDep())
|
||||
excludeInAndroidStudio(rootProject) { compileOnly(intellijPluginDep("maven")) }
|
||||
|
||||
testCompile(projectTests(":idea"))
|
||||
testCompile(projectTests(":compiler:tests-common"))
|
||||
testCompile(projectTests(":idea:idea-test-framework"))
|
||||
|
||||
testCompileOnly(intellijDep())
|
||||
//testCompileOnly(intellijPluginDep("maven"))
|
||||
|
||||
testCompile(project(":idea:idea-native")) { isTransitive = false }
|
||||
testRuntime(project(":kotlin-native:kotlin-native-library-reader")) { isTransitive = false }
|
||||
testRuntime(project(":kotlin-native:kotlin-native-utils")) { isTransitive = false }
|
||||
|
||||
testRuntime(project(":kotlin-reflect"))
|
||||
testRuntime(project(":idea:idea-jvm"))
|
||||
testRuntime(project(":idea:idea-android"))
|
||||
testRuntime(project(":plugins:android-extensions-ide"))
|
||||
testRuntime(project(":plugins:lint"))
|
||||
testRuntime(project(":sam-with-receiver-ide-plugin"))
|
||||
testRuntime(project(":allopen-ide-plugin"))
|
||||
testRuntime(project(":noarg-ide-plugin"))
|
||||
testRuntime(project(":kotlin-scripting-idea"))
|
||||
testRuntime(project(":kotlinx-serialization-ide-plugin"))
|
||||
|
||||
testRuntime(intellijDep())
|
||||
// TODO: the order of the plugins matters here, consider avoiding order-dependency
|
||||
testRuntime(intellijPluginDep("junit"))
|
||||
testRuntime(intellijPluginDep("testng"))
|
||||
testRuntime(intellijPluginDep("properties"))
|
||||
testRuntime(intellijPluginDep("gradle"))
|
||||
testRuntime(intellijPluginDep("Groovy"))
|
||||
testRuntime(intellijPluginDep("coverage"))
|
||||
//testRuntime(intellijPluginDep("maven"))
|
||||
testRuntime(intellijPluginDep("android"))
|
||||
testRuntime(intellijPluginDep("smali"))
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { /*projectDefault()*/ }
|
||||
"test" { /*projectDefault()*/ }
|
||||
}
|
||||
|
||||
testsJar()
|
||||
|
||||
projectTest {
|
||||
workingDir = rootDir
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
|
||||
description = "Kotlin Gradle Tooling support"
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
jvmTarget = "1.6"
|
||||
|
||||
dependencies {
|
||||
compile(project(":kotlin-stdlib"))
|
||||
compile(project(":compiler:cli-common"))
|
||||
compile(intellijPluginDep("gradle"))
|
||||
compileOnly(intellijDep()) { includeJars("slf4j-api-1.7.10") }
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" {}
|
||||
}
|
||||
|
||||
runtimeJar()
|
||||
|
||||
ideaPlugin()
|
||||
@@ -1,434 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.gradle
|
||||
|
||||
import org.gradle.api.Named
|
||||
import org.gradle.api.NamedDomainObjectContainer
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.artifacts.Configuration
|
||||
import org.gradle.api.artifacts.Dependency
|
||||
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.file.SourceDirectorySet
|
||||
import org.gradle.api.logging.Logging
|
||||
import org.jetbrains.plugins.gradle.model.*
|
||||
import org.jetbrains.plugins.gradle.tooling.ErrorMessageBuilder
|
||||
import org.jetbrains.plugins.gradle.tooling.ModelBuilderService
|
||||
import org.jetbrains.plugins.gradle.tooling.util.DependencyResolver
|
||||
import org.jetbrains.plugins.gradle.tooling.util.DependencyResolverImpl
|
||||
import org.jetbrains.plugins.gradle.tooling.util.SourceSetCachedFinder
|
||||
import java.io.File
|
||||
import java.lang.reflect.Method
|
||||
|
||||
class KotlinMPPGradleModelBuilder : ModelBuilderService {
|
||||
override fun getErrorMessageBuilder(project: Project, e: Exception): ErrorMessageBuilder {
|
||||
return ErrorMessageBuilder
|
||||
.create(project, e, "Gradle import errors")
|
||||
.withDescription("Unable to build Kotlin project configuration")
|
||||
}
|
||||
|
||||
override fun canBuild(modelName: String?): Boolean {
|
||||
return modelName == KotlinMPPGradleModel::class.java.name
|
||||
}
|
||||
|
||||
override fun buildAll(modelName: String, project: Project): Any? {
|
||||
val dependencyResolver = DependencyResolverImpl(
|
||||
project,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
SourceSetCachedFinder(project)
|
||||
)
|
||||
val sourceSets = buildSourceSets(dependencyResolver, project) ?: return null
|
||||
val sourceSetMap = sourceSets.map { it.name to it }.toMap()
|
||||
val targets = buildTargets(sourceSetMap, dependencyResolver, project) ?: return null
|
||||
computeSourceSetsDeferredInfo(sourceSets, targets)
|
||||
val coroutinesState = getCoroutinesState(project)
|
||||
reportUnresolvedDependencies(targets)
|
||||
return KotlinMPPGradleModelImpl(sourceSetMap, targets, ExtraFeaturesImpl(coroutinesState))
|
||||
}
|
||||
|
||||
private fun reportUnresolvedDependencies(targets: Collection<KotlinTarget>) {
|
||||
targets
|
||||
.asSequence()
|
||||
.flatMap { it.compilations.asSequence() }
|
||||
.flatMap { it.dependencies.asSequence() }
|
||||
.mapNotNull { (it as? UnresolvedExternalDependency)?.failureMessage }
|
||||
.toSet()
|
||||
.forEach { logger.warn(it) }
|
||||
}
|
||||
|
||||
private fun getCoroutinesState(project: Project): String? {
|
||||
val kotlinExt = project.extensions.findByName("kotlin") ?: return null
|
||||
val getExperimental = kotlinExt.javaClass.getMethodOrNull("getExperimental") ?: return null
|
||||
val experimentalExt = getExperimental(kotlinExt) ?: return null
|
||||
val getCoroutines = experimentalExt.javaClass.getMethodOrNull("getCoroutines") ?: return null
|
||||
return getCoroutines(experimentalExt) as? String
|
||||
}
|
||||
|
||||
private fun buildSourceSets(dependencyResolver: DependencyResolver, project: Project): Collection<KotlinSourceSetImpl>? {
|
||||
val kotlinExt = project.extensions.findByName("kotlin") ?: return null
|
||||
val getSourceSets = kotlinExt.javaClass.getMethodOrNull("getSourceSets") ?: return null
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val sourceSets =
|
||||
(getSourceSets(kotlinExt) as? NamedDomainObjectContainer<Named>)?.asMap?.values ?: emptyList<Named>()
|
||||
return sourceSets.mapNotNull { buildSourceSet(it, dependencyResolver, project) }
|
||||
}
|
||||
|
||||
private fun buildSourceSet(
|
||||
gradleSourceSet: Named,
|
||||
dependencyResolver: DependencyResolver,
|
||||
project: Project
|
||||
): KotlinSourceSetImpl? {
|
||||
val sourceSetClass = gradleSourceSet.javaClass
|
||||
val getLanguageSettings = sourceSetClass.getMethodOrNull("getLanguageSettings") ?: return null
|
||||
val getSourceDirSet = sourceSetClass.getMethodOrNull("getKotlin") ?: return null
|
||||
val getResourceDirSet = sourceSetClass.getMethodOrNull("getResources") ?: return null
|
||||
val getDependsOn = sourceSetClass.getMethodOrNull("getDependsOn") ?: return null
|
||||
val languageSettings = getLanguageSettings(gradleSourceSet)?.let { buildLanguageSettings(it) } ?: return null
|
||||
val sourceDirs = (getSourceDirSet(gradleSourceSet) as? SourceDirectorySet)?.srcDirs ?: emptySet()
|
||||
val resourceDirs = (getResourceDirSet(gradleSourceSet) as? SourceDirectorySet)?.srcDirs ?: emptySet()
|
||||
val dependencies = buildSourceSetDependencies(gradleSourceSet, dependencyResolver, project)
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val dependsOnSourceSets = (getDependsOn(gradleSourceSet) as? Set<Named>)?.mapTo(LinkedHashSet()) { it.name } ?: emptySet<String>()
|
||||
return KotlinSourceSetImpl(gradleSourceSet.name, languageSettings, sourceDirs, resourceDirs, dependencies, dependsOnSourceSets)
|
||||
}
|
||||
|
||||
private fun buildLanguageSettings(gradleLanguageSettings: Any): KotlinLanguageSettings? {
|
||||
val languageSettingsClass = gradleLanguageSettings.javaClass
|
||||
val getLanguageVersion = languageSettingsClass.getMethodOrNull("getLanguageVersion") ?: return null
|
||||
val getApiVersion = languageSettingsClass.getMethodOrNull("getApiVersion") ?: return null
|
||||
val getProgressiveMode = languageSettingsClass.getMethodOrNull("getProgressiveMode") ?: return null
|
||||
val getEnabledLanguageFeatures = languageSettingsClass.getMethodOrNull("getEnabledLanguageFeatures") ?: return null
|
||||
val getExperimentalAnnotationsInUse = languageSettingsClass.getMethodOrNull("getExperimentalAnnotationsInUse")
|
||||
val getCompilerPluginArguments = languageSettingsClass.getMethodOrNull("getCompilerPluginArguments")
|
||||
val getCompilerPluginClasspath = languageSettingsClass.getMethodOrNull("getCompilerPluginClasspath")
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return KotlinLanguageSettingsImpl(
|
||||
getLanguageVersion(gradleLanguageSettings) as? String,
|
||||
getApiVersion(gradleLanguageSettings) as? String,
|
||||
getProgressiveMode(gradleLanguageSettings) as? Boolean ?: false,
|
||||
getEnabledLanguageFeatures(gradleLanguageSettings) as? Set<String> ?: emptySet(),
|
||||
getExperimentalAnnotationsInUse?.invoke(gradleLanguageSettings) as? Set<String> ?: emptySet(),
|
||||
getCompilerPluginArguments?.invoke(gradleLanguageSettings) as? List<String> ?: emptyList(),
|
||||
(getCompilerPluginClasspath?.invoke(gradleLanguageSettings) as? FileCollection)?.files ?: emptySet()
|
||||
)
|
||||
}
|
||||
|
||||
private fun buildDependencies(
|
||||
dependencyHolder: Any,
|
||||
dependencyResolver: DependencyResolver,
|
||||
configurationNameAccessor: String,
|
||||
scope: String,
|
||||
project: Project
|
||||
): Collection<KotlinDependency> {
|
||||
val dependencyHolderClass = dependencyHolder.javaClass
|
||||
val getConfigurationName = dependencyHolderClass.getMethodOrNull(configurationNameAccessor) ?: return emptyList()
|
||||
val configurationName = getConfigurationName(dependencyHolder) as? String ?: return emptyList()
|
||||
val configuration = project.configurations.findByName(configurationName) ?: return emptyList()
|
||||
if (!configuration.isCanBeResolved) return emptyList()
|
||||
|
||||
val dependencyAdjuster = DependencyAdjuster(configuration, scope, project)
|
||||
|
||||
val resolvedDependencies = dependencyResolver
|
||||
.resolveDependencies(configuration)
|
||||
.apply {
|
||||
forEach<ExternalDependency?> { (it as? AbstractExternalDependency)?.scope = scope }
|
||||
}
|
||||
.flatMap(dependencyAdjuster::adjustDependency)
|
||||
val singleDependencyFiles = resolvedDependencies.mapNotNullTo(LinkedHashSet<File>()) {
|
||||
(it as? FileCollectionDependency)?.files?.singleOrNull()
|
||||
}
|
||||
// Workaround for duplicated dependencies specified as a file collection (KT-26675)
|
||||
// Drop this code when the issue is fixed in the platform
|
||||
return resolvedDependencies.filter { dependency ->
|
||||
if (dependency !is FileCollectionDependency) return@filter true
|
||||
val files = dependency.files
|
||||
if (files.size <= 1) return@filter true
|
||||
(files.any { it !in singleDependencyFiles })
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildTargets(
|
||||
sourceSetMap: Map<String, KotlinSourceSet>,
|
||||
dependencyResolver: DependencyResolver,
|
||||
project: Project
|
||||
): Collection<KotlinTarget>? {
|
||||
return project.getTargets()?.mapNotNull { buildTarget(it, sourceSetMap, dependencyResolver, project) }
|
||||
}
|
||||
|
||||
private fun buildTarget(
|
||||
gradleTarget: Named,
|
||||
sourceSetMap: Map<String, KotlinSourceSet>,
|
||||
dependencyResolver: DependencyResolver,
|
||||
project: Project
|
||||
): KotlinTarget? {
|
||||
val targetClass = gradleTarget.javaClass
|
||||
val getPlatformType = targetClass.getMethodOrNull("getPlatformType") ?: return null
|
||||
val getCompilations = targetClass.getMethodOrNull("getCompilations") ?: return null
|
||||
val getDisambiguationClassifier = targetClass.getMethodOrNull("getDisambiguationClassifier") ?: return null
|
||||
val platformId = (getPlatformType.invoke(gradleTarget) as? Named)?.name ?: return null
|
||||
val platform = KotlinPlatform.byId(platformId) ?: return null
|
||||
val disambiguationClassifier = getDisambiguationClassifier(gradleTarget) as? String
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val gradleCompilations =
|
||||
(getCompilations.invoke(gradleTarget) as? NamedDomainObjectContainer<Named>)?.asMap?.values ?: emptyList<Named>()
|
||||
val compilations = gradleCompilations.mapNotNull {
|
||||
buildCompilation(it, disambiguationClassifier, sourceSetMap, dependencyResolver, project)
|
||||
}
|
||||
val jar = buildTargetJar(gradleTarget, project)
|
||||
val target = KotlinTargetImpl(gradleTarget.name, null, disambiguationClassifier, platform, compilations, jar)
|
||||
compilations.forEach { it.target = target }
|
||||
return target
|
||||
}
|
||||
|
||||
private fun buildTargetJar(gradleTarget: Named, project: Project): KotlinTargetJar? {
|
||||
val targetClass = gradleTarget.javaClass
|
||||
val getArtifactsTaskName = targetClass.getMethodOrNull("getArtifactsTaskName") ?: return null
|
||||
val artifactsTaskName = getArtifactsTaskName(gradleTarget) as? String ?: return null
|
||||
val jarTask = project.tasks.findByName(artifactsTaskName) ?: return null
|
||||
val jarTaskClass = jarTask.javaClass
|
||||
val getArchivePath = jarTaskClass.getMethodOrNull("getArchivePath")
|
||||
val archiveFile = getArchivePath?.invoke(jarTask) as? File?
|
||||
return KotlinTargetJarImpl(archiveFile)
|
||||
}
|
||||
|
||||
private fun buildCompilation(
|
||||
gradleCompilation: Named,
|
||||
classifier: String?,
|
||||
sourceSetMap: Map<String, KotlinSourceSet>,
|
||||
dependencyResolver: DependencyResolver,
|
||||
project: Project
|
||||
): KotlinCompilationImpl? {
|
||||
val compilationClass = gradleCompilation.javaClass
|
||||
val getKotlinSourceSets = compilationClass.getMethodOrNull("getKotlinSourceSets") ?: return null
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val kotlinGradleSourceSets = (getKotlinSourceSets(gradleCompilation) as? Collection<Named>) ?: return null
|
||||
val kotlinSourceSets = kotlinGradleSourceSets.mapNotNull { sourceSetMap[it.name] }
|
||||
val getCompileKotlinTaskName = compilationClass.getMethodOrNull("getCompileKotlinTaskName") ?: return null
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val compileKotlinTaskName = (getCompileKotlinTaskName(gradleCompilation) as? String) ?: return null
|
||||
val compileKotlinTask = project.tasks.findByName(compileKotlinTaskName) ?: return null
|
||||
val output = buildCompilationOutput(gradleCompilation, compileKotlinTask) ?: return null
|
||||
val arguments = buildCompilationArguments(compileKotlinTask) ?: return null
|
||||
val dependencyClasspath = buildDependencyClasspath(compileKotlinTask)
|
||||
val dependencies = buildCompilationDependencies(gradleCompilation, classifier, sourceSetMap, dependencyResolver, project)
|
||||
return KotlinCompilationImpl(gradleCompilation.name, kotlinSourceSets, dependencies, output, arguments, dependencyClasspath)
|
||||
}
|
||||
|
||||
private fun buildCompilationDependencies(
|
||||
gradleCompilation: Named,
|
||||
classifier: String?,
|
||||
sourceSetMap: Map<String, KotlinSourceSet>,
|
||||
dependencyResolver: DependencyResolver,
|
||||
project: Project
|
||||
): Set<KotlinDependency> {
|
||||
return LinkedHashSet<KotlinDependency>().apply {
|
||||
this += buildDependencies(
|
||||
gradleCompilation, dependencyResolver, "getCompileDependencyConfigurationName", "COMPILE", project
|
||||
)
|
||||
this += buildDependencies(
|
||||
gradleCompilation, dependencyResolver, "getRuntimeDependencyConfigurationName", "RUNTIME", project
|
||||
)
|
||||
this += sourceSetMap[compilationFullName(gradleCompilation.name, classifier)]?.dependencies ?: emptySet()
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildSourceSetDependencies(
|
||||
gradleSourceSet: Named,
|
||||
dependencyResolver: DependencyResolver,
|
||||
project: Project
|
||||
): Set<KotlinDependency> {
|
||||
return LinkedHashSet<KotlinDependency>().apply {
|
||||
this += buildDependencies(
|
||||
gradleSourceSet, dependencyResolver, "getApiMetadataConfigurationName", "COMPILE", project
|
||||
)
|
||||
this += buildDependencies(
|
||||
gradleSourceSet, dependencyResolver, "getImplementationMetadataConfigurationName", "COMPILE", project
|
||||
)
|
||||
this += buildDependencies(
|
||||
gradleSourceSet, dependencyResolver, "getCompileOnlyMetadataConfigurationName", "COMPILE", project
|
||||
)
|
||||
this += buildDependencies(
|
||||
gradleSourceSet, dependencyResolver, "getRuntimeOnlyMetadataConfigurationName", "RUNTIME", project
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private fun safelyGetArguments(compileKotlinTask: Task, accessor: Method?) = try {
|
||||
accessor?.invoke(compileKotlinTask) as? List<String>
|
||||
} catch (e: Exception) {
|
||||
logger.info(e.message, e)
|
||||
null
|
||||
} ?: emptyList()
|
||||
|
||||
private fun buildCompilationArguments(compileKotlinTask: Task): KotlinCompilationArguments? {
|
||||
val compileTaskClass = compileKotlinTask.javaClass
|
||||
val getCurrentArguments = compileTaskClass.getMethodOrNull("getSerializedCompilerArguments")
|
||||
val getDefaultArguments = compileTaskClass.getMethodOrNull("getDefaultSerializedCompilerArguments")
|
||||
val currentArguments = safelyGetArguments(compileKotlinTask, getCurrentArguments)
|
||||
val defaultArguments = safelyGetArguments(compileKotlinTask, getDefaultArguments)
|
||||
return KotlinCompilationArgumentsImpl(defaultArguments, currentArguments)
|
||||
}
|
||||
|
||||
private fun buildDependencyClasspath(compileKotlinTask: Task): List<String> {
|
||||
val abstractKotlinCompileClass =
|
||||
compileKotlinTask.javaClass.classLoader.loadClass(AbstractKotlinGradleModelBuilder.ABSTRACT_KOTLIN_COMPILE_CLASS)
|
||||
val getCompileClasspath =
|
||||
abstractKotlinCompileClass.getDeclaredMethodOrNull("getCompileClasspath") ?: return emptyList()
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return (getCompileClasspath(compileKotlinTask) as? Collection<File>)?.map { it.path } ?: emptyList()
|
||||
}
|
||||
|
||||
private fun buildCompilationOutput(
|
||||
gradleCompilation: Named,
|
||||
compileKotlinTask: Task
|
||||
): KotlinCompilationOutput? {
|
||||
val compilationClass = gradleCompilation.javaClass
|
||||
val getOutput = compilationClass.getMethodOrNull("getOutput") ?: return null
|
||||
val gradleOutput = getOutput(gradleCompilation) ?: return null
|
||||
val gradleOutputClass = gradleOutput.javaClass
|
||||
val getClassesDirs = gradleOutputClass.getMethodOrNull("getClassesDirs") ?: return null
|
||||
val getResourcesDir = gradleOutputClass.getMethodOrNull("getResourcesDir") ?: return null
|
||||
val compileKotlinTaskClass = compileKotlinTask.javaClass
|
||||
val getDestinationDir = compileKotlinTaskClass.getMethodOrNull("getDestinationDir") ?: return null
|
||||
val classesDirs = getClassesDirs(gradleOutput) as? FileCollection ?: return null
|
||||
val resourcesDir = getResourcesDir(gradleOutput) as? File ?: return null
|
||||
val destinationDir = getDestinationDir(compileKotlinTask) as? File
|
||||
return KotlinCompilationOutputImpl(classesDirs.files, destinationDir, resourcesDir)
|
||||
}
|
||||
|
||||
private fun computeSourceSetsDeferredInfo(
|
||||
sourceSets: Collection<KotlinSourceSetImpl>,
|
||||
targets: Collection<KotlinTarget>
|
||||
) {
|
||||
val sourceSetToCompilations = LinkedHashMap<KotlinSourceSet, MutableSet<KotlinCompilation>>()
|
||||
for (target in targets) {
|
||||
for (compilation in target.compilations) {
|
||||
for (sourceSet in compilation.sourceSets) {
|
||||
sourceSetToCompilations.getOrPut(sourceSet) { LinkedHashSet() } += compilation
|
||||
}
|
||||
}
|
||||
}
|
||||
for (sourceSet in sourceSets) {
|
||||
val name = sourceSet.name
|
||||
if (name == KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME) {
|
||||
sourceSet.platform = KotlinPlatform.COMMON
|
||||
sourceSet.isTestModule = false
|
||||
continue
|
||||
}
|
||||
if (name == KotlinSourceSet.COMMON_TEST_SOURCE_SET_NAME) {
|
||||
sourceSet.platform = KotlinPlatform.COMMON
|
||||
sourceSet.isTestModule = true
|
||||
continue
|
||||
}
|
||||
|
||||
val compilations = sourceSetToCompilations[sourceSet]
|
||||
if (compilations != null) {
|
||||
sourceSet.platform = compilations.map { it.platform }.distinct().singleOrNull() ?: KotlinPlatform.COMMON
|
||||
sourceSet.isTestModule = compilations.all { it.isTestModule }
|
||||
} else {
|
||||
// TODO: change me after design about it
|
||||
sourceSet.isTestModule = "Test" in sourceSet.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class DependencyAdjuster(
|
||||
private val configuration: Configuration,
|
||||
private val scope: String,
|
||||
private val project: Project
|
||||
) {
|
||||
private val adjustmentMap = HashMap<ExternalDependency, List<ExternalDependency>>()
|
||||
|
||||
val dependenciesByProjectPath by lazy {
|
||||
configuration
|
||||
.resolvedConfiguration
|
||||
.lenientConfiguration
|
||||
.allModuleDependencies
|
||||
.mapNotNull { dependency ->
|
||||
val artifact = dependency.moduleArtifacts.firstOrNull {
|
||||
it.id.componentIdentifier is ProjectComponentIdentifier
|
||||
} ?: return@mapNotNull null
|
||||
dependency to artifact
|
||||
}
|
||||
.groupBy { (it.second.id.componentIdentifier as ProjectComponentIdentifier).projectPath }
|
||||
}
|
||||
|
||||
private fun wrapDependency(dependency: ExternalProjectDependency, newConfigurationName: String): ExternalProjectDependency {
|
||||
return DefaultExternalProjectDependency(dependency).apply {
|
||||
this.configurationName = newConfigurationName
|
||||
|
||||
val nestedDependencies = this.dependencies.flatMap(::adjustDependency)
|
||||
this.dependencies.clear()
|
||||
this.dependencies.addAll(nestedDependencies)
|
||||
}
|
||||
}
|
||||
|
||||
fun adjustDependency(dependency: ExternalDependency): List<ExternalDependency> {
|
||||
return adjustmentMap.getOrPut(dependency) {
|
||||
if (dependency !is ExternalProjectDependency
|
||||
|| dependency.configurationName != Dependency.DEFAULT_CONFIGURATION
|
||||
) return@getOrPut listOf(dependency)
|
||||
val artifacts = dependenciesByProjectPath[dependency.projectPath] ?: return@getOrPut listOf(dependency)
|
||||
val artifactConfiguration = artifacts.mapTo(LinkedHashSet()) {
|
||||
it.first.configuration
|
||||
}.singleOrNull() ?: return@getOrPut listOf(dependency)
|
||||
val taskGetterName = when (scope) {
|
||||
"COMPILE" -> "getApiElementsConfigurationName"
|
||||
"RUNTIME" -> "getRuntimeElementsConfigurationName"
|
||||
else -> return@getOrPut listOf(dependency)
|
||||
}
|
||||
val dependencyProject =
|
||||
if (project.rootProject.path == dependency.projectPath)
|
||||
project.rootProject
|
||||
else
|
||||
project.rootProject.getChildProjectByPath(dependency.projectPath)
|
||||
|
||||
val targets = dependencyProject?.getTargets() ?: return@getOrPut listOf(dependency)
|
||||
val gradleTarget = targets.firstOrNull {
|
||||
val getter = it.javaClass.getMethodOrNull(taskGetterName) ?: return@firstOrNull false
|
||||
getter(it) == artifactConfiguration
|
||||
} ?: return@getOrPut listOf(dependency)
|
||||
val classifier = gradleTarget.javaClass.getMethodOrNull("getDisambiguationClassifier")?.invoke(gradleTarget) as? String
|
||||
?: return@getOrPut listOf(dependency)
|
||||
val platformDependency = if (classifier != KotlinTarget.METADATA_TARGET_NAME) {
|
||||
wrapDependency(dependency, compilationFullName(KotlinCompilation.MAIN_COMPILATION_NAME, classifier))
|
||||
} else null
|
||||
val commonDependency = wrapDependency(dependency, KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME)
|
||||
return if (platformDependency != null) listOf(platformDependency, commonDependency) else listOf(commonDependency)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = Logging.getLogger(KotlinMPPGradleModelBuilder::class.java)
|
||||
|
||||
fun Project.getTargets(): Collection<Named>? {
|
||||
val kotlinExt = project.extensions.findByName("kotlin") ?: return null
|
||||
val getTargets = kotlinExt.javaClass.getMethodOrNull("getTargets") ?: return null
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return (getTargets.invoke(kotlinExt) as? NamedDomainObjectContainer<Named>)?.asMap?.values ?: emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Project.getChildProjectByPath(path: String): Project? {
|
||||
var project = this
|
||||
for (name in path.split(":").asSequence().drop(1)) {
|
||||
project = project.childProjects[name] ?: return null
|
||||
}
|
||||
return project
|
||||
}
|
||||
|
||||
private fun Project.getTargets(): Collection<Named>? {
|
||||
val kotlinExt = project.extensions.findByName("kotlin") ?: return null
|
||||
val getTargets = kotlinExt.javaClass.getMethodOrNull("getTargets") ?: return null
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return (getTargets.invoke(kotlinExt) as? NamedDomainObjectContainer<Named>)?.asMap?.values ?: emptyList()
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
<idea-plugin>
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<externalAnnotator language="kotlin" implementationClass="org.jetbrains.android.inspections.lint.AndroidLintExternalAnnotator"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
@@ -1,122 +0,0 @@
|
||||
<idea-plugin xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
<xi:include href="android-lint.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<gotoDeclarationHandler implementation="org.jetbrains.kotlin.android.navigation.KotlinAndroidGotoDeclarationHandler"/>
|
||||
|
||||
<moduleService serviceInterface="org.jetbrains.kotlin.android.synthetic.res.AndroidLayoutXmlFileManager"
|
||||
serviceImplementation="org.jetbrains.kotlin.android.synthetic.idea.res.IDEAndroidLayoutXmlFileManager"/>
|
||||
<psi.treeChangePreprocessor implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidPsiTreeChangePreprocessor"/>
|
||||
<errorHandler implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidExtensionsReportSubmitter"/>
|
||||
<gotoDeclarationHandler implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidGotoDeclarationHandler"/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.android.inspection.IllegalIdentifierInspection"
|
||||
displayName="Illegal Android Identifier"
|
||||
groupName="Kotlin Android"
|
||||
enabledByDefault="true"
|
||||
level="ERROR"/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.android.inspection.TypeParameterFindViewByIdInspection"
|
||||
displayName="Cast can be converted to findViewById with type parameter"
|
||||
groupName="Kotlin Android"
|
||||
enabledByDefault="true"
|
||||
cleanupTool="true"
|
||||
level="WEAK WARNING"
|
||||
language="kotlin" />
|
||||
|
||||
<codeInsight.lineMarkerProvider language="kotlin" implementationClass="org.jetbrains.kotlin.android.KotlinAndroidLineMarkerProvider"/>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.android.intention.KotlinAndroidAddStringResource</className>
|
||||
<category>Kotlin Android</category>
|
||||
</intentionAction>
|
||||
|
||||
<externalProjectDataService implementation="org.jetbrains.kotlin.android.configure.KotlinGradleAndroidModuleModelProjectDataService"/>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.android.intention.AddActivityToManifest</className>
|
||||
<category>Kotlin Android</category>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.android.intention.AddServiceToManifest</className>
|
||||
<category>Kotlin Android</category>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.android.intention.AddBroadcastReceiverToManifest</className>
|
||||
<category>Kotlin Android</category>
|
||||
</intentionAction>
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.android.intention.ImplementParcelableAction</className>
|
||||
<category>Kotlin Android</category>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.android.intention.RemoveParcelableAction</className>
|
||||
<category>Kotlin Android</category>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.android.intention.RedoParcelableAction</className>
|
||||
<category>Kotlin Android</category>
|
||||
</intentionAction>
|
||||
|
||||
<codeInsight.unresolvedReferenceQuickFixProvider
|
||||
implementation="org.jetbrains.kotlin.android.inspection.KotlinAndroidResourceQuickFixProvider"/>
|
||||
|
||||
<lang.foldingBuilder language="kotlin" implementationClass="org.jetbrains.kotlin.android.folding.ResourceFoldingBuilder" />
|
||||
|
||||
<annotator language="kotlin" implementationClass="org.jetbrains.kotlin.android.AndroidResourceReferenceAnnotator" />
|
||||
|
||||
<referencesSearch implementation="org.jetbrains.kotlin.AndroidExtensionsReferenceSearchExecutor"/>
|
||||
|
||||
<externalProjectDataService implementation="org.jetbrains.kotlin.android.configure.KotlinAndroidGradleLibraryDataService"/>
|
||||
<externalProjectDataService implementation="org.jetbrains.kotlin.android.configure.KotlinAndroidGradleMPPModuleDataService"/>
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="org.jetbrains.plugins.gradle">
|
||||
<projectResolve implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidExtensionsProjectResolverExtension"/>
|
||||
<projectResolve implementation="org.jetbrains.kotlin.android.configure.KotlinAndroidMPPGradleProjectResolver"/>
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="org.jetbrains.kotlin">
|
||||
<expressionCodegenExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.IDEAndroidExtensionsExpressionCodegenExtension"/>
|
||||
<storageComponentContainerContributor implementation="org.jetbrains.kotlin.android.synthetic.AndroidExtensionPropertiesComponentContainerContributor"/>
|
||||
<classBuilderFactoryInterceptorExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.IDEAndroidOnDestroyClassBuilderInterceptorExtension"/>
|
||||
<packageFragmentProviderExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.res.IDEAndroidPackageFragmentProviderExtension"/>
|
||||
<simpleNameReferenceExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidSimpleNameReferenceExtension"/>
|
||||
<kotlinIndicesHelperExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidIndicesHelperExtension"/>
|
||||
<gradleProjectImportHandler implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidExtensionsGradleImportHandler"/>
|
||||
|
||||
<quickFixContributor implementation="org.jetbrains.kotlin.android.quickfix.AndroidQuickFixRegistrar"/>
|
||||
<projectConfigurator implementation="org.jetbrains.kotlin.android.configure.KotlinAndroidGradleModuleConfigurator"/>
|
||||
<gradleModelFacade implementation="org.jetbrains.kotlin.android.configure.AndroidGradleModelFacade"/>
|
||||
<completionInformationProvider implementation="org.jetbrains.kotlin.AndroidExtensionsCompletionInformationProvider" />
|
||||
|
||||
<expressionCodegenExtension implementation="org.jetbrains.kotlin.android.parcel.IDEParcelableCodegenExtension"/>
|
||||
<syntheticResolveExtension implementation="org.jetbrains.kotlin.android.parcel.IDEParcelableResolveExtension"/>
|
||||
<classBuilderFactoryInterceptorExtension implementation="org.jetbrains.kotlin.android.synthetic.codegen.ParcelableClinitClassBuilderInterceptorExtension"/>
|
||||
<quickFixContributor implementation="org.jetbrains.kotlin.android.parcel.quickfixes.ParcelableQuickFixContributor"/>
|
||||
|
||||
<androidDexer implementation="org.jetbrains.kotlin.android.debugger.AndroidDexerImpl"/>
|
||||
|
||||
<highlighterExtension implementation="org.jetbrains.kotlin.android.AndroidHighlighterExtension"/>
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="com.android.tools.idea">
|
||||
<npw.template.convertJavaToKotlinProvider implementation="org.jetbrains.kotlin.android.ConvertJavaToKotlinProviderImpl"/>
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="org.jetbrains.android">
|
||||
<androidLintQuickFixProvider implementation="org.jetbrains.kotlin.android.quickfix.KotlinAndroidQuickFixProvider" />
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="com.android.gradle.sync">
|
||||
<postSyncModuleSetupStep implementation="org.jetbrains.kotlin.android.configure.KotlinAndroidModuleSetupStep"/>
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="org.jetbrains.kotlin.android.model">
|
||||
<androidModuleInfoProvider implementation="org.jetbrains.kotlin.android.model.impl.AndroidModuleInfoProviderImpl"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
@@ -1,73 +0,0 @@
|
||||
<idea-plugin xmlns:xi="http://www.w3.org/2001/XInclude" version="2" url="http://kotlinlang.org" allow-bundled-update="true">
|
||||
<id>org.jetbrains.kotlin</id>
|
||||
|
||||
<name>Kotlin</name>
|
||||
<description><![CDATA[
|
||||
The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
|
||||
<br>
|
||||
<a href="http://kotlinlang.org/docs/tutorials/getting-started.html">Getting Started in IntelliJ IDEA</a><br>
|
||||
<a href="http://kotlinlang.org/docs/tutorials/kotlin-android.html">Getting Started in Android Studio</a><br>
|
||||
<a href="http://slack.kotlinlang.org/">Public Slack</a><br>
|
||||
<a href="https://youtrack.jetbrains.com/issues/KT">Issue tracker</a><br>
|
||||
]]></description>
|
||||
<version>@snapshot@</version>
|
||||
<vendor url="http://www.jetbrains.com">JetBrains</vendor>
|
||||
|
||||
<idea-version since-build="173.1" until-build="173.*"/>
|
||||
|
||||
<depends>com.intellij.modules.platform</depends>
|
||||
<depends>com.intellij.modules.androidstudio</depends>
|
||||
|
||||
<!-- required for Kotlin/Native plugin -->
|
||||
<depends optional="true">org.jetbrains.kotlin.native.platform.deps</depends>
|
||||
|
||||
<depends optional="true" config-file="junit.xml">JUnit</depends>
|
||||
<depends optional="true" config-file="gradle.xml">org.jetbrains.plugins.gradle</depends>
|
||||
<depends optional="true" config-file="maven.xml">org.jetbrains.idea.maven</depends>
|
||||
<depends optional="true" config-file="testng-j.xml">TestNG-J</depends>
|
||||
<depends optional="true" config-file="kotlin-copyright.xml">com.intellij.copyright</depends>
|
||||
<depends optional="true" config-file="android.xml">org.jetbrains.android</depends>
|
||||
<depends optional="true" config-file="coverage.xml">Coverage</depends>
|
||||
<depends optional="true" config-file="i18n.xml">com.intellij.java-i18n</depends>
|
||||
<depends optional="true" config-file="injection.xml">org.intellij.intelliLang</depends>
|
||||
<depends optional="true" config-file="decompiler.xml">org.jetbrains.java.decompiler</depends>
|
||||
<depends optional="true" config-file="git4idea.xml">Git4Idea</depends>
|
||||
|
||||
<!-- ULTIMATE-PLUGIN-PLACEHOLDER -->
|
||||
|
||||
<!-- CIDR-PLUGIN-PLACEHOLDER-START -->
|
||||
<depends>com.intellij.modules.java</depends>
|
||||
<depends optional="true" config-file="javaScriptDebug.xml">JavaScriptDebugger</depends>
|
||||
<!-- CIDR-PLUGIN-PLACEHOLDER-END -->
|
||||
|
||||
<xi:include href="plugin-common.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
|
||||
<!-- CIDR-PLUGIN-EXCLUDE-START -->
|
||||
<xi:include href="jvm.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
<!-- CIDR-PLUGIN-EXCLUDE-END -->
|
||||
|
||||
<xi:include href="tipsAndTricks.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
|
||||
<xi:include href="extensions/ide.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
|
||||
<project-components>
|
||||
<component>
|
||||
<!-- This is a workaround for IDEA < 183. For details, see IDEA-200525. -->
|
||||
<implementation-class>org.jetbrains.kotlin.idea.caches.ProjectRootModificationTrackerFixer</implementation-class>
|
||||
</component>
|
||||
</project-components>
|
||||
|
||||
<extensionPoints>
|
||||
<xi:include href="extensions/compiler.xml" xpointer="xpointer(/idea-plugin/extensionPoints/*)"/>
|
||||
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.pluginUpdateVerifier"
|
||||
interface="org.jetbrains.kotlin.idea.update.PluginUpdateVerifier"/>
|
||||
</extensionPoints>
|
||||
|
||||
<xi:include href="plugin-kotlin-extensions.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<projectService serviceInterface="org.jetbrains.kotlin.idea.core.script.ScriptModificationListener"
|
||||
serviceImplementation="org.jetbrains.kotlin.idea.core.script.ScriptModificationListener"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
@@ -1,237 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.actions
|
||||
|
||||
import com.intellij.codeInsight.navigation.NavigationUtil
|
||||
import com.intellij.ide.highlighter.JavaFileType
|
||||
import com.intellij.ide.scratch.ScratchFileService
|
||||
import com.intellij.ide.scratch.ScratchRootType
|
||||
import com.intellij.openapi.actionSystem.AnAction
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys
|
||||
import com.intellij.openapi.command.CommandProcessor
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.ui.MessageType
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import com.intellij.openapi.ui.ex.MessagesEx
|
||||
import com.intellij.openapi.ui.popup.JBPopupFactory
|
||||
import com.intellij.openapi.vfs.VfsUtilCore
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.vfs.VirtualFileVisitor
|
||||
import com.intellij.openapi.wm.WindowManager
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiErrorElement
|
||||
import com.intellij.psi.PsiJavaFile
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.idea.j2k.IdeaJavaToKotlinServices
|
||||
import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor
|
||||
import org.jetbrains.kotlin.idea.refactoring.toPsiFile
|
||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||
import org.jetbrains.kotlin.idea.util.application.progressIndicatorNullable
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.j2k.ConverterSettings
|
||||
import org.jetbrains.kotlin.j2k.JavaToKotlinConverter
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
||||
class JavaToKotlinAction : AnAction() {
|
||||
companion object {
|
||||
private fun uniqueKotlinFileName(javaFile: VirtualFile): String {
|
||||
val ioFile = File(javaFile.path.replace('/', File.separatorChar))
|
||||
|
||||
var i = 0
|
||||
while (true) {
|
||||
val fileName = javaFile.nameWithoutExtension + (if (i > 0) i else "") + ".kt"
|
||||
if (!ioFile.resolveSibling(fileName).exists()) return fileName
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
val title = "Convert Java to Kotlin"
|
||||
|
||||
private fun saveResults(javaFiles: List<PsiJavaFile>, convertedTexts: List<String>): List<VirtualFile> {
|
||||
val result = ArrayList<VirtualFile>()
|
||||
for ((psiFile, text) in javaFiles.zip(convertedTexts)) {
|
||||
try {
|
||||
val document = PsiDocumentManager.getInstance(psiFile.project).getDocument(psiFile)
|
||||
if (document == null) {
|
||||
MessagesEx.error(psiFile.project, "Failed to save conversion result: couldn't find document for " + psiFile.name).showLater()
|
||||
continue
|
||||
}
|
||||
document.replaceString(0, document.textLength, text)
|
||||
FileDocumentManager.getInstance().saveDocument(document)
|
||||
|
||||
val virtualFile = psiFile.virtualFile
|
||||
if (ScratchRootType.getInstance().containsFile(virtualFile)) {
|
||||
val mapping = ScratchFileService.getInstance().scratchesMapping
|
||||
mapping.setMapping(virtualFile, KotlinFileType.INSTANCE.language)
|
||||
}
|
||||
else {
|
||||
val fileName = uniqueKotlinFileName(virtualFile)
|
||||
virtualFile.rename(this, fileName)
|
||||
}
|
||||
}
|
||||
catch (e: IOException) {
|
||||
MessagesEx.error(psiFile.project, e.message ?: "").showLater()
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fun convertFiles(
|
||||
javaFiles: List<PsiJavaFile>, project: Project,
|
||||
enableExternalCodeProcessing: Boolean = true,
|
||||
askExternalCodeProcessing: Boolean = true
|
||||
): List<KtFile> {
|
||||
var converterResult: JavaToKotlinConverter.FilesResult? = null
|
||||
fun convert() {
|
||||
val converter = JavaToKotlinConverter(project, ConverterSettings.defaultSettings, IdeaJavaToKotlinServices)
|
||||
converterResult = converter.filesToKotlin(
|
||||
javaFiles, J2kPostProcessor(formatCode = true),
|
||||
ProgressManager.getInstance().progressIndicatorNullable!!
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(
|
||||
::convert,
|
||||
title,
|
||||
true,
|
||||
project)) return emptyList()
|
||||
|
||||
|
||||
var externalCodeUpdate: (() -> Unit)? = null
|
||||
|
||||
if (enableExternalCodeProcessing && converterResult!!.externalCodeProcessing != null) {
|
||||
val question = "Some code in the rest of your project may require corrections after performing this conversion. Do you want to find such code and correct it too?"
|
||||
if (!askExternalCodeProcessing || (Messages.showOkCancelDialog(project, question, title, Messages.getQuestionIcon()) == Messages.OK)) {
|
||||
ProgressManager.getInstance().runProcessWithProgressSynchronously(
|
||||
{
|
||||
runReadAction {
|
||||
externalCodeUpdate = converterResult!!.externalCodeProcessing!!.prepareWriteOperation(
|
||||
ProgressManager.getInstance().progressIndicatorNullable!!
|
||||
)
|
||||
}
|
||||
},
|
||||
title,
|
||||
true,
|
||||
project)
|
||||
}
|
||||
}
|
||||
|
||||
return project.executeWriteCommand("Convert files from Java to Kotlin", null) {
|
||||
CommandProcessor.getInstance().markCurrentCommandAsGlobal(project)
|
||||
|
||||
val newFiles = saveResults(javaFiles, converterResult!!.results)
|
||||
|
||||
externalCodeUpdate?.invoke()
|
||||
|
||||
PsiDocumentManager.getInstance(project).commitAllDocuments()
|
||||
|
||||
newFiles.singleOrNull()?.let {
|
||||
FileEditorManager.getInstance(project).openFile(it, true)
|
||||
}
|
||||
|
||||
newFiles.map { it.toPsiFile(project) as KtFile }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
val javaFiles = selectedJavaFiles(e).filter { it.isWritable }.toList()
|
||||
val project = CommonDataKeys.PROJECT.getData(e.dataContext)!!
|
||||
|
||||
if (javaFiles.isEmpty()) {
|
||||
val statusBar = WindowManager.getInstance().getStatusBar(project)
|
||||
JBPopupFactory.getInstance()
|
||||
.createHtmlTextBalloonBuilder("Nothing to convert:<br>No writable Java files found", MessageType.ERROR, null)
|
||||
.createBalloon()
|
||||
.showInCenterOf(statusBar.component)
|
||||
}
|
||||
|
||||
val firstSyntaxError = javaFiles.asSequence().map { PsiTreeUtil.findChildOfType(it, PsiErrorElement::class.java) }.firstOrNull()
|
||||
|
||||
if (firstSyntaxError != null) {
|
||||
val count = javaFiles.filter { PsiTreeUtil.hasErrorElements(it) }.count()
|
||||
val question = firstSyntaxError.containingFile.name +
|
||||
(if (count > 1) " and ${count - 1} more Java files" else " file") +
|
||||
" contain syntax errors, the conversion result may be incorrect"
|
||||
|
||||
val okText = "Investigate Errors"
|
||||
val cancelText = "Proceed with Conversion"
|
||||
if (Messages.showOkCancelDialog(
|
||||
project,
|
||||
question,
|
||||
title,
|
||||
okText,
|
||||
cancelText,
|
||||
Messages.getWarningIcon()
|
||||
) == Messages.OK) {
|
||||
NavigationUtil.activateFileWithPsiElement(firstSyntaxError.navigationElement)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
convertFiles(javaFiles, project)
|
||||
}
|
||||
|
||||
override fun update(e: AnActionEvent) {
|
||||
val virtualFiles = e.getData(CommonDataKeys.VIRTUAL_FILE_ARRAY) ?: return
|
||||
val project = e.project ?: return
|
||||
|
||||
e.presentation.isEnabled = isAnyJavaFileSelected(project, virtualFiles)
|
||||
}
|
||||
|
||||
private fun isAnyJavaFileSelected(project: Project, files: Array<VirtualFile>): Boolean {
|
||||
if (files.any { it.isDirectory }) return true // Giving up on directories
|
||||
val manager = PsiManager.getInstance(project)
|
||||
return files.any { it.extension == JavaFileType.DEFAULT_EXTENSION && manager.findFile(it) is PsiJavaFile && it.isWritable }
|
||||
}
|
||||
|
||||
private fun selectedJavaFiles(e: AnActionEvent): Sequence<PsiJavaFile> {
|
||||
val virtualFiles = e.getData(CommonDataKeys.VIRTUAL_FILE_ARRAY) ?: return sequenceOf()
|
||||
val project = e.project ?: return sequenceOf()
|
||||
return allJavaFiles(virtualFiles, project)
|
||||
}
|
||||
|
||||
private fun allJavaFiles(filesOrDirs: Array<VirtualFile>, project: Project): Sequence<PsiJavaFile> {
|
||||
val manager = PsiManager.getInstance(project)
|
||||
return allFiles(filesOrDirs)
|
||||
.asSequence()
|
||||
.mapNotNull { manager.findFile(it) as? PsiJavaFile }
|
||||
}
|
||||
|
||||
private fun allFiles(filesOrDirs: Array<VirtualFile>): Collection<VirtualFile> {
|
||||
val result = ArrayList<VirtualFile>()
|
||||
for (file in filesOrDirs) {
|
||||
VfsUtilCore.visitChildrenRecursively(file, object : VirtualFileVisitor<Unit>() {
|
||||
override fun visitFile(file: VirtualFile): Boolean {
|
||||
result.add(file)
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.reporter
|
||||
|
||||
import com.intellij.diagnostic.ITNReporter
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.diagnostic.IdeaLoggingEvent
|
||||
import com.intellij.openapi.diagnostic.SubmittedReportInfo
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import com.intellij.util.Consumer
|
||||
import org.jetbrains.kotlin.idea.KotlinPluginUpdater
|
||||
import org.jetbrains.kotlin.idea.KotlinPluginUtil
|
||||
import org.jetbrains.kotlin.idea.PluginUpdateStatus
|
||||
import java.awt.Component
|
||||
|
||||
/**
|
||||
* We need to wrap ITNReporter for force showing or errors from kotlin plugin even from released version of IDEA.
|
||||
*/
|
||||
class KotlinReportSubmitter : ITNReporter() {
|
||||
private var hasUpdate = false
|
||||
private var hasLatestVersion = false
|
||||
|
||||
override fun showErrorInRelease(event: IdeaLoggingEvent): Boolean {
|
||||
val notificationEnabled = "disabled" != System.getProperty("kotlin.fatal.error.notification", "enabled")
|
||||
return notificationEnabled && (!hasUpdate || ApplicationManager.getApplication().isInternal)
|
||||
}
|
||||
|
||||
override fun submit(events: Array<IdeaLoggingEvent>, additionalInfo: String?, parentComponent: Component?, consumer: Consumer<SubmittedReportInfo>): Boolean {
|
||||
if (hasUpdate) {
|
||||
if (ApplicationManager.getApplication().isInternal) {
|
||||
return super.submit(events, additionalInfo, parentComponent, consumer)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if (hasLatestVersion) {
|
||||
return super.submit(events, additionalInfo, parentComponent, consumer)
|
||||
}
|
||||
|
||||
KotlinPluginUpdater.getInstance().runUpdateCheck { status ->
|
||||
if (status is PluginUpdateStatus.Update) {
|
||||
hasUpdate = true
|
||||
if (parentComponent != null) {
|
||||
|
||||
if (ApplicationManager.getApplication().isInternal) {
|
||||
super.submit(events, additionalInfo, parentComponent, consumer)
|
||||
}
|
||||
|
||||
val rc = Messages.showDialog(parentComponent,
|
||||
"You're running Kotlin plugin version ${KotlinPluginUtil.getPluginVersion()}, " +
|
||||
"while the latest version is ${status.pluginDescriptor.version}",
|
||||
"Update Kotlin Plugin",
|
||||
arrayOf("Update", "Ignore"),
|
||||
0, Messages.getInformationIcon())
|
||||
if (rc == 0) {
|
||||
KotlinPluginUpdater.getInstance().installPluginUpdate(status)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
hasLatestVersion = true
|
||||
super.submit(events, additionalInfo, parentComponent, consumer)
|
||||
}
|
||||
false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package org.jetbrains.kotlin.idea
|
||||
|
||||
import com.intellij.featureStatistics.FeatureStatisticsBundleProvider
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.extensions.Extensions
|
||||
import java.lang.IllegalStateException
|
||||
|
||||
private const val CIDR_FEATURE_STATISTICS_PROVIDER_FQNAME = "com.jetbrains.cidr.lang.OCFeatureStatisticsBundleProvider"
|
||||
|
||||
fun registerAdditionalResourceBundleInTests() {
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||
return
|
||||
}
|
||||
|
||||
val isAlreadyRegistered = FeatureStatisticsBundleProvider.EP_NAME.extensions.any { provider ->
|
||||
provider.javaClass.name == CIDR_FEATURE_STATISTICS_PROVIDER_FQNAME
|
||||
}
|
||||
if (isAlreadyRegistered) {
|
||||
throw IllegalStateException("Remove this registration for the current platform: bundle is already registered.")
|
||||
}
|
||||
|
||||
val cidrFSBundleProviderClass = try {
|
||||
Class.forName(CIDR_FEATURE_STATISTICS_PROVIDER_FQNAME)
|
||||
} catch (_: ClassNotFoundException) {
|
||||
throw IllegalStateException("Remove this registration for the current platform: class wasn't found.")
|
||||
}
|
||||
|
||||
val cidrFSBundleProvider = cidrFSBundleProviderClass.newInstance() as FeatureStatisticsBundleProvider
|
||||
|
||||
Extensions.getRootArea().getExtensionPoint(FeatureStatisticsBundleProvider.EP_NAME).registerExtension(cidrFSBundleProvider)
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintSupportAnnotationUsageInspection
|
||||
// DEPENDENCY: IntRange.java -> android/support/annotation/IntRange.java
|
||||
// DEPENDENCY: RequiresPermission.java -> android/support/annotation/RequiresPermission.java
|
||||
|
||||
|
||||
import android.support.annotation.IntRange
|
||||
import android.support.annotation.RequiresPermission
|
||||
import android.Manifest
|
||||
import android.view.View
|
||||
|
||||
const val constantVal = 0L
|
||||
|
||||
<error descr="Invalid range: the `from` attribute must be less than the `to` attribute">@IntRange(from = 10, to = 0)</error>
|
||||
fun invalidRange1a(): Int = 5
|
||||
|
||||
@IntRange(from = constantVal, to = 10) // ok
|
||||
fun invalidRange0b(): Int = 5
|
||||
|
||||
<error descr="Invalid range: the `from` attribute must be less than the `to` attribute">@IntRange(from = 10, to = constantVal)</error>
|
||||
fun invalidRange1b(): Int = 5
|
||||
|
||||
|
||||
// should be ok, KT-16600
|
||||
@RequiresPermission(anyOf = arrayOf(Manifest.permission.ACCESS_CHECKIN_PROPERTIES,
|
||||
Manifest.permission.ACCESS_FINE_LOCATION))
|
||||
fun needsPermissions1() { }
|
||||
|
||||
// should be ok, KT-16600
|
||||
@RequiresPermission(Manifest.permission.ACCESS_CHECKIN_PROPERTIES)
|
||||
fun needsPermissions2() { }
|
||||
|
||||
// error
|
||||
<error descr="Only specify one of `value`, `anyOf` or `allOf`">@RequiresPermission(
|
||||
value = Manifest.permission.ACCESS_CHECKIN_PROPERTIES,
|
||||
anyOf = arrayOf(Manifest.permission.ACCESS_CHECKIN_PROPERTIES, Manifest.permission.ACCESS_FINE_LOCATION))</error>
|
||||
fun needsPermissions3() { }
|
||||
@@ -1,14 +0,0 @@
|
||||
// INTENTION_TEXT: Add Parcelable Implementation
|
||||
// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintParcelCreatorInspection
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
class <caret>MissingCreator : Parcelable {
|
||||
override fun writeToParcel(dest: Parcel?, flags: Int) {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun describeContents(): Int {
|
||||
TODO("not implemented")
|
||||
}
|
||||
}
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
// INTENTION_TEXT: Add Parcelable Implementation
|
||||
// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintParcelCreatorInspection
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
class MissingCreator() : Parcelable {
|
||||
constructor(parcel: Parcel) : this() {
|
||||
}
|
||||
|
||||
override fun writeToParcel(dest: Parcel?, flags: Int) {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun describeContents(): Int {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
companion object CREATOR : Parcelable.Creator<MissingCreator> {
|
||||
override fun createFromParcel(parcel: Parcel): MissingCreator {
|
||||
return MissingCreator(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<MissingCreator?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
// INTENTION_TEXT: Add Parcelable Implementation
|
||||
// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintParcelCreatorInspection
|
||||
import android.os.Parcelable
|
||||
|
||||
class <caret>NoImplementation : Parcelable
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
// INTENTION_TEXT: Add Parcelable Implementation
|
||||
// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintParcelCreatorInspection
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
class NoImplementation() : Parcelable {
|
||||
constructor(parcel: Parcel) : this() {
|
||||
}
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun describeContents(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
companion object CREATOR : Parcelable.Creator<NoImplementation> {
|
||||
override fun createFromParcel(parcel: Parcel): NoImplementation {
|
||||
return NoImplementation(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<NoImplementation?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation
|
||||
// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection
|
||||
// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java
|
||||
|
||||
import android.graphics.drawable.VectorDrawable
|
||||
|
||||
class VectorDrawableProvider {
|
||||
companion object {
|
||||
val VECTOR_DRAWABLE = <caret>VectorDrawable()
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
// INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation
|
||||
// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection
|
||||
// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java
|
||||
|
||||
import android.graphics.drawable.VectorDrawable
|
||||
import android.os.Build
|
||||
import android.support.annotation.RequiresApi
|
||||
|
||||
class VectorDrawableProvider {
|
||||
companion object {
|
||||
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
val VECTOR_DRAWABLE = VectorDrawable()
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
// INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation
|
||||
// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection
|
||||
// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java
|
||||
|
||||
import android.graphics.drawable.VectorDrawable
|
||||
|
||||
|
||||
fun withDefaultParameter(vector: VectorDrawable = <caret>VectorDrawable()) {
|
||||
|
||||
}
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
// INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation
|
||||
// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection
|
||||
// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java
|
||||
|
||||
import android.graphics.drawable.VectorDrawable
|
||||
import android.os.Build
|
||||
import android.support.annotation.RequiresApi
|
||||
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
fun withDefaultParameter(vector: VectorDrawable = VectorDrawable()) {
|
||||
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
// INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation
|
||||
// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection
|
||||
// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java
|
||||
|
||||
import android.graphics.drawable.VectorDrawable
|
||||
|
||||
class MyVectorDrawable : <caret>VectorDrawable() {
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
// INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation
|
||||
// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection
|
||||
// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java
|
||||
|
||||
import android.graphics.drawable.VectorDrawable
|
||||
import android.os.Build
|
||||
import android.support.annotation.RequiresApi
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
class MyVectorDrawable : VectorDrawable() {
|
||||
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
// INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation
|
||||
// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection
|
||||
// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java
|
||||
|
||||
import android.graphics.drawable.VectorDrawable
|
||||
|
||||
class VectorDrawableProvider {
|
||||
fun getVectorDrawable(): VectorDrawable {
|
||||
with(this) {
|
||||
return <caret>VectorDrawable()
|
||||
}
|
||||
}
|
||||
}
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
// INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation
|
||||
// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection
|
||||
// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java
|
||||
|
||||
import android.graphics.drawable.VectorDrawable
|
||||
import android.os.Build
|
||||
import android.support.annotation.RequiresApi
|
||||
|
||||
class VectorDrawableProvider {
|
||||
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
fun getVectorDrawable(): VectorDrawable {
|
||||
with(this) {
|
||||
return VectorDrawable()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
// INTENTION_TEXT: Add @RequiresApi(KITKAT) Annotation
|
||||
// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintInlinedApiInspection
|
||||
// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java
|
||||
|
||||
class Test {
|
||||
fun foo(): Int {
|
||||
return android.R.attr.<caret>windowTranslucentStatus
|
||||
}
|
||||
}
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
import android.os.Build
|
||||
import android.support.annotation.RequiresApi
|
||||
|
||||
// INTENTION_TEXT: Add @RequiresApi(KITKAT) Annotation
|
||||
// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintInlinedApiInspection
|
||||
// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java
|
||||
|
||||
class Test {
|
||||
@RequiresApi(Build.VERSION_CODES.KITKAT)
|
||||
fun foo(): Int {
|
||||
return android.R.attr.windowTranslucentStatus
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation
|
||||
// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection
|
||||
// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java
|
||||
|
||||
import android.graphics.drawable.VectorDrawable
|
||||
|
||||
class VectorDrawableProvider {
|
||||
fun getVectorDrawable(): VectorDrawable {
|
||||
return <caret>VectorDrawable()
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user