diff --git a/kotlin-bundled-compiler/META-INF/MANIFEST.MF b/kotlin-bundled-compiler/META-INF/MANIFEST.MF index d9e6d721f..c6b1613ee 100644 --- a/kotlin-bundled-compiler/META-INF/MANIFEST.MF +++ b/kotlin-bundled-compiler/META-INF/MANIFEST.MF @@ -222,6 +222,7 @@ Export-Package: com.google.common.collect, kotlin.script.templates.standard, kotlin.sequences, kotlin.text, + kotlinx.coroutines, org.jetbrains.annotations, org.jetbrains.kotlin, org.jetbrains.kotlin.analyzer, diff --git a/kotlin-bundled-compiler/build.gradle.kts b/kotlin-bundled-compiler/build.gradle.kts index b1b9c6fbc..b52150e8f 100644 --- a/kotlin-bundled-compiler/build.gradle.kts +++ b/kotlin-bundled-compiler/build.gradle.kts @@ -2,7 +2,6 @@ import com.intellij.buildsupport.dependencies.PackageListFromSimpleFile import com.intellij.buildsupport.resolve.http.HttpArtifact import com.intellij.buildsupport.resolve.http.HttpArtifactsResolver import com.intellij.buildsupport.resolve.http.idea.IntellijIdeaArtifactsResolver -import com.intellij.buildsupport.resolve.tc.kotlin.KotlinCompilerTCArtifactsResolver import com.intellij.buildsupport.utils.FileUtils apply(plugin = "base") @@ -78,6 +77,12 @@ tasks.named("clean") { } } +val deleteLibrariesFromLibFolder by tasks.registering { + doFirst { + libDir.listFiles()?.filter { it.isFile }?.forEach { it.deleteRecursively() } + } +} + val downloadTestData by tasks.registering { val locallyDownloadedTestDataFile by extra { if(localTCArtifacts){ @@ -106,9 +111,12 @@ val downloadTestFrameworkDependencies by tasks.registering(Copy::class) { } val downloadKotlinCompilerPluginAndExtractSelectedJars by tasks.registering { + dependsOn(deleteLibrariesFromLibFolder) + + val kotlinDownloadDir = file("$downloadDir/kotlin-$kotlinCompilerVersion/$kotlinIdeaCompatibleVersionMinor") val locallyDownloadedCompilerFile by extra { - file(downloadDir).listFiles()?.firstOrNull { it.name.startsWith("kotlin-plugin-") } - ?: file("$downloadDir/kotlin-plugin.zip") + file(kotlinDownloadDir).listFiles()?.firstOrNull { it.name.startsWith("kotlin-plugin-") } + ?: file("$kotlinDownloadDir/kotlin-plugin.zip") } doLast { @@ -172,7 +180,9 @@ val extractPackagesFromPlugin by tasks.registering(Jar::class) { } val downloadIntellijCoreAndExtractSelectedJars by tasks.registering { - val locallyDownloadedIntellijCoreFile by extra { file("$downloadDir/intellij-core.zip") } + dependsOn(deleteLibrariesFromLibFolder) + val ideaDownloadDir = file("$downloadDir/idea-$ideaVersion") + val locallyDownloadedIntellijCoreFile by extra { file("$ideaDownloadDir/intellij-core.zip") } doLast { if(!locallyDownloadedIntellijCoreFile.exists()) { @@ -191,7 +201,9 @@ val downloadIntellijCoreAndExtractSelectedJars by tasks.registering { } val downloadIdeaDistributionZipAndExtractSelectedJars by tasks.registering { - val locallyDownloadedIdeaZipFile by extra { file("$downloadDir/ideaIC.zip") } + dependsOn(deleteLibrariesFromLibFolder) + val ideaDownloadDir = file("$downloadDir/idea-$ideaVersion") + val locallyDownloadedIdeaZipFile by extra { file("$ideaDownloadDir/ideaIC.zip") } val chosenJars by extra { setOf(//"openapi", "platform-util-ui", "util", @@ -301,14 +313,12 @@ val repackageIdeaAndKotlinCompilerSources by tasks.registering(Zip::class) { val downloadBundled by tasks.registering { if (localTCArtifacts) { - dependsOn(downloadKotlinCompilerPluginAndExtractSelectedJars, - extractPackagesFromPlugin, + dependsOn(extractPackagesFromPlugin, downloadIntellijCoreAndExtractSelectedJars, createIdeDependenciesJar, downloadKotlinxLibraries) } else { - dependsOn(downloadKotlinCompilerPluginAndExtractSelectedJars, - extractPackagesFromPlugin, + dependsOn(extractPackagesFromPlugin, downloadIntellijCoreAndExtractSelectedJars, createIdeDependenciesJar, downloadKotlinxLibraries) diff --git a/kotlin-bundled-compiler/buildSrc/build.gradle b/kotlin-bundled-compiler/buildSrc/build.gradle index 2cb2b707b..eef3ad4cd 100644 --- a/kotlin-bundled-compiler/buildSrc/build.gradle +++ b/kotlin-bundled-compiler/buildSrc/build.gradle @@ -9,14 +9,14 @@ repositories { dependencies { - compile 'org.jetbrains.teamcity:teamcity-rest-client:1.5.0' + implementation 'org.jetbrains.teamcity:teamcity-rest-client:1.5.0' - testCompile('org.spockframework.spock:spock-core:spock-1.3') { + testImplementation('org.spockframework.spock:spock-core:spock-1.3') { exclude module : 'groovy-all' } - testCompile 'com.github.stefanbirkner:system-rules:1.19.0' - testCompile 'org.apache.commons:commons-lang3:3.8.1' + testImplementation 'com.github.stefanbirkner:system-rules:1.19.0' + testImplementation 'org.apache.commons:commons-lang3:3.8.1' } -test.enabled = false // otherwise integration tests will run always before the actual build \ No newline at end of file +test.enabled = false // otherwise integration tests will run always before the actual build diff --git a/kotlin-bundled-compiler/buildSrc/src/main/groovy/com/intellij/buildsupport/resolve/http/HttpArtifactsResolver.groovy b/kotlin-bundled-compiler/buildSrc/src/main/groovy/com/intellij/buildsupport/resolve/http/HttpArtifactsResolver.groovy index 46a385ff4..8df349e85 100644 --- a/kotlin-bundled-compiler/buildSrc/src/main/groovy/com/intellij/buildsupport/resolve/http/HttpArtifactsResolver.groovy +++ b/kotlin-bundled-compiler/buildSrc/src/main/groovy/com/intellij/buildsupport/resolve/http/HttpArtifactsResolver.groovy @@ -26,7 +26,7 @@ class HttpArtifactsResolver { destinationFile.parentFile.mkdirs() def ant = new AntBuilder() - if (!proxyProps.isEmpty()) { + if (!proxyProps.isEmpty() && proxyProps['https.proxyHost'] != null) { if (proxyProps.get("https.proxyUser") == null) { ant.setproxy(proxyHost: proxyProps['https.proxyHost'], proxyPort: proxyProps['https.proxyPort']) } else { diff --git a/kotlin-bundled-compiler/gradle/wrapper/gradle-wrapper.properties b/kotlin-bundled-compiler/gradle/wrapper/gradle-wrapper.properties index 4b7e1f3d3..442d9132e 100644 --- a/kotlin-bundled-compiler/gradle/wrapper/gradle-wrapper.properties +++ b/kotlin-bundled-compiler/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/asJava/KotlinLightClassGeneration.kt b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/asJava/KotlinLightClassGeneration.kt index e4fa9b22c..72126b6fd 100644 --- a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/asJava/KotlinLightClassGeneration.kt +++ b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/asJava/KotlinLightClassGeneration.kt @@ -78,8 +78,8 @@ object KotlinLightClassGeneration { return checkByInternalName(internalName, requestedClassName) } - override fun shouldGeneratePackagePart(jetFile: KtFile): Boolean { - val internalName = JvmFileClassUtil.getFileClassInternalName(jetFile) + override fun shouldGeneratePackagePart(ktFile: KtFile): Boolean { + val internalName = JvmFileClassUtil.getFileClassInternalName(ktFile) return checkByInternalName(internalName, requestedClassName) } diff --git a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/asJava/LightClassBuilderFactory.java b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/asJava/LightClassBuilderFactory.java index 6e1d69928..5b871cd7b 100644 --- a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/asJava/LightClassBuilderFactory.java +++ b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/asJava/LightClassBuilderFactory.java @@ -54,10 +54,10 @@ private void saveJvmSignature(@NotNull JvmDeclarationOrigin origin, @NotNull Str if (element != null) { Set> userData = element.getUserData(JVM_SIGNATURE); if (userData == null) { - userData = Collections.newSetFromMap(new ConcurrentHashMap, Boolean>()); + userData = Collections.newSetFromMap(new ConcurrentHashMap<>()); element.putUserData(JVM_SIGNATURE, userData); } - userData.add(new Pair(desc, name)); + userData.add(new Pair<>(desc, name)); } } }; @@ -77,4 +77,4 @@ public byte[] asBytes(ClassBuilder builder) { @Override public void close() { } -} \ No newline at end of file +} diff --git a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/compiler/KotlinCompiler.kt b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/compiler/KotlinCompiler.kt index 1738d87ea..f1a08a618 100644 --- a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/compiler/KotlinCompiler.kt +++ b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/compiler/KotlinCompiler.kt @@ -4,68 +4,74 @@ import com.intellij.openapi.util.Disposer import org.eclipse.jdt.core.IJavaProject import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments -import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSourceLocation import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.ERROR import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.EXCEPTION +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSourceLocation import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.config.JVMConfigurationKeys +import org.jetbrains.kotlin.core.filesystem.KotlinLightClassManager.Companion.KOTLIN_TOUCHED_FILES_FILE_NAME import org.jetbrains.kotlin.core.launch.CompilerOutputData import org.jetbrains.kotlin.core.launch.CompilerOutputParser import org.jetbrains.kotlin.core.launch.KotlinCLICompiler import org.jetbrains.kotlin.core.model.KOTLIN_COMPILER_PATH import org.jetbrains.kotlin.core.model.KotlinEnvironment import org.jetbrains.kotlin.core.preferences.CompilerPlugin +import org.jetbrains.kotlin.core.utils.DependencyResolverException import org.jetbrains.kotlin.core.utils.ProjectUtils import org.jetbrains.kotlin.incremental.makeIncrementally -import java.io.BufferedReader -import java.io.ByteArrayOutputStream -import java.io.File -import java.io.PrintStream -import java.io.Reader -import java.io.StringReader +import java.io.* object KotlinCompiler { private fun compileKotlinFiles( - javaProject: IJavaProject, - compilation: (IJavaProject, File, List) -> KotlinCompilerResult + javaProject: IJavaProject, + compilation: (IJavaProject, File, List) -> KotlinCompilerResult ): KotlinCompilerResult = - ProjectUtils.getSrcOutDirectories(javaProject) - .groupingBy { it.second }.fold(mutableListOf()) { list, key -> - list.apply { add(key.first) } - }.map { (out, sources) -> - compilation(javaProject, out, sources) - }.fold(KotlinCompilerResult(true, CompilerOutputData())) { previous, current -> - KotlinCompilerResult(previous.result and current.result, CompilerOutputData().apply { - previous.compilerOutput.list.union(current.compilerOutput.list).forEach { - add(it.messageSeverity, it.message, it.messageLocation) + ProjectUtils.getSrcOutDirectories(javaProject) + .groupingBy { it.second }.fold(mutableListOf()) { list, key -> + list.apply { add(key.first) } + }.onEach { (out) -> + val tempFile = File(out, KOTLIN_TOUCHED_FILES_FILE_NAME).takeIf { it.exists() } + tempFile?.readLines()?.map(::File)?.flatMap { tempFileToDelete -> + val tempName = tempFileToDelete.nameWithoutExtension + val tempFiles = tempFileToDelete.parentFile?.listFiles(FileFilter { it.name == "$tempName.class" || (it.name.startsWith("$tempName$") && it.name.endsWith(".class")) }) + tempFiles?.toList() ?: emptyList() + }?.distinct()?.forEach(File::delete) + tempFile?.delete() + out.walkTopDown().filter { it.extension == "kt" }.forEach { it.delete() } + }.map { (out, sources) -> + compilation(javaProject, out, sources) + }.fold(KotlinCompilerResult(true, CompilerOutputData())) { previous, current -> + KotlinCompilerResult(previous.result and current.result, CompilerOutputData().apply { + previous.compilerOutput.list.union(current.compilerOutput.list).forEach { + add(it.messageSeverity, it.message, it.messageLocation) + } + }) } - }) - } @JvmStatic fun compileKotlinFiles(javaProject: IJavaProject): KotlinCompilerResult = - compileKotlinFiles(javaProject) { project, path, sources -> - execKotlinCompiler(configureCompilerArguments(project, path.absolutePath, sources)) - } + compileKotlinFiles(javaProject) { project, path, sources -> + execKotlinCompiler(configureCompilerArguments(project, path.absolutePath, sources)) + } @JvmStatic fun compileIncrementallyFiles( - javaProject: IJavaProject + javaProject: IJavaProject ): KotlinCompilerResult = - compileKotlinFiles(javaProject) { project, path, sources -> - execIncrementalKotlinCompiler(project, path.absoluteFile, sources) - } + compileKotlinFiles(javaProject) { project, path, sources -> + execIncrementalKotlinCompiler(project, path.absoluteFile, sources) + } private fun execIncrementalKotlinCompiler( - javaProject: IJavaProject, - outputDir: File, - sourceDirs: List + javaProject: IJavaProject, + outputDir: File, + sourceDirs: List ): KotlinCompilerResult { val arguments = getCompilerArguments(javaProject, outputDir) val messageCollector = CompilerMessageCollector() @@ -76,7 +82,7 @@ object KotlinCompiler { put(CLIConfigurationKeys.INTELLIJ_PLUGIN_ROOT, KOTLIN_COMPILER_PATH) } KotlinCoreEnvironment.getOrCreateApplicationEnvironmentForProduction(disposable, config) - var cacheDir = File("${outputDir.parentFile.absolutePath}/cache").also { it.mkdirs() } + val cacheDir = File("${outputDir.parentFile.absolutePath}/cache").also { it.mkdirs() } makeIncrementally(cacheDir, sourceDirs, arguments, messageCollector) return messageCollector.getCompilerResult() } @@ -88,7 +94,7 @@ object KotlinCompiler { private fun getCompilerArguments(javaProject: IJavaProject, outputDir: File) = K2JVMCompilerArguments().apply { val kotlinProperties = - KotlinEnvironment.getEnvironment(javaProject.project).compilerProperties + KotlinEnvironment.getEnvironment(javaProject.project).compilerProperties kotlinHome = ProjectUtils.ktHome destination = outputDir.absolutePath @@ -122,17 +128,20 @@ object KotlinCompiler { pluginClasspaths = pluginClasspathsList.toTypedArray() pluginOptions = pluginOptionsList.toTypedArray() - classpath = ProjectUtils.collectClasspathWithDependenciesForLaunch(javaProject, jdkUndefined) - .joinToString(separator = System.getProperty("path.separator")) { it.absolutePath } - + val tempFiles = try { + ProjectUtils.collectClasspathWithDependenciesForLaunch(javaProject, jdkUndefined) + } catch (e: DependencyResolverException) { + e.resolvedFiles + } + classpath = tempFiles.joinToString(separator = System.getProperty("path.separator")) { it.absolutePath } } private fun configureCompilerArguments( - javaProject: IJavaProject, outputDir: String, sourceDirs: List + javaProject: IJavaProject, outputDir: String, sourceDirs: List ): Array = with(mutableListOf()) { val kotlinProperties = - KotlinEnvironment.getEnvironment(javaProject.project).compilerProperties + KotlinEnvironment.getEnvironment(javaProject.project).compilerProperties add("-kotlin-home") add(ProjectUtils.ktHome) @@ -164,9 +173,14 @@ object KotlinCompiler { } add("-classpath") - ProjectUtils.collectClasspathWithDependenciesForLaunch(javaProject, jdkUndefined) - .joinToString(separator = System.getProperty("path.separator")) { it.absolutePath } - .let { add(it) } + + val tempFiles = try { + ProjectUtils.collectClasspathWithDependenciesForLaunch(javaProject, jdkUndefined) + } catch (e: DependencyResolverException) { + e.resolvedFiles + } + + add(tempFiles.joinToString(separator = System.getProperty("path.separator")) { it.absolutePath }) add("-d") add(outputDir) @@ -194,9 +208,9 @@ object KotlinCompiler { val compilerOutput = CompilerOutputData() override fun report( - severity: CompilerMessageSeverity, - message: String, - location: CompilerMessageSourceLocation? + severity: CompilerMessageSeverity, + message: String, + location: CompilerMessageSourceLocation? ) { hasErrors == hasErrors || severity.isError severities.add(severity) @@ -215,7 +229,7 @@ object KotlinCompiler { } fun getCompilerResult(): KotlinCompilerResult = - KotlinCompilerResult(severities.firstOrNull { it == ERROR || it == EXCEPTION } == null, compilerOutput) + KotlinCompilerResult(severities.firstOrNull { it == ERROR || it == EXCEPTION } == null, compilerOutput) } private fun parseCompilerOutput(reader: Reader): KotlinCompilerResult { diff --git a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/compiler/KotlinCompilerUtils.java b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/compiler/KotlinCompilerUtils.java deleted file mode 100644 index 3286b9d2a..000000000 --- a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/compiler/KotlinCompilerUtils.java +++ /dev/null @@ -1,46 +0,0 @@ -/******************************************************************************* - * Copyright 2010-2014 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.core.compiler; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; -import org.eclipse.debug.core.DebugPlugin; -import org.eclipse.debug.core.IStatusHandler; -import org.eclipse.jdt.core.IJavaProject; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.kotlin.core.Activator; -import org.jetbrains.kotlin.core.launch.CompilerOutputData; - -public class KotlinCompilerUtils { - - public static KotlinCompilerResult compileWholeProject(@NotNull IJavaProject javaProject) throws CoreException { - return KotlinCompiler.compileKotlinFiles(javaProject); - } - - public static KotlinCompilerResult compileProjectIncrementally(@NotNull IJavaProject javaProject) { - return KotlinCompiler.compileIncrementallyFiles(javaProject); - } - - public static void handleCompilerOutput(@NotNull CompilerOutputData compilerOutput) throws CoreException { - IStatus status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, 1, "", null); - IStatusHandler handler = DebugPlugin.getDefault().getStatusHandler(status); - - if (handler != null) { - handler.handleStatus(status, compilerOutput); - } - } -} diff --git a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/compiler/KotlinCompilerUtils.kt b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/compiler/KotlinCompilerUtils.kt new file mode 100644 index 000000000..6c57deec4 --- /dev/null +++ b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/compiler/KotlinCompilerUtils.kt @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright 2010-2014 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.core.compiler + +import org.jetbrains.kotlin.core.compiler.KotlinCompiler.compileKotlinFiles +import org.jetbrains.kotlin.core.compiler.KotlinCompiler.compileIncrementallyFiles +import org.eclipse.core.runtime.CoreException +import org.eclipse.jdt.core.IJavaProject +import org.eclipse.core.runtime.IStatus +import org.eclipse.core.runtime.Status +import org.eclipse.debug.core.IStatusHandler +import org.eclipse.debug.core.DebugPlugin +import org.jetbrains.kotlin.core.Activator +import org.jetbrains.kotlin.core.launch.CompilerOutputData + +object KotlinCompilerUtils { + + fun compileWholeProject(javaProject: IJavaProject): KotlinCompilerResult = compileKotlinFiles(javaProject) + + fun compileProjectIncrementally(javaProject: IJavaProject): KotlinCompilerResult = + compileIncrementallyFiles(javaProject) + + fun handleCompilerOutput(compilerOutput: CompilerOutputWithProject) { + val status: IStatus = Status(IStatus.ERROR, Activator.PLUGIN_ID, 1, "", null) + val handler = DebugPlugin.getDefault().getStatusHandler(status) + handler?.handleStatus(status, compilerOutput) + } + + data class CompilerOutputWithProject(val data: CompilerOutputData, val project: IJavaProject) +} \ No newline at end of file diff --git a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/filesystem/KotlinLightClassManager.java b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/filesystem/KotlinLightClassManager.java deleted file mode 100644 index 5a599c8bb..000000000 --- a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/filesystem/KotlinLightClassManager.java +++ /dev/null @@ -1,307 +0,0 @@ -package org.jetbrains.kotlin.core.filesystem; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import org.eclipse.core.internal.jobs.JobStatus; -import org.eclipse.core.resources.IContainer; -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IFolder; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.ResourcesPlugin; -import org.eclipse.core.resources.WorkspaceJob; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IPath; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Path; -import org.eclipse.jdt.internal.core.util.LRUCache; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.kotlin.core.asJava.LightClassFile; -import org.jetbrains.kotlin.core.builder.KotlinPsiManager; -import org.jetbrains.kotlin.core.log.KotlinLogger; -import org.jetbrains.kotlin.core.model.KotlinEnvironment; -import org.jetbrains.kotlin.core.model.KotlinJavaManager; -import org.jetbrains.kotlin.core.utils.ProjectUtils; -import org.jetbrains.kotlin.fileClasses.FileClasses; -import org.jetbrains.kotlin.fileClasses.NoResolveFileClassesProvider; -import org.jetbrains.kotlin.name.FqName; -import org.jetbrains.kotlin.psi.KtClassOrObject; -import org.jetbrains.kotlin.psi.KtFile; -import org.jetbrains.kotlin.psi.KtNamedFunction; -import org.jetbrains.kotlin.psi.KtProperty; -import org.jetbrains.kotlin.psi.KtSecondaryConstructor; -import org.jetbrains.kotlin.psi.KtVisitorVoid; - -import com.intellij.openapi.components.ServiceManager; -import com.intellij.openapi.project.Project; -import com.intellij.psi.PsiElement; -import com.intellij.psi.util.PsiTreeUtil; - -public class KotlinLightClassManager { - private static final int LIGHT_CLASSES_CACHE_SIZE = 300; - - private static final String WORKSPACE_JOB_ID = "updateLightClassesJob"; - - private final LRUCache cachedLightClasses = new LRUCache(LIGHT_CLASSES_CACHE_SIZE); - - private final IProject project; - - private final ConcurrentMap> sourceFiles = new ConcurrentHashMap<>(); - - @NotNull - public static KotlinLightClassManager getInstance(@NotNull IProject project) { - Project ideaProject = KotlinEnvironment.Companion.getEnvironment(project).getProject(); - return ServiceManager.getService(ideaProject, KotlinLightClassManager.class); - } - - public KotlinLightClassManager(@NotNull IProject project) { - this.project = project; - } - - @Nullable - public synchronized byte[] getCachedLightClass(File file) { - Object lightClass = cachedLightClasses.get(file); - if (lightClass != null) return (byte[]) lightClass; - - return null; - } - - public synchronized void cacheLightClass(File file, @NotNull byte[] lightClass) { - cachedLightClasses.put(file, lightClass); - } - - public synchronized void removeLightClass(@NotNull File file) { - cachedLightClasses.flush(file); - } - - public void computeLightClassesSources() { - Map> newSourceFilesMap = new HashMap<>(); - for (IFile sourceFile : KotlinPsiManager.INSTANCE.getFilesByProject(project)) { - List lightClassesPaths = getLightClassesPaths(sourceFile); - - for (IPath path : lightClassesPaths) { - LightClassFile lightClassFile = new LightClassFile(project.getFile(path)); - - Set newSourceFiles = newSourceFilesMap.get(lightClassFile.asFile()); - if (newSourceFiles == null) { - newSourceFiles = new HashSet<>(); - newSourceFilesMap.put(lightClassFile.asFile(), newSourceFiles); - } - newSourceFiles.add(sourceFile); - } - } - - sourceFiles.clear(); - sourceFiles.putAll(newSourceFilesMap); - } - - public void updateLightClasses(@NotNull Set affectedFiles, Boolean resourceTreeBlocked) { - List toCreate = new ArrayList<>(); - List toRemove = new ArrayList<>(); - for (Map.Entry> entry : sourceFiles.entrySet()) { - IFile lightClassIFile = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(entry.getKey().getPath())); - if (lightClassIFile == null) continue; - - LightClassFile lightClassFile = new LightClassFile(lightClassIFile); - - if (!lightClassFile.exists()) { - toCreate.add(lightClassFile); - } - - for (IFile sourceFile : entry.getValue()) { - if (affectedFiles.contains(sourceFile)) { - toRemove.add(lightClassFile); - break; - } - } - } - if (resourceTreeBlocked) { - if (!toCreate.isEmpty() || !toRemove.isEmpty()) { - WorkspaceJob job = new WorkspaceJob(WORKSPACE_JOB_ID) { - @Override - public IStatus runInWorkspace(IProgressMonitor monitor) { - monitor.beginTask("Light class generation started", 0); - updateLightClasses(toCreate, toRemove); - monitor.done(); - return new JobStatus(0, this, "Light classes generation finished"); - } - }; - job.setRule(ResourcesPlugin.getWorkspace().getRuleFactory().createRule( - project.getFolder(KotlinJavaManager.KOTLIN_BIN_FOLDER))); - job.schedule(); - } - } else { - updateLightClasses(toCreate, toRemove); - } - } - - private void updateLightClasses(List toCreate, List toRemove) { - for (LightClassFile lightClassFile: toCreate) { - createParentDirsFor(lightClassFile); - lightClassFile.createIfNotExists(); - } - for (LightClassFile lightClassFile: toRemove) { - removeLightClass(lightClassFile.asFile()); - lightClassFile.touchFile(); - } - cleanOutdatedLightClasses(project); - } - - public List getSourceFiles(@NotNull File file) { - if (sourceFiles.isEmpty()) { - computeLightClassesSources(); - } - - return getSourceKtFiles(file); - } - - @Nullable - public static String getInternalName(KtClassOrObject classOrObject) { - FqName fullFqName = classOrObject.getFqName(); - if (fullFqName == null) return null; - - KtClassOrObject topmostClassOrObject = PsiTreeUtil.getTopmostParentOfType(classOrObject, KtClassOrObject.class); - if (topmostClassOrObject == null) return makeInternalByToplevel(fullFqName); - - FqName topLevelFqName = topmostClassOrObject.getFqName(); - if (topLevelFqName == null) return null; - - String nestedPart = fullFqName.asString().substring(topLevelFqName.asString().length()).replaceAll("\\.", "\\$"); - - return makeInternalByToplevel(topLevelFqName) + nestedPart; - } - - private static String makeInternalByToplevel(FqName fqName) { - return fqName.asString().replaceAll("\\.", "/"); - } - - @NotNull - private List getSourceKtFiles(@NotNull File lightClass) { - Set sourceIOFiles = sourceFiles.get(lightClass); - if (sourceIOFiles != null) { - List jetSourceFiles = new ArrayList<>(); - for (IFile sourceFile : sourceIOFiles) { - KtFile jetFile = KotlinPsiManager.getKotlinParsedFile(sourceFile); - if (jetFile != null) { - jetSourceFiles.add(jetFile); - } - } - - return jetSourceFiles; - } - - return Collections.emptyList(); - } - - @NotNull - private List getLightClassesPaths(@NotNull IFile sourceFile) { - List lightClasses = new ArrayList(); - - KtFile ktFile = KotlinPsiManager.INSTANCE.getParsedFile(sourceFile); - for (KtClassOrObject classOrObject : findLightClasses(ktFile)) { - String internalName = getInternalName(classOrObject); - if (internalName != null) { - lightClasses.add(computePathByInternalName(internalName)); - } - } - - if (ktFile.hasTopLevelCallables()) { - String newFacadeInternalName = FileClasses.getFileClassInternalName( - NoResolveFileClassesProvider.INSTANCE, ktFile); - lightClasses.add(computePathByInternalName(newFacadeInternalName)); - } - - return lightClasses; - } - - private List findLightClasses(@NotNull KtFile ktFile) { - final ArrayList lightClasses = new ArrayList(); - ktFile.acceptChildren(new KtVisitorVoid() { - @Override - public void visitClassOrObject(@NotNull KtClassOrObject classOrObject) { - lightClasses.add(classOrObject); - super.visitClassOrObject(classOrObject); - } - - @Override - public void visitNamedFunction(@NotNull KtNamedFunction function) { - } - - @Override - public void visitSecondaryConstructor(@NotNull KtSecondaryConstructor constructor) { - } - - @Override - public void visitProperty(@NotNull KtProperty property) { - } - - @Override - public void visitElement(@Nullable PsiElement element) { - if (element != null) { - element.acceptChildren(this); - } - } - }); - return lightClasses; - } - - private IPath computePathByInternalName(String internalName) { - Path relativePath = new Path(internalName + ".class"); - return KotlinJavaManager.KOTLIN_BIN_FOLDER.append(relativePath); - } - - private void cleanOutdatedLightClasses(IProject project) { - ProjectUtils.cleanFolder(KotlinJavaManager.INSTANCE.getKotlinBinFolderFor(project), resource -> { - if (resource instanceof IFile) { - IFile eclipseFile = (IFile) resource; - LightClassFile lightClass = new LightClassFile(eclipseFile); - Set sources = sourceFiles.get(lightClass.asFile()); - - boolean dropLightClass = sources == null || sources.isEmpty(); - if (dropLightClass) { - removeLightClass(lightClass.asFile()); - } - - return dropLightClass; - } else if (resource instanceof IFolder) { - try { - return ((IFolder) resource).members().length == 0; - } catch (CoreException e) { - KotlinLogger.logAndThrow(e); - } - } - - return false; - }); - } - - private void createParentDirsFor(@NotNull LightClassFile lightClassFile) { - IFolder parent = (IFolder) lightClassFile.getResource().getParent(); - if (parent != null && !parent.exists()) { - createParentDirs(parent); - } - } - - private void createParentDirs(IFolder folder) { - IContainer parent = folder.getParent(); - if (!parent.exists()) { - createParentDirs((IFolder) parent); - } - - try { - folder.create(true, true, null); - } catch (CoreException e) { - KotlinLogger.logAndThrow(e); - } - } -} \ No newline at end of file diff --git a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/filesystem/KotlinLightClassManager.kt b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/filesystem/KotlinLightClassManager.kt new file mode 100644 index 000000000..7b8f1d56d --- /dev/null +++ b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/filesystem/KotlinLightClassManager.kt @@ -0,0 +1,261 @@ +package org.jetbrains.kotlin.core.filesystem + +import com.intellij.openapi.components.ServiceManager +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElement +import com.intellij.psi.util.PsiTreeUtil +import org.eclipse.core.internal.jobs.JobStatus +import org.eclipse.core.resources.* +import org.eclipse.core.runtime.* +import org.eclipse.jdt.core.JavaCore +import org.eclipse.jdt.internal.core.util.LRUCache +import org.jetbrains.kotlin.core.asJava.LightClassFile +import org.jetbrains.kotlin.core.builder.KotlinPsiManager.getFilesByProject +import org.jetbrains.kotlin.core.builder.KotlinPsiManager.getKotlinParsedFile +import org.jetbrains.kotlin.core.builder.KotlinPsiManager.getParsedFile +import org.jetbrains.kotlin.core.log.KotlinLogger.logAndThrow +import org.jetbrains.kotlin.core.model.KotlinEnvironment.Companion.getEnvironment +import org.jetbrains.kotlin.core.model.KotlinJavaManager +import org.jetbrains.kotlin.core.model.KotlinJavaManager.getKotlinBinFolderFor +import org.jetbrains.kotlin.core.utils.ProjectUtils.cleanFolder +import org.jetbrains.kotlin.core.utils.ProjectUtils.getAllOutputFolders +import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.* +import java.io.File +import java.io.IOException +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.ConcurrentMap +import kotlin.text.Charsets.UTF_8 + +class KotlinLightClassManager(private val project: IProject) { + private val cachedLightClasses = LRUCache(LIGHT_CLASSES_CACHE_SIZE) + private val sourceFiles: ConcurrentMap> = ConcurrentHashMap() + + @Synchronized + fun getCachedLightClass(file: File): ByteArray? { + val lightClass: Any? = cachedLightClasses[file] + return if (lightClass != null) lightClass as ByteArray? else null + } + + @Synchronized + fun cacheLightClass(file: File, lightClass: ByteArray) { + cachedLightClasses.put(file, lightClass) + } + + @Synchronized + fun removeLightClass(file: File) { + cachedLightClasses.flush(file) + val tempFolders = getAllOutputFolders(JavaCore.create(project)) + val tempSegments = file.path.split("[/\\\\]".toRegex()).toTypedArray() + val tempRealPath = tempSegments.copyOfRange(3, tempSegments.size) + for (tempFolder in tempFolders) { + val tempRootFolder = tempFolder.location.toFile() + var tempCurrentFolder = tempRootFolder + for ((tempIndex, tempSegment) in tempRealPath.withIndex()) { + if (tempIndex == tempRealPath.lastIndex) { + val tempFile = File(tempCurrentFolder, tempSegment).takeIf { it.exists() } ?: break + val tempTouchedFilesFile = File(tempRootFolder, KOTLIN_TOUCHED_FILES_FILE_NAME) + try { + if (!tempTouchedFilesFile.exists()) tempTouchedFilesFile.createNewFile() + + val tempLines = tempTouchedFilesFile.readLines(UTF_8).toMutableSet() + tempLines.add(tempFile.absolutePath) + tempTouchedFilesFile.writeText(tempLines.joinToString("\n"), UTF_8) + } catch (e: IOException) { + e.printStackTrace() + } + } else { + tempCurrentFolder = File(tempCurrentFolder, tempSegment).takeIf { it.exists() } ?: break + } + } + } + } + + fun computeLightClassesSources() { + val newSourceFilesMap: MutableMap> = HashMap() + for (sourceFile in getFilesByProject(project)) { + val lightClassesPaths = getLightClassesPaths(sourceFile) + for (path in lightClassesPaths) { + val lightClassFile = LightClassFile(project.getFile(path)) + val newSourceFiles = newSourceFilesMap.computeIfAbsent(lightClassFile.asFile()) { HashSet() } + newSourceFiles.add(sourceFile) + } + } + sourceFiles.clear() + sourceFiles.putAll(newSourceFilesMap) + } + + fun updateLightClasses(affectedFiles: Set, resourceTreeBlocked: Boolean) { + val toCreate: MutableList = ArrayList() + val toRemove: MutableList = ArrayList() + for ((key, value) in sourceFiles) { + val lightClassIFile = ResourcesPlugin.getWorkspace().root.getFile(Path(key.path)) + ?: continue + val lightClassFile = LightClassFile(lightClassIFile) + if (!lightClassFile.exists()) { + toCreate.add(lightClassFile) + } + for (sourceFile in value) { + if (affectedFiles.contains(sourceFile)) { + toRemove.add(lightClassFile) + break + } + } + } + if (resourceTreeBlocked) { + if (toCreate.isNotEmpty() || toRemove.isNotEmpty()) { + val job: WorkspaceJob = object : WorkspaceJob(WORKSPACE_JOB_ID) { + override fun runInWorkspace(monitor: IProgressMonitor): IStatus { + monitor.beginTask("Light class generation started", 0) + updateLightClasses(toCreate, toRemove) + monitor.done() + return JobStatus(0, this, "Light classes generation finished") + } + } + job.rule = ResourcesPlugin.getWorkspace().ruleFactory.createRule( + project.getFolder(KotlinJavaManager.KOTLIN_BIN_FOLDER)) + job.schedule() + } + } else { + updateLightClasses(toCreate, toRemove) + } + } + + private fun updateLightClasses(toCreate: List, toRemove: List) { + for (lightClassFile in toCreate) { + createParentDirsFor(lightClassFile) + lightClassFile.createIfNotExists() + } + for (lightClassFile in toRemove) { + removeLightClass(lightClassFile.asFile()) + lightClassFile.touchFile() + } + cleanOutdatedLightClasses(project) + } + + fun getSourceFiles(file: File): List { + if (sourceFiles.isEmpty()) { + computeLightClassesSources() + } + return getSourceKtFiles(file) + } + + private fun getSourceKtFiles(lightClass: File): List { + val sourceIOFiles: Set? = sourceFiles[lightClass] + if (sourceIOFiles != null) { + val jetSourceFiles: MutableList = ArrayList() + for (sourceFile in sourceIOFiles) { + val jetFile = getKotlinParsedFile(sourceFile) + if (jetFile != null) { + jetSourceFiles.add(jetFile) + } + } + return jetSourceFiles + } + return emptyList() + } + + private fun getLightClassesPaths(sourceFile: IFile): List { + val lightClasses: MutableList = ArrayList() + val ktFile = getParsedFile(sourceFile) + for (classOrObject in findLightClasses(ktFile)) { + val internalName = getInternalName(classOrObject) + if (internalName != null) { + lightClasses.add(computePathByInternalName(internalName)) + } + } + if (ktFile.hasTopLevelCallables()) { + val newFacadeInternalName = JvmFileClassUtil.getFileClassInternalName(ktFile) + lightClasses.add(computePathByInternalName(newFacadeInternalName)) + } + return lightClasses + } + + private fun findLightClasses(ktFile: KtFile): List { + val lightClasses = ArrayList() + ktFile.acceptChildren(object : KtVisitorVoid() { + override fun visitClassOrObject(classOrObject: KtClassOrObject) { + lightClasses.add(classOrObject) + super.visitClassOrObject(classOrObject) + } + + override fun visitNamedFunction(function: KtNamedFunction) {} + override fun visitSecondaryConstructor(constructor: KtSecondaryConstructor) {} + override fun visitProperty(property: KtProperty) {} + override fun visitElement(element: PsiElement) { + element.acceptChildren(this) + } + }) + return lightClasses + } + + private fun computePathByInternalName(internalName: String): IPath { + val relativePath = Path("$internalName.class") + return KotlinJavaManager.KOTLIN_BIN_FOLDER.append(relativePath) + } + + private fun cleanOutdatedLightClasses(project: IProject) { + cleanFolder(getKotlinBinFolderFor(project)) { resource: IResource? -> + if (resource is IFile) { + val lightClass = LightClassFile(resource) + val sources: Set? = sourceFiles[lightClass.asFile()] + val dropLightClass = sources == null || sources.isEmpty() + if (dropLightClass) { + removeLightClass(lightClass.asFile()) + } + return@cleanFolder dropLightClass + } else if (resource is IFolder) { + try { + return@cleanFolder resource.members().isEmpty() + } catch (e: CoreException) { + logAndThrow(e) + } + } + false + } + } + + private fun createParentDirsFor(lightClassFile: LightClassFile) { + val parent = lightClassFile.resource.parent as? IFolder + if (parent != null && !parent.exists()) { + createParentDirs(parent) + } + } + + private fun createParentDirs(folder: IFolder) { + val parent = folder.parent + if (!parent.exists()) { + createParentDirs(parent as IFolder) + } + try { + folder.create(true, true, null) + } catch (e: CoreException) { + logAndThrow(e) + } + } + + companion object { + const val KOTLIN_TOUCHED_FILES_FILE_NAME = "META-INF/kotlinTouchedFiles" + + private const val LIGHT_CLASSES_CACHE_SIZE = 300 + private const val WORKSPACE_JOB_ID = "updateLightClassesJob" + fun getInstance(project: IProject): KotlinLightClassManager { + val ideaProject: Project = getEnvironment(project).project + return ServiceManager.getService(ideaProject, KotlinLightClassManager::class.java) + } + + fun getInternalName(classOrObject: KtClassOrObject): String? { + val fullFqName = classOrObject.fqName ?: return null + val topmostClassOrObject = PsiTreeUtil.getTopmostParentOfType(classOrObject, KtClassOrObject::class.java) + ?: return makeInternalByToplevel(fullFqName) + val topLevelFqName = topmostClassOrObject.fqName ?: return null + val nestedPart = fullFqName.asString().substring(topLevelFqName.asString().length).replace("\\.".toRegex(), "\\$") + return makeInternalByToplevel(topLevelFqName) + nestedPart + } + + private fun makeInternalByToplevel(fqName: FqName): String { + return fqName.asString().replace("\\.".toRegex(), "/") + } + } +} \ No newline at end of file diff --git a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/model/CachedEnvironment.kt b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/model/CachedEnvironment.kt index bfd9ae8d7..c81699e06 100644 --- a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/model/CachedEnvironment.kt +++ b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/model/CachedEnvironment.kt @@ -42,6 +42,13 @@ class CachedEnvironment { removeEnvironmentInternal(resource) } + + fun removeEnvironmentIf(check: (KotlinCommonEnvironment) -> Boolean): List { + val tempToRemove = environmentCache.filter { check(it.value) }.map { it.key } + tempToRemove.forEach(::removeEnvironmentInternal) + return tempToRemove + } + fun removeAllEnvironments() = synchronized(environmentLock) { environmentCache.keys.toList().forEach { removeEnvironmentInternal(it) diff --git a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/model/EclipseScriptDefinitionProvider.kt b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/model/EclipseScriptDefinitionProvider.kt index 078ce87ae..c8cd81510 100644 --- a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/model/EclipseScriptDefinitionProvider.kt +++ b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/model/EclipseScriptDefinitionProvider.kt @@ -11,6 +11,7 @@ import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionProvider import org.jetbrains.kotlin.scripting.resolve.KtFileScriptSource import java.io.File +import java.net.URLClassLoader import kotlin.reflect.KClass import kotlin.reflect.full.hasAnnotation import kotlin.script.experimental.annotations.KotlinScript @@ -18,7 +19,6 @@ import kotlin.script.experimental.api.KotlinType import kotlin.script.experimental.api.SourceCode import kotlin.script.experimental.host.ScriptingHostConfiguration import kotlin.script.experimental.host.configurationDependencies -import kotlin.script.experimental.host.createEvaluationConfigurationFromTemplate import kotlin.script.experimental.host.getScriptingClass import kotlin.script.experimental.jvm.JvmDependency import kotlin.script.experimental.jvm.JvmGetScriptingClass @@ -52,7 +52,9 @@ class EclipseScriptDefinitionProvider : ScriptDefinitionProvider { } private val scriptDefinitions: Sequence - get() = contributions.asSequence().map { it.definition } + get() = contributions.asSequence().mapNotNull { it.definition } + + fun getContribution(script: SourceCode): ScriptTemplateContribution? = contributions.find { it.definition?.isScript(script) == true }?.contribution fun isScript(script: SourceCode) = scriptDefinitions.any { it.isScript(script) } @@ -61,7 +63,7 @@ class EclipseScriptDefinitionProvider : ScriptDefinitionProvider { ?.let { KotlinPsiManager.getKotlinParsedFile(it) } ?.let { KtFileScriptSource(it) } ?.let { source -> - contributions.find { it.definition.isScript(source) } + contributions.find { it.definition?.isScript(source) == true } ?.contribution?.scriptEnvironment(scriptFile) ?: emptyMap() } @@ -69,35 +71,48 @@ class EclipseScriptDefinitionProvider : ScriptDefinitionProvider { } private class WrappedContribution(val contribution: ScriptTemplateContribution) { - val definition: ScriptDefinition by lazy { - if (contribution.template.hasAnnotation()) { - ScriptDefinition.FromTemplate( - baseHostConfiguration = ScriptingHostConfiguration { - getScriptingClass(JvmGetScriptingClass()) - configurationDependencies(JvmDependency(extractClasspath(contribution.template) + scriptingDependencies)) - }, - template = contribution.template, - contextClass = contribution::class - ) - } else { - ScriptDefinition.FromLegacyTemplate( - hostConfiguration = ScriptingHostConfiguration { - getScriptingClass(JvmGetScriptingClass()) - configurationDependencies(JvmDependency(extractClasspath(contribution.template) + scriptingDependencies)) - }, - template = contribution.template - ) + + private var _definition: ScriptDefinition? = null + + val definition: ScriptDefinition? + get() { + return _definition ?: run { + try { + if (contribution.template.hasAnnotation()) { + ScriptDefinition.FromTemplate( + baseHostConfiguration = ScriptingHostConfiguration { + getScriptingClass(JvmGetScriptingClass()) + configurationDependencies(JvmDependency(extractClasspath(contribution.template) + scriptingDependencies)) + }, + template = contribution.template, + contextClass = contribution.template + ) + } else { + ScriptDefinition.FromLegacyTemplate( + hostConfiguration = ScriptingHostConfiguration { + getScriptingClass(JvmGetScriptingClass()) + configurationDependencies(JvmDependency(extractClasspath(contribution.template) + scriptingDependencies)) + }, + template = contribution.template + ) + } + } catch (e: Exception) { + null + } + }?.also { _definition = it } } - } } // TODO: hack for now, will definitely need rethinking -private fun extractClasspath(kClass: KClass<*>): List = - (kClass.java.classLoader as? EquinoxClassLoader) - ?.classpathManager - ?.hostClasspathEntries - ?.map { entry -> entry.bundleFile.baseFile.resolve("bin") } - .orEmpty() +private fun extractClasspath(kClass: KClass<*>): List { + return when (val tempLoader = kClass.java.classLoader) { + is EquinoxClassLoader -> tempLoader.classpathManager + .hostClasspathEntries + .map { entry -> entry.bundleFile.baseFile.resolve("bin") } + is URLClassLoader -> tempLoader.urLs.mapNotNull { File(it.file).takeIf { it.exists() } } + else -> null + } ?: emptyList() +} private val scriptingDependencies: List by lazy { listOf("kotlin-scripting-jvm") diff --git a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/model/KotlinCommonEnvironment.kt b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/model/KotlinCommonEnvironment.kt index d89ad8fab..1d43e1b9d 100644 --- a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/model/KotlinCommonEnvironment.kt +++ b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/model/KotlinCommonEnvironment.kt @@ -52,6 +52,7 @@ import com.intellij.psi.impl.PsiTreeChangePreprocessor import com.intellij.psi.impl.compiled.ClsCustomNavigationPolicy import com.intellij.psi.impl.file.impl.JavaFileManager import org.eclipse.core.runtime.IPath +import org.eclipse.jdt.core.IJavaProject import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport import org.jetbrains.kotlin.asJava.LightClassGenerationSupport import org.jetbrains.kotlin.asJava.finder.JavaElementFinder @@ -107,11 +108,15 @@ private fun setIdeaIoUseFallback() { abstract class KotlinCommonEnvironment(disposable: Disposable) { val project: MockProject + var hasError = false + val projectEnvironment: JavaCoreProjectEnvironment private val roots = LinkedHashSet() val configuration = CompilerConfiguration() + abstract val javaProject: IJavaProject + init { setIdeaIoUseFallback() diff --git a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/model/KotlinEnvironment.kt b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/model/KotlinEnvironment.kt index 126e86831..04d85ce01 100644 --- a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/model/KotlinEnvironment.kt +++ b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/model/KotlinEnvironment.kt @@ -57,6 +57,7 @@ import org.jetbrains.kotlin.core.preferences.CompilerPlugin import org.jetbrains.kotlin.core.preferences.KotlinBuildingProperties import org.jetbrains.kotlin.core.preferences.KotlinProperties import org.jetbrains.kotlin.core.resolve.lang.kotlin.EclipseVirtualFileFinderFactory +import org.jetbrains.kotlin.core.utils.DependencyResolverException import org.jetbrains.kotlin.core.utils.ProjectUtils import org.jetbrains.kotlin.core.utils.asFile import org.jetbrains.kotlin.core.utils.buildLibPath @@ -122,7 +123,7 @@ class KotlinScriptEnvironment private constructor( val definitionClasspath: Collection = definition?.contextClassLoader?.let(::classpathFromClassloader).orEmpty() - val javaProject: IJavaProject = JavaCore.create(eclipseFile.project) + override val javaProject: IJavaProject = JavaCore.create(eclipseFile.project) init { configureClasspath() @@ -186,6 +187,14 @@ class KotlinScriptEnvironment private constructor( cachedEnvironment.removeEnvironment(file) } + fun removeAllEnvironments() { + cachedEnvironment.removeAllEnvironments() + } + + fun removeEnvironmentIf(check: (KotlinCommonEnvironment) -> Boolean) { + cachedEnvironment.removeEnvironmentIf(check) + } + @JvmStatic fun getEclipseFile(project: Project): IFile? = cachedEnvironment.getEclipseResource(project) @@ -214,9 +223,13 @@ class KotlinScriptEnvironment private constructor( if (!javaProject.exists()) return - for (file in ProjectUtils.collectClasspathWithDependenciesForBuild(javaProject)) { - addToClasspath(file) + val tempFiles = try { + ProjectUtils.collectClasspathWithDependenciesForBuild(javaProject) + } catch (e: DependencyResolverException) { + hasError = true + e.resolvedFiles } + tempFiles.forEach(::addToClasspath) } private fun addDependenciesToClasspath(dependencies: ScriptDependencies?) { @@ -312,7 +325,7 @@ class SamWithReceiverResolverExtension( class KotlinEnvironment private constructor(val eclipseProject: IProject, disposable: Disposable) : KotlinCommonEnvironment(disposable) { - val javaProject = JavaCore.create(eclipseProject) + override val javaProject = JavaCore.create(eclipseProject) val projectCompilerProperties: KotlinProperties = KotlinProperties(ProjectScope(eclipseProject)) @@ -382,9 +395,14 @@ class KotlinEnvironment private constructor(val eclipseProject: IProject, dispos private fun configureClasspath(javaProject: IJavaProject) { if (!javaProject.exists()) return - for (file in ProjectUtils.collectClasspathWithDependenciesForBuild(javaProject)) { - addToClasspath(file) + val tempFiles = try { + ProjectUtils.collectClasspathWithDependenciesForBuild(javaProject) + } catch (e: DependencyResolverException) { + hasError = true + e.resolvedFiles + e.resolvedFiles } + tempFiles.forEach(::addToClasspath) } companion object { @@ -405,6 +423,17 @@ class KotlinEnvironment private constructor(val eclipseProject: IProject, dispos KotlinAnalysisProjectCache.resetCache(eclipseProject) } + fun removeEnvironmentIf(check: (KotlinCommonEnvironment) -> Boolean) { + val tempRemoved = cachedEnvironment.removeEnvironmentIf(check) + if(tempRemoved.isNotEmpty()) { + KotlinPsiManager.invalidateCachedProjectSourceFiles() + KotlinAnalysisFileCache.resetCache() + for (removedPrj in tempRemoved) { + KotlinAnalysisProjectCache.resetCache(removedPrj) + } + } + } + @JvmStatic fun removeAllEnvironments() { cachedEnvironment.removeAllEnvironments() diff --git a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/model/KotlinJavaManager.kt b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/model/KotlinJavaManager.kt index af23d90b9..46688601b 100644 --- a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/model/KotlinJavaManager.kt +++ b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/model/KotlinJavaManager.kt @@ -16,77 +16,58 @@ *******************************************************************************/ package org.jetbrains.kotlin.core.model +import com.intellij.psi.util.PsiTreeUtil import org.eclipse.core.resources.IFolder import org.eclipse.core.resources.IProject import org.eclipse.core.runtime.Path -import org.eclipse.jdt.core.IJavaProject -import org.eclipse.jdt.core.IType -import org.eclipse.jdt.core.JavaModelException -import org.jetbrains.kotlin.core.filesystem.KotlinFileSystem -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.resolve.source.KotlinSourceElement -import org.jetbrains.kotlin.core.resolve.lang.java.resolver.EclipseJavaSourceElement -import org.eclipse.jdt.core.IJavaElement -import org.jetbrains.kotlin.descriptors.SourceElement -import org.jetbrains.kotlin.psi.KtDeclaration -import com.intellij.psi.PsiElement -import org.eclipse.jdt.core.IMethod -import org.jetbrains.kotlin.core.asJava.equalsJvmSignature -import org.jetbrains.kotlin.core.asJava.getTypeFqName +import org.eclipse.jdt.core.* import org.eclipse.jdt.core.dom.IBinding import org.eclipse.jdt.core.dom.IMethodBinding -import org.jetbrains.kotlin.psi.KtClassOrObject -import org.jetbrains.kotlin.psi.KtElement -import org.jetbrains.kotlin.psi.KtNamedFunction -import org.jetbrains.kotlin.psi.KtSecondaryConstructor -import org.jetbrains.kotlin.psi.KtFunction -import org.jetbrains.kotlin.psi.KtPropertyAccessor -import org.eclipse.jdt.core.IMember -import org.jetbrains.kotlin.psi.KtProperty -import com.intellij.psi.util.PsiTreeUtil -import org.jetbrains.kotlin.psi.KtObjectDeclaration -import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.core.asJava.equalsJvmSignature +import org.jetbrains.kotlin.core.asJava.getTypeFqName import org.jetbrains.kotlin.core.builder.KotlinPsiManager -import org.jetbrains.kotlin.psi.KtPrimaryConstructor +import org.jetbrains.kotlin.core.filesystem.KotlinFileSystem import org.jetbrains.kotlin.core.log.KotlinLogger +import org.jetbrains.kotlin.core.resolve.lang.java.resolver.EclipseJavaSourceElement +import org.jetbrains.kotlin.descriptors.SourceElement +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.source.KotlinSourceElement -public object KotlinJavaManager { +object KotlinJavaManager { @JvmField - public val KOTLIN_BIN_FOLDER: Path = Path("kotlin_bin") + val KOTLIN_BIN_FOLDER: Path = Path("kotlin_bin") - public fun getKotlinBinFolderFor(project: IProject): IFolder = project.getFolder(KOTLIN_BIN_FOLDER) + fun getKotlinBinFolderFor(project: IProject): IFolder = project.getFolder(KOTLIN_BIN_FOLDER) - public fun findEclipseType(jetClass: KtClassOrObject, javaProject: IJavaProject): IType? { - return jetClass.getFqName().let { + fun findEclipseType(jetClass: KtClassOrObject, javaProject: IJavaProject): IType? { + return jetClass.fqName.let { if (it != null) javaProject.findType(it.asString()) else null } } - public fun findEclipseMembers(declaration: KtDeclaration, javaProject: IJavaProject, + fun findEclipseMembers(declaration: KtDeclaration, javaProject: IJavaProject, klass: Class): List { val containingElement = PsiTreeUtil.getParentOfType(declaration, KtClassOrObject::class.java, KtFile::class.java) val seekInParent: Boolean = containingElement is KtObjectDeclaration && containingElement.isCompanion() if (containingElement == null) return emptyList() - val declaringTypeFqName = getTypeFqName(containingElement) - if (declaringTypeFqName == null) return emptyList() - - val eclipseType = javaProject.findType(declaringTypeFqName.asString()) - if (eclipseType == null) return emptyList() - + val declaringTypeFqName = getTypeFqName(containingElement) ?: return emptyList() + + val eclipseType = javaProject.findType(declaringTypeFqName.asString()) ?: return emptyList() + val typeMembers = findMembersIn(eclipseType, declaration, klass) return if (seekInParent) { - val parentMembers = findMembersIn(eclipseType.getDeclaringType(), declaration, klass) + val parentMembers = findMembersIn(eclipseType.declaringType, declaration, klass) typeMembers + parentMembers } else { typeMembers } } - public fun hasLinkedKotlinBinFolder(project: IProject): Boolean { - val folder = project.getFolder(KotlinJavaManager.KOTLIN_BIN_FOLDER) - return folder.isLinked() && KotlinFileSystem.SCHEME == folder.getLocationURI().getScheme() + fun hasLinkedKotlinBinFolder(project: IProject): Boolean { + val folder = project.getFolder(KOTLIN_BIN_FOLDER) + return folder.isLinked && KotlinFileSystem.SCHEME == folder.locationURI.scheme } private fun findMembersIn(eclipseType: IType, declaration: KtDeclaration, klass: Class): List { @@ -95,14 +76,14 @@ public object KotlinJavaManager { return klass.isAssignableFrom(member.javaClass) && equalsJvmSignature(declaration, member) } - val methods = eclipseType.getMethods().filter { check(it) } - val fields = eclipseType.getFields().filter { check(it) } + val methods = eclipseType.methods.filter { check(it) } + val fields = eclipseType.fields.filter { check(it) } return methods + fields } } -public fun KtElement.toLightElements(): List { +fun KtElement.toLightElements(): List { val javaProject = KotlinPsiManager.getJavaProject(this) if (javaProject == null) { KotlinLogger.logWarning("Cannot resolve jetElement ($this) to light elements: there is no corresponding java project") @@ -119,28 +100,33 @@ public fun KtElement.toLightElements(): List { is KtSecondaryConstructor, is KtPrimaryConstructor, is KtPropertyAccessor -> KotlinJavaManager.findEclipseMembers(this as KtDeclaration, javaProject, IMethod::class.java) - is KtProperty -> KotlinJavaManager.findEclipseMembers(this, javaProject, IMember::class.java) + is KtProperty -> { + val list = KotlinJavaManager.findEclipseMembers(this, javaProject, IMember::class.java) + val getterList = getter?.toLightElements() ?: emptyList() + val setterList = setter?.toLightElements() ?: emptyList() + list + getterList + setterList + } else -> emptyList() } } -public fun SourceElement.toJavaElements(): List { +fun SourceElement.toJavaElements(): List { return when (this) { - is EclipseJavaSourceElement -> obtainJavaElement(this.getElementBinding())?.let(::listOf) ?: emptyList() - is KotlinSourceElement -> this.psi.toLightElements() - else -> emptyList() + is EclipseJavaSourceElement -> obtainJavaElement(elementBinding)?.let(::listOf) ?: emptyList() + is KotlinSourceElement -> psi.toLightElements() + else -> emptyList() } } -public fun sourceElementsToLightElements(sourceElements: List): List { +fun sourceElementsToLightElements(sourceElements: List): List { return sourceElements.flatMap { it.toJavaElements() } } private fun obtainJavaElement(binding: IBinding): IJavaElement? { - return if (binding is IMethodBinding && binding.isDefaultConstructor()) { - binding.getDeclaringClass().getJavaElement() + return if (binding is IMethodBinding && binding.isDefaultConstructor) { + binding.declaringClass.javaElement } else { - binding.getJavaElement() + binding.javaElement } } diff --git a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/references/KotlinReference.kt b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/references/KotlinReference.kt index 12935dea6..3dd8ac6eb 100644 --- a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/references/KotlinReference.kt +++ b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/references/KotlinReference.kt @@ -19,9 +19,13 @@ package org.jetbrains.kotlin.core.references import com.intellij.util.SmartList import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors +import org.jetbrains.kotlin.descriptors.accessors +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getParentOfType import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.callUtil.getCall import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall @@ -30,26 +34,28 @@ import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor import org.jetbrains.kotlin.types.expressions.OperatorConventions import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.utils.addIfNotNull -import java.util.* -inline private fun ArrayList.register(e: KtElement, action: (T) -> KotlinReference) { +inline private fun ArrayList>.register( + e: KtElement, + action: (T) -> KotlinReference +) { if (e is T) this.add(action(e)) } -inline private fun ArrayList.registerMulti( +inline private fun ArrayList>.registerMulti( e: KtElement, - action: (T) -> List + action: (T) -> List> ) { if (e is T) this.addAll(action(e)) } -public fun createReferences(element: KtReferenceExpression): List { - return arrayListOf().apply { - register(element, ::KotlinSimpleNameReference) +public fun createReferences(element: KtElement): List> { + return arrayListOf>().apply { + register(element, ::KotlinSimpleNameReference) - register(element, ::KotlinInvokeFunctionReference) + register(element, ::KotlinInvokeFunctionReference) - register(element, ::KotlinConstructorDelegationReference) + register(element, ::KotlinConstructorDelegationReference) registerMulti(element) { if (it.getReferencedNameElementType() != KtTokens.IDENTIFIER) return@registerMulti emptyList() @@ -64,18 +70,41 @@ public fun createReferences(element: KtReferenceExpression): List(element, ::KotlinReferenceExpressionReference) + + register(element, ::KotlinKtPropertyDelegateReference) } } -public interface KotlinReference { - val expression: KtReferenceExpression +public interface KotlinReference { + + val expression: KtElement fun getTargetDescriptors(context: BindingContext): Collection val resolvesByNames: Collection } -open class KotlinSimpleNameReference(override val expression: KtSimpleNameExpression) : KotlinReference { +class KotlinKtPropertyDelegateReference(override val expression: KtPropertyDelegate) : + KotlinReference { + + override fun getTargetDescriptors(context: BindingContext): Collection { + val tempProperty = expression.getParentOfType(false) ?: return emptyList() + val tempDescriptor = + tempProperty.resolveToDescriptorIfAny() as? VariableDescriptorWithAccessors ?: return emptyList() + val tempProviderDescriptor = context[BindingContext.PROVIDE_DELEGATE_RESOLVED_CALL, tempDescriptor]?.candidateDescriptor + val tempGetDescriptor = tempDescriptor.getter?.let { context[BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, it]?.candidateDescriptor } + val tempSetDescriptor = tempDescriptor.setter?.let { context[BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, it]?.candidateDescriptor } + + return listOfNotNull(tempProviderDescriptor, tempGetDescriptor, tempSetDescriptor) + } + + override val resolvesByNames: Collection + get() = emptyList() + +} + +open class KotlinSimpleNameReference(override val expression: KtSimpleNameExpression) : + KotlinReference { override fun getTargetDescriptors(context: BindingContext): Collection { return expression.getReferenceTargets(context) } @@ -104,7 +133,8 @@ open class KotlinSimpleNameReference(override val expression: KtSimpleNameExpres } } -public class KotlinInvokeFunctionReference(override val expression: KtCallExpression) : KotlinReference { +public class KotlinInvokeFunctionReference(override val expression: KtCallExpression) : + KotlinReference { override val resolvesByNames: Collection get() = listOf(OperatorNameConventions.INVOKE) @@ -120,7 +150,7 @@ public class KotlinInvokeFunctionReference(override val expression: KtCallExpres } sealed class KotlinSyntheticPropertyAccessorReference( - override val expression: KtNameReferenceExpression, + expression: KtNameReferenceExpression, private val getter: Boolean ) : KotlinSimpleNameReference(expression) { override fun getTargetDescriptors(context: BindingContext): Collection { @@ -148,14 +178,15 @@ sealed class KotlinSyntheticPropertyAccessorReference( } public class KotlinConstructorDelegationReference(override val expression: KtConstructorDelegationReferenceExpression) : - KotlinReference { + KotlinReference { override fun getTargetDescriptors(context: BindingContext) = expression.getReferenceTargets(context) override val resolvesByNames: Collection get() = emptyList() } -class KotlinReferenceExpressionReference(override val expression: KtReferenceExpression) : KotlinReference { +class KotlinReferenceExpressionReference(override val expression: KtReferenceExpression) : + KotlinReference { override fun getTargetDescriptors(context: BindingContext): Collection { return expression.getReferenceTargets(context) } @@ -172,4 +203,4 @@ fun KtReferenceExpression.getReferenceTargets(context: BindingContext): Collecti } else { context[BindingContext.AMBIGUOUS_REFERENCE_TARGET, this].orEmpty() } -} \ No newline at end of file +} diff --git a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/references/referenceUtils.kt b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/references/referenceUtils.kt index 4116c949a..c0469445c 100644 --- a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/references/referenceUtils.kt +++ b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/references/referenceUtils.kt @@ -16,123 +16,106 @@ *******************************************************************************/ package org.jetbrains.kotlin.core.references -import org.jetbrains.kotlin.descriptors.SourceElement -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.core.resolve.EclipseDescriptorUtils -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.core.references.KotlinReference +import com.intellij.openapi.util.Key import com.intellij.psi.PsiElement -import org.jetbrains.kotlin.psi.KtReferenceExpression import com.intellij.psi.util.PsiTreeUtil -import org.jetbrains.kotlin.psi.KtElement -import org.eclipse.jdt.core.IJavaElement -import org.jetbrains.kotlin.core.resolve.lang.java.resolver.EclipseJavaSourceElement -import org.jetbrains.kotlin.resolve.source.KotlinSourceElement import org.eclipse.jdt.core.IJavaProject -import org.jetbrains.kotlin.core.resolve.KotlinAnalyzer -import org.jetbrains.kotlin.core.model.sourceElementsToLightElements import org.eclipse.jdt.core.JavaCore import org.jetbrains.kotlin.core.builder.KotlinPsiManager -import com.intellij.openapi.util.Key -import org.jetbrains.kotlin.psi.KtObjectDeclaration -import org.jetbrains.kotlin.psi.KtDeclaration -import org.jetbrains.kotlin.core.model.toJavaElements -import org.jetbrains.kotlin.core.log.KotlinLogger -import org.jetbrains.kotlin.psi.KtExpression -import org.jetbrains.kotlin.psi.KtParenthesizedExpression -import org.jetbrains.kotlin.psi.KtAnnotatedExpression -import org.jetbrains.kotlin.psi.KtLabeledExpression -import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis -import org.jetbrains.kotlin.psi.psiUtil.getAssignmentByLHS -import org.jetbrains.kotlin.lexer.KtTokens -import org.jetbrains.kotlin.types.expressions.OperatorConventions -import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall -import org.jetbrains.kotlin.resolve.calls.model.isReallySuccess -import org.jetbrains.kotlin.psi.KtUnaryExpression -import org.jetbrains.kotlin.utils.addToStdlib.constant +import org.jetbrains.kotlin.core.resolve.EclipseDescriptorUtils +import org.jetbrains.kotlin.core.resolve.KotlinAnalyzer +import org.jetbrains.kotlin.core.utils.getBindingContext +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.SourceElement +import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors +import org.jetbrains.kotlin.descriptors.accessors +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny import org.jetbrains.kotlin.idea.imports.canBeReferencedViaImport -import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension -import org.jetbrains.kotlin.psi.KtNameReferenceExpression import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver -import org.jetbrains.kotlin.psi.KtThisExpression -import org.jetbrains.kotlin.psi.KtSuperExpression +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getAssignmentByLHS +import org.jetbrains.kotlin.psi.psiUtil.getParentOfType +import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension +import org.jetbrains.kotlin.resolve.source.KotlinSourceElement +import org.jetbrains.kotlin.utils.addToStdlib.constant -public val FILE_PROJECT: Key = Key.create("FILE_PROJECT") +val FILE_PROJECT: Key = Key.create("FILE_PROJECT") -public fun List.resolveToSourceElements(): List { +fun List>.resolveToSourceElements(ktFile: KtFile): List { if (isEmpty()) return emptyList() - - val jetFile = first().expression.getContainingKtFile() - val javaProject = JavaCore.create(KotlinPsiManager.getEclipseFile(jetFile)?.getProject()) - ?: jetFile.getUserData(FILE_PROJECT) - if (javaProject == null) return emptyList() - return resolveToSourceElements( - KotlinAnalyzer.analyzeFile(jetFile).analysisResult.bindingContext, - javaProject) + val javaProject = JavaCore.create(KotlinPsiManager.getEclipseFile(ktFile)?.project) + ?: ktFile.getUserData(FILE_PROJECT) ?: return emptyList() + + return resolveToSourceElements(ktFile.getBindingContext(), javaProject) } -public fun List.resolveToSourceElements(context: BindingContext, javaProject: IJavaProject): List { +fun List>.resolveToSourceElements( + context: BindingContext, + javaProject: IJavaProject +): List { return flatMap { it.getTargetDescriptors(context) } - .flatMap { EclipseDescriptorUtils.descriptorToDeclarations(it, javaProject.project) } + .flatMap { EclipseDescriptorUtils.descriptorToDeclarations(it, javaProject.project) } } -public fun getReferenceExpression(element: PsiElement): KtReferenceExpression? { - return PsiTreeUtil.getNonStrictParentOfType(element, KtReferenceExpression::class.java) +fun getReferenceExpression(element: PsiElement): KtReferenceExpression? { + return PsiTreeUtil.getNonStrictParentOfType(element, KtReferenceExpression::class.java) } -public fun KtElement.resolveToSourceDeclaration(): List { - val jetElement = this - return when (jetElement) { - is KtDeclaration -> { - listOf(KotlinSourceElement(jetElement)) - } - +fun KtElement.resolveToSourceDeclaration(): List { + return when (val jetElement = this) { + is KtDeclaration -> listOf(KotlinSourceElement(jetElement)) else -> { // Try search declaration by reference - val referenceExpression = getReferenceExpression(jetElement) - if (referenceExpression == null) return emptyList() - + val referenceExpression = getReferenceExpression(jetElement) ?: jetElement val reference = createReferences(referenceExpression) - reference.resolveToSourceElements() - } + reference.resolveToSourceElements(jetElement.containingKtFile) + } } } -public enum class ReferenceAccess(val isRead: Boolean, val isWrite: Boolean) { +enum class ReferenceAccess(val isRead: Boolean, val isWrite: Boolean) { READ(true, false), WRITE(false, true), READ_WRITE(true, true) } -public fun KtExpression.readWriteAccess(): ReferenceAccess { +fun KtExpression.readWriteAccess(): ReferenceAccess { var expression = getQualifiedExpressionForSelectorOrThis() loop@ while (true) { - val parent = expression.parent - when (parent) { - is KtParenthesizedExpression, is KtAnnotatedExpression, is KtLabeledExpression -> expression = parent as KtExpression + when (val parent = expression.parent) { + is KtParenthesizedExpression, is KtAnnotatedExpression, is KtLabeledExpression -> expression = + parent as KtExpression else -> break@loop } } val assignment = expression.getAssignmentByLHS() if (assignment != null) { - when (assignment.operationToken) { - KtTokens.EQ -> return ReferenceAccess.WRITE - else -> return ReferenceAccess.READ_WRITE + return when (assignment.operationToken) { + KtTokens.EQ -> ReferenceAccess.WRITE + else -> ReferenceAccess.READ_WRITE } } - return if ((expression.parent as? KtUnaryExpression)?.operationToken in constant { setOf(KtTokens.PLUSPLUS, KtTokens.MINUSMINUS) }) + return if ((expression.parent as? KtUnaryExpression)?.operationToken in constant { + setOf( + KtTokens.PLUSPLUS, + KtTokens.MINUSMINUS + ) + }) ReferenceAccess.READ_WRITE else ReferenceAccess.READ } // TODO: obtain this function from referenceUtil.kt (org.jetbrains.kotlin.idea.references) -fun KotlinReference.canBeResolvedViaImport(target: DeclarationDescriptor): Boolean { +fun KotlinReference<*>.canBeResolvedViaImport(target: DeclarationDescriptor): Boolean { if (!target.canBeReferencedViaImport()) return false if (target.isExtension) return true // assume that any type of reference can use imports when resolved to extension val referenceExpression = this.expression as? KtNameReferenceExpression ?: return false if (CallTypeAndReceiver.detect(referenceExpression).receiver != null) return false if (expression.parent is KtThisExpression || expression.parent is KtSuperExpression) return false // TODO: it's a bad design of PSI tree, we should change it return true -} \ No newline at end of file +} diff --git a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/resolve/EclipseAnalyzerFacadeForJVM.kt b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/resolve/EclipseAnalyzerFacadeForJVM.kt index 0d220159d..3fbab17a7 100644 --- a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/resolve/EclipseAnalyzerFacadeForJVM.kt +++ b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/resolve/EclipseAnalyzerFacadeForJVM.kt @@ -17,9 +17,10 @@ package org.jetbrains.kotlin.core.resolve import com.intellij.openapi.project.Project -import com.intellij.psi.PsiFileFactory import com.intellij.psi.search.GlobalSearchScope +import org.eclipse.core.runtime.Path import org.eclipse.jdt.core.IJavaProject +import org.eclipse.jdt.core.JavaCore import org.jetbrains.kotlin.analyzer.AnalysisResult import org.jetbrains.kotlin.builtins.jvm.JvmBuiltIns import org.jetbrains.kotlin.builtins.jvm.JvmBuiltInsPackageFragmentProvider @@ -35,6 +36,7 @@ import org.jetbrains.kotlin.context.MutableModuleContext import org.jetbrains.kotlin.context.ProjectContext import org.jetbrains.kotlin.core.builder.KotlinPsiManager import org.jetbrains.kotlin.core.log.KotlinLogger +import org.jetbrains.kotlin.core.model.EclipseScriptDefinitionProvider import org.jetbrains.kotlin.core.model.KotlinCommonEnvironment import org.jetbrains.kotlin.core.model.KotlinEnvironment import org.jetbrains.kotlin.core.model.KotlinScriptEnvironment @@ -44,16 +46,20 @@ import org.jetbrains.kotlin.core.utils.asResource import org.jetbrains.kotlin.descriptors.PackageFragmentProvider import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider import org.jetbrains.kotlin.frontend.java.di.initJvmBuiltInsForTopDownAnalysis -import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtPsiFactory import org.jetbrains.kotlin.resolve.* import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformConfigurator import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer +import org.jetbrains.kotlin.resolve.lazy.data.KtClassLikeInfo +import org.jetbrains.kotlin.resolve.lazy.data.KtScriptInfo +import org.jetbrains.kotlin.resolve.lazy.declarations.AbstractPsiBasedDeclarationProvider +import org.jetbrains.kotlin.resolve.lazy.declarations.ClassMemberDeclarationProvider import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactory import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter @@ -62,7 +68,6 @@ import org.jetbrains.kotlin.scripting.resolve.KtFileScriptSource import org.jetbrains.kotlin.scripting.resolve.refineScriptCompilationConfiguration import org.jetbrains.kotlin.storage.StorageManager import org.jetbrains.kotlin.util.KotlinFrontEndException -import kotlin.math.absoluteValue import kotlin.script.experimental.api.KotlinType import kotlin.script.experimental.api.valueOrNull import kotlin.script.experimental.util.PropertiesCollection @@ -103,108 +108,120 @@ object EclipseAnalyzerFacadeForJVM { scriptFile: KtFile ): AnalysisResultWithProvider { //TODO actually take dependencies from script configuration! - val javaProject = environment.javaProject/*.apply { - /*tempClasspath += JavaRuntime.getDefaultVMInstall() - ?.let { JavaRuntime.getLibraryLocations(it) } - ?.map { JavaCore.newLibraryEntry(it.systemLibraryPath, null, null) } - .orEmpty()*/ - - /*tempClasspath += (environment.dependencies - ?.classpath.orEmpty() + environment.definitionClasspath) - .map { JavaCore.newLibraryEntry(Path(it.absolutePath), null, null) }*/ - - val tempClasspath = environment.getRoots().mapTo(hashSetOf()) { - val tempFile = it.file - if(it.type == JavaRoot.RootType.SOURCE) { - JavaCore.newSourceEntry(Path(tempFile.path)) - } else { - JavaCore.newLibraryEntry(Path(tempFile.path), null, null) - } - }.toTypedArray() - - setRawClasspath(tempClasspath, null) - }*/ - - val allFiles = LinkedHashSet().run { - add(scriptFile) - environment.dependencies?.sources?.toList() - .orEmpty() - .mapNotNull { it.asResource } - .mapNotNull { KotlinPsiManager.getKotlinParsedFile(it) } - .toCollection(this) - } + val javaProject = environment.javaProject + + val tempOrigClasspath = javaProject.rawClasspath + + try { + val tempSourceCode = KtFileScriptSource(scriptFile) + val tempContribution = EclipseScriptDefinitionProvider.getContribution(tempSourceCode) + + val tempNewClasspath = tempContribution?.createClasspath(environment) ?: (tempOrigClasspath + environment.definitionClasspath.map { + JavaCore.newLibraryEntry(Path(it.absolutePath), null, null) + }).distinctBy { it.path }.toTypedArray() + + javaProject.setRawClasspath(tempNewClasspath, null) + + val allFiles = LinkedHashSet().run { + add(scriptFile) + environment.dependencies?.sources?.toList() + .orEmpty() + .mapNotNull { it.asResource } + .mapNotNull { KotlinPsiManager.getKotlinParsedFile(it) } + .toCollection(this) + } - ProjectUtils.getSourceFilesWithDependencies(environment.javaProject).toCollection(allFiles) + ProjectUtils.getSourceFilesWithDependencies(environment.javaProject).toCollection(allFiles) - val tempRefinedConfig = environment.definition?.let { - refineScriptCompilationConfiguration(KtFileScriptSource(scriptFile), it, environment.project) - }?.valueOrNull()?.configuration + val tempRefinedConfig = environment.definition?.let { + refineScriptCompilationConfiguration(tempSourceCode, it, environment.project) + }?.valueOrNull()?.configuration - val tempDefaultImports = - tempRefinedConfig?.get(PropertiesCollection.Key("defaultImports", emptyList())) ?: emptyList() - val tempImports = ArrayList(tempDefaultImports) - val analyzerService = object : PlatformDependentAnalyzerServices() { + val tempDefaultImports = + tempRefinedConfig?.get(PropertiesCollection.Key("defaultImports", emptyList())) ?: emptyList() + val tempImports = ArrayList(tempDefaultImports) - override val defaultLowPriorityImports: List = listOf(ImportPath.fromString("java.lang.*")) + val analyzerService = object : PlatformDependentAnalyzerServices() { - override val platformConfigurator: PlatformConfigurator = JvmPlatformConfigurator + override val defaultLowPriorityImports: List = listOf(ImportPath.fromString("java.lang.*")) - override fun computePlatformSpecificDefaultImports( - storageManager: StorageManager, - result: MutableList - ) { - result.add(ImportPath.fromString("kotlin.jvm.*")) - tempImports.map(ImportPath::fromString).toCollection(result) + override val platformConfigurator: PlatformConfigurator = JvmPlatformConfigurator - fun addAllClassifiersFromScope(scope: MemberScope) { - for (descriptor in scope.getContributedDescriptors( - DescriptorKindFilter.CLASSIFIERS, - MemberScope.ALL_NAME_FILTER - )) { - result.add(ImportPath(DescriptorUtils.getFqNameSafe(descriptor), false)) + override fun computePlatformSpecificDefaultImports( + storageManager: StorageManager, + result: MutableList + ) { + result.add(ImportPath.fromString("kotlin.jvm.*")) + tempImports.map(ImportPath::fromString).toCollection(result) + + fun addAllClassifiersFromScope(scope: MemberScope) { + for (descriptor in scope.getContributedDescriptors( + DescriptorKindFilter.CLASSIFIERS, + MemberScope.ALL_NAME_FILTER + )) { + result.add(ImportPath(DescriptorUtils.getFqNameSafe(descriptor), false)) + } } - } - for (builtInPackage in JvmBuiltIns( - storageManager, - JvmBuiltIns.Kind.FROM_CLASS_LOADER - ).builtInPackagesImportedByDefault) { - addAllClassifiersFromScope(builtInPackage.memberScope) + for (builtInPackage in JvmBuiltIns( + storageManager, + JvmBuiltIns.Kind.FROM_CLASS_LOADER + ).builtInPackagesImportedByDefault) { + addAllClassifiersFromScope(builtInPackage.memberScope) + } } } - } - val tempProperties = - tempRefinedConfig?.get(PropertiesCollection.Key("providedProperties", emptyMap())) - - if (!tempProperties.isNullOrEmpty()) { - val tempPackageName = "scriptParameters${scriptFile.virtualFilePath.hashCode().absoluteValue}" - val tempContent = - "package $tempPackageName\n" + tempProperties.entries.joinToString(separator = "\n") { (key, value) -> - """ - |@Deprecated(message = "Do not import this explicitly! Used only in eclipse as workaround for providedProperties in Scripts!", level = DeprecationLevel.WARNING) - |val $key: ${value.typeName}? = null - """.trimMargin("|") + val tempProperties = + tempRefinedConfig?.get(PropertiesCollection.Key("providedProperties", emptyMap())) + + val declarationProviderFactory: (StorageManager) -> DeclarationProviderFactory = { storageManager -> + object : FileBasedDeclarationProviderFactory(storageManager, allFiles) { + + private val factory = KtPsiFactory(environment.project, true) + + override fun getClassMemberDeclarationProvider(classLikeInfo: KtClassLikeInfo): ClassMemberDeclarationProvider { + if (classLikeInfo is KtScriptInfo) { + return object : AbstractPsiBasedDeclarationProvider(storageManager), + ClassMemberDeclarationProvider { + override val ownerInfo: KtClassLikeInfo = classLikeInfo + + override fun doCreateIndex(index: Index) { + val tempProvidedProperties = tempProperties?.entries?.map { (key, value) -> + val isNullable = tempContribution?.isNullable(key, tempRefinedConfig) ?: true + val tempTypeName = value.fromClass?.qualifiedName ?: value.typeName + val tempText = + """ + /** Provided property '$key' of type: $tempTypeName */ + val $key: $tempTypeName${'$'}${if (isNullable) "? = null" else " = TODO()"}""".trimIndent() + factory.createProperty(tempText) + } ?: emptyList() + + val tempDeclarations = ownerInfo.declarations + + ownerInfo.primaryConstructorParameters + + tempProvidedProperties + + tempDeclarations.forEach(index::putToIndex) + } + } + } + return super.getClassMemberDeclarationProvider(classLikeInfo) + } } - - tempImports.add("$tempPackageName.*") - - val tempKtFile = PsiFileFactory.getInstance(environment.project) - .createFileFromText("scriptParameters.kt", KotlinLanguage.INSTANCE, tempContent) as? KtFile - - if (tempKtFile != null) { - allFiles.add(tempKtFile) } - } - return analyzeKotlin( - filesToAnalyze = listOf(scriptFile), - allFiles = allFiles, - environment = environment, - javaProject = javaProject, - analyzerService = analyzerService - ) + return analyzeKotlin( + filesToAnalyze = listOf(scriptFile), + allFiles = allFiles, + environment = environment, + javaProject = javaProject, + analyzerService = analyzerService, + providerFactoryCreator = declarationProviderFactory + ) + } finally { + javaProject.setRawClasspath(tempOrigClasspath, null) + } } private fun analyzeKotlin( @@ -213,14 +230,18 @@ object EclipseAnalyzerFacadeForJVM { environment: KotlinCommonEnvironment, javaProject: IJavaProject?, jvmTarget: JvmTarget = JvmTarget.DEFAULT, - analyzerService: PlatformDependentAnalyzerServices? = null + analyzerService: PlatformDependentAnalyzerServices? = null, + providerFactoryCreator: (StorageManager) -> DeclarationProviderFactory = { storageManager -> + FileBasedDeclarationProviderFactory(storageManager, allFiles) + } + ): AnalysisResultWithProvider { val project = environment.project val moduleContext = createModuleContext(project, environment.configuration, true) val storageManager = moduleContext.storageManager val module = moduleContext.module - val providerFactory = FileBasedDeclarationProviderFactory(moduleContext.storageManager, allFiles) + val providerFactory = providerFactoryCreator(storageManager) val trace = CliBindingTrace() val sourceScope = TopDownAnalyzerFacadeForJVM.newModuleSearchScope(project, filesToAnalyze) @@ -356,4 +377,4 @@ object EclipseAnalyzerFacadeForJVM { } } } -} \ No newline at end of file +} diff --git a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/resolve/KotlinPackagePartProvider.kt b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/resolve/KotlinPackagePartProvider.kt index cddf2f7c5..e6393866e 100644 --- a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/resolve/KotlinPackagePartProvider.kt +++ b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/resolve/KotlinPackagePartProvider.kt @@ -1,19 +1,19 @@ /******************************************************************************* -* 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. -* -*******************************************************************************/ + * 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.core.resolve import com.intellij.openapi.vfs.VirtualFile @@ -25,22 +25,23 @@ import org.jetbrains.kotlin.metadata.jvm.deserialization.ModuleMapping import org.jetbrains.kotlin.metadata.jvm.deserialization.PackageParts import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.resolve.CompilerDeserializationConfiguration -import org.jetbrains.kotlin.utils.SmartList import org.jetbrains.kotlin.serialization.deserialization.ClassData +import org.jetbrains.kotlin.utils.SmartList import java.io.EOFException +import java.io.FileNotFoundException public class KotlinPackagePartProvider(private val environment: KotlinCommonEnvironment) : PackagePartProvider { private data class ModuleMappingInfo(val root: VirtualFile, val mapping: ModuleMapping, val name: String) - + private val notLoadedRoots by lazy(LazyThreadSafetyMode.NONE) { - environment.getRoots() - .map { it.file } - .filter { it.findChild("META-INF") != null } - .toMutableList() + environment.getRoots() + .map { it.file } + .filter { it.findChild("META-INF") != null } + .toMutableList() } - + private val loadedModules: MutableList = SmartList() - + private val deserializationConfiguration = CompilerDeserializationConfiguration(LanguageVersionSettingsImpl.DEFAULT) override fun getAnnotationsOnBinaryModule(moduleName: String): List = @@ -93,8 +94,7 @@ public class KotlinPackagePartProvider(private val environment: KotlinCommonEnvi val relevantRoots = notLoadedRoots.filter { //filter all roots by package path existing - pathParts.fold(it) { - parent, part -> + pathParts.fold(it) { parent, part -> if (part.isEmpty()) parent else parent.findChild(part) ?: return@filter false } @@ -115,10 +115,12 @@ public class KotlinPackagePartProvider(private val environment: KotlinCommonEnvi ) { KotlinLogger.logWarning("Incompatible version for '$moduleFile': $it") } - } - catch (e: EOFException) { + } catch (e: EOFException) { throw RuntimeException("Error on reading package parts for '$packageFqName' package in '$moduleFile', " + - "roots: $notLoadedRoots", e) + "roots: $notLoadedRoots", e) + } catch (e: FileNotFoundException) { + notLoadedRoots.add(root) + continue } loadedModules.add(ModuleMappingInfo(root, mapping, moduleFile.nameWithoutExtension)) } diff --git a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/resolve/lang/java/structure/EclipseJavaArrayType.java b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/resolve/lang/java/structure/EclipseJavaArrayType.java deleted file mode 100644 index ac4ba406b..000000000 --- a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/resolve/lang/java/structure/EclipseJavaArrayType.java +++ /dev/null @@ -1,36 +0,0 @@ -/******************************************************************************* - * Copyright 2000-2014 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.core.resolve.lang.java.structure; - -import org.eclipse.jdt.core.dom.ITypeBinding; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.kotlin.load.java.structure.JavaArrayType; -import org.jetbrains.kotlin.load.java.structure.JavaType; - -public class EclipseJavaArrayType extends EclipseJavaType implements JavaArrayType { - - public EclipseJavaArrayType(@NotNull ITypeBinding typeBinding) { - super(typeBinding); - } - - @Override - @NotNull - public JavaType getComponentType() { - return EclipseJavaType.create(getBinding().getComponentType()); - } - -} diff --git a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/resolve/lang/java/structure/EclipseJavaArrayType.kt b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/resolve/lang/java/structure/EclipseJavaArrayType.kt new file mode 100644 index 000000000..c07b84343 --- /dev/null +++ b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/resolve/lang/java/structure/EclipseJavaArrayType.kt @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright 2000-2014 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.core.resolve.lang.java.structure + +import org.eclipse.jdt.core.dom.ITypeBinding +import org.jetbrains.kotlin.load.java.structure.JavaArrayType +import org.jetbrains.kotlin.load.java.structure.JavaType + +class EclipseJavaArrayType(typeBinding: ITypeBinding) : EclipseJavaType(typeBinding), JavaArrayType { + + override val componentType: JavaType get() = create(binding.componentType) +} diff --git a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/resolve/lang/java/structure/EclipseJavaElementUtil.java b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/resolve/lang/java/structure/EclipseJavaElementUtil.java index 360579264..bd81ee450 100644 --- a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/resolve/lang/java/structure/EclipseJavaElementUtil.java +++ b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/resolve/lang/java/structure/EclipseJavaElementUtil.java @@ -36,7 +36,7 @@ import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation; -import org.eclipse.jdt.internal.core.AnnotationInfo; +import org.eclipse.jdt.internal.core.BinaryMethod; import org.eclipse.jdt.internal.core.BinaryType; import org.eclipse.jdt.internal.core.ClassFile; import org.jetbrains.annotations.NotNull; @@ -209,6 +209,20 @@ public static boolean isFromKotlinBinFolder(@NotNull IResource resource) { return false; } + public static boolean isFromKotlinBinFolder(@NotNull IJavaElement element) { + IClassFile classFile; + if (element instanceof IClassFile) { + classFile = (IClassFile) element; + } else if (element instanceof BinaryType) { + classFile = ((BinaryType) element).getClassFile(); + } else if(element instanceof BinaryMethod) { + classFile = ((BinaryMethod) element).getClassFile(); + } else { + return false; + } + return classFile.getResource() == null || isFromKotlinBinFolder(classFile.getResource()); + } + public static boolean isKotlinBinaryElement(@NotNull IJavaElement element) { IClassFile classFile; if (element instanceof IClassFile) { diff --git a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/script/ScriptTemplateContribution.kt b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/script/ScriptTemplateContribution.kt index acdd48212..14531dbdf 100644 --- a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/script/ScriptTemplateContribution.kt +++ b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/script/ScriptTemplateContribution.kt @@ -1,15 +1,22 @@ package org.jetbrains.kotlin.core.script +import org.eclipse.jdt.core.IClasspathEntry +import org.jetbrains.kotlin.core.model.KotlinScriptEnvironment import java.io.File import kotlin.reflect.KClass +import kotlin.script.experimental.api.ScriptCompilationConfiguration abstract class ScriptTemplateContribution { open val priority = 0 protected abstract fun loadTemplate(): KClass<*> + open fun createClasspath(environment: KotlinScriptEnvironment): Array = environment.javaProject.rawClasspath + val template by lazy { loadTemplate() } + open fun isNullable(propName: String, compilationConfig: ScriptCompilationConfiguration): Boolean = true + open fun scriptEnvironment(script: File): Map = emptyMap() } diff --git a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/script/template/ProjectScriptTemplateContribution.kt b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/script/template/ProjectScriptTemplateContribution.kt index 73130a849..ccf9ee850 100644 --- a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/script/template/ProjectScriptTemplateContribution.kt +++ b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/script/template/ProjectScriptTemplateContribution.kt @@ -17,15 +17,18 @@ package org.jetbrains.kotlin.core.script.template import org.eclipse.core.resources.IFile +import org.eclipse.core.runtime.Path +import org.eclipse.jdt.core.IClasspathEntry +import org.eclipse.jdt.core.JavaCore import org.jetbrains.kotlin.core.model.KotlinScriptEnvironment import org.jetbrains.kotlin.core.script.ScriptTemplateContribution -import org.jetbrains.kotlin.core.utils.ProjectUtils -import org.jetbrains.kotlin.core.utils.asResource -import org.jetbrains.kotlin.core.utils.isInClasspath -import org.jetbrains.kotlin.core.utils.javaProject +import org.jetbrains.kotlin.core.utils.* import java.io.File class ProjectScriptTemplateContribution : ScriptTemplateContribution() { + + override val priority: Int get() = Int.MAX_VALUE + override fun loadTemplate() = ProjectScriptTemplate::class override fun scriptEnvironment(script: File): Map { @@ -46,8 +49,17 @@ class ProjectScriptTemplateContribution : ScriptTemplateContribution() { val javaProject = file.javaProject ?: return emptyList() if (!file.isInClasspath) return emptyList() - val projectClasspath = ProjectUtils.collectClasspathWithDependenciesForLaunch(javaProject, false) + val projectClasspath = try { + ProjectUtils.collectClasspathWithDependenciesForLaunch(javaProject, false) + } catch (e: DependencyResolverException) { + e.resolvedFiles + } val outputFolders = ProjectUtils.getAllOutputFolders(javaProject).map { it.location.toFile() } return projectClasspath + outputFolders } + + override fun createClasspath(environment: KotlinScriptEnvironment): Array = + (environment.javaProject.rawClasspath + environment.definitionClasspath.map { + JavaCore.newLibraryEntry(Path(it.absolutePath), null, null) + }).distinctBy { it.path }.toTypedArray() } \ No newline at end of file diff --git a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/utils/DependencyResolverException.kt b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/utils/DependencyResolverException.kt new file mode 100644 index 000000000..23fa559fe --- /dev/null +++ b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/utils/DependencyResolverException.kt @@ -0,0 +1,5 @@ +package org.jetbrains.kotlin.core.utils + +import java.io.File + +class DependencyResolverException(val resolvedFiles: List) : RuntimeException() \ No newline at end of file diff --git a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/utils/ProjectUtils.kt b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/utils/ProjectUtils.kt index bccee1ed8..0da697d66 100644 --- a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/utils/ProjectUtils.kt +++ b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/utils/ProjectUtils.kt @@ -21,6 +21,7 @@ import org.eclipse.core.runtime.* import org.eclipse.jdt.core.IClasspathEntry import org.eclipse.jdt.core.IJavaProject import org.eclipse.jdt.core.JavaCore +import org.eclipse.jdt.core.JavaModelException import org.eclipse.jdt.launching.JavaRuntime import org.jetbrains.kotlin.core.KotlinClasspathContainer import org.jetbrains.kotlin.core.builder.KotlinPsiManager @@ -96,7 +97,7 @@ object ProjectUtils { fun getSourceFiles(project: IProject): List { var tempFiles = KotlinPsiManager.getFilesByProject(project) - if(tempFiles.any { !it.asFile.exists() }) { + if (tempFiles.any { !it.asFile.exists() }) { project.refreshLocal(IResource.DEPTH_INFINITE, NullProgressMonitor()) } tempFiles = KotlinPsiManager.getFilesByProject(project) @@ -144,10 +145,22 @@ object ProjectUtils { includeBinFolders: Boolean, entryPredicate: Function1 ): List { val orderedFiles = LinkedHashSet() + var wasError = false for (classpathEntry in javaProject.getResolvedClasspath(true)) { if (classpathEntry.entryKind == IClasspathEntry.CPE_PROJECT && includeDependencies) { - orderedFiles.addAll(expandDependentProjectClasspath(classpathEntry, includeBinFolders, entryPredicate)) + try { + orderedFiles.addAll( + expandDependentProjectClasspath( + classpathEntry, + includeBinFolders, + entryPredicate + ) + ) + } catch (e: DependencyResolverException) { + wasError = true + orderedFiles.addAll(e.resolvedFiles) + } } else { // Source folder or library if (entryPredicate.invoke(classpathEntry)) { orderedFiles.addAll(getFileByEntry(classpathEntry, javaProject)) @@ -155,6 +168,10 @@ object ProjectUtils { } } + if(wasError) { + throw DependencyResolverException(orderedFiles.toList()) + } + return orderedFiles.toList() } @@ -167,7 +184,6 @@ object ProjectUtils { ?.let { listOf(it) } ?: emptyList() - private fun expandDependentProjectClasspath( projectEntry: IClasspathEntry, includeBinFolders: Boolean, entryPredicate: Function1 @@ -177,36 +193,51 @@ object ProjectUtils { val javaProject = JavaCore.create(dependentProject) val orderedFiles = LinkedHashSet() + var wasError = false - for (classpathEntry in javaProject.getResolvedClasspath(true)) { - if (!(classpathEntry.isExported || classpathEntry.entryKind == IClasspathEntry.CPE_SOURCE)) { - continue - } + try { + for (classpathEntry in javaProject.getResolvedClasspath(true)) { + if (!(classpathEntry.isExported || classpathEntry.entryKind == IClasspathEntry.CPE_SOURCE)) { + continue + } - if (classpathEntry.entryKind == IClasspathEntry.CPE_PROJECT) { - orderedFiles.addAll(expandDependentProjectClasspath(classpathEntry, includeBinFolders, entryPredicate)) - } else { - if (entryPredicate.invoke(classpathEntry)) { - orderedFiles.addAll(getFileByEntry(classpathEntry, javaProject)) + if (classpathEntry.entryKind == IClasspathEntry.CPE_PROJECT) { + try { + orderedFiles.addAll( + expandDependentProjectClasspath( + classpathEntry, + includeBinFolders, + entryPredicate + ) + ) + } catch (e: DependencyResolverException) { + wasError = true + orderedFiles.addAll(e.resolvedFiles) + } + } else { + if (entryPredicate.invoke(classpathEntry)) { + orderedFiles.addAll(getFileByEntry(classpathEntry, javaProject)) + } } } + + + if (includeBinFolders) { + getAllOutputFolders(javaProject) + .map { it.location.toFile() } + .toCollection(orderedFiles) + } + } catch (e: JavaModelException) { + throw DependencyResolverException(emptyList()) } - if (includeBinFolders) { - getAllOutputFolders(javaProject) - .map { it.location.toFile() } - .toCollection(orderedFiles) + if(wasError) { + throw DependencyResolverException(orderedFiles.toList()) } return orderedFiles.toList() } - @JvmStatic - fun getSrcDirectories(javaProject: IJavaProject): List = - expandClasspath(javaProject, false, false) { entry -> - entry.entryKind == IClasspathEntry.CPE_SOURCE - } - @JvmStatic fun getSrcOutDirectories(javaProject: IJavaProject): List> { val projectOutput = javaProject.outputLocation diff --git a/kotlin-eclipse-ui/plugin.xml b/kotlin-eclipse-ui/plugin.xml index d2dffc685..495b556bb 100644 --- a/kotlin-eclipse-ui/plugin.xml +++ b/kotlin-eclipse-ui/plugin.xml @@ -29,7 +29,7 @@ icon="icons/newfile_wiz.gif" id="org.jetbrains.kotlin.wizard" name="Kotlin File" - preferredPerspectives="org.jetbrains.kotlin.perspective" + preferredPerspectives="org.jetbrains.kotlin.perspective,org.eclipse.jdt.ui.JavaPerspective" project="false"> Create a new Kotlin file @@ -49,6 +49,20 @@ Create a new Kotlin class + + + Create a new Kotlin sealed class + + - Create a new Kotlin trait + Create a new Kotlin interface + + + + + Create a new Kotlin sealed interface + + + Create a new Kotlin data class + + + + + Create a new Kotlin annotation + + + + + + + + @@ -370,14 +442,29 @@ label="References"> + + + + + + @@ -600,8 +687,7 @@ class="org.jetbrains.kotlin.ui.editors.KotlinElementHyperlinkDetector" id="org.jetbrains.kotlin.ui.editors.KotlinElementHyperlinkDetector" name="KotlinElementHyperlinkDetector" - targetId="org.jetbrains.kotlin.ui.editors.kotlinCode"> - + targetId="org.jetbrains.kotlin.ui.editors.kotlinCode"/> diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/eclipse/ui/utils/KotlinEclipseScope.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/eclipse/ui/utils/KotlinEclipseScope.kt new file mode 100644 index 000000000..c4bb8430e --- /dev/null +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/eclipse/ui/utils/KotlinEclipseScope.kt @@ -0,0 +1,7 @@ +package org.jetbrains.kotlin.eclipse.ui.utils + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob + +object KotlinEclipseScope : CoroutineScope by CoroutineScope(SupervisorJob() + Dispatchers.IO) diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/eclipse/ui/utils/KotlinImageProvider.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/eclipse/ui/utils/KotlinImageProvider.kt index a22e0a677..d17576d31 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/eclipse/ui/utils/KotlinImageProvider.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/eclipse/ui/utils/KotlinImageProvider.kt @@ -16,36 +16,28 @@ *******************************************************************************/ package org.jetbrains.kotlin.eclipse.ui.utils +import org.eclipse.jdt.ui.ISharedImages import org.eclipse.jdt.ui.JavaUI import org.eclipse.swt.graphics.Image -import org.eclipse.jdt.ui.ISharedImages -import org.jetbrains.kotlin.psi.KtVariableDeclaration -import org.jetbrains.kotlin.psi.KtFunction +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtElement -import org.jetbrains.kotlin.descriptors.VariableDescriptor -import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.PackageViewDescriptor -import org.jetbrains.kotlin.descriptors.PropertyDescriptor -import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor -import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor +import org.jetbrains.kotlin.psi.KtFunction +import org.jetbrains.kotlin.psi.KtVariableDeclaration -public object KotlinImageProvider { - public fun getImage(descriptor: DeclarationDescriptor): Image? { +object KotlinImageProvider { + fun getImage(descriptor: DeclarationDescriptor): Image? { return when(descriptor) { is ClassDescriptor, is TypeParameterDescriptor, is TypeAliasDescriptor -> getImageFromJavaUI(ISharedImages.IMG_OBJS_CLASS) is FunctionDescriptor -> getImageFromJavaUI(ISharedImages.IMG_OBJS_PUBLIC) is VariableDescriptor -> getImageFromJavaUI(ISharedImages.IMG_FIELD_PUBLIC) is PackageViewDescriptor -> getImageFromJavaUI(ISharedImages.IMG_OBJS_PACKAGE) - is PropertyDescriptor -> getImageFromJavaUI(ISharedImages.IMG_FIELD_PUBLIC) else -> null } } - public fun getImage(element: KtElement): Image? { + fun getImage(element: KtElement): Image? { return when(element) { is KtClassOrObject -> getImageFromJavaUI(ISharedImages.IMG_OBJS_CLASS) is KtFunction -> getImageFromJavaUI(ISharedImages.IMG_OBJS_PUBLIC) @@ -55,4 +47,4 @@ public object KotlinImageProvider { } private fun getImageFromJavaUI(imageName: String): Image = JavaUI.getSharedImages().getImage(imageName) -} \ No newline at end of file +} diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/BaseKotlinBuilderElement.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/BaseKotlinBuilderElement.kt index 505709d79..7120a562e 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/BaseKotlinBuilderElement.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/BaseKotlinBuilderElement.kt @@ -4,14 +4,19 @@ import org.eclipse.core.resources.IFile import org.eclipse.core.resources.IProject import org.eclipse.core.resources.IResourceDelta import org.eclipse.core.resources.ResourcesPlugin +import org.eclipse.core.runtime.Status +import org.eclipse.core.runtime.jobs.Job import org.eclipse.jdt.core.IJavaProject import org.eclipse.ui.PlatformUI import org.jetbrains.kotlin.core.asJava.KotlinLightClassGeneration import org.jetbrains.kotlin.core.builder.KotlinPsiManager import org.jetbrains.kotlin.core.model.KotlinScriptEnvironment +import org.jetbrains.kotlin.core.model.runJob +import org.jetbrains.kotlin.core.resolve.KotlinAnalyzer import org.jetbrains.kotlin.core.resolve.lang.java.structure.EclipseJavaElementUtil import org.jetbrains.kotlin.eclipse.ui.utils.EditorUtil import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics +import org.jetbrains.kotlin.ui.KotlinPluginUpdater import org.jetbrains.kotlin.ui.editors.KotlinFileEditor import org.jetbrains.kotlin.ui.editors.annotations.AnnotationManager import org.jetbrains.kotlin.ui.editors.annotations.DiagnosticAnnotation @@ -73,6 +78,51 @@ abstract class BaseKotlinBuilderElement { clearMarkersFromFiles(affectedFiles) addMarkersToProject(DiagnosticAnnotationUtil.INSTANCE.handleDiagnostics(diagnostics), affectedFiles) } + + protected fun postBuild(delta: IResourceDelta?, javaProject: IJavaProject) { + val allAffectedFiles = if (delta != null) getAllAffectedFiles(delta) else emptySet() + + if (allAffectedFiles.isNotEmpty()) { + if (isAllFilesApplicableForFilters(allAffectedFiles, javaProject)) { + return + } + } + + val kotlinAffectedFiles = + allAffectedFiles + .filterTo(hashSetOf()) { KotlinPsiManager.isKotlinSourceFile(it, javaProject) } + + val existingAffectedFiles = kotlinAffectedFiles.filter { it.exists() } + + commitFiles(existingAffectedFiles) + + KotlinLightClassGeneration.updateLightClasses(javaProject.project, kotlinAffectedFiles) + if (kotlinAffectedFiles.isNotEmpty()) { + + runJob("Checking for update", Job.DECORATE) { + KotlinPluginUpdater.kotlinFileEdited() + Status.OK_STATUS + } + } + + val ktFiles = existingAffectedFiles.map { KotlinPsiManager.getParsedFile(it) } + + val analysisResultWithProvider = if (ktFiles.isEmpty()) + KotlinAnalyzer.analyzeProject(javaProject.project) + else + KotlinAnalyzer.analyzeFiles(ktFiles) + + clearProblemAnnotationsFromOpenEditorsExcept(existingAffectedFiles) + updateLineMarkers(analysisResultWithProvider.analysisResult.bindingContext.diagnostics, existingAffectedFiles) + + runCancellableAnalysisFor(javaProject) { analysisResult -> + val projectFiles = KotlinPsiManager.getFilesByProject(javaProject.project) + updateLineMarkers( + analysisResult.bindingContext.diagnostics, + (projectFiles - existingAffectedFiles.toSet()).toList() + ) + } + } } private fun clearMarkersFromFiles(files: List) { diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/IncrementalKotlinBuilderElement.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/IncrementalKotlinBuilderElement.kt index 2e868e923..7bbb58cc1 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/IncrementalKotlinBuilderElement.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/IncrementalKotlinBuilderElement.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.core.compiler.KotlinCompilerUtils import org.jetbrains.kotlin.core.model.runJob import org.jetbrains.kotlin.core.resolve.KotlinAnalyzer import org.jetbrains.kotlin.ui.KotlinPluginUpdater +import org.jetbrains.kotlin.ui.launch.removeKotlinConsoles class IncrementalKotlinBuilderElement : BaseKotlinBuilderElement() { @@ -25,51 +26,10 @@ class IncrementalKotlinBuilderElement : BaseKotlinBuilderElement() { return null } + removeKotlinConsoles(javaProject) compileKotlinFilesIncrementally(javaProject) - val allAffectedFiles = if (delta != null) getAllAffectedFiles(delta) else emptySet() - - if (allAffectedFiles.isNotEmpty()) { - if (isAllFilesApplicableForFilters(allAffectedFiles, javaProject)) { - return null - } - } - - val kotlinAffectedFiles = - allAffectedFiles - .filter { KotlinPsiManager.isKotlinSourceFile(it, javaProject) } - .toSet() - - val existingAffectedFiles = kotlinAffectedFiles.filter { it.exists() } - - commitFiles(existingAffectedFiles) - - KotlinLightClassGeneration.updateLightClasses(javaProject.project, kotlinAffectedFiles) - if (kotlinAffectedFiles.isNotEmpty()) { - - runJob("Checking for update", Job.DECORATE) { - KotlinPluginUpdater.kotlinFileEdited() - Status.OK_STATUS - } - } - - val ktFiles = existingAffectedFiles.map { KotlinPsiManager.getParsedFile(it) } - - val analysisResultWithProvider = if (ktFiles.isEmpty()) - KotlinAnalyzer.analyzeProject(project) - else - KotlinAnalyzer.analyzeFiles(ktFiles) - - clearProblemAnnotationsFromOpenEditorsExcept(existingAffectedFiles) - updateLineMarkers(analysisResultWithProvider.analysisResult.bindingContext.diagnostics, existingAffectedFiles) - - runCancellableAnalysisFor(javaProject) { analysisResult -> - val projectFiles = KotlinPsiManager.getFilesByProject(javaProject.project) - updateLineMarkers( - analysisResult.bindingContext.diagnostics, - (projectFiles - existingAffectedFiles).toList() - ) - } + postBuild(delta, javaProject) return null } @@ -77,7 +37,7 @@ class IncrementalKotlinBuilderElement : BaseKotlinBuilderElement() { private fun compileKotlinFilesIncrementally(javaProject: IJavaProject) { val compilerResult: KotlinCompilerResult = KotlinCompilerUtils.compileProjectIncrementally(javaProject) if (!compilerResult.compiledCorrectly()) { - KotlinCompilerUtils.handleCompilerOutput(compilerResult.compilerOutput) + KotlinCompilerUtils.handleCompilerOutput(KotlinCompilerUtils.CompilerOutputWithProject(compilerResult.compilerOutput, javaProject)) } } } \ No newline at end of file diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/KotlinBuilderElement.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/KotlinBuilderElement.kt index 4818651a9..5b753c947 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/KotlinBuilderElement.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/KotlinBuilderElement.kt @@ -15,12 +15,14 @@ import org.jetbrains.kotlin.core.compiler.KotlinCompilerUtils import org.jetbrains.kotlin.core.model.runJob import org.jetbrains.kotlin.core.resolve.KotlinAnalyzer import org.jetbrains.kotlin.ui.KotlinPluginUpdater +import org.jetbrains.kotlin.ui.launch.removeKotlinConsoles class KotlinBuilderElement : BaseKotlinBuilderElement() { override fun build(project: IProject, delta: IResourceDelta?, kind: Int): Array? { val javaProject = JavaCore.create(project) if (isBuildingForLaunch()) { + removeKotlinConsoles(javaProject) compileKotlinFiles(javaProject) return null } @@ -30,49 +32,7 @@ class KotlinBuilderElement : BaseKotlinBuilderElement() { return null } - val allAffectedFiles = if (delta != null) getAllAffectedFiles(delta) else emptySet() - - if (allAffectedFiles.isNotEmpty()) { - if (isAllFilesApplicableForFilters(allAffectedFiles, javaProject)) { - return null - } - } - - val kotlinAffectedFiles = - allAffectedFiles - .filter { KotlinPsiManager.isKotlinSourceFile(it, javaProject) } - .toSet() - - val existingAffectedFiles = kotlinAffectedFiles.filter { it.exists() } - - commitFiles(existingAffectedFiles) - - KotlinLightClassGeneration.updateLightClasses(javaProject.project, kotlinAffectedFiles) - if (kotlinAffectedFiles.isNotEmpty()) { - - runJob("Checking for update", Job.DECORATE) { - KotlinPluginUpdater.kotlinFileEdited() - Status.OK_STATUS - } - } - - val ktFiles = existingAffectedFiles.map { KotlinPsiManager.getParsedFile(it) } - - val analysisResultWithProvider = if (ktFiles.isEmpty()) - KotlinAnalyzer.analyzeProject(project) - else - KotlinAnalyzer.analyzeFiles(ktFiles) - - clearProblemAnnotationsFromOpenEditorsExcept(existingAffectedFiles) - updateLineMarkers(analysisResultWithProvider.analysisResult.bindingContext.diagnostics, existingAffectedFiles) - - runCancellableAnalysisFor(javaProject) { analysisResult -> - val projectFiles = KotlinPsiManager.getFilesByProject(javaProject.project) - updateLineMarkers( - analysisResult.bindingContext.diagnostics, - (projectFiles - existingAffectedFiles).toList() - ) - } + postBuild(delta, javaProject) return null } @@ -87,7 +47,7 @@ class KotlinBuilderElement : BaseKotlinBuilderElement() { private fun compileKotlinFiles(javaProject: IJavaProject) { val compilerResult: KotlinCompilerResult = KotlinCompilerUtils.compileWholeProject(javaProject) if (!compilerResult.compiledCorrectly()) { - KotlinCompilerUtils.handleCompilerOutput(compilerResult.compilerOutput) + KotlinCompilerUtils.handleCompilerOutput(KotlinCompilerUtils.CompilerOutputWithProject(compilerResult.compilerOutput, javaProject)) } } } \ No newline at end of file diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/ResourceChangeListener.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/ResourceChangeListener.kt index c801eb3c7..faea7b6fe 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/ResourceChangeListener.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/ResourceChangeListener.kt @@ -1,65 +1,58 @@ /******************************************************************************* -* Copyright 2000-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. -* -*******************************************************************************/ + * Copyright 2000-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.ui.builder -import org.eclipse.core.resources.IFile -import org.eclipse.core.resources.IProject -import org.eclipse.core.resources.IResource -import org.eclipse.core.resources.IResourceChangeEvent -import org.eclipse.core.resources.IResourceChangeListener -import org.eclipse.core.resources.IResourceDelta -import org.eclipse.core.resources.IResourceDeltaVisitor +import org.eclipse.core.resources.* +import org.eclipse.jdt.core.JavaCore import org.jetbrains.kotlin.core.builder.KotlinPsiManager -import org.jetbrains.kotlin.core.model.KotlinEnvironment -import org.jetbrains.kotlin.core.model.KotlinNature -import org.jetbrains.kotlin.core.model.setKotlinBuilderBeforeJavaBuilder +import org.jetbrains.kotlin.core.model.* +import org.jetbrains.kotlin.core.utils.ProjectUtils +import org.jetbrains.kotlin.core.utils.asFile +import kotlin.script.experimental.host.FileScriptSource -public class ResourceChangeListener : IResourceChangeListener { - override public fun resourceChanged(event: IResourceChangeEvent) { +class ResourceChangeListener : IResourceChangeListener { + override fun resourceChanged(event: IResourceChangeEvent) { val eventResource = event.resource if (eventResource != null) { val type = event.type if (type == IResourceChangeEvent.PRE_CLOSE || type == IResourceChangeEvent.PRE_DELETE) { updateManager(eventResource, IResourceDelta.REMOVED) } - + return } - - val delta = event.delta - if (delta != null) { - delta.accept(ProjectChangeListener()) - } + + event.delta?.accept(ProjectChangeListener()) } } class ProjectChangeListener : IResourceDeltaVisitor { - override public fun visit(delta: IResourceDelta) : Boolean { + override fun visit(delta: IResourceDelta): Boolean { val resource = delta.resource if ((delta.flags and IResourceDelta.OPEN) != 0) { if (resource is IProject && resource.isOpen) { return updateManager(resource, IResourceDelta.ADDED) } } - - if (delta.getKind() == IResourceDelta.CHANGED) { + + if (delta.kind == IResourceDelta.CHANGED) { return true } - + return updateManager(resource, delta.kind) } } @@ -67,30 +60,48 @@ class ProjectChangeListener : IResourceDeltaVisitor { private fun updateManager(resource: IResource, deltaKind: Int): Boolean { return when (resource) { is IFile -> { + //IF we got a source file we update the psi! if (KotlinPsiManager.isKotlinSourceFile(resource)) { KotlinPsiManager.updateProjectPsiSources(resource, deltaKind) } - + + //If we got a script file and it was deleted we remove the environment. + if(EclipseScriptDefinitionProvider.isScript(FileScriptSource(resource.asFile))) { + if(deltaKind == IResourceDelta.REMOVED) { + KotlinScriptEnvironment.removeKotlinEnvironment(resource) + } + } false } - + is IProject -> { - if (!resource.isAccessible || !KotlinNature.hasKotlinNature(resource)) { - return false - } - + //If we got a project removed we need to remove all environments that represent + //this project or dependet on that project. For simplicity we remove all environments for now. if (deltaKind == IResourceDelta.REMOVED) { KotlinPsiManager.removeProjectFromManager(resource) - KotlinEnvironment.removeEnvironment(resource) + KotlinEnvironment.removeEnvironmentIf { it: KotlinCommonEnvironment -> + val tempDepPrjs = ProjectUtils.getDependencyProjects(it.javaProject) + resource in tempDepPrjs + } + KotlinScriptEnvironment.removeEnvironmentIf { it: KotlinCommonEnvironment -> + val tempDepPrjs = ProjectUtils.getDependencyProjects(it.javaProject) + resource in tempDepPrjs + } } - - if (deltaKind == IResourceDelta.ADDED && KotlinNature.hasKotlinBuilder(resource)) { - setKotlinBuilderBeforeJavaBuilder(resource) + + //If a project was added we need to make sure the kotlin builder is invoked before the java builder. + //Also we need to refresh all environments that had errors before, as they could be resolved by the new project. + if (deltaKind == IResourceDelta.ADDED) { + if(KotlinNature.hasKotlinBuilder(resource)) { + setKotlinBuilderBeforeJavaBuilder(resource) + } + KotlinEnvironment.removeEnvironmentIf { it.hasError } + KotlinScriptEnvironment.removeEnvironmentIf { it.hasError } } - + false } - + else -> true // folder } } \ No newline at end of file diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/commands/findReferences/KotlinFindReferencesAction.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/commands/findReferences/KotlinFindReferencesAction.kt index c4877eac3..e583156f3 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/commands/findReferences/KotlinFindReferencesAction.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/commands/findReferences/KotlinFindReferencesAction.kt @@ -18,7 +18,6 @@ package org.jetbrains.kotlin.ui.commands.findReferences import org.eclipse.core.commands.AbstractHandler import org.eclipse.core.commands.ExecutionEvent -import org.eclipse.core.resources.IFile import org.eclipse.jdt.core.IJavaProject import org.eclipse.jdt.core.JavaCore import org.eclipse.jdt.core.search.IJavaSearchConstants @@ -45,14 +44,14 @@ abstract class KotlinFindReferencesHandler : AbstractHandler() { override fun execute(event: ExecutionEvent): Any? { val editor = HandlerUtil.getActiveEditor(event) if (editor !is KotlinCommonEditor) return null - - getAction(editor).run(editor.getViewer().getSelectionProvider().getSelection() as ITextSelection) - + + getAction(editor).run(editor.viewer.selectionProvider.selection as ITextSelection) + return null } - + abstract fun getAction(editor: KotlinCommonEditor): KotlinFindReferencesAction - + } class KotlinFindReferencesInProjectHandler : KotlinFindReferencesHandler() { @@ -61,83 +60,139 @@ class KotlinFindReferencesInProjectHandler : KotlinFindReferencesHandler() { } } + class KotlinFindReferencesInWorkspaceHandler : KotlinFindReferencesHandler() { override fun getAction(editor: KotlinCommonEditor): KotlinFindReferencesAction { return KotlinFindReferencesInWorkspaceAction(editor) } } -public class KotlinFindReferencesInProjectAction(editor: KotlinCommonEditor) : KotlinFindReferencesAction(editor) { +class KotlinFindImplementationsInWorkspaceHandler : KotlinFindReferencesHandler() { + + override fun getAction(editor: KotlinCommonEditor): KotlinFindReferencesAction { + return KotlinFindImplementationsInWorkspaceAction(editor) + } +} + +class KotlinFindImplementationsInProjectHandler : KotlinFindReferencesHandler() { + + override fun getAction(editor: KotlinCommonEditor): KotlinFindReferencesAction { + return KotlinFindImplementationsInProjectAction(editor) + } +} + +class KotlinFindImplementationsInProjectAction(editor: KotlinCommonEditor) : KotlinFindReferencesAction(editor) { + + init { + actionDefinitionId = IJavaEditorActionDefinitionIds.SEARCH_IMPLEMENTORS_IN_WORKSPACE + text = SearchMessages.Search_FindImplementorsAction_label + toolTipText = SearchMessages.Search_FindImplementorsAction_tooltip + imageDescriptor = JavaPluginImages.DESC_OBJS_SEARCH_REF + PlatformUI.getWorkbench().helpSystem.setHelp(this, IJavaHelpContextIds.FIND_IMPLEMENTORS_IN_WORKSPACE_ACTION) + } + + override fun createScopeQuerySpecification(jetElement: KtElement): QuerySpecification { + val factory = JavaSearchScopeFactory.getInstance() + return createQuerySpecification( + jetElement, + factory.createJavaProjectSearchScope(javaProject, false), + factory.getProjectScopeDescription(javaProject, false), + IMPLEMENTORS_LIMIT_TO + ) + } + + companion object { + const val IMPLEMENTORS_LIMIT_TO = 48 + } +} + +class KotlinFindImplementationsInWorkspaceAction(editor: KotlinCommonEditor) : KotlinFindReferencesAction(editor) { + + init { + actionDefinitionId = IJavaEditorActionDefinitionIds.SEARCH_IMPLEMENTORS_IN_WORKSPACE + text = SearchMessages.Search_FindImplementorsAction_label + toolTipText = SearchMessages.Search_FindImplementorsAction_tooltip + imageDescriptor = JavaPluginImages.DESC_OBJS_SEARCH_REF + PlatformUI.getWorkbench().helpSystem.setHelp(this, IJavaHelpContextIds.FIND_IMPLEMENTORS_IN_WORKSPACE_ACTION) + } + + override fun createScopeQuerySpecification(jetElement: KtElement): QuerySpecification { + val factory = JavaSearchScopeFactory.getInstance() + return createQuerySpecification( + jetElement, + factory.createWorkspaceScope(false), + factory.getWorkspaceScopeDescription(false), + KotlinFindImplementationsInProjectAction.IMPLEMENTORS_LIMIT_TO + ) + } +} + +class KotlinFindReferencesInProjectAction(editor: KotlinCommonEditor) : KotlinFindReferencesAction(editor) { init { - setActionDefinitionId(IJavaEditorActionDefinitionIds.SEARCH_REFERENCES_IN_PROJECT) - setText(SearchMessages.Search_FindReferencesInProjectAction_label) - setToolTipText(SearchMessages.Search_FindReferencesInProjectAction_tooltip) - setImageDescriptor(JavaPluginImages.DESC_OBJS_SEARCH_REF) - PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IJavaHelpContextIds.FIND_REFERENCES_IN_PROJECT_ACTION) + actionDefinitionId = IJavaEditorActionDefinitionIds.SEARCH_REFERENCES_IN_PROJECT + text = SearchMessages.Search_FindReferencesInProjectAction_label + toolTipText = SearchMessages.Search_FindReferencesInProjectAction_tooltip + imageDescriptor = JavaPluginImages.DESC_OBJS_SEARCH_REF + PlatformUI.getWorkbench().helpSystem.setHelp(this, IJavaHelpContextIds.FIND_REFERENCES_IN_PROJECT_ACTION) } - + companion object { - val ACTION_ID = "SearchReferencesInProject" + const val ACTION_ID = "SearchReferencesInProject" } - + override fun createScopeQuerySpecification(jetElement: KtElement): QuerySpecification { val factory = JavaSearchScopeFactory.getInstance() return createQuerySpecification( - jetElement, - factory.createJavaProjectSearchScope(javaProject, false), - factory.getProjectScopeDescription(javaProject, false)) + jetElement, + factory.createJavaProjectSearchScope(javaProject, false), + factory.getProjectScopeDescription(javaProject, false) + ) } } -public class KotlinFindReferencesInWorkspaceAction(editor: KotlinCommonEditor) : KotlinFindReferencesAction(editor) { +class KotlinFindReferencesInWorkspaceAction(editor: KotlinCommonEditor) : KotlinFindReferencesAction(editor) { init { - setActionDefinitionId(IJavaEditorActionDefinitionIds.SEARCH_REFERENCES_IN_WORKSPACE) - setText(SearchMessages.Search_FindReferencesAction_label) - setToolTipText(SearchMessages.Search_FindReferencesAction_tooltip) - setImageDescriptor(JavaPluginImages.DESC_OBJS_SEARCH_REF) - PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IJavaHelpContextIds.FIND_REFERENCES_IN_WORKSPACE_ACTION) + actionDefinitionId = IJavaEditorActionDefinitionIds.SEARCH_REFERENCES_IN_WORKSPACE + text = SearchMessages.Search_FindReferencesAction_label + toolTipText = SearchMessages.Search_FindReferencesAction_tooltip + imageDescriptor = JavaPluginImages.DESC_OBJS_SEARCH_REF + PlatformUI.getWorkbench().helpSystem.setHelp(this, IJavaHelpContextIds.FIND_REFERENCES_IN_WORKSPACE_ACTION) } - + companion object { - val ACTION_ID = "SearchReferencesInWorkspace" + const val ACTION_ID = "SearchReferencesInWorkspace" } - + override fun createScopeQuerySpecification(jetElement: KtElement): QuerySpecification { val factory = JavaSearchScopeFactory.getInstance() return createQuerySpecification( - jetElement, - factory.createWorkspaceScope(false), - factory.getWorkspaceScopeDescription(false)) + jetElement, + factory.createWorkspaceScope(false), + factory.getWorkspaceScopeDescription(false) + ) } } -abstract class KotlinFindReferencesAction(val editor: KotlinCommonEditor) : SelectionDispatchAction(editor.getSite()) { +abstract class KotlinFindReferencesAction(val editor: KotlinCommonEditor) : SelectionDispatchAction(editor.site) { var javaProject: IJavaProject by Delegates.notNull() - - override public fun run(selection: ITextSelection) { - val file = editor.eclipseFile - if (file == null) return - - javaProject = JavaCore.create(file.getProject()) - - val jetElement = EditorUtil.getJetElement(editor, selection.getOffset()) - if (jetElement == null) return - + + override fun run(selection: ITextSelection) { + val file = editor.eclipseFile ?: return + + javaProject = JavaCore.create(file.project) + + val jetElement = EditorUtil.getJetElement(editor, selection.offset) ?: return + val querySpecification = createScopeQuerySpecification(jetElement) val query = JavaSearchQuery(querySpecification) - + SearchUtil.runQueryInBackground(query) } - + abstract fun createScopeQuerySpecification(jetElement: KtElement): QuerySpecification - - private fun getFile(event: ExecutionEvent): IFile? { - val activeEditor = HandlerUtil.getActiveEditor(event) - return EditorUtil.getFile(activeEditor) - } } -fun createQuerySpecification(jetElement: KtElement, scope: IJavaSearchScope, description: String): QuerySpecification { +fun createQuerySpecification(jetElement: KtElement, scope: IJavaSearchScope, description: String, limitTo: Int = IJavaSearchConstants.REFERENCES): QuerySpecification { val sourceElements = jetElement.resolveToSourceDeclaration() - return KotlinJavaQuerySpecification(sourceElements, IJavaSearchConstants.REFERENCES, scope, description) -} \ No newline at end of file + return KotlinJavaQuerySpecification(sourceElements, limitTo, scope, description) +} diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/commands/findReferences/querySpecifications.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/commands/findReferences/querySpecifications.kt index a163f41fe..287c659e2 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/commands/findReferences/querySpecifications.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/commands/findReferences/querySpecifications.kt @@ -1,15 +1,14 @@ package org.jetbrains.kotlin.ui.commands.findReferences +import org.eclipse.core.resources.IFile +import org.eclipse.core.resources.ResourcesPlugin +import org.eclipse.core.runtime.IPath +import org.eclipse.jdt.core.IJavaElement import org.eclipse.jdt.core.search.IJavaSearchConstants -import org.eclipse.jdt.ui.search.PatternQuerySpecification import org.eclipse.jdt.core.search.IJavaSearchScope -import org.jetbrains.kotlin.psi.KtElement -import org.eclipse.jdt.core.IJavaElement -import org.eclipse.core.runtime.IPath -import org.eclipse.jdt.ui.search.ElementQuerySpecification -import org.eclipse.core.resources.ResourcesPlugin -import org.eclipse.core.resources.IFile +import org.eclipse.jdt.ui.search.PatternQuerySpecification import org.jetbrains.kotlin.descriptors.SourceElement +import org.jetbrains.kotlin.psi.KtElement interface KotlinAndJavaSearchable { val sourceElements: List @@ -29,7 +28,7 @@ class KotlinScopedQuerySpecification( override val sourceElements: List, override val searchScope: List, limitTo: Int, - description: String) : KotlinDummyQuerySpecification(EmptyJavaSearchScope, description, limitTo), + description: String) : KotlinDummyQuerySpecification(EmptyJavaSearchScope, description, limitTo), KotlinAndJavaSearchable, KotlinScoped class KotlinOnlyQuerySpecification( @@ -41,35 +40,37 @@ class KotlinOnlyQuerySpecification( // After passing this query specification to java, it will try to find some usages and to ensure that nothing will found // before KotlinQueryParticipant here is using dummy element '------------' abstract class KotlinDummyQuerySpecification( - searchScope: IJavaSearchScope, - description: String, - limitTo: Int = IJavaSearchConstants.REFERENCES) : PatternQuerySpecification( - "Kotlin Find References", - IJavaSearchConstants.CLASS, - true, - limitTo, - searchScope, - description) + searchScope: IJavaSearchScope, + description: String, + limitTo: Int = IJavaSearchConstants.REFERENCES +) : PatternQuerySpecification( + "Kotlin Find ${if (limitTo == IJavaSearchConstants.REFERENCES) "References" else if (limitTo == KotlinFindImplementationsInProjectAction.IMPLEMENTORS_LIMIT_TO) "Implementations" else "???"}", + IJavaSearchConstants.CLASS, + true, + limitTo, + searchScope, + description +) object EmptyJavaSearchScope : IJavaSearchScope { override fun setIncludesClasspaths(includesClasspaths: Boolean) { } - + override fun setIncludesBinaries(includesBinaries: Boolean) { } - + override fun enclosingProjectsAndJars(): Array { val base = ResourcesPlugin.getWorkspace().getRoot().getLocation() return ResourcesPlugin.getWorkspace().getRoot().getProjects() .map { it.getLocation().makeRelativeTo(base) } .toTypedArray() } - + override fun includesBinaries(): Boolean = false - + override fun includesClasspaths(): Boolean = false - + override fun encloses(resourcePath: String?): Boolean = false - + override fun encloses(element: IJavaElement?): Boolean = false -} \ No newline at end of file +} diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/debug/commands/KotlinStepIntoSelectionHandler.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/debug/commands/KotlinStepIntoSelectionHandler.kt index 372ca4895..78a93c467 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/debug/commands/KotlinStepIntoSelectionHandler.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/debug/commands/KotlinStepIntoSelectionHandler.kt @@ -41,10 +41,10 @@ import org.eclipse.debug.core.model.IThread import org.jetbrains.kotlin.core.log.KotlinLogger import org.eclipse.jdt.core.IType -public class KotlinStepIntoSelectionHandler : AbstractHandler() { +class KotlinStepIntoSelectionHandler : AbstractHandler() { override fun execute(event: ExecutionEvent): Any? { val editor = HandlerUtil.getActiveEditor(event) as KotlinFileEditor - val selection = editor.getEditorSite().getSelectionProvider().getSelection() + val selection = editor.editorSite.selectionProvider.selection if (selection is ITextSelection) { stepIntoSelection(editor, selection) } @@ -55,23 +55,20 @@ public class KotlinStepIntoSelectionHandler : AbstractHandler() { private fun stepIntoSelection(editor: KotlinFileEditor, selection: ITextSelection) { val frame = EvaluationContextManager.getEvaluationContext(editor) - if (frame == null || !frame.isSuspended()) return + if (frame == null || !frame.isSuspended) return - val psiElement = EditorUtil.getPsiElement(editor, selection.getOffset()) - if (psiElement == null) return - - val expression = getReferenceExpression(psiElement) - if (expression == null) return - - val sourceElements = createReferences(expression).resolveToSourceElements() + val psiElement = EditorUtil.getPsiElement(editor, selection.offset) ?: return + + val expression = getReferenceExpression(psiElement) ?: return + + val sourceElements = createReferences(expression).resolveToSourceElements(expression.containingKtFile) val javaElements = sourceElementsToLightElements(sourceElements) if (javaElements.size > 1) { KotlinLogger.logWarning("There are more than one java element for $sourceElements") return } - val element = javaElements.first() - val method = when (element) { + val method = when (val element = javaElements.first()) { is IMethod -> element is IType -> element.getMethod(element.elementName, emptyArray()) else -> null @@ -81,8 +78,8 @@ private fun stepIntoSelection(editor: KotlinFileEditor, selection: ITextSelectio } private fun stepIntoElement(method: IMethod, frame: IJavaStackFrame, selection: ITextSelection, editor: KotlinFileEditor) { - if (selection.getStartLine() + 1 == frame.getLineNumber()) { - val handler = StepIntoSelectionHandler(frame.getThread() as IJavaThread, frame, method) + if (selection.startLine + 1 == frame.lineNumber) { + val handler = StepIntoSelectionHandler(frame.thread as IJavaThread, frame, method) handler.step() } else { val refMethod = StepIntoSelectionUtils::class.java.getDeclaredMethod( @@ -92,7 +89,7 @@ private fun stepIntoElement(method: IMethod, frame: IJavaStackFrame, selection: ITextSelection::class.java, IThread::class.java, IMethod::class.java) - refMethod.setAccessible(true) - refMethod.invoke(null, editor, frame.getReceivingTypeName(), selection, frame.getThread(), method) + refMethod.isAccessible = true + refMethod.invoke(null, editor, frame.receivingTypeName, selection, frame.thread, method) } -} \ No newline at end of file +} diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/FileEditorConfiguration.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/FileEditorConfiguration.kt index 1bafbd6e5..addc38422 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/FileEditorConfiguration.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/FileEditorConfiguration.kt @@ -30,6 +30,7 @@ import org.eclipse.jface.text.reconciler.MonoReconciler import org.eclipse.jface.text.source.ISourceViewer import org.eclipse.swt.graphics.Color import org.jetbrains.kotlin.ui.editors.codeassist.KotlinCompletionProcessor +import org.jetbrains.kotlin.ui.editors.codeassist.KotlinContextInfoContentAssistProcessor class FileEditorConfiguration(colorManager: IColorManager, private val fileEditor: KotlinEditor, @@ -49,11 +50,13 @@ class FileEditorConfiguration(colorManager: IColorManager, override fun getAutoEditStrategies(sourceViewer: ISourceViewer, contentType: String) = arrayOf(KotlinAutoIndentStrategy(fileEditor)) - override fun getContentAssistant(sourceViewer: ISourceViewer): IContentAssistant? = ContentAssistant().apply { + override fun getContentAssistant(sourceViewer: ISourceViewer): IContentAssistant = ContentAssistant(true).apply { KotlinCompletionProcessor.createKotlinCompletionProcessors(fileEditor, this).forEach { addContentAssistProcessor(it, IDocument.DEFAULT_CONTENT_TYPE) } - + + addContentAssistProcessor(KotlinContextInfoContentAssistProcessor(fileEditor), IDocument.DEFAULT_CONTENT_TYPE) + val autoActivation = fPreferenceStore.getBoolean(PreferenceConstants.CODEASSIST_AUTOACTIVATION) enableAutoActivation(autoActivation) @@ -83,4 +86,4 @@ class FileEditorConfiguration(colorManager: IColorManager, fun getColor(store: IPreferenceStore, key: String, manager: IColorManager): Color { val rgb = PreferenceConverter.getColor(store, key) return manager.getColor(rgb) -} \ No newline at end of file +} diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/KotlinElementHyperlink.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/KotlinElementHyperlink.kt index b0c0d6fc0..73ca168ca 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/KotlinElementHyperlink.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/KotlinElementHyperlink.kt @@ -18,25 +18,37 @@ package org.jetbrains.kotlin.ui.editors import org.eclipse.jface.text.IRegion import org.eclipse.jface.text.hyperlink.IHyperlink -import org.jetbrains.kotlin.psi.KtReferenceExpression +import org.jetbrains.kotlin.core.utils.getBindingContext +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny +import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.psi.KtPropertyDelegate +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.ui.editors.codeassist.getParentOfType import org.jetbrains.kotlin.ui.editors.navigation.KotlinOpenDeclarationAction +import org.jetbrains.kotlin.ui.editors.navigation.gotoElement -class KotlinElementHyperlink( - private val openAction: KotlinOpenDeclarationAction, +class KTGenericHyperLink( private val region: IRegion, - private val refExpression: KtReferenceExpression? = null -) : IHyperlink { + private val label: String, + private val editor: KotlinEditor, + private val targetDescriptor: DeclarationDescriptor, + private val fromElement: KtElement +) : + IHyperlink { override fun getHyperlinkRegion(): IRegion = region override fun getTypeLabel(): String? = null - override fun getHyperlinkText(): String = HYPERLINK_TEXT + override fun getHyperlinkText(): String = label override fun open() { - refExpression?.let { openAction.run(it) } ?: openAction.run() + val tempPrj = editor.javaProject ?: return + gotoElement(targetDescriptor, fromElement, editor, tempPrj) } - companion object { - private const val HYPERLINK_TEXT = "Open Kotlin Declaration" - } -} \ No newline at end of file +} + +private const val HYPERLINK_TEXT = "Open Kotlin Declaration" diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/KotlinElementHyperlinkDetector.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/KotlinElementHyperlinkDetector.kt index db82e7bf7..f6d275f34 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/KotlinElementHyperlinkDetector.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/KotlinElementHyperlinkDetector.kt @@ -23,14 +23,16 @@ import org.eclipse.jface.text.Region import org.eclipse.jface.text.hyperlink.AbstractHyperlinkDetector import org.eclipse.jface.text.hyperlink.IHyperlink import org.eclipse.ui.texteditor.ITextEditor +import org.jetbrains.kotlin.core.references.createReferences +import org.jetbrains.kotlin.core.utils.getBindingContext import org.jetbrains.kotlin.eclipse.ui.utils.EditorUtil import org.jetbrains.kotlin.eclipse.ui.utils.LineEndUtil import org.jetbrains.kotlin.psi.KtArrayAccessExpression import org.jetbrains.kotlin.psi.KtCallExpression import org.jetbrains.kotlin.psi.KtOperationReferenceExpression import org.jetbrains.kotlin.psi.KtReferenceExpression +import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.ui.editors.navigation.KotlinOpenDeclarationAction -import org.jetbrains.kotlin.ui.editors.navigation.KotlinOpenDeclarationAction.Companion.OPEN_EDITOR_TEXT @Suppress("unused") class KotlinElementHyperlinkDetector : AbstractHyperlinkDetector() { @@ -38,12 +40,11 @@ class KotlinElementHyperlinkDetector : AbstractHyperlinkDetector() { textViewer: ITextViewer, region: IRegion?, canShowMultipleHyperlinks: Boolean - ): Array? { + ): Array? { val textEditor = getAdapter(ITextEditor::class.java) if (region == null || textEditor !is KotlinEditor) return null - val openAction = textEditor.getAction(OPEN_EDITOR_TEXT) as? KotlinOpenDeclarationAction - ?: return null + val tempProject = textEditor.javaProject ?: return null val tempDocument = textEditor.documentProvider.getDocument(textEditor.editorInput) @@ -60,17 +61,37 @@ class KotlinElementHyperlinkDetector : AbstractHyperlinkDetector() { val tempOffset = LineEndUtil.convertLfOffsetForMixedDocument(tempDocument, tempReferenceExpression.textOffset) wordRegion = Region(tempOffset, tempReferenceExpression.textLength) - } - else if (tempReferenceExpression is KtCallExpression) { - if(textEditor.javaProject != null && KotlinOpenDeclarationAction.getNavigationData(tempReferenceExpression, textEditor.javaProject!!) != null) { + } else if (tempReferenceExpression is KtCallExpression) { + if (KotlinOpenDeclarationAction.getNavigationData(tempReferenceExpression, tempProject) != null) { val tempOffset = LineEndUtil.convertLfOffsetForMixedDocument(tempDocument, tempReferenceExpression.textOffset) wordRegion = Region(tempOffset, tempReferenceExpression.textLength) } } } - tempReferenceExpression ?: EditorUtil.getReferenceExpression(textEditor, region.offset) ?: return null - return arrayOf(KotlinElementHyperlink(openAction, wordRegion, tempReferenceExpression)) + val tempRenderer = DescriptorRenderer.SHORT_NAMES_IN_TYPES.withOptions { + modifiers = emptySet() + includeAdditionalModifiers = false + } + + val tempRef = tempReferenceExpression + ?: EditorUtil.getReferenceExpression(textEditor, region.offset) + ?: EditorUtil.getJetElement(textEditor, region.offset) + ?: return null + + val context = tempRef.getBindingContext() + val tempTargets = createReferences(tempRef) + .flatMap { it.getTargetDescriptors(context) } + + return tempTargets.map { + KTGenericHyperLink( + wordRegion, + tempRenderer.render(it), + textEditor, + it, + tempRef + ) + }.toTypedArray().takeIf { it.isNotEmpty() } } -} \ No newline at end of file +} diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/codeassist/CompletionElementType.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/codeassist/CompletionElementType.kt new file mode 100644 index 000000000..a9e71093e --- /dev/null +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/codeassist/CompletionElementType.kt @@ -0,0 +1,34 @@ +package org.jetbrains.kotlin.ui.editors.codeassist + +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension + +enum class CompletionElementType { + KFUNCTION_TOP, + KVARIABLE_TOP, + KFUNCTION_EXT, + KVARIABLE_EXT, + KFUNCTION, + KVARIABLE, + KCLASS_OBJECT, + UNKNOWN; + + companion object { + fun from(descriptor: DeclarationDescriptor) : CompletionElementType { + return when(descriptor) { + is ClassDescriptor, is TypeParameterDescriptor, is TypeAliasDescriptor -> KCLASS_OBJECT + is FunctionDescriptor -> when { + descriptor.isExtension -> KFUNCTION_EXT + descriptor.isTopLevelInPackage() -> KFUNCTION_TOP + else -> KFUNCTION + } + is VariableDescriptor -> when { + descriptor.isExtension -> KVARIABLE_EXT + descriptor.isTopLevelInPackage() -> KVARIABLE_TOP + else -> KVARIABLE + } + else -> UNKNOWN + } + } + } +} \ No newline at end of file diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/codeassist/KotlinCompletionProcessor.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/codeassist/KotlinCompletionProcessor.kt index ea1c396bc..8ce424cbc 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/codeassist/KotlinCompletionProcessor.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/codeassist/KotlinCompletionProcessor.kt @@ -1,19 +1,19 @@ /******************************************************************************* -* Copyright 2000-2014 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. -* -*******************************************************************************/ + * Copyright 2000-2014 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.ui.editors.codeassist import com.intellij.psi.PsiElement @@ -26,17 +26,9 @@ import org.eclipse.jdt.internal.ui.viewsupport.JavaElementImageProvider import org.eclipse.jface.text.IRegion import org.eclipse.jface.text.ITextViewer import org.eclipse.jface.text.Region -import org.eclipse.jface.text.contentassist.ContentAssistEvent -import org.eclipse.jface.text.contentassist.ContentAssistant -import org.eclipse.jface.text.contentassist.ICompletionListener -import org.eclipse.jface.text.contentassist.ICompletionProposal -import org.eclipse.jface.text.contentassist.ICompletionProposalSorter -import org.eclipse.jface.text.contentassist.IContentAssistProcessor -import org.eclipse.jface.text.contentassist.IContextInformation -import org.eclipse.jface.text.contentassist.IContextInformationValidator +import org.eclipse.jface.text.contentassist.* import org.eclipse.jface.text.templates.TemplateContext import org.eclipse.jface.text.templates.TemplateProposal -import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.eclipse.ui.utils.KotlinImageProvider import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver @@ -44,26 +36,35 @@ import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtSimpleNameExpression import org.jetbrains.kotlin.renderer.DescriptorRenderer -import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.ui.editors.KotlinEditor import org.jetbrains.kotlin.ui.editors.completion.KotlinCompletionUtils import org.jetbrains.kotlin.ui.editors.templates.KotlinApplicableTemplateContext import org.jetbrains.kotlin.ui.editors.templates.KotlinDocumentTemplateContext import org.jetbrains.kotlin.ui.editors.templates.KotlinTemplateManager -import java.util.Comparator + +sealed class KotlinBasicCompletionProposal { + + abstract val descriptor: DeclarationDescriptor + + class Proposal(val proposal: KotlinCompletionProposal, override val descriptor: DeclarationDescriptor) : + KotlinBasicCompletionProposal() + + class Descriptor(override val descriptor: DeclarationDescriptor) : KotlinBasicCompletionProposal() +} abstract class KotlinCompletionProcessor( val editor: KotlinEditor, private val assistant: ContentAssistant?, - private val needSorting: Boolean) : IContentAssistProcessor, ICompletionListener { + private val needSorting: Boolean +) : IContentAssistProcessor, ICompletionListener { companion object { private val VALID_PROPOSALS_CHARS = charArrayOf() - private val VALID_INFO_CHARS = charArrayOf('(', ',') fun createKotlinCompletionProcessors( editor: KotlinEditor, assistant: ContentAssistant? = null, - needSorting: Boolean = false) = listOf( + needSorting: Boolean = false + ) = listOf( object : KotlinCompletionProcessor(editor, assistant, needSorting) { override fun computeProposals( identifierPart: String, @@ -88,7 +89,11 @@ abstract class KotlinCompletionProcessor( offset: Int ): List? = simpleNameExpression?.takeIf { identifierPart.isNotBlank() }?.let { - generateNonImportedCompletionProposals(identifierPart, simpleNameExpression, editor.javaProject!!) + generateNonImportedCompletionProposals( + identifierPart, + simpleNameExpression, + editor.javaProject!! + ) } }, object : KotlinCompletionProcessor(editor, assistant, needSorting) { @@ -117,31 +122,23 @@ abstract class KotlinCompletionProcessor( } ) } - - private val kotlinParameterValidator by lazy { - KotlinParameterListValidator(editor) - } - + override fun computeCompletionProposals(viewer: ITextViewer, offset: Int): Array { if (assistant != null) { configureContentAssistant(assistant) } - + val generatedProposals = generateCompletionProposals(viewer, offset).let { if (needSorting) sortProposals(it) else it } - + return generatedProposals.toTypedArray() } private fun sortProposals(proposals: List): List { - return proposals.sortedWith(object : Comparator { - override fun compare(o1: ICompletionProposal, o2: ICompletionProposal): Int { - return KotlinCompletionSorter.compare(o1, o2) - } - }) + return proposals.sortedWith(KotlinCompletionSorter::compare) } - + private fun configureContentAssistant(contentAssistant: ContentAssistant) { contentAssistant.setEmptyMessage("No Default Proposals") contentAssistant.setSorter(KotlinCompletionSorter) @@ -164,122 +161,150 @@ abstract class KotlinCompletionProcessor( ): List? protected fun generateNonImportedCompletionProposals( - identifierPart: String, - expression: KtSimpleNameExpression, - javaProject: IJavaProject): List { + identifierPart: String, + expression: KtSimpleNameExpression, + javaProject: IJavaProject + ): List { val file = editor.eclipseFile ?: return emptyList() val ktFile = editor.parsedFile ?: return emptyList() - return lookupNonImportedTypes(expression, identifierPart, ktFile, javaProject).map { + val tempTypeProposals = lookupNonImportedTypes(expression, identifierPart, ktFile, javaProject).map { val imageDescriptor = JavaElementImageProvider.getTypeImageDescriptor(false, false, it.modifiers, false) val image = JavaPlugin.getImageDescriptorRegistry().get(imageDescriptor) - KotlinImportCompletionProposal(it, image, file, identifierPart) + KotlinImportTypeCompletionProposal(it, image, file, identifierPart) } + + return tempTypeProposals } - protected fun generateBasicCompletionProposals(identifierPart: String, expression: KtSimpleNameExpression): Collection { - val file = editor.eclipseFile ?: - throw IllegalStateException("Failed to retrieve IFile from editor $editor") - + protected fun generateBasicCompletionProposals( + identifierPart: String, + expression: KtSimpleNameExpression + ): Collection { + val file = editor.eclipseFile ?: throw IllegalStateException("Failed to retrieve IFile from editor $editor") + val ktFile = editor.parsedFile ?: throw IllegalStateException("Failed to retrieve KTFile from editor $editor") + val nameFilter: (Name) -> Boolean = { name -> KotlinCompletionUtils.applicableNameFor(identifierPart, name) } - - return KotlinCompletionUtils.getReferenceVariants(expression, nameFilter, file, identifierPart) + + return KotlinCompletionUtils.getReferenceVariants( + expression, + nameFilter, + ktFile, + file, + identifierPart, + editor.javaProject!! + ) } - - protected fun collectCompletionProposals(descriptors: Collection, part: String): List { - return descriptors.map { descriptor -> - val completion = descriptor.name.identifier - val image = KotlinImageProvider.getImage(descriptor) - val presentableString = DescriptorRenderer.ONLY_NAMES_WITH_SHORT_TYPES.render(descriptor) - val containmentPresentableString = if (descriptor is ClassDescriptor) { - val fqName = DescriptorUtils.getFqName(descriptor) - if (fqName.isRoot) "" else fqName.parent().asString() - } else { - null + + protected fun collectCompletionProposals( + descriptors: Collection, + part: String + ): List { + return descriptors.map { basicDescriptor -> + when (basicDescriptor) { + is KotlinBasicCompletionProposal.Descriptor -> { + val descriptor = basicDescriptor.descriptor + val completion = descriptor.name.identifier + val image = KotlinImageProvider.getImage(descriptor) + + val tempRenderer = DescriptorRenderer.SHORT_NAMES_IN_TYPES.withOptions { + modifiers = emptySet() + includeAdditionalModifiers = false + } + + val presentableString = tempRenderer.render(descriptor) + val containmentPresentableString = null + + val proposal = KotlinCompletionProposal( + completion, + image, + presentableString, + containmentPresentableString, + null, + completion, + part, + CompletionElementType.from(descriptor) + ) + withKotlinInsertHandler(descriptor, proposal) + } + is KotlinBasicCompletionProposal.Proposal -> basicDescriptor.proposal } - - val proposal = KotlinCompletionProposal( - completion, - image, - presentableString, - containmentPresentableString, - null, - completion, - part) - - withKotlinInsertHandler(descriptor, proposal, part) } } - + protected fun generateTemplateProposals( - psiFile: PsiFile, viewer: ITextViewer, offset: Int, identifierPart: String): List { - - val contextTypeIds = KotlinApplicableTemplateContext.getApplicableContextTypeIds(viewer, psiFile, offset - identifierPart.length) + psiFile: PsiFile, viewer: ITextViewer, offset: Int, identifierPart: String + ): List { + + val contextTypeIds = + KotlinApplicableTemplateContext.getApplicableContextTypeIds(viewer, psiFile, offset - identifierPart.length) val region = Region(offset - identifierPart.length, identifierPart.length) - + val templateIcon = JavaPluginImages.get(JavaPluginImages.IMG_OBJS_TEMPLATE) val templates = KotlinApplicableTemplateContext.getTemplatesByContextTypeIds(contextTypeIds) - + return templates - .filter { it.name.startsWith(identifierPart) } - .map { - val templateContext = createTemplateContext(region, it.contextTypeId) - TemplateProposal(it, templateContext, region, templateIcon) - } - + .filter { it.name.startsWith(identifierPart) } + .map { + val templateContext = createTemplateContext(region, it.contextTypeId) + TemplateProposal(it, templateContext, region, templateIcon) + } + } - + private fun createTemplateContext(region: IRegion, contextTypeID: String): TemplateContext { return KotlinDocumentTemplateContext( - KotlinTemplateManager.INSTANCE.contextTypeRegistry.getContextType(contextTypeID), - editor, region.offset, region.length + KotlinTemplateManager.INSTANCE.contextTypeRegistry.getContextType(contextTypeID), + editor, region.offset, region.length ) } - - protected fun generateKeywordProposals(identifierPart: String, expression: PsiElement): List { - val callTypeAndReceiver = if (expression is KtSimpleNameExpression) CallTypeAndReceiver.detect(expression) else null - + + protected fun generateKeywordProposals( + identifierPart: String, + expression: PsiElement + ): List { + val callTypeAndReceiver = + if (expression is KtSimpleNameExpression) CallTypeAndReceiver.detect(expression) else null + return arrayListOf().apply { KeywordCompletion.complete(expression, identifierPart, true) { keywordProposal -> if (!KotlinCompletionUtils.applicableNameFor(identifierPart, keywordProposal)) return@complete - + when (keywordProposal) { "break", "continue" -> { if (expression is KtSimpleNameExpression) { addAll(breakOrContinueExpressionItems(expression, keywordProposal)) } - } - + } + "class" -> { if (callTypeAndReceiver !is CallTypeAndReceiver.CALLABLE_REFERENCE) { add(keywordProposal) } } - + "this", "return" -> { if (expression is KtExpression) { add(keywordProposal) } } - + else -> add(keywordProposal) } } }.map { KotlinKeywordCompletionProposal(it, identifierPart) } } - override fun computeContextInformation(viewer: ITextViewer?, offset: Int): Array { - return KotlinFunctionParameterInfoAssist.computeContextInformation(editor, offset) - } + override fun computeContextInformation(viewer: ITextViewer?, offset: Int): Array = emptyArray() override fun getCompletionProposalAutoActivationCharacters(): CharArray = VALID_PROPOSALS_CHARS - override fun getContextInformationAutoActivationCharacters(): CharArray = VALID_INFO_CHARS + override fun getContextInformationAutoActivationCharacters(): CharArray = charArrayOf() override fun getErrorMessage(): String? = "" - override fun getContextInformationValidator(): IContextInformationValidator = kotlinParameterValidator + override fun getContextInformationValidator() = null override fun assistSessionStarted(event: ContentAssistEvent?) { } @@ -287,26 +312,34 @@ abstract class KotlinCompletionProcessor( override fun assistSessionEnded(event: ContentAssistEvent?) { } - override fun selectionChanged(proposal: ICompletionProposal?, smartToggle: Boolean) { } + override fun selectionChanged(proposal: ICompletionProposal?, smartToggle: Boolean) {} } private object KotlinCompletionSorter : ICompletionProposalSorter { override fun compare(p1: ICompletionProposal, p2: ICompletionProposal): Int { - val relevance2 = p2.relevance() - val relevance1 = p1.relevance() - + + // simple and lazy hashing to make relevance more accurate. + val relevance2 = ((p2.relevance() * p2.typeRelevance()) + (p2.typeRelevance() / 2)) + val relevance1 = ((p1.relevance() * p1.typeRelevance()) + (p1.typeRelevance() / 2)) return when { relevance2 > relevance1 -> 1 relevance2 < relevance1 -> -1 else -> p1.sortString().compareTo(p2.sortString(), ignoreCase = true) } } - - private fun ICompletionProposal.sortString(): String { - return if (this is KotlinCompletionProposal) this.replacementString else this.displayString + + private fun ICompletionProposal.sortString(): String = + if (this is KotlinCompletionProposal) replacementString else displayString + + private fun ICompletionProposal.relevance(): Int = (this as? KotlinRelevanceCompletionProposal)?.getRelevance() ?: 0 + + private fun ICompletionProposal.typeRelevance(): Int { + return when (this) { + is KotlinKeywordCompletionProposal -> 0 + is KotlinImportTypeCompletionProposal -> 1 + is TemplateProposal -> 2 + is KotlinTypedCompletionProposal -> 3 + type.ordinal + else -> 4 + } } - - private fun ICompletionProposal.relevance(): Int { - return if (this is KotlinCompletionProposal) this.getRelevance() else 0 - } -} \ No newline at end of file +} diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/codeassist/KotlinCompletionProposal.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/codeassist/KotlinCompletionProposal.kt index 001f48120..65980dbbd 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/codeassist/KotlinCompletionProposal.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/codeassist/KotlinCompletionProposal.kt @@ -1,19 +1,19 @@ /******************************************************************************* -* 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. -* -*******************************************************************************/ + * 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.ui.editors.codeassist import org.eclipse.core.resources.IFile @@ -31,36 +31,44 @@ import org.eclipse.swt.graphics.Image import org.eclipse.swt.graphics.Point import org.jetbrains.kotlin.builtins.isExtensionFunctionType import org.jetbrains.kotlin.builtins.isFunctionType -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.core.imports.FunctionCandidate +import org.jetbrains.kotlin.core.imports.TypeCandidate +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.resolve.calls.util.getValueParametersCountFromFunctionType +import org.jetbrains.kotlin.ui.editors.codeassist.CaretPosition.AFTER_BRACKETS +import org.jetbrains.kotlin.ui.editors.codeassist.CaretPosition.IN_BRACKETS import org.jetbrains.kotlin.ui.editors.completion.KotlinCompletionUtils import org.jetbrains.kotlin.ui.editors.quickfix.placeImports -import org.jetbrains.kotlin.core.imports.TypeCandidate -public fun withKotlinInsertHandler( - descriptor: DeclarationDescriptor, - proposal: KotlinCompletionProposal, - identifierPart: String): KotlinCompletionProposal { +fun withKotlinInsertHandler( + descriptor: DeclarationDescriptor, + proposal: KotlinCompletionProposal +): ICompletionProposal { return when (descriptor) { is FunctionDescriptor -> { - val parameters = descriptor.getValueParameters() + val parameters = descriptor.valueParameters when (parameters.size) { - 0 -> KotlinFunctionCompletionProposal(proposal, CaretPosition.AFTER_BRACKETS, false, identifierPart) + 0 -> KotlinFunctionCompletionProposal(proposal, AFTER_BRACKETS, false) 1 -> { - val parameterType = parameters.single().getType() + val parameter = parameters.single() + val parameterType = parameter.type if (parameterType.isFunctionType || parameterType.isExtensionFunctionType) { val parameterCount = getValueParametersCountFromFunctionType(parameterType) - if (parameterCount <= 1) { - // otherwise additional item with lambda template is to be added - return KotlinFunctionCompletionProposal(proposal, CaretPosition.IN_BRACKETS, true, identifierPart) + return if (parameterCount <= 1) { + KotlinFunctionCompletionProposal(proposal, IN_BRACKETS, true) + } else { + val tempParamNames = getLambdaParamNames(parameter) + KotlinFunctionCompletionProposal(proposal, IN_BRACKETS, true, tempParamNames) } } - KotlinFunctionCompletionProposal(proposal, CaretPosition.IN_BRACKETS, false, identifierPart) + KotlinFunctionCompletionProposal(proposal, IN_BRACKETS, false) } - else -> KotlinFunctionCompletionProposal(proposal, CaretPosition.IN_BRACKETS, false, identifierPart) + else -> KotlinFunctionCompletionProposal(proposal, IN_BRACKETS, false) } } @@ -68,6 +76,30 @@ public fun withKotlinInsertHandler( } } +private fun getLambdaParamNames(parameter: ValueParameterDescriptor): String { + val typeCharMap = mutableMapOf() + fun Char.nextParamName(): String { + val tempChar = lowercaseChar() + val tempCurrentNum = typeCharMap[tempChar] + return if (tempCurrentNum == null) { + typeCharMap[tempChar] = 2 + "$tempChar" + } else { + typeCharMap[tempChar] = tempCurrentNum + 1 + "$tempChar$tempCurrentNum" + } + } + + val tempParamNames = + parameter.type.arguments.dropLast(1).joinToString(", ", postfix = " -> ") { + val tempAnnotation = + it.type.annotations.findAnnotation(FqName(ParameterName::class.qualifiedName!!)) + tempAnnotation?.allValueArguments?.get(Name.identifier(ParameterName::name.name)) + ?.value?.toString() ?: it.type.toString().first().nextParamName() + } + return tempParamNames +} + fun getIdentifierInfo(document: IDocument, offset: Int): IdentifierInfo { val text = document.get() var identStartOffset = offset @@ -79,37 +111,45 @@ fun getIdentifierInfo(document: IDocument, offset: Int): IdentifierInfo { data class IdentifierInfo(val identifierPart: String, val identifierStart: Int) -open class KotlinCompletionProposal( - val replacementString: String, - val img: Image?, - val presentableString: String, - val containmentPresentableString: String? = null, - val information: IContextInformation? = null, - val additionalInfo: String? = null, - identifierPart: String) : ICompletionProposal, ICompletionProposalExtension2, ICompletionProposalExtension6 { - - var selectedOffset = -1 - - private @Volatile var identifierPart = identifierPart - - open fun getRelevance(): Int { +interface KotlinRelevanceCompletionProposal { + fun getRelevance(): Int +} + +interface KotlinTypedCompletionProposal { + + val type: CompletionElementType +} + +open class KotlinCompletionProposal constructor( + val replacementString: String, + private val img: Image?, + private val presentableString: String, + private val containmentPresentableString: String? = null, + private val information: IContextInformation? = null, + private val additionalInfo: String? = null, + @Volatile private var identifierPart: String, + override val type: CompletionElementType = CompletionElementType.UNKNOWN +) : ICompletionProposal, ICompletionProposalExtension2, ICompletionProposalExtension6, KotlinTypedCompletionProposal, KotlinRelevanceCompletionProposal { + + private var selectedOffset = -1 + override fun getRelevance(): Int { return computeCaseMatchingRelevance(identifierPart.toCharArray(), replacementString.toCharArray()) } - + override fun apply(viewer: ITextViewer, trigger: Char, stateMask: Int, offset: Int) { val document = viewer.document val (identifierPart, identifierStart) = getIdentifierInfo(document, offset) document.replace(identifierStart, offset - identifierStart, replacementString) - + selectedOffset = offset - identifierPart.length + replacementString.length } - + override fun validate(document: IDocument, offset: Int, event: DocumentEvent): Boolean { val identiferInfo = getIdentifierInfo(document, offset) identifierPart = identiferInfo.identifierPart return KotlinCompletionUtils.applicableNameFor(identiferInfo.identifierPart, replacementString) } - + override fun getSelection(document: IDocument): Point? = Point(selectedOffset, 0) override fun getAdditionalProposalInfo(): String? = additionalInfo @@ -119,60 +159,95 @@ open class KotlinCompletionProposal( override fun getImage(): Image? = img override fun getContextInformation(): IContextInformation? = information - + override fun getStyledDisplayString(): StyledString { return if (containmentPresentableString != null) { - createStyledString(getDisplayString(), containmentPresentableString) + createStyledString(displayString, containmentPresentableString) } else { - StyledString(getDisplayString()) + StyledString(displayString) } } - + override fun selected(viewer: ITextViewer?, smartToggle: Boolean) { } - + override fun unselected(viewer: ITextViewer?) { } - - override final fun apply(document: IDocument) { + + final override fun apply(document: IDocument) { // should not be called } } -class KotlinImportCompletionProposal(val typeName: TypeNameMatch, image: Image?, val file: IFile, identifierPart: String) : - KotlinCompletionProposal( - typeName.simpleTypeName, - image, - typeName.simpleTypeName, - typeName.fullyQualifiedName.removeSuffix(".${typeName.simpleTypeName}"), - identifierPart = identifierPart) { +class KotlinImportTypeCompletionProposal( + private val typeName: TypeNameMatch, + image: Image?, + val file: IFile, + identifierPart: String +) : + KotlinCompletionProposal( + typeName.simpleTypeName, + image, + typeName.simpleTypeName, + typeName.fullyQualifiedName.removeSuffix(".${typeName.simpleTypeName}"), + identifierPart = identifierPart + ) { + + private var importShift = -1 - var importShift = -1 - override fun apply(viewer: ITextViewer, trigger: Char, stateMask: Int, offset: Int) { super.apply(viewer, trigger, stateMask, offset) importShift = placeImports(listOf(TypeCandidate(typeName)), file, viewer.document) } - + override fun getSelection(document: IDocument): Point? { val selection = super.getSelection(document) return if (importShift > 0 && selection != null) Point(selection.x + importShift, 0) else selection } - + override fun getRelevance(): Int { return -1 } } +class KotlinImportCallableCompletionProposal( + val descriptor: CallableDescriptor, + image: Image?, + val file: IFile, + identifierPart: String +) : + KotlinCompletionProposal( + "${descriptor.name.identifier}${if (descriptor is PropertyDescriptor) "" else "()"}", + image, + DescriptorRenderer.SHORT_NAMES_IN_TYPES.render(descriptor), + null, + identifierPart = identifierPart + ) { + + private var importShift = -1 + + override fun apply(viewer: ITextViewer, trigger: Char, stateMask: Int, offset: Int) { + super.apply(viewer, trigger, stateMask, offset) + importShift = placeImports(listOf(FunctionCandidate(descriptor)), file, viewer.document) + } + + override fun getSelection(document: IDocument): Point? { + val selection = super.getSelection(document) + return if (importShift > 0 && selection != null) Point(selection.x + importShift, 0) else selection + } + + override fun getRelevance(): Int = -1 +} + class KotlinKeywordCompletionProposal(keyword: String, identifierPart: String) : - KotlinCompletionProposal(keyword, null, keyword, identifierPart = identifierPart) + KotlinCompletionProposal(keyword, null, keyword, identifierPart = identifierPart) private fun createStyledString(simpleName: String, containingDeclaration: String): StyledString { - return StyledString().apply { + return StyledString().apply { append(simpleName) if (containingDeclaration.isNotBlank()) { append(JavaElementLabels.CONCAT_STRING, StyledString.QUALIFIER_STYLER) append(containingDeclaration, StyledString.QUALIFIER_STYLER) } } -} \ No newline at end of file +} diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/codeassist/KotlinContextInfoContentAssistProcessor.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/codeassist/KotlinContextInfoContentAssistProcessor.kt new file mode 100644 index 000000000..0196815e8 --- /dev/null +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/codeassist/KotlinContextInfoContentAssistProcessor.kt @@ -0,0 +1,32 @@ +package org.jetbrains.kotlin.ui.editors.codeassist + +import org.eclipse.jface.text.ITextViewer +import org.eclipse.jface.text.contentassist.ICompletionProposal +import org.eclipse.jface.text.contentassist.IContentAssistProcessor +import org.eclipse.jface.text.contentassist.IContextInformation +import org.jetbrains.kotlin.ui.editors.KotlinEditor + +class KotlinContextInfoContentAssistProcessor(private val editor: KotlinEditor) : IContentAssistProcessor { + + private val kotlinParameterValidator by lazy { + KotlinParameterListValidator(editor) + } + + override fun computeCompletionProposals(p0: ITextViewer?, p1: Int): Array = emptyArray() + + override fun computeContextInformation(p0: ITextViewer?, offset: Int): Array { + return KotlinFunctionParameterInfoAssist.computeContextInformation(editor, offset) + } + + override fun getCompletionProposalAutoActivationCharacters() = charArrayOf() + + override fun getContextInformationAutoActivationCharacters() = VALID_INFO_CHARS + + override fun getErrorMessage() = "" + + override fun getContextInformationValidator() = kotlinParameterValidator + + companion object { + private val VALID_INFO_CHARS = charArrayOf('(', ',') + } +} diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/codeassist/KotlinFunctionCompletionProposal.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/codeassist/KotlinFunctionCompletionProposal.kt index a22d483ba..00bf17d64 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/codeassist/KotlinFunctionCompletionProposal.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/codeassist/KotlinFunctionCompletionProposal.kt @@ -1,118 +1,107 @@ /******************************************************************************* -* 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. -* -*******************************************************************************/ + * 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.ui.editors.codeassist -import org.eclipse.jface.text.contentassist.ICompletionProposal -import org.eclipse.jface.text.contentassist.ICompletionProposalExtension import org.eclipse.jface.text.IDocument -import org.jetbrains.kotlin.eclipse.ui.utils.LineEndUtil -import com.intellij.psi.util.PsiTreeUtil -import com.intellij.psi.PsiElement -import org.jetbrains.kotlin.ui.editors.KotlinFileEditor -import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2 import org.eclipse.jface.text.ITextViewer -import org.eclipse.jface.text.DocumentEvent -import org.eclipse.jface.text.TextSelection -import org.eclipse.jface.text.Position +import org.eclipse.jface.text.contentassist.ICompletionProposal +import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2 +import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6 import org.eclipse.swt.graphics.Point -public enum class CaretPosition { +enum class CaretPosition { IN_BRACKETS, AFTER_BRACKETS } -public class KotlinFunctionCompletionProposal( - proposal: KotlinCompletionProposal, - val caretPosition: CaretPosition, - val hasLambda: Boolean, - identifierPart: String) : - KotlinCompletionProposal( - proposal.replacementString, - proposal.img, - proposal.presentableString, - identifierPart = identifierPart) { - +class KotlinFunctionCompletionProposal( + private val proposal: KotlinCompletionProposal, + private val caretPosition: CaretPosition, + private val hasLambda: Boolean, + private val lambdaParamNames: String = "" +) : ICompletionProposal by proposal, ICompletionProposalExtension2 by proposal, + ICompletionProposalExtension6 by proposal, KotlinTypedCompletionProposal by proposal, + KotlinRelevanceCompletionProposal by proposal { + init { if (caretPosition == CaretPosition.AFTER_BRACKETS && hasLambda) { throw IllegalArgumentException("CaretPosition.AFTER_BRACKETS with lambdaInfo != null combination is not supported") } } - + override fun apply(viewer: ITextViewer, trigger: Char, stateMask: Int, offset: Int) { - super.apply(viewer, trigger, stateMask, offset) - - addBrackets(viewer, trigger, super.getSelection(viewer.getDocument())!!.x) + proposal.apply(viewer, trigger, stateMask, offset) + + addBrackets(viewer, trigger, proposal.getSelection(viewer.document)!!.x) if (trigger == '.') { - val closeBracketOffset = viewer.getTextWidget().getCaretOffset() - viewer.getDocument().replace(closeBracketOffset, 0, trigger.toString()) - viewer.getTextWidget().setCaretOffset(closeBracketOffset + 1) + val closeBracketOffset = viewer.textWidget.caretOffset + viewer.document.replace(closeBracketOffset, 0, trigger.toString()) + viewer.textWidget.caretOffset = closeBracketOffset + 1 } } - + override fun getSelection(document: IDocument): Point? = null - - + + private fun addBrackets(viewer: ITextViewer, completionChar: Char, completionOffset: Int) { - val document = viewer.getDocument() + val document = viewer.document val braces = hasLambda && completionChar != '(' - + val openingBracket = if (braces) '{' else '(' val closingBracket = if (braces) '}' else ')' - + var openingBracketOffset = indexOfSkippingSpace(document, openingBracket, completionOffset) var inBracketsShift = 0 if (openingBracketOffset == -1) { if (braces) { - document.replace(completionOffset, 0, " { }") + document.replace(completionOffset, 0, " { $lambdaParamNames }") inBracketsShift = 1 - } - else { + } else { document.replace(completionOffset, 0, "()") } } - + openingBracketOffset = indexOfSkippingSpace(document, openingBracket, completionOffset) assert(openingBracketOffset != -1) { "If there wasn't open bracket it should already have been inserted" } - + val closeBracketOffset = indexOfSkippingSpace(document, closingBracket, openingBracketOffset + 1) - + if (shouldPlaceCaretInBrackets(completionChar) || closeBracketOffset == -1) { viewer.setSelectedRange(openingBracketOffset + 1 + inBracketsShift, 0) - } - else { + } else { viewer.setSelectedRange(closeBracketOffset + 1, 0) } } - - private fun indexOfSkippingSpace(document: IDocument, ch : Char, startIndex : Int) : Int { + + private fun indexOfSkippingSpace(document: IDocument, ch: Char, startIndex: Int): Int { val text = document.get() - for (i in startIndex..text.length - 1) { + for (i in startIndex until text.length) { val currentChar = text[i] if (ch == currentChar) return i if (currentChar != ' ' && currentChar != '\t') return -1 } return -1 } - + private fun shouldPlaceCaretInBrackets(completionChar: Char): Boolean { - return when { - completionChar == '.' -> false - completionChar == '(' -> true + return when (completionChar) { + '.' -> false + '(' -> true else -> caretPosition == CaretPosition.IN_BRACKETS } } -} \ No newline at end of file +} diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/codeassist/KotlinFunctionParameterInfoAssist.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/codeassist/KotlinFunctionParameterInfoAssist.kt index 31883c6bd..dd6502e17 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/codeassist/KotlinFunctionParameterInfoAssist.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/codeassist/KotlinFunctionParameterInfoAssist.kt @@ -16,53 +16,55 @@ *******************************************************************************/ package org.jetbrains.kotlin.ui.editors.codeassist -import org.jetbrains.kotlin.ui.editors.KotlinEditor -import org.jetbrains.kotlin.eclipse.ui.utils.EditorUtil import com.intellij.psi.util.PsiTreeUtil -import org.jetbrains.kotlin.psi.KtValueArgumentList import org.eclipse.jface.text.contentassist.IContextInformation -import org.jetbrains.kotlin.psi.KtSimpleNameExpression -import org.jetbrains.kotlin.psi.KtCallElement -import org.jetbrains.kotlin.psi.psiUtil.getCallNameExpression -import org.jetbrains.kotlin.ui.editors.completion.KotlinCompletionUtils -import org.jetbrains.kotlin.ui.editors.KotlinFileEditor -import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.descriptors.ConstructorDescriptor -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.eclipse.swt.graphics.Image -import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor -import org.jetbrains.kotlin.renderer.DescriptorRenderer +import org.jetbrains.kotlin.core.resolve.EclipseDescriptorUtils +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.eclipse.ui.utils.EditorUtil import org.jetbrains.kotlin.eclipse.ui.utils.KotlinImageProvider import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.KtCallElement +import org.jetbrains.kotlin.psi.KtParameter +import org.jetbrains.kotlin.psi.KtSimpleNameExpression +import org.jetbrains.kotlin.psi.KtValueArgumentList +import org.jetbrains.kotlin.psi.psiUtil.getCallNameExpression +import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.resolve.descriptorUtil.declaresOrInheritsDefaultValue -import org.jetbrains.kotlin.core.resolve.EclipseDescriptorUtils -import org.jetbrains.kotlin.descriptors.SourceElement import org.jetbrains.kotlin.resolve.source.KotlinSourceElement -import org.jetbrains.kotlin.psi.KtParameter -import org.jetbrains.kotlin.core.references.getReferenceExpression -import org.jetbrains.kotlin.core.references.createReferences import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.ui.editors.KotlinEditor +import org.jetbrains.kotlin.ui.editors.completion.KotlinCompletionUtils + +object KotlinFunctionParameterInfoAssist { + fun computeContextInformation(editor: KotlinEditor, offset: Int): Array { + val file = editor.eclipseFile ?: throw IllegalStateException("Failed to retrieve IFile from editor $editor") + val ktFile = editor.parsedFile ?: throw IllegalStateException("Failed to retrieve KTFile from editor $editor") + val javaProject = editor.javaProject ?: throw IllegalStateException("Failed to retrieve JavaProject from editor $editor") + + val expression = getCallSimpleNameExpression(editor, offset) ?: return emptyArray() -public object KotlinFunctionParameterInfoAssist { - public fun computeContextInformation(editor: KotlinEditor, offset: Int): Array { - val expression = getCallSimpleNameExpression(editor, offset) - if (expression == null) return emptyArray() - val referencedName = expression.getReferencedName() val nameFilter: (Name) -> Boolean = { name -> name.asString() == referencedName } - val variants = KotlinCompletionUtils.getReferenceVariants(expression, nameFilter, editor.eclipseFile!!, null) + val variants = KotlinCompletionUtils.getReferenceVariants( + expression, + nameFilter, + ktFile, + file, + referencedName, + javaProject + ) - return variants + return variants.map { it.descriptor } .flatMap { when (it) { is FunctionDescriptor -> listOf(it) - is ClassDescriptor -> it.getConstructors() + is ClassDescriptor -> it.constructors else -> emptyList() } } - .filter { it.getValueParameters().isNotEmpty() } + .filter { it.valueParameters.isNotEmpty() } .map { KotlinFunctionParameterContextInformation(it) } .toTypedArray() } @@ -70,19 +72,20 @@ public object KotlinFunctionParameterInfoAssist { fun getCallSimpleNameExpression(editor: KotlinEditor, offset: Int): KtSimpleNameExpression? { val psiElement = EditorUtil.getPsiElement(editor, offset) - val argumentList = PsiTreeUtil.getParentOfType(psiElement, KtValueArgumentList::class.java) - if (argumentList == null) return null - - val argumentListParent = argumentList.getParent() + val argumentList = PsiTreeUtil.getParentOfType(psiElement, KtValueArgumentList::class.java) ?: return null + + val argumentListParent = argumentList.parent return if (argumentListParent is KtCallElement) argumentListParent.getCallNameExpression() else null } -public class KotlinFunctionParameterContextInformation(descriptor: FunctionDescriptor) : IContextInformation { - val displayString = DescriptorRenderer.ONLY_NAMES_WITH_SHORT_TYPES.render(descriptor) - val renderedParameters = descriptor.getValueParameters().map { renderParameter(it) } - val informationString = renderedParameters.joinToString(", ") - val displayImage = KotlinImageProvider.getImage(descriptor) - val name = if (descriptor is ConstructorDescriptor) descriptor.getContainingDeclaration().getName() else descriptor.getName() +class KotlinFunctionParameterContextInformation(descriptor: FunctionDescriptor) : IContextInformation { + private val displayString = DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.withOptions { + this.withDefinedIn = false + }.render(descriptor) + val renderedParameters = descriptor.valueParameters.map { renderParameter(it) } + private val informationString = renderedParameters.joinToString(", ") + private val displayImage = KotlinImageProvider.getImage(descriptor) + val name = if (descriptor is ConstructorDescriptor) descriptor.containingDeclaration.name else descriptor.name override fun getContextDisplayString(): String = displayString @@ -94,7 +97,7 @@ public class KotlinFunctionParameterContextInformation(descriptor: FunctionDescr val result = StringBuilder() if (parameter.varargElementType != null) result.append("vararg ") - result.append(parameter.getName()) + result.append(parameter.name) .append(": ") .append(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(getActualParameterType(parameter))) @@ -111,7 +114,7 @@ public class KotlinFunctionParameterContextInformation(descriptor: FunctionDescr private fun getDefaultExpressionString(parameterDeclaration: SourceElement): String { val parameterText: String? = if (parameterDeclaration is KotlinSourceElement) { val parameter = parameterDeclaration.psi - (parameter as? KtParameter)?.getDefaultValue()?.getText() + (parameter as? KtParameter)?.defaultValue?.text } else { null } @@ -120,6 +123,6 @@ public class KotlinFunctionParameterContextInformation(descriptor: FunctionDescr } private fun getActualParameterType(descriptor: ValueParameterDescriptor): KotlinType { - return descriptor.varargElementType ?: descriptor.getType() + return descriptor.varargElementType ?: descriptor.type } -} \ No newline at end of file +} diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/codeassist/KotlinParameterListValidator.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/codeassist/KotlinParameterListValidator.kt index 009a415de..e9e140dde 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/codeassist/KotlinParameterListValidator.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/codeassist/KotlinParameterListValidator.kt @@ -32,39 +32,38 @@ import org.jetbrains.kotlin.psi.KtValueArgumentList import org.jetbrains.kotlin.ui.editors.KotlinEditor import kotlin.properties.Delegates -public class KotlinParameterListValidator(val editor: KotlinEditor) : IContextInformationValidator, - IContextInformationPresenter { +class KotlinParameterListValidator(val editor: KotlinEditor) : IContextInformationValidator, + IContextInformationPresenter { var info: KotlinFunctionParameterContextInformation by Delegates.notNull() var viewer: ITextViewer by Delegates.notNull() var position: Int by Delegates.notNull() var previousIndex: Int by Delegates.notNull() - + override fun install(info: IContextInformation, viewer: ITextViewer, offset: Int) { this.info = info as KotlinFunctionParameterContextInformation this.viewer = viewer this.position = offset this.previousIndex = -1 } - + override fun isContextInformationValid(offset: Int): Boolean { - if (info !is KotlinFunctionParameterContextInformation) return false EditorUtil.updatePsiFile(editor) - - val document = viewer.getDocument() + + val document = viewer.document val line = document.getLineInformationOfOffset(position) - - if (offset < line.getOffset()) return false - + + if (offset < line.offset) return false + val currentArgumentIndex = getCurrentArgumentIndex(offset) if (currentArgumentIndex == null || isIndexOutOfBound(currentArgumentIndex)) { return false } - + val expression = getCallSimpleNameExpression(editor, offset) - + return expression?.getReferencedName() == info.name.asString() } - + override fun updatePresentation(offset: Int, presentation: TextPresentation): Boolean { val currentArgumentIndex = getCurrentArgumentIndex(offset) if (currentArgumentIndex == null || previousIndex == currentArgumentIndex) { @@ -72,43 +71,44 @@ public class KotlinParameterListValidator(val editor: KotlinEditor) : IContextIn } presentation.clear() previousIndex = currentArgumentIndex - + if (isIndexOutOfBound(currentArgumentIndex)) return false - + val renderedParameter = info.renderedParameters[currentArgumentIndex] - - val displayString = info.getInformationDisplayString() + + val displayString = info.informationDisplayString val start = displayString.indexOf(renderedParameter) if (start >= 0) { presentation.addStyleRange(StyleRange(0, start, null, null, SWT.NORMAL)) - + val end = start + renderedParameter.length presentation.addStyleRange(StyleRange(start, end - start, null, null, SWT.BOLD)) presentation.addStyleRange(StyleRange(end, displayString.length - end, null, null, SWT.NORMAL)) - + return true } - + return true } - + private fun isIndexOutOfBound(index: Int): Boolean = info.renderedParameters.size <= index - -// Copied with some changes from JetFunctionParameterInfoHandler.java + + // Copied with some changes from JetFunctionParameterInfoHandler.java private fun getCurrentArgumentIndex(offset: Int): Int? { val psiElement = EditorUtil.getPsiElement(editor, offset) - val argumentList = PsiTreeUtil.getNonStrictParentOfType(psiElement, KtValueArgumentList::class.java) - if (argumentList == null) return null - + val argumentList = + PsiTreeUtil.getNonStrictParentOfType(psiElement, KtValueArgumentList::class.java) ?: return null + val offsetInPSI = LineEndUtil.convertCrToDocumentOffset(editor.document, offset) - var child = argumentList.getNode().getFirstChildNode() + var child = argumentList.node.firstChildNode var index = 0 - while (child != null && child.getStartOffset() < offsetInPSI) { - if (child.getElementType() == KtTokens.COMMA || - (child.getText() == "," && child is PsiErrorElement)) ++index - child = child.getTreeNext() + while (child != null && child.startOffset < offsetInPSI) { + if (child.elementType == KtTokens.COMMA || + (child.text == "," && child is PsiErrorElement) + ) ++index + child = child.treeNext } - + return index } -} \ No newline at end of file +} diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/completion/KotlinCompletionUtils.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/completion/KotlinCompletionUtils.kt index 501e57f94..52ae759e9 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/completion/KotlinCompletionUtils.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/completion/KotlinCompletionUtils.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.ui.editors.completion import com.intellij.openapi.util.text.StringUtilRt import com.intellij.psi.PsiElement import org.eclipse.core.resources.IFile +import org.eclipse.jdt.core.IJavaProject import org.eclipse.jdt.core.search.SearchPattern import org.eclipse.jdt.internal.ui.JavaPlugin import org.eclipse.jdt.ui.PreferenceConstants @@ -37,6 +38,7 @@ import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtSimpleNameExpression import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.ui.editors.KotlinEditor +import org.jetbrains.kotlin.ui.editors.codeassist.KotlinBasicCompletionProposal import org.jetbrains.kotlin.ui.editors.codeassist.getResolutionScope import org.jetbrains.kotlin.ui.editors.codeassist.isVisible @@ -54,11 +56,13 @@ object KotlinCompletionUtils { } fun getReferenceVariants( - simpleNameExpression: KtSimpleNameExpression, - nameFilter: (Name) -> Boolean, - file: IFile, - identifierPart: String? - ): Collection { + simpleNameExpression: KtSimpleNameExpression, + nameFilter: (Name) -> Boolean, + ktFile: KtFile, + file: IFile, + identifierPart: String?, + javaProject: IJavaProject + ): Collection { val (analysisResult, container) = KotlinAnalyzer.analyzeFile(simpleNameExpression.containingKtFile) if (container == null) return emptyList() @@ -84,13 +88,13 @@ object KotlinCompletionUtils { val collectAll = (identifierPart == null || identifierPart.length > 2) || !KotlinScriptEnvironment.isScript(file) val kind = if (collectAll) DescriptorKindFilter.ALL else DescriptorKindFilter.CALLABLES - - return KotlinReferenceVariantsHelper ( - analysisResult.bindingContext, - KotlinResolutionFacade(file, container, analysisResult.moduleDescriptor), - analysisResult.moduleDescriptor, - visibilityFilter).getReferenceVariants( - simpleNameExpression, kind, nameFilter) + + return KotlinReferenceVariantsHelper( + analysisResult.bindingContext, + KotlinResolutionFacade(file, container, analysisResult.moduleDescriptor), + analysisResult.moduleDescriptor, + visibilityFilter + ).getReferenceVariants(simpleNameExpression, kind, nameFilter, javaProject, ktFile, file, identifierPart) } fun getPsiElement(editor: KotlinEditor, identOffset: Int): PsiElement? { @@ -110,4 +114,4 @@ object KotlinCompletionUtils { val offsetWithoutCR = LineEndUtil.convertCrToDocumentOffset(sourceCodeWithMarker, identOffset, editor.document) return jetFile.findElementAt(offsetWithoutCR) } -} \ No newline at end of file +} diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/completion/KotlinReferenceVariantsHelper.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/completion/KotlinReferenceVariantsHelper.kt index 81072b3c3..00bdefd16 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/completion/KotlinReferenceVariantsHelper.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/completion/KotlinReferenceVariantsHelper.kt @@ -1,26 +1,43 @@ package org.jetbrains.kotlin.ui.editors.completion import com.intellij.psi.PsiElement +import kotlinx.coroutines.* +import org.eclipse.core.resources.IFile +import org.eclipse.jdt.core.Flags +import org.eclipse.jdt.core.IJavaProject +import org.eclipse.jdt.core.JavaCore +import org.eclipse.jdt.core.search.* +import org.eclipse.jdt.internal.ui.search.JavaSearchScopeFactory import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.core.resolve.KotlinResolutionFacade +import org.jetbrains.kotlin.core.utils.ProjectUtils import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.eclipse.ui.utils.KotlinEclipseScope +import org.jetbrains.kotlin.eclipse.ui.utils.KotlinImageProvider import org.jetbrains.kotlin.idea.FrontendInternals +import org.jetbrains.kotlin.idea.imports.importableFqName import org.jetbrains.kotlin.idea.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.util.* import org.jetbrains.kotlin.incremental.KotlinLookupLocation import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi import org.jetbrains.kotlin.load.kotlin.toSourceElement +import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.ImportedFromObjectCallableDescriptor import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfoBefore import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastManager import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver +import org.jetbrains.kotlin.resolve.descriptorUtil.classValueType import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension import org.jetbrains.kotlin.resolve.scopes.* +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter.Companion.CALLABLES +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter.Companion.FUNCTIONS_MASK +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter.Companion.VARIABLES_MASK import org.jetbrains.kotlin.resolve.scopes.receivers.ClassQualifier import org.jetbrains.kotlin.resolve.scopes.utils.collectDescriptorsFiltered import org.jetbrains.kotlin.resolve.scopes.utils.memberScopeAsImportingScope @@ -28,7 +45,10 @@ import org.jetbrains.kotlin.resolve.source.getPsi import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.expressions.DoubleColonLHS +import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf import org.jetbrains.kotlin.types.typeUtil.isUnit +import org.jetbrains.kotlin.ui.editors.codeassist.KotlinBasicCompletionProposal +import org.jetbrains.kotlin.ui.editors.codeassist.KotlinImportCallableCompletionProposal import org.jetbrains.kotlin.ui.refactorings.extract.parentsWithSelf import java.util.* @@ -41,30 +61,41 @@ class KotlinReferenceVariantsHelper( fun getReferenceVariants( simpleNameExpression: KtSimpleNameExpression, kindFilter: DescriptorKindFilter, - nameFilter: (Name) -> Boolean - ): Collection { + nameFilter: (Name) -> Boolean, + javaProject: IJavaProject, + ktFile: KtFile, + file: IFile, + identifierPart: String? + ): Collection { val callTypeAndReceiver = CallTypeAndReceiver.detect(simpleNameExpression) - var variants: Collection = - getReferenceVariants(simpleNameExpression, callTypeAndReceiver, kindFilter, nameFilter) - .filter { - !resolutionFacade.frontendService().isHiddenInResolution(it) && visibilityFilter( - it - ) - } - - ShadowedDeclarationsFilter.create(bindingContext, resolutionFacade, simpleNameExpression, callTypeAndReceiver) - ?.let { - variants = it.filter(variants) + var variants: Collection = + getReferenceVariants( + simpleNameExpression, + callTypeAndReceiver, + kindFilter, + nameFilter, + javaProject, + ktFile, + file, + identifierPart + ).filter { + !resolutionFacade.frontendService().isHiddenInResolution(it.descriptor) && + visibilityFilter(it.descriptor) } - if (kindFilter.kindMask.and(DescriptorKindFilter.FUNCTIONS_MASK) != 0) { - variants = filterOutJavaGettersAndSetters(variants) - } - if (kindFilter.kindMask.and(DescriptorKindFilter.VARIABLES_MASK) != 0) { - variants = excludeNonInitializedVariable(variants, simpleNameExpression) + val tempFilter = ShadowedDeclarationsFilter.create( + bindingContext, + resolutionFacade, + simpleNameExpression, + callTypeAndReceiver + ) + if (tempFilter != null) { + variants = variants.mapNotNull { + if (tempFilter.filter(listOf(it.descriptor)).isEmpty()) null else it + } } - return variants + return variants.filter { kindFilter.accepts(it.descriptor) } } private fun getVariantsForImportOrPackageDirective( @@ -110,8 +141,8 @@ class KotlinReferenceVariantsHelper( useReceiverType: KotlinType?, kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean - ): Collection { - val descriptors = LinkedHashSet() + ): Collection { + val descriptors = LinkedHashSet() val resolutionScope = contextElement.getResolutionScope(bindingContext, resolutionFacade) @@ -143,7 +174,10 @@ class KotlinReferenceVariantsHelper( if (isStatic) { explicitReceiverTypes .mapNotNull { (it.constructor.declarationDescriptor as? ClassDescriptor)?.staticScope } - .flatMapTo(descriptors) { it.collectStaticMembers(resolutionFacade, kindFilter, nameFilter) } + .flatMapTo(descriptors) { scope -> + scope.collectStaticMembers(resolutionFacade, kindFilter, nameFilter) + .map { KotlinBasicCompletionProposal.Descriptor(it) } + } } } else { descriptors.addNonExtensionCallablesAndConstructors( @@ -159,8 +193,12 @@ class KotlinReferenceVariantsHelper( simpleNameExpression: KtSimpleNameExpression, callTypeAndReceiver: CallTypeAndReceiver<*, *>, kindFilter: DescriptorKindFilter, - nameFilter: (Name) -> Boolean - ): Collection { + nameFilter: (Name) -> Boolean, + javaProject: IJavaProject, + ktFile: KtFile, + file: IFile, + identifierPart: String? + ): Collection { val callType = callTypeAndReceiver.callType @Suppress("NAME_SHADOWING") @@ -170,10 +208,12 @@ class KotlinReferenceVariantsHelper( when (callTypeAndReceiver) { is CallTypeAndReceiver.IMPORT_DIRECTIVE -> { return getVariantsForImportOrPackageDirective(callTypeAndReceiver.receiver, kindFilter, nameFilter) + .map { KotlinBasicCompletionProposal.Descriptor(it) } } is CallTypeAndReceiver.PACKAGE_DIRECTIVE -> { return getVariantsForImportOrPackageDirective(callTypeAndReceiver.receiver, kindFilter, nameFilter) + .map { KotlinBasicCompletionProposal.Descriptor(it) } } is CallTypeAndReceiver.TYPE -> { @@ -182,7 +222,7 @@ class KotlinReferenceVariantsHelper( simpleNameExpression, kindFilter, nameFilter - ) + ).map { KotlinBasicCompletionProposal.Descriptor(it) } } is CallTypeAndReceiver.ANNOTATION -> { @@ -191,7 +231,7 @@ class KotlinReferenceVariantsHelper( simpleNameExpression, kindFilter, nameFilter - ) + ).map { KotlinBasicCompletionProposal.Descriptor(it) } } is CallTypeAndReceiver.CALLABLE_REFERENCE -> { @@ -234,7 +274,7 @@ class KotlinReferenceVariantsHelper( ) }.toSet() - val descriptors = LinkedHashSet() + val descriptors = LinkedHashSet() val filterWithoutExtensions = kindFilter exclude DescriptorKindExclude.Extensions if (receiverExpression != null) { @@ -245,7 +285,7 @@ class KotlinReferenceVariantsHelper( resolutionFacade, filterWithoutExtensions, nameFilter - ) + ).map { KotlinBasicCompletionProposal.Descriptor(it) } ) } @@ -263,7 +303,12 @@ class KotlinReferenceVariantsHelper( resolutionScope, callType, kindFilter, - nameFilter + nameFilter, + javaProject, + ktFile, + file, + identifierPart, + false ) } else { descriptors.processAll( @@ -272,7 +317,12 @@ class KotlinReferenceVariantsHelper( resolutionScope, callType, kindFilter, - nameFilter + nameFilter, + javaProject, + ktFile, + file, + identifierPart, + true ) descriptors.addAll( @@ -280,50 +330,209 @@ class KotlinReferenceVariantsHelper( filterWithoutExtensions, nameFilter, changeNamesForAliased = true - ) + ).map { KotlinBasicCompletionProposal.Descriptor(it) } ) } if (callType == CallType.SUPER_MEMBERS) { // we need to unwrap fake overrides in case of "super." because ShadowedDeclarationsFilter does not work correctly - return descriptors.flatMapTo(LinkedHashSet()) { - if (it is CallableMemberDescriptor && it.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) it.overriddenDescriptors else listOf( - it - ) - } + return descriptors.filterIsInstance() + .flatMapTo(LinkedHashSet()) { descriptor -> + if (descriptor.descriptor is CallableMemberDescriptor && descriptor.descriptor.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) { + descriptor.descriptor.overriddenDescriptors.map { KotlinBasicCompletionProposal.Descriptor(it) } + } else { + listOf(descriptor) + } + } } - return descriptors + return descriptors.distinctBy { (it.descriptor as? ImportedFromObjectCallableDescriptor<*>)?.callableFromObject ?: it.descriptor } } - private fun MutableSet.processAll( + private fun MutableSet.processAll( implicitReceiverTypes: Collection, receiverTypes: Collection, resolutionScope: LexicalScope, callType: CallType<*>, kindFilter: DescriptorKindFilter, - nameFilter: (Name) -> Boolean + nameFilter: (Name) -> Boolean, + javaProject: IJavaProject, + ktFile: KtFile, + file: IFile, + identifierPart: String?, + allowNoReceiver: Boolean ) { - addNonExtensionMembers(receiverTypes, kindFilter, nameFilter, constructorFilter = { it.isInner }) - addMemberExtensions(implicitReceiverTypes, receiverTypes, callType, kindFilter, nameFilter) - addScopeAndSyntheticExtensions(resolutionScope, receiverTypes, callType, kindFilter, nameFilter) + runBlocking { + val tempJobs = mutableListOf() + tempJobs += KotlinEclipseScope.launch { + addNonExtensionMembers(receiverTypes, kindFilter, nameFilter, constructorFilter = { it.isInner }) + } + tempJobs += KotlinEclipseScope.launch { + addMemberExtensions(implicitReceiverTypes, receiverTypes, callType, kindFilter, nameFilter) + } + tempJobs += KotlinEclipseScope.launch { + addNotImportedTopLevelCallables( + receiverTypes, + kindFilter, + nameFilter, + javaProject, + ktFile, + file, + identifierPart, + allowNoReceiver + ) + println("Finished!") + } + tempJobs += KotlinEclipseScope.launch { + addScopeAndSyntheticExtensions(resolutionScope, receiverTypes, callType, kindFilter, nameFilter) + } + tempJobs.joinAll() + } } - private fun MutableSet.addMemberExtensions( + private suspend fun MutableSet.addNotImportedTopLevelCallables( + receiverTypes: Collection, + kindFilter: DescriptorKindFilter, + nameFilter: (Name) -> Boolean, + javaProject: IJavaProject, + ktFile: KtFile, + file: IFile, + identifierPart: String?, + allowNoReceiver: Boolean + ) { + if (!identifierPart.isNullOrBlank()) { + val searchEngine = SearchEngine() + + val dependencyProjects = arrayListOf().apply { + addAll(ProjectUtils.getDependencyProjects(javaProject).map { JavaCore.create(it) }) + add(javaProject) + } + + val javaProjectSearchScope = + JavaSearchScopeFactory.getInstance().createJavaSearchScope(dependencyProjects.toTypedArray(), false) + + val tempClassNames = hashMapOf() + + val collector = object : MethodNameMatchRequestor() { + override fun acceptMethodNameMatch(match: MethodNameMatch) { + if (Flags.isPublic(match.modifiers)) { + tempClassNames[match.method.declaringType.getTypeQualifiedName('.')] = + match.method.declaringType.packageFragment.elementName + } + } + } + + searchEngine.searchAllMethodNames( + null, + SearchPattern.R_EXACT_MATCH, + null, + SearchPattern.R_EXACT_MATCH, + null, + SearchPattern.R_EXACT_MATCH, + identifierPart.toCharArray(), + SearchPattern.R_PREFIX_MATCH, + javaProjectSearchScope, + collector, + IJavaSearchConstants.FORCE_IMMEDIATE_SEARCH, + null + ) + + val tempCapitalIdentifier = + identifierPart.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() } + + searchEngine.searchAllMethodNames( + null, + SearchPattern.R_EXACT_MATCH, + null, + SearchPattern.R_EXACT_MATCH, + null, + SearchPattern.R_EXACT_MATCH, + "get$tempCapitalIdentifier".toCharArray(), + SearchPattern.R_PREFIX_MATCH, + javaProjectSearchScope, + collector, + IJavaSearchConstants.FORCE_IMMEDIATE_SEARCH, + null + ) + + val tempPackages = mutableListOf() + + val tempClasses = tempClassNames.mapNotNull { (className, packageName) -> + val tempClassId = ClassId(FqName(packageName), FqName(className), false) + moduleDescriptor.findClassAcrossModuleDependencies(tempClassId) ?: run { + tempPackages.add(resolutionFacade.moduleDescriptor.getPackage(FqName(packageName))) + null + } + } + + val importsSet = ktFile.importDirectives + .mapNotNull { it.importedFqName?.asString() } + .toSet() + + val originPackage = ktFile.packageFqName.asString() + + fun MemberScope.filterByKindAndName() = getDescriptorsFiltered( + kindFilter.intersect(CALLABLES), + nameFilter + ).asSequence() + .filterIsInstance() + .filter { callDesc -> + val tempFuzzy = callDesc.fuzzyExtensionReceiverType() + //We need all where the receiver matches. + val anyReceiverMatch = tempFuzzy != null && receiverTypes.any { receiverType -> + tempFuzzy.checkIsSuperTypeOf(receiverType) != null + } + //Or all, where we have no receiver, it's ok to have no receiver and the containing class of the callable Descriptor is not in the list of receivers, as this would give us the same completion twice. + val isTopLevelOrObjectCallable = callDesc.isTopLevelInPackage() || (callDesc.containingDeclaration as? ClassDescriptor)?.let { containing -> + val tempContainingKotlinType = containing.classValueType + val tempIsObject = containing.kind == ClassKind.OBJECT + val tempContainingIsReceiver = tempContainingKotlinType != null && receiverTypes.any { receiver -> receiver.isSubtypeOf(tempContainingKotlinType) } + tempIsObject && !tempContainingIsReceiver + } == true + val noReceiverMatches = allowNoReceiver && tempFuzzy == null && isTopLevelOrObjectCallable + noReceiverMatches || anyReceiverMatch + } + .filter { callDesc -> + callDesc.importableFqName?.asString() !in importsSet && + callDesc.importableFqName?.parent()?.asString() != originPackage + }.toList() + + val tempDeferreds = + tempPackages.map { desc -> KotlinEclipseScope.async { desc.memberScope.filterByKindAndName() } } + + tempClasses.map { desc -> KotlinEclipseScope.async { desc.unsubstitutedMemberScope.filterByKindAndName() } } + + val tempDescriptors = tempDeferreds.awaitAll().flatten() + + tempDescriptors + .map { + KotlinBasicCompletionProposal.Proposal( + KotlinImportCallableCompletionProposal( + it, + KotlinImageProvider.getImage(it), + file, + identifierPart + ), it + ) + }.toCollection(this) + } + } + + private fun MutableSet.addMemberExtensions( dispatchReceiverTypes: Collection, extensionReceiverTypes: Collection, callType: CallType<*>, kindFilter: DescriptorKindFilter, - nameFilter: (Name) -> Boolean + nameFilter: (Name) -> Boolean, ) { val memberFilter = kindFilter exclude DescriptorKindExclude.NonExtensions for (dispatchReceiverType in dispatchReceiverTypes) { for (member in dispatchReceiverType.memberScope.getDescriptorsFiltered(memberFilter, nameFilter)) { - addAll((member as CallableDescriptor).substituteExtensionIfCallable(extensionReceiverTypes, callType)) + addAll((member as CallableDescriptor).substituteExtensionIfCallable(extensionReceiverTypes, callType) + .map { KotlinBasicCompletionProposal.Descriptor(it) }) } } } - private fun MutableSet.addNonExtensionMembers( + private fun MutableSet.addNonExtensionMembers( receiverTypes: Collection, kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean, @@ -345,19 +554,19 @@ class KotlinReferenceVariantsHelper( } } - private fun MutableSet.addNonExtensionCallablesAndConstructors( + private fun MutableSet.addNonExtensionCallablesAndConstructors( scope: HierarchicalScope, kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean, constructorFilter: (ClassDescriptor) -> Boolean, classesOnly: Boolean ) { - var filterToUse = DescriptorKindFilter(kindFilter.kindMask and DescriptorKindFilter.CALLABLES.kindMask).exclude( + var filterToUse = DescriptorKindFilter(kindFilter.kindMask and CALLABLES.kindMask).exclude( DescriptorKindExclude.Extensions ) // should process classes if we need constructors - if (filterToUse.acceptsKinds(DescriptorKindFilter.FUNCTIONS_MASK)) { + if (filterToUse.acceptsKinds(FUNCTIONS_MASK)) { filterToUse = filterToUse.withKinds(DescriptorKindFilter.NON_SINGLETON_CLASSIFIERS_MASK) } @@ -365,14 +574,15 @@ class KotlinReferenceVariantsHelper( if (descriptor is ClassDescriptor) { if (descriptor.modality == Modality.ABSTRACT || descriptor.modality == Modality.SEALED) continue if (!constructorFilter(descriptor)) continue - descriptor.constructors.filterTo(this) { kindFilter.accepts(it) } + descriptor.constructors.map { KotlinBasicCompletionProposal.Descriptor(it) } + .filterTo(this) { kindFilter.accepts(it.descriptor) } } else if (!classesOnly && kindFilter.accepts(descriptor)) { - this.add(descriptor) + this.add(KotlinBasicCompletionProposal.Descriptor(descriptor)) } } } - private fun MutableSet.addScopeAndSyntheticExtensions( + private fun MutableSet.addScopeAndSyntheticExtensions( scope: LexicalScope, receiverTypes: Collection, callType: CallType<*>, @@ -385,9 +595,11 @@ class KotlinReferenceVariantsHelper( fun process(extensionOrSyntheticMember: CallableDescriptor) { if (kindFilter.accepts(extensionOrSyntheticMember) && nameFilter(extensionOrSyntheticMember.name)) { if (extensionOrSyntheticMember.isExtension) { - addAll(extensionOrSyntheticMember.substituteExtensionIfCallable(receiverTypes, callType)) + addAll( + extensionOrSyntheticMember.substituteExtensionIfCallable(receiverTypes, callType) + .map { KotlinBasicCompletionProposal.Descriptor(it) }) } else { - add(extensionOrSyntheticMember) + add(KotlinBasicCompletionProposal.Descriptor(extensionOrSyntheticMember)) } } } @@ -401,7 +613,7 @@ class KotlinReferenceVariantsHelper( } val syntheticScopes = resolutionFacade.getFrontendService(SyntheticScopes::class.java) - if (kindFilter.acceptsKinds(DescriptorKindFilter.VARIABLES_MASK)) { + if (kindFilter.acceptsKinds(VARIABLES_MASK)) { val lookupLocation = (scope.ownerDescriptor.toSourceElement.getPsi() as? KtElement)?.let { KotlinLookupLocation(it) } ?: NoLookupLocation.FROM_IDE @@ -411,7 +623,7 @@ class KotlinReferenceVariantsHelper( } } - if (kindFilter.acceptsKinds(DescriptorKindFilter.FUNCTIONS_MASK)) { + if (kindFilter.acceptsKinds(FUNCTIONS_MASK)) { for (syntheticMember in syntheticScopes.collectSyntheticMemberFunctions(receiverTypes)) { process(syntheticMember) } @@ -472,10 +684,11 @@ fun ResolutionScope.collectSyntheticStaticMembersAndConstructors( val syntheticScopes = resolutionFacade.getFrontendService(SyntheticScopes::class.java) val functionDescriptors = this.getContributedDescriptors(DescriptorKindFilter.FUNCTIONS) val classifierDescriptors = this.getContributedDescriptors(DescriptorKindFilter.CLASSIFIERS) - return (syntheticScopes.collectSyntheticStaticFunctions(functionDescriptors) + syntheticScopes.collectSyntheticConstructors(classifierDescriptors)) + return (syntheticScopes.collectSyntheticStaticFunctions(functionDescriptors) + syntheticScopes.collectSyntheticConstructors( + classifierDescriptors + )) .filter { kindFilter.accepts(it) && nameFilter(it.name) } } @OptIn(FrontendInternals::class) -private inline fun ResolutionFacade.frontendService(): T - = this.getFrontendService(T::class.java) +private inline fun ResolutionFacade.frontendService(): T = this.getFrontendService(T::class.java) diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/highlighting/KotlinSemanticHighlightingVisitor.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/highlighting/KotlinSemanticHighlightingVisitor.kt index 0d52ccc2d..227ad9987 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/highlighting/KotlinSemanticHighlightingVisitor.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/highlighting/KotlinSemanticHighlightingVisitor.kt @@ -66,7 +66,7 @@ public class KotlinSemanticHighlightingVisitor(val ktFile: KtFile, val document: if (target == null) return val smartCast = bindingContext.get(BindingContext.SMARTCAST, expression) - val typeName = smartCast?.defaultType?.let { DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(it) } ?: null + val typeName = smartCast?.defaultType?.let { DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(it) } when (target) { is TypeParameterDescriptor -> highlightTypeParameter(expression) @@ -210,4 +210,4 @@ sealed class HighlightPosition(offset: Int, length: Int) : Position(offset, leng class StyleAttributes(val styleAttributes: KotlinHighlightingAttributes, offset: Int, length: Int) : HighlightPosition(offset, length) class SmartCast(val typeName: String, offset: Int, length: Int) : HighlightPosition(offset, length) -} \ No newline at end of file +} diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/navigation/KotlinOpenDeclarationAction.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/navigation/KotlinOpenDeclarationAction.kt index 085230fb8..6b464c73f 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/navigation/KotlinOpenDeclarationAction.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/navigation/KotlinOpenDeclarationAction.kt @@ -1,19 +1,19 @@ /******************************************************************************* -* 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. -* -*******************************************************************************/ + * 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.ui.editors.navigation import org.eclipse.jdt.core.IJavaProject @@ -26,16 +26,16 @@ import org.jetbrains.kotlin.core.utils.getBindingContext import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.SourceElement import org.jetbrains.kotlin.eclipse.ui.utils.EditorUtil -import org.jetbrains.kotlin.psi.KtReferenceExpression +import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.ui.editors.KotlinEditor class KotlinOpenDeclarationAction(val editor: KotlinEditor) : SelectionDispatchAction(editor.javaEditor.site) { companion object { const val OPEN_EDITOR_TEXT = "OpenEditor" - fun getNavigationData(referenceExpression: KtReferenceExpression, javaProject: IJavaProject): NavigationData? { - val context = referenceExpression.getBindingContext() - return createReferences(referenceExpression) + fun getNavigationData(ktElement: KtElement, javaProject: IJavaProject): NavigationData? { + val context = ktElement.getBindingContext() + return createReferences(ktElement) .asSequence() .flatMap { it.getTargetDescriptors(context).asSequence() } .mapNotNull { descriptor -> @@ -47,25 +47,19 @@ class KotlinOpenDeclarationAction(val editor: KotlinEditor) : SelectionDispatchA data class NavigationData(val sourceElement: SourceElement, val descriptor: DeclarationDescriptor) } - + init { text = ActionMessages.OpenAction_declaration_label actionDefinitionId = IJavaEditorActionDefinitionIds.OPEN_EDITOR } - + override fun run(selection: ITextSelection) { - val selectedExpression = EditorUtil.getReferenceExpression(editor, selection.offset) ?: return + val selectedExpression = EditorUtil.getReferenceExpression(editor, selection.offset) ?: + EditorUtil.getJetElement(editor, selection.offset) ?: return val javaProject = editor.javaProject ?: return - + val data = getNavigationData(selectedExpression, javaProject) ?: return gotoElement(data.sourceElement, data.descriptor, selectedExpression, editor, javaProject) } - - internal fun run(refElement: KtReferenceExpression) { - val javaProject = editor.javaProject ?: return - val data = getNavigationData(refElement, javaProject) ?: return - - gotoElement(data.sourceElement, data.descriptor, refElement, editor, javaProject) - } -} \ No newline at end of file +} diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/navigation/KotlinOpenSuperImplementationAction.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/navigation/KotlinOpenSuperImplementationAction.kt index bcbaecdf9..69c48bbeb 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/navigation/KotlinOpenSuperImplementationAction.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/navigation/KotlinOpenSuperImplementationAction.kt @@ -1,23 +1,25 @@ /******************************************************************************* -* 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. -* -*******************************************************************************/ + * 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.ui.editors.navigation import com.intellij.psi.util.PsiTreeUtil +import org.eclipse.core.commands.AbstractHandler +import org.eclipse.core.commands.ExecutionEvent import org.eclipse.jdt.internal.ui.actions.ActionMessages import org.eclipse.jdt.ui.actions.IJavaEditorActionDefinitionIds import org.eclipse.jdt.ui.actions.SelectionDispatchAction @@ -26,116 +28,147 @@ import org.eclipse.jface.viewers.ArrayContentProvider import org.eclipse.jface.window.Window import org.eclipse.ui.PlatformUI import org.eclipse.ui.dialogs.ListDialog +import org.eclipse.ui.handlers.HandlerUtil import org.jetbrains.kotlin.core.model.KotlinAnalysisFileCache import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor -import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DECLARATION -import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DELEGATION -import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.FAKE_OVERRIDE -import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.SYNTHESIZED +import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.* import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.MemberDescriptor import org.jetbrains.kotlin.eclipse.ui.utils.EditorUtil -import org.jetbrains.kotlin.psi.KtClass -import org.jetbrains.kotlin.psi.KtDeclaration -import org.jetbrains.kotlin.psi.KtNamedFunction -import org.jetbrains.kotlin.psi.KtObjectDeclaration -import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.stubs.KotlinStubWithFqName import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.OverridingUtil import org.jetbrains.kotlin.ui.editors.KotlinCommonEditor import org.jetbrains.kotlin.ui.overrideImplement.KotlinCallableLabelProvider -import java.util.LinkedHashSet -public class KotlinOpenSuperImplementationAction(val editor: KotlinCommonEditor) : SelectionDispatchAction(editor.site) { +class KotlinOpenSuperImplementationActionHandler : AbstractHandler() { + override fun execute(event: ExecutionEvent): Any? { + val editor = HandlerUtil.getActiveEditor(event) + if (editor !is KotlinCommonEditor) return null + + KotlinOpenSuperImplementationAction(editor).run() + + return null + } +} + +class KotlinOpenAllSuperImplementationActionHandler : AbstractHandler() { + override fun execute(event: ExecutionEvent): Any? { + val editor = HandlerUtil.getActiveEditor(event) + if (editor !is KotlinCommonEditor) return null + + KotlinOpenSuperImplementationAction(editor, true).run() + + return null + } +} + +class KotlinOpenSuperImplementationAction( + private val editor: KotlinCommonEditor, + private val recursive: Boolean = false +) : SelectionDispatchAction(editor.site) { init { - setActionDefinitionId(IJavaEditorActionDefinitionIds.OPEN_SUPER_IMPLEMENTATION) - setText(ActionMessages.OpenSuperImplementationAction_label) - setDescription(ActionMessages.OpenSuperImplementationAction_description) + actionDefinitionId = IJavaEditorActionDefinitionIds.OPEN_SUPER_IMPLEMENTATION + text = ActionMessages.OpenSuperImplementationAction_label + description = ActionMessages.OpenSuperImplementationAction_description } - + companion object { - val ACTION_ID = "OpenSuperImplementation" + const val ACTION_ID = "OpenSuperImplementation" } - + override fun run(selection: ITextSelection) { val ktFile = editor.parsedFile val project = editor.javaProject if (ktFile == null || project == null) return - - val psiElement = EditorUtil.getPsiElement(editor, selection.offset) - if (psiElement == null) return - - val declaration: KtDeclaration? = PsiTreeUtil.getParentOfType(psiElement, + + val psiElement = EditorUtil.getPsiElement(editor, selection.offset) ?: return + + val declaration: KtTypeParameterListOwnerStub>> = + PsiTreeUtil.getParentOfType( + psiElement, KtNamedFunction::class.java, KtClass::class.java, KtProperty::class.java, - KtObjectDeclaration::class.java) - if (declaration == null) return - + KtObjectDeclaration::class.java + ) ?: return + val descriptor = resolveToDescriptor(declaration) if (descriptor !is DeclarationDescriptor) return - - val superDeclarations = findSuperDeclarations(descriptor) + + val superDeclarations = + if (recursive) findSuperDeclarationsRecursive(descriptor) else findSuperDeclarations(descriptor) if (superDeclarations.isEmpty()) return - - val superDeclaration = when { - superDeclarations.isEmpty() -> null - superDeclarations.size == 1 -> superDeclarations.first() + + val superDeclaration = when (superDeclarations.size) { + 1 -> superDeclarations.first() else -> chooseFromSelectionDialog(superDeclarations) - } - - if (superDeclaration == null) return - + } ?: return + gotoElement(superDeclaration, declaration, editor, project) } - + private fun chooseFromSelectionDialog(declarations: Set): MemberDescriptor? { - val shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell() + val shell = PlatformUI.getWorkbench().activeWorkbenchWindow.shell val dialog = ListDialog(shell) - + dialog.setTitle("Super Declarations") dialog.setMessage("Select a declaration to navigate") dialog.setContentProvider(ArrayContentProvider()) dialog.setLabelProvider(KotlinCallableLabelProvider()) - + dialog.setInput(declarations.toTypedArray()) - + if (dialog.open() == Window.CANCEL) { - return null; + return null } - - val result = dialog.getResult() + + val result = dialog.result if (result == null || result.size != 1) { return null } - + return result[0] as MemberDescriptor } - + private fun resolveToDescriptor(declaration: KtDeclaration): DeclarationDescriptor? { - val context = KotlinAnalysisFileCache.getAnalysisResult(declaration.getContainingKtFile()).analysisResult.bindingContext + val context = + KotlinAnalysisFileCache.getAnalysisResult(declaration.containingKtFile).analysisResult.bindingContext return context[BindingContext.DECLARATION_TO_DESCRIPTOR, declaration] } - + private fun findSuperDeclarations(descriptor: DeclarationDescriptor): Set { val superDescriptors = when (descriptor) { is ClassDescriptor -> { - descriptor.typeConstructor.supertypes.mapNotNull { + descriptor.typeConstructor.supertypes.mapNotNull { val declarationDescriptor = it.constructor.declarationDescriptor if (declarationDescriptor is ClassDescriptor) declarationDescriptor else null }.toSet() } - + is CallableMemberDescriptor -> descriptor.getDirectlyOverriddenDeclarations().toSet() - + else -> emptySet() } - + return superDescriptors } - fun D.getDirectlyOverriddenDeclarations(): Collection { + private fun findSuperDeclarationsRecursive(descriptor: DeclarationDescriptor): Set { + val tempResult = mutableSetOf() + var tempNewResults = setOf(descriptor) + while (tempNewResults.isNotEmpty()) { + val tempSuperResults = tempNewResults.flatMapTo(hashSetOf()) { findSuperDeclarations(it) } + tempSuperResults -= tempResult + tempResult += tempSuperResults + tempNewResults = tempSuperResults + } + return tempResult + } + + private fun D.getDirectlyOverriddenDeclarations(): Collection { val result = LinkedHashSet() for (overriddenDescriptor in overriddenDescriptors) { @Suppress("UNCHECKED_CAST") @@ -150,4 +183,4 @@ public class KotlinOpenSuperImplementationAction(val editor: KotlinCommonEditor) } return OverridingUtil.filterOutOverridden(result) } -} \ No newline at end of file +} diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/occurrences/KotlinMarkOccurrences.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/occurrences/KotlinMarkOccurrences.kt index 4604cb7bc..d841859d9 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/occurrences/KotlinMarkOccurrences.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/occurrences/KotlinMarkOccurrences.kt @@ -29,25 +29,24 @@ import org.eclipse.search.ui.text.Match import org.eclipse.ui.ISelectionListener import org.eclipse.ui.IWorkbenchPart import org.jetbrains.kotlin.core.builder.KotlinPsiManager +import org.jetbrains.kotlin.core.model.runJob import org.jetbrains.kotlin.core.references.resolveToSourceDeclaration import org.jetbrains.kotlin.descriptors.SourceElement import org.jetbrains.kotlin.eclipse.ui.utils.EditorUtil import org.jetbrains.kotlin.eclipse.ui.utils.getTextDocumentOffset -import org.jetbrains.kotlin.core.model.runJob import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.ui.commands.findReferences.KotlinScopedQuerySpecification import org.jetbrains.kotlin.ui.editors.KotlinCommonEditor import org.jetbrains.kotlin.ui.editors.KotlinEditor -import org.jetbrains.kotlin.ui.editors.KotlinFileEditor import org.jetbrains.kotlin.ui.editors.annotations.AnnotationManager import org.jetbrains.kotlin.ui.refactorings.rename.getLengthOfIdentifier import org.jetbrains.kotlin.ui.search.KotlinElementMatch import org.jetbrains.kotlin.ui.search.KotlinQueryParticipant import org.jetbrains.kotlin.ui.search.getContainingClassOrObjectForConstructor -public class KotlinMarkOccurrences(val kotlinEditor: KotlinCommonEditor) : ISelectionListener { +class KotlinMarkOccurrences(private val kotlinEditor: KotlinCommonEditor) : ISelectionListener { companion object { - private val ANNOTATION_TYPE = "org.eclipse.jdt.ui.occurrences" + private const val ANNOTATION_TYPE = "org.eclipse.jdt.ui.occurrences" } override fun selectionChanged(part: IWorkbenchPart, selection: ISelection) { @@ -58,16 +57,12 @@ public class KotlinMarkOccurrences(val kotlinEditor: KotlinCommonEditor) : ISele val file = part.eclipseFile if (file == null || !file.exists()) return@runJob Status.CANCEL_STATUS - val document = part.getDocumentSafely() - if (document == null) return@runJob Status.CANCEL_STATUS - + val document = part.getDocumentSafely() ?: return@runJob Status.CANCEL_STATUS + KotlinPsiManager.getKotlinFileIfExist(file, document.get()) - val ktElement = EditorUtil.getJetElement(part, selection.getOffset()) - if (ktElement == null) { - return@runJob Status.CANCEL_STATUS - } - + val ktElement = EditorUtil.getJetElement(part, selection.offset) ?: return@runJob Status.CANCEL_STATUS + val occurrences = findOccurrences(part, ktElement, file) updateOccurrences(part, occurrences) } @@ -88,25 +83,24 @@ public class KotlinMarkOccurrences(val kotlinEditor: KotlinCommonEditor) : ISele val searchingElements = getSearchingElements(sourceElements) val querySpecification = KotlinScopedQuerySpecification(searchingElements, listOf(file), - IJavaSearchConstants.ALL_OCCURRENCES, "Searching in ${file.getName()}") + IJavaSearchConstants.ALL_OCCURRENCES, "Searching in ${file.name}") val occurrences = arrayListOf() KotlinQueryParticipant().search({ occurrences.add(it) }, querySpecification, NullProgressMonitor()) - return occurrences.map { - if (it !is KotlinElementMatch) return@map null + return occurrences.mapNotNull { + if (it !is KotlinElementMatch) return@mapNotNull null val element = it.jetElement - val length = getLengthOfIdentifier(element) - if (length == null) return@map null - - val document = editor.getDocumentSafely() ?: return@map null + val length = getLengthOfIdentifier(element) ?: return@mapNotNull null + + val document = editor.getDocumentSafely() ?: return@mapNotNull null Position(element.getTextDocumentOffset(document), length) - }.filterNotNull() + } } private fun getSearchingElements(sourceElements: List): List { val classOrObjects = getContainingClassOrObjectForConstructor(sourceElements) - return if (classOrObjects.isNotEmpty()) classOrObjects else sourceElements + return classOrObjects.ifEmpty { sourceElements } } -} \ No newline at end of file +} diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/launch/CompilerStatusHandler.java b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/launch/CompilerStatusHandler.java deleted file mode 100644 index 4271a4d6f..000000000 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/launch/CompilerStatusHandler.java +++ /dev/null @@ -1,125 +0,0 @@ -/******************************************************************************* - * Copyright 2000-2014 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.ui.launch; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.debug.core.IStatusHandler; -import org.eclipse.swt.graphics.Color; -import org.eclipse.swt.graphics.RGB; -import org.eclipse.ui.console.ConsolePlugin; -import org.eclipse.ui.console.MessageConsole; -import org.eclipse.ui.console.MessageConsoleStream; -import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation; -import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity; -import org.jetbrains.kotlin.core.launch.CompilerOutputData; -import org.jetbrains.kotlin.core.launch.CompilerOutputElement; - -public class CompilerStatusHandler implements IStatusHandler { - - public static final String CONSOLE_NAME = "org.jetbrains.kotlin.ui.console"; - - private static final RGB CONSOLE_RED = new RGB(229, 43, 80); - private static final RGB CONSOLE_YELLOW = new RGB(218, 165, 32); - private static final RGB CONSOLE_BLACK = new RGB(0, 0, 0); - - @Override - public Object handleStatus(IStatus status, Object source) throws CoreException { - if (!(source instanceof CompilerOutputData)) { - return null; - } - - List outputDataList = ((CompilerOutputData) source).getList(); - - Map> sortedOutput = groupOutputByPath(outputDataList); - - MessageConsole msgConsole = KotlinConsoleKt.createCleanKotlinConsole(); - for (List outputList : sortedOutput.values()) { - printCompilerOutputList(outputList, msgConsole); - } - - if (status.getSeverity() == IStatus.ERROR) { - ConsolePlugin.getDefault().getConsoleManager().showConsoleView(msgConsole); - } - - return null; - } - - private void printCompilerOutputList(List outputList, MessageConsole msgConsole) { - - CompilerMessageLocation location = outputList.get(0).getMessageLocation(); - printlnToConsole(location != null ? location.getPath() : "No Location", CONSOLE_BLACK, msgConsole); - - for (CompilerOutputElement dataElement : outputList) { - RGB color = getColorByMessageSeverity(dataElement.getMessageSeverity()); - StringBuilder message = new StringBuilder(); - - message.append("\t"); - message.append(dataElement.getMessageSeverity().toString() + ": " + dataElement.getMessage()); - if (dataElement.getMessageLocation() != null) { - message.append(" (" + dataElement.getMessageLocation().getLine() + ", " + dataElement.getMessageLocation().getColumn() + ")"); - } - - printlnToConsole(message.toString(), color, msgConsole); - } - } - - private RGB getColorByMessageSeverity(CompilerMessageSeverity messageSeverity) { - RGB color = null; - switch (messageSeverity) { - case ERROR: - color = CONSOLE_RED; - break; - case WARNING: - color = CONSOLE_YELLOW; - break; - default: - color = CONSOLE_BLACK; - break; - } - - return color; - } - - private void printlnToConsole(String message, RGB color, MessageConsole msgConsole) { - MessageConsoleStream msgStream = msgConsole.newMessageStream(); - msgStream.setColor(new Color(null, color)); - - msgStream.println(message); - } - - private Map> groupOutputByPath(List outputData) { - Map> res = new HashMap>(); - String emptyPath = ""; - for (CompilerOutputElement dataElement : outputData) { - CompilerMessageLocation location = dataElement.getMessageLocation(); - String path = location != null ? location.getPath() : emptyPath; - if (!res.containsKey(path)) { - res.put(path, new ArrayList()); - } - - res.get(path).add(dataElement); - } - - return res; - } -} \ No newline at end of file diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/launch/CompilerStatusHandler.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/launch/CompilerStatusHandler.kt new file mode 100644 index 000000000..f1d4b9d7a --- /dev/null +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/launch/CompilerStatusHandler.kt @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright 2000-2014 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.ui.launch + +import org.eclipse.core.runtime.CoreException +import org.eclipse.core.runtime.IStatus +import org.eclipse.debug.core.IStatusHandler +import org.eclipse.jdt.core.IJavaProject +import org.eclipse.swt.graphics.Color +import org.eclipse.swt.graphics.RGB +import org.eclipse.ui.console.ConsolePlugin +import org.eclipse.ui.console.MessageConsole +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity +import org.jetbrains.kotlin.core.compiler.KotlinCompilerUtils +import org.jetbrains.kotlin.core.launch.CompilerOutputElement + +class CompilerStatusHandler : IStatusHandler { + + @Throws(CoreException::class) + override fun handleStatus(status: IStatus, source: Any): Any? { + if (source !is KotlinCompilerUtils.CompilerOutputWithProject) { + return null + } + val javaProject: IJavaProject = source.project + val outputDataList: List = source.data.list + val sortedOutput = groupOutputByPath(outputDataList) + val msgConsole = createCleanKotlinConsole(javaProject) + for (outputList in sortedOutput.values) { + printCompilerOutputList(outputList, msgConsole) + } + if (status.severity == IStatus.ERROR) { + ConsolePlugin.getDefault().consoleManager.showConsoleView(msgConsole) + } + return null + } + + private fun printCompilerOutputList(outputList: List, msgConsole: MessageConsole) { + val path = outputList[0].messageLocation?.path + msgConsole.println(path ?: "No Location", CONSOLE_BLACK) + for (dataElement in outputList) { + val color = dataElement.messageSeverity.color + val message = StringBuilder() + message.append("\t") + message.append(dataElement.messageSeverity.toString() + ": " + dataElement.message) + if (dataElement.messageLocation != null) { + message.append(" (" + dataElement.messageLocation.line + ", " + dataElement.messageLocation.column + ")") + } + msgConsole.println(message.toString(), color) + } + } + + private val CompilerMessageSeverity.color: RGB + get() = when (this) { + CompilerMessageSeverity.ERROR -> CONSOLE_RED + CompilerMessageSeverity.WARNING -> CONSOLE_YELLOW + else -> CONSOLE_BLACK + } + + private fun MessageConsole.println(message: String, color: RGB?) = newMessageStream().apply { + this.color = Color(null, color) + println(message) + } + + private fun groupOutputByPath(outputData: List): Map> { + val res: MutableMap> = HashMap() + val emptyPath = "" + for (dataElement in outputData) { + val path = dataElement.messageLocation?.path ?: emptyPath + if (!res.containsKey(path)) { + res[path] = ArrayList() + } + res[path]!!.add(dataElement) + } + return res + } + + companion object { + private val CONSOLE_RED = RGB(229, 43, 80) + private val CONSOLE_YELLOW = RGB(218, 165, 32) + private val CONSOLE_BLACK = RGB(0, 0, 0) + } +} \ No newline at end of file diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/launch/kotlinConsole.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/launch/kotlinConsole.kt index 10c1d3622..decb07cdb 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/launch/kotlinConsole.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/launch/kotlinConsole.kt @@ -16,24 +16,31 @@ *******************************************************************************/ package org.jetbrains.kotlin.ui.launch +import org.eclipse.jdt.core.IJavaProject import org.eclipse.ui.console.ConsolePlugin -import org.eclipse.ui.console.IConsoleManager import org.eclipse.ui.console.MessageConsole private const val KOTLIN_CONSOLE_ID = "org.jetbrains.kotlin.ui.console" -fun createCleanKotlinConsole(): MessageConsole { - val consoleManager = ConsolePlugin.getDefault().getConsoleManager() - removeKotlinConsoles(consoleManager) - - val messageConsole = MessageConsole(KOTLIN_CONSOLE_ID, null) - consoleManager.addConsoles(arrayOf(messageConsole)) - +private val manager get() = ConsolePlugin.getDefault().consoleManager + +private val IJavaProject.consoleName get() = "$KOTLIN_CONSOLE_ID.${project.name}" + +fun createCleanKotlinConsole(javaProject: IJavaProject): MessageConsole { + removeKotlinConsoles(javaProject) + + return createNewKotlinConsole(javaProject) +} + +private fun createNewKotlinConsole(javaProject: IJavaProject): MessageConsole { + val messageConsole = MessageConsole(javaProject.consoleName, null) + manager.addConsoles(arrayOf(messageConsole)) + return messageConsole } -fun removeKotlinConsoles(manager: IConsoleManager) { +fun removeKotlinConsoles(javaProject: IJavaProject) { manager.removeConsoles(manager.consoles - .filter { it.name == KOTLIN_CONSOLE_ID } + .filter { it.name == javaProject.consoleName } .toTypedArray()) } \ No newline at end of file diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/navigation/KotlinOpenEditor.java b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/navigation/KotlinOpenEditor.java index c3c02d09f..f4a7ac762 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/navigation/KotlinOpenEditor.java +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/navigation/KotlinOpenEditor.java @@ -66,7 +66,7 @@ public static List findSourceFiles(@NotNull IJavaElement element) { } File lightClass = resource.getFullPath().toFile(); - List sourceFiles = KotlinLightClassManager + List sourceFiles = KotlinLightClassManager.Companion .getInstance(element.getJavaProject().getProject()).getSourceFiles(lightClass); KtFile navigationFile = KotlinOpenEditorUtilsKt.findNavigationFileFromSources(element, sourceFiles); diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/refactorings/rename/KotlinRenameParticipant.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/refactorings/rename/KotlinRenameParticipant.kt index 4c74f0d34..d186de726 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/refactorings/rename/KotlinRenameParticipant.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/refactorings/rename/KotlinRenameParticipant.kt @@ -106,19 +106,15 @@ open class KotlinRenameParticipant : RenameParticipant() { val jetElement = match.jetElement - val eclipseFile = KotlinPsiManager.getEclipseFile(jetElement.getContainingKtFile()) - if (eclipseFile == null) return null - + val eclipseFile = KotlinPsiManager.getEclipseFile(jetElement.containingKtFile) ?: return null + val document = EditorUtil.getDocument(eclipseFile) // TODO: make workaround here later - val textLength = getLengthOfIdentifier(jetElement) - return if (textLength != null) { - FileEdit( - eclipseFile, - ReplaceEdit(jetElement.getTextDocumentOffset(document), textLength, newName)) - } else { - null - } + val textLength = getLengthOfIdentifier(jetElement) ?: return null + + return FileEdit( + eclipseFile, + ReplaceEdit(jetElement.getTextDocumentOffset(document), textLength, newName)) } private fun obtainOriginElement(javaElement: IJavaElement): IJavaElement { @@ -130,4 +126,4 @@ open class KotlinRenameParticipant : RenameParticipant() { } } -data class FileEdit(val file: IFile, val edit: TextEdit) \ No newline at end of file +data class FileEdit(val file: IFile, val edit: TextEdit) diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/refactorings/rename/refactoringUtils.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/refactorings/rename/refactoringUtils.kt index 742572a86..5584ac2cb 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/refactorings/rename/refactoringUtils.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/refactorings/rename/refactoringUtils.kt @@ -22,8 +22,8 @@ import org.jetbrains.kotlin.psi.KtReferenceExpression fun getLengthOfIdentifier(jetElement: KtElement): Int? { return when (jetElement) { - is KtNamedDeclaration -> jetElement.getNameIdentifier()!!.getTextLength() + is KtNamedDeclaration -> jetElement.nameIdentifier!!.textLength is KtReferenceExpression -> jetElement.getTextLength() else -> null } -} \ No newline at end of file +} diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/search/KotlinElementMatchCreator.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/search/KotlinElementMatchCreator.kt new file mode 100644 index 000000000..d749be762 --- /dev/null +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/search/KotlinElementMatchCreator.kt @@ -0,0 +1,69 @@ +package org.jetbrains.kotlin.ui.search + +import com.intellij.psi.util.PsiTreeUtil +import org.eclipse.jdt.ui.search.QuerySpecification +import org.eclipse.search.ui.text.Match +import org.jetbrains.kotlin.core.utils.getBindingContext +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.renderer.DescriptorRenderer +import org.jetbrains.kotlin.resolve.calls.callUtil.getCall +import org.jetbrains.kotlin.ui.commands.findReferences.KotlinFindImplementationsInProjectAction + +interface KotlinElementMatchCreator { + + fun createMatch(element: KtElement): Match? + + companion object { + fun getMatchCreator(querySpecification: QuerySpecification) = when (querySpecification.limitTo) { + KotlinFindImplementationsInProjectAction.IMPLEMENTORS_LIMIT_TO -> InheritorsSearchMatchCreator + else -> BasicSearchMatchCreator + } + } +} + +object InheritorsSearchMatchCreator : KotlinElementMatchCreator { + override fun createMatch(element: KtElement): Match? { + val (descriptor: DeclarationDescriptor?, nameIdentifier) = when (element) { + is KtNamedFunction -> element.resolveToDescriptorIfAny() to element.nameIdentifier + is KtClass -> element.resolveToDescriptorIfAny() to element.nameIdentifier + is KtProperty -> element.resolveToDescriptorIfAny() to element.nameIdentifier + is KtObjectDeclaration -> element.resolveToDescriptorIfAny() to element.nameIdentifier + else -> return null + } + val tempLabel = + descriptor?.let { DescriptorRenderer.SHORT_NAMES_IN_TYPES.render(it) } ?: BasicSearchMatchCreator.render( + element + ) + + return KotlinElementMatch(element, tempLabel, nameIdentifier ?: element) + } + +} + +object BasicSearchMatchCreator : KotlinElementMatchCreator { + + override fun createMatch(element: KtElement): Match { + return KotlinElementMatch(element, render(element), element) + } + + fun render(element: KtElement): String { + val tempElement = element.getCall(element.getBindingContext())?.toString() ?: element.text + + val tempParentDescriptor = + PsiTreeUtil.getParentOfType(element, KtDeclaration::class.java)?.resolveToDescriptorIfAny() + + return buildString { + append(tempElement.lines().first()) + if (tempParentDescriptor != null) { + append(" in ") + append(DescriptorRenderer.SHORT_NAMES_IN_TYPES.withOptions { + modifiers = emptySet() + includeAdditionalModifiers = false + }.render(tempParentDescriptor)) + } + } + } + +} diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/search/KotlinQueryParticipant.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/search/KotlinQueryParticipant.kt index 6060a426b..c261800d5 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/search/KotlinQueryParticipant.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/search/KotlinQueryParticipant.kt @@ -16,6 +16,7 @@ *******************************************************************************/ package org.jetbrains.kotlin.ui.search +import com.intellij.psi.PsiElement import com.intellij.psi.util.PsiTreeUtil import org.eclipse.core.resources.IFile import org.eclipse.core.resources.IProject @@ -24,6 +25,7 @@ import org.eclipse.core.runtime.IAdaptable import org.eclipse.core.runtime.IProgressMonitor import org.eclipse.core.runtime.ISafeRunnable import org.eclipse.jdt.core.IJavaElement +import org.eclipse.jdt.core.IType import org.eclipse.jdt.core.search.IJavaSearchScope import org.eclipse.jdt.internal.core.JavaModel import org.eclipse.jdt.internal.ui.search.AbstractJavaSearchResult @@ -44,27 +46,48 @@ import org.eclipse.ui.model.IWorkbenchAdapter import org.jetbrains.kotlin.core.builder.KotlinPsiManager import org.jetbrains.kotlin.core.log.KotlinLogger import org.jetbrains.kotlin.core.model.sourceElementsToLightElements -import org.jetbrains.kotlin.core.model.toJavaElements import org.jetbrains.kotlin.core.references.resolveToSourceDeclaration -import org.jetbrains.kotlin.core.utils.getBindingContext +import org.jetbrains.kotlin.core.resolve.lang.java.structure.EclipseJavaElementUtil +import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.SourceElement import org.jetbrains.kotlin.eclipse.ui.utils.EditorUtil import org.jetbrains.kotlin.eclipse.ui.utils.findElementByDocumentOffset -import org.jetbrains.kotlin.idea.util.findAnnotation +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny import org.jetbrains.kotlin.lexer.KtSingleValueToken import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.resolve.calls.callUtil.getCall +import org.jetbrains.kotlin.psi.psiUtil.getParentOfType import org.jetbrains.kotlin.resolve.source.KotlinSourceElement -import org.jetbrains.kotlin.resolve.source.toSourceElement import org.jetbrains.kotlin.types.expressions.OperatorConventions -import org.jetbrains.kotlin.ui.commands.findReferences.KotlinAndJavaSearchable -import org.jetbrains.kotlin.ui.commands.findReferences.KotlinJavaQuerySpecification -import org.jetbrains.kotlin.ui.commands.findReferences.KotlinOnlyQuerySpecification -import org.jetbrains.kotlin.ui.commands.findReferences.KotlinScoped +import org.jetbrains.kotlin.ui.commands.findReferences.* class KotlinQueryParticipant : IQueryParticipant { + + private inner class DelegatedUniqueMatchSearchRequestor( + private val delegate: ISearchRequestor, + private val querySpecification: QuerySpecification + ) : ISearchRequestor { + + private val seenMatches: MutableSet = mutableSetOf() + override fun reportMatch(match: Match) { + val tempElement = match.element + if ((match is KotlinElementMatch && !seenMatches.add(match.jetElement)) || (tempElement is IJavaElement && !seenMatches.add(tempElement))) return + + delegate.reportMatch(match) + + if (querySpecification.limitTo == KotlinFindImplementationsInProjectAction.IMPLEMENTORS_LIMIT_TO && match is KotlinElementMatch && match.jetElement is KtClass) { + val tempNewSpec = KotlinOnlyQuerySpecification( + match.jetElement, + querySpecification.getFilesInScope(), + KotlinFindImplementationsInProjectAction.IMPLEMENTORS_LIMIT_TO, + querySpecification.scopeDescription + ) + search(this, tempNewSpec, null) + } + } + } + override fun search( requestor: ISearchRequestor, querySpecification: QuerySpecification, @@ -72,14 +95,19 @@ class KotlinQueryParticipant : IQueryParticipant { ) { SafeRunnable.run(object : ISafeRunnable { override fun run() { + val tempRequestor = + requestor as? DelegatedUniqueMatchSearchRequestor ?: DelegatedUniqueMatchSearchRequestor( + requestor, + querySpecification + ) val searchElements = getSearchElements(querySpecification) if (searchElements.isEmpty()) return - + if (querySpecification is KotlinAndJavaSearchable) { - runCompositeSearch(searchElements, requestor, querySpecification, monitor) + runCompositeSearch(searchElements, tempRequestor, querySpecification, monitor) return } - + val kotlinFiles = getKotlinFilesByScope(querySpecification) if (kotlinFiles.isEmpty()) return if (monitor?.isCanceled == true) return @@ -87,114 +115,126 @@ class KotlinQueryParticipant : IQueryParticipant { if (searchElements.size > 1) { KotlinLogger.logWarning("There are more than one elements to search: $searchElements") } - + // We assume that there is only one search element, it could be IJavaElement or KtElement val searchElement = searchElements.first() val searchResult = searchTextOccurrences(searchElement, kotlinFiles) ?: return if (monitor?.isCanceled == true) return - - val elements = obtainElements(searchResult as FileSearchResult, kotlinFiles) + + val elements = obtainElements(searchResult as FileSearchResult, kotlinFiles).flatMap { + val tempImportAlias = it.getParentOfType(false)?.alias + + if (tempImportAlias != null) { + val tempEclipseFile = KotlinPsiManager.getEclipseFile(tempImportAlias.containingKtFile)!! + val tempResult = searchTextOccurrences( + SearchElement.KotlinSearchElement(tempImportAlias), + listOf(tempEclipseFile) + ) + return@flatMap obtainElements(tempResult as FileSearchResult, listOf(tempEclipseFile)) + it + } + + listOf(it) + } + if (monitor?.isCanceled == true) return val matchedReferences = resolveElementsAndMatch(elements, searchElement, querySpecification) if (monitor?.isCanceled == true) return matchedReferences.forEach { ktElement -> - val tempElement = ktElement.getCall(ktElement.getBindingContext())?.toString() ?: ktElement.text - var tempFunction = PsiTreeUtil.getNonStrictParentOfType(ktElement, KtFunction::class.java) - while(tempFunction?.isLocal == true) { - tempFunction = PsiTreeUtil.getParentOfType(tempFunction, KtFunction::class.java) - } - val tempClassObjectOrFileName = PsiTreeUtil.getNonStrictParentOfType(ktElement, KtClassOrObject::class.java)?.name - ?: ktElement.containingKtFile.name - - val tempLabel = buildString { - append(tempClassObjectOrFileName) - if(tempFunction != null) { - append("#") - append(tempFunction.name) - } - if(isNotEmpty()) { - append(": ") - } - append(tempElement) - } - - requestor.reportMatch(KotlinElementMatch(ktElement, tempLabel)) + val tempCreator = KotlinElementMatchCreator.getMatchCreator(querySpecification) + tempCreator.createMatch(ktElement)?.let { tempRequestor.reportMatch(it) } } } - + override fun handleException(exception: Throwable) { KotlinLogger.logError(exception) } }) } - + override fun estimateTicks(specification: QuerySpecification): Int = 500 - + override fun getUIParticipant() = KotlinReferenceMatchPresentation() - + private fun runCompositeSearch( elements: List, requestor: ISearchRequestor, originSpecification: QuerySpecification, monitor: IProgressMonitor? ) { - + + fun reportMatch(match: Match) { + val tempElement = match.element + if (tempElement is IJavaElement && EclipseJavaElementUtil.isFromKotlinBinFolder(tempElement)) { + return + } + requestor.reportMatch(match) + } + fun reportSearchResults(result: AbstractJavaSearchResult) { for (searchElement in result.elements) { - result.getMatches(searchElement).forEach { requestor.reportMatch(it) } + result.getMatches(searchElement).forEach { match -> + reportMatch(match) + } } } - - val specifications = elements.map { searchElement -> + + val specifications = elements.map { searchElement -> when (searchElement) { - is SearchElement.JavaSearchElement -> + is SearchElement.JavaSearchElement -> ElementQuerySpecification( - searchElement.javaElement, + searchElement.javaElement, originSpecification.limitTo, originSpecification.scope, originSpecification.scopeDescription ) - - is SearchElement.KotlinSearchElement -> + + is SearchElement.KotlinSearchElement -> KotlinOnlyQuerySpecification( searchElement.kotlinElement, - originSpecification.getFilesInScope(), + originSpecification.getFilesInScope(), originSpecification.limitTo, originSpecification.scopeDescription ) } } - for (specification in specifications) { + for (specification in specifications) { if (specification is KotlinScoped) { - KotlinQueryParticipant().search({ requestor.reportMatch(it) }, specification, monitor) - } else { + search(requestor, specification, monitor) + } else if (specification is ElementQuerySpecification && specification.limitTo == KotlinFindImplementationsInProjectAction.IMPLEMENTORS_LIMIT_TO && specification.element is IType) { + //We need this workaround to find java subclasses! + val tempElement = specification.element as IType + val tempSubTypes = tempElement.newTypeHierarchy(monitor).getAllSubtypes(tempElement) + for (tempMatch in tempSubTypes.map { Match(it, it.nameRange.offset, it.nameRange.length) }) { + reportMatch(tempMatch) + } + } else { val searchQuery = JavaSearchQuery(specification) searchQuery.run(monitor) reportSearchResults(searchQuery.searchResult as AbstractJavaSearchResult) } } } - + sealed class SearchElement private constructor() { abstract fun getSearchText(): String? - + class JavaSearchElement(val javaElement: IJavaElement) : SearchElement() { override fun getSearchText(): String = javaElement.elementName } - + class KotlinSearchElement(val kotlinElement: KtElement) : SearchElement() { override fun getSearchText(): String? = kotlinElement.name } } - - + + private fun getSearchElements(querySpecification: QuerySpecification): List { fun obtainSearchElements(sourceElements: List): List { val (javaElements, kotlinElements) = getJavaAndKotlinElements(sourceElements) - return javaElements.map { SearchElement.JavaSearchElement(it) } + - kotlinElements.map { SearchElement.KotlinSearchElement(it) } - + return javaElements.map { SearchElement.JavaSearchElement(it) } + + kotlinElements.map { SearchElement.KotlinSearchElement(it) } + } - + return when (querySpecification) { is ElementQuerySpecification -> listOf(SearchElement.JavaSearchElement(querySpecification.element)) is KotlinOnlyQuerySpecification -> listOf(SearchElement.KotlinSearchElement(querySpecification.kotlinElement)) @@ -202,92 +242,111 @@ class KotlinQueryParticipant : IQueryParticipant { else -> emptyList() } } - + private fun searchTextOccurrences(searchElement: SearchElement, filesScope: List): ISearchResult? { var searchText = searchElement.getSearchText() ?: return null var asRegex = false if (searchElement is SearchElement.KotlinSearchElement) { if (searchElement.kotlinElement is KtFunction) { + //Either it has an operator keyword directly, or it overrides something. In this case we could look in the overridden element, or we could just try to search for it! if (searchElement.kotlinElement.hasModifier(KtTokens.OPERATOR_KEYWORD)) { - val tempOperationSymbol = - (OperatorConventions.getOperationSymbolForName(Name.identifier(searchText)) as? KtSingleValueToken)?.value?.let { "\\Q$it\\E" } - ?: when (searchText) { - "get" -> "\\[.*?]" - "set" -> "\\[.*?]\\s*?=" - "invoke" -> "\\(.*?\\)" - "contains" -> "in|!in" - else -> null - } - if (tempOperationSymbol != null) { - asRegex = true - searchText = "(\\b$searchText\\b|$tempOperationSymbol)" + val pair = getSearchTextAsRegex(searchText) + asRegex = pair.first + searchText = pair.second + } else if (searchElement.kotlinElement.hasModifier(KtTokens.OVERRIDE_KEYWORD)) { + val tempDescriptor = searchElement.kotlinElement.resolveToDescriptorIfAny() as? FunctionDescriptor + if (tempDescriptor?.isOperator == true) { + val pair = getSearchTextAsRegex(searchText) + asRegex = pair.first + searchText = pair.second } } } } - + val scope = FileTextSearchScope.newSearchScope(filesScope.toTypedArray(), null as Array?, false) - + val query = DefaultTextSearchQueryProvider().createQuery(object : TextSearchInput() { override fun isWholeWordSearch(): Boolean = !asRegex - + override fun getSearchText(): String = searchText - + override fun isCaseSensitiveSearch(): Boolean = true - + override fun isRegExSearch(): Boolean = asRegex - + override fun getScope(): FileTextSearchScope = scope }) - + query.run(null) - + return query.searchResult } - + + private fun getSearchTextAsRegex( + searchText: String + ): Pair { + val tempOperationSymbol = + (OperatorConventions.getOperationSymbolForName(Name.identifier(searchText)) as? KtSingleValueToken)?.value?.let { "\\Q$it\\E" } + ?: when (searchText) { + "get" -> "\\[.*?]" + "set" -> "\\[.*?]\\s*?=" + "invoke" -> "\\(.*?\\)" + "contains" -> "in|!in" + "getValue", "setValue", "provideDelegate" -> "by" + else -> null + } + if (tempOperationSymbol != null) { + return Pair(true, "(\\b$searchText\\b|$tempOperationSymbol)") + } + return Pair(false, searchText) + } + private fun resolveElementsAndMatch( elements: List, searchElement: SearchElement, querySpecification: QuerySpecification ): List { val beforeResolveFilters = getBeforeResolveFilters(querySpecification) - val afterResolveFilters = getAfterResolveFilters() - + val afterResolveFilters = getAfterResolveFilters(querySpecification) + val mapper = SearchParentObjectMapper.getMapper(querySpecification) + // This is important for optimization: - // we will consequentially cache files one by one which are containing these references + // we will consequentially cache files one by one which do contain these references val sortedByFileNameElements = elements.sortedBy { it.containingKtFile.name } - - return sortedByFileNameElements.mapNotNull { element -> - var tempElement: KtElement? = element - var beforeResolveCheck = beforeResolveFilters.all { it.isApplicable(tempElement!!) } - if (!beforeResolveCheck) { - tempElement = PsiTreeUtil.getParentOfType(tempElement, KtReferenceExpression::class.java) - } - if (tempElement != null) { - beforeResolveCheck = beforeResolveFilters.all { it.isApplicable(tempElement) } - } - if (!beforeResolveCheck) return@mapNotNull null - - val sourceElements = tempElement!!.resolveToSourceDeclaration() - if (sourceElements.isEmpty()) return@mapNotNull null - + + return sortedByFileNameElements.flatMap { element -> + val tempElement = findApplicableElement(element, beforeResolveFilters, mapper) ?: return@flatMap emptyList() + + val sourceElements = tempElement.resolveToSourceDeclaration() + if (sourceElements.isEmpty()) return@flatMap emptyList() + val additionalElements = getContainingClassOrObjectForConstructor(sourceElements) - + if (afterResolveFilters.all { it.isApplicable(sourceElements, searchElement) } || afterResolveFilters.all { it.isApplicable(additionalElements, searchElement) }) { - return@mapNotNull tempElement + return@flatMap listOf(tempElement) } - null + emptyList() } } - + + private fun findApplicableElement( + element: KtElement, beforeResolveFilters: List, mapper: SearchParentObjectMapper + ): KtElement? { + if (beforeResolveFilters.all { it.isApplicable(element) }) return element + return mapper.map(element)?.takeIf { refExp -> + beforeResolveFilters.all { it.isApplicable(refExp) } + } + } + private fun obtainElements(searchResult: FileSearchResult, files: List): List { val elements = ArrayList() for (file in files) { val matches = searchResult.getMatches(file) val jetFile = KotlinPsiManager.getParsedFile(file) val document = EditorUtil.getDocument(file) - + matches .map { match -> val element = jetFile.findElementByDocumentOffset(match.offset, document) @@ -295,10 +354,10 @@ class KotlinQueryParticipant : IQueryParticipant { } .filterNotNullTo(elements) } - + return elements } - + private fun getKotlinFilesByScope(querySpecification: QuerySpecification): List { return when (querySpecification) { is ElementQuerySpecification, @@ -317,34 +376,30 @@ fun getContainingClassOrObjectForConstructor(sourceElements: List return@mapNotNull KotlinSourceElement(psi.getContainingClassOrObject()) } } - + null } } -fun getJavaAndKotlinElements(sourceElements: List): Pair, List> { +fun getJavaAndKotlinElements( + sourceElements: List +): Pair, List> { val javaElements = sourceElementsToLightElements(sourceElements) - - // Filter out Kotlin elements which have light elements because Javas search will call KotlinQueryParticipant - // to look up for these elements - val kotlinElements = sourceElementsToKotlinElements(sourceElements).filterNot { kotlinElement -> - (kotlinElement !is KtFunction || !kotlinElement.hasModifier(KtTokens.OPERATOR_KEYWORD)) && javaElements.any { it.elementName == kotlinElement.name } - } - + val kotlinElements = sourceElementsToKotlinElements(sourceElements) return Pair(javaElements, kotlinElements) } private fun sourceElementsToKotlinElements(sourceElements: List): List { return sourceElements - .filterIsInstance(KotlinSourceElement::class.java) - .map { it.psi } + .filterIsInstance(KotlinSourceElement::class.java) + .map { it.psi } } fun IJavaSearchScope.getKotlinFiles(): List { return enclosingProjectsAndJars() - .map { JavaModel.getTarget(it, true) } - .filterIsInstance(IProject::class.java) - .flatMap { KotlinPsiManager.getFilesByProject(it) } + .map { JavaModel.getTarget(it, true) } + .filterIsInstance(IProject::class.java) + .flatMap { KotlinPsiManager.getFilesByProject(it) } } fun QuerySpecification.getFilesInScope(): List { @@ -354,8 +409,8 @@ fun QuerySpecification.getFilesInScope(): List { } } -class KotlinElementMatch(val jetElement: KtElement, val label: String) : - Match(KotlinAdaptableElement(jetElement, label), jetElement.textOffset, jetElement.textLength) +class KotlinElementMatch(val jetElement: KtElement, val label: String, val identifier: PsiElement) : + Match(KotlinAdaptableElement(jetElement, label), identifier.textOffset, identifier.textLength) class KotlinAdaptableElement(val jetElement: KtElement, val label: String) : IAdaptable { @Suppress("UNCHECKED_CAST") @@ -378,4 +433,4 @@ class KotlinAdaptableElement(val jetElement: KtElement, val label: String) : IAd else -> null } } -} \ No newline at end of file +} diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/search/KotlinReferenceMatchPresentation.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/search/KotlinReferenceMatchPresentation.kt index 10960a032..d4cbfa9d2 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/search/KotlinReferenceMatchPresentation.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/search/KotlinReferenceMatchPresentation.kt @@ -16,39 +16,22 @@ *******************************************************************************/ package org.jetbrains.kotlin.ui.search +import com.intellij.psi.util.PsiTreeUtil import org.eclipse.jdt.ui.search.IMatchPresentation import org.eclipse.jface.viewers.ILabelProvider -import org.eclipse.search.ui.text.Match import org.eclipse.jface.viewers.LabelProvider -import org.jetbrains.kotlin.psi.KtReferenceExpression -import org.eclipse.swt.graphics.Image -import com.intellij.psi.util.PsiTreeUtil -import org.eclipse.jdt.ui.JavaUI -import org.eclipse.jdt.ui.ISharedImages -import org.jetbrains.kotlin.eclipse.ui.utils.EditorUtil -import org.jetbrains.kotlin.eclipse.ui.utils.getTextDocumentOffset import org.eclipse.search.internal.ui.text.EditorOpener -import org.eclipse.core.resources.ResourcesPlugin +import org.eclipse.search.ui.text.Match +import org.eclipse.swt.graphics.Image import org.eclipse.ui.PlatformUI +import org.jetbrains.kotlin.core.asJava.getTypeFqName import org.jetbrains.kotlin.core.builder.KotlinPsiManager -import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider -import org.eclipse.jface.viewers.StyledString -import org.eclipse.jface.viewers.ITreeContentProvider -import org.eclipse.jface.viewers.Viewer -import org.eclipse.jdt.ui.JavaElementLabels -import org.eclipse.jdt.internal.corext.util.Strings -import org.jetbrains.kotlin.psi.KtElement -import org.jetbrains.kotlin.psi.KtNamedDeclaration -import org.jetbrains.kotlin.psi.KtClass -import org.jetbrains.kotlin.ui.editors.completion.KotlinCompletionUtils -import org.jetbrains.kotlin.psi.KtNamedFunction -import org.jetbrains.kotlin.psi.KtClassOrObject -import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.eclipse.ui.utils.EditorUtil import org.jetbrains.kotlin.eclipse.ui.utils.KotlinImageProvider -import org.jetbrains.kotlin.psi.KtFile -import org.jetbrains.kotlin.core.asJava.getTypeFqName +import org.jetbrains.kotlin.eclipse.ui.utils.getTextDocumentOffset +import org.jetbrains.kotlin.psi.* -public class KotlinReferenceMatchPresentation : IMatchPresentation { +class KotlinReferenceMatchPresentation : IMatchPresentation { private val editorOpener = EditorOpener() override fun createLabelProvider(): ILabelProvider = KotlinReferenceLabelProvider() @@ -56,32 +39,31 @@ public class KotlinReferenceMatchPresentation : IMatchPresentation { override fun showMatch(match: Match, currentOffset: Int, currentLength: Int, activate: Boolean) { if (match !is KotlinElementMatch) return + val identifier = match.identifier val element = match.jetElement - val eclipseFile = KotlinPsiManager.getEclipseFile(element.getContainingKtFile()) + val eclipseFile = KotlinPsiManager.getEclipseFile(element.containingKtFile) if (eclipseFile != null) { val document = EditorUtil.getDocument(eclipseFile) editorOpener.openAndSelect( - PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), - eclipseFile, - element.getTextDocumentOffset(document), - element.getTextLength(), - activate) + PlatformUI.getWorkbench().activeWorkbenchWindow.activePage, + eclipseFile, + identifier.getTextDocumentOffset(document), + identifier.textLength, + activate + ) } } } -public class KotlinReferenceLabelProvider : LabelProvider() { +class KotlinReferenceLabelProvider : LabelProvider() { override fun getText(element: Any): String { if (element !is KotlinAdaptableElement) { throw IllegalArgumentException("KotlinReferenceLabelProvider asked for non-reference expression: $element") } - - val declaration = getContainingDeclaration(element.jetElement) - return when (declaration) { - is KtNamedDeclaration -> declaration.let { - with (it) { - getFqName()?.asString() ?: getNameAsSafeName().asString() - } + + return when (val declaration = getContainingDeclaration(element.jetElement)) { + is KtNamedDeclaration -> with (declaration) { + fqName?.asString() ?: nameAsSafeName.asString() } is KtFile -> getTypeFqName(declaration)?.asString() ?: "" else -> "" @@ -91,7 +73,7 @@ public class KotlinReferenceLabelProvider : LabelProvider() { override fun getImage(element: Any): Image? { val jetElement = (element as KotlinAdaptableElement).jetElement val containingDeclaration = getContainingDeclaration(jetElement) - return containingDeclaration?.let { KotlinImageProvider.getImage(it) } ?: null + return containingDeclaration?.let { KotlinImageProvider.getImage(it) } } private fun getContainingDeclaration(jetElement: KtElement): KtElement? { @@ -101,4 +83,4 @@ public class KotlinReferenceLabelProvider : LabelProvider() { KtClassOrObject::class.java, KtFile::class.java) } -} \ No newline at end of file +} diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/search/SearchParentObjectMapper.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/search/SearchParentObjectMapper.kt new file mode 100644 index 000000000..1940b7230 --- /dev/null +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/search/SearchParentObjectMapper.kt @@ -0,0 +1,39 @@ +package org.jetbrains.kotlin.ui.search + +import org.eclipse.jdt.core.search.IJavaSearchConstants +import org.eclipse.jdt.ui.search.QuerySpecification +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getParentOfType +import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypes +import org.jetbrains.kotlin.ui.commands.findReferences.KotlinFindImplementationsInProjectAction + +fun interface SearchParentObjectMapper { + + fun map(element: KtElement): KtElement? + + companion object { + fun getMapper(querySpecification: QuerySpecification): SearchParentObjectMapper = + when (querySpecification.limitTo) { + IJavaSearchConstants.REFERENCES -> ReferencesParentObjectMapper + KotlinFindImplementationsInProjectAction.IMPLEMENTORS_LIMIT_TO -> ImplementationsParentObjectMapper + else -> NO_MAPPING + } + + private val NO_MAPPING = SearchParentObjectMapper { null } + } +} + +object ReferencesParentObjectMapper : SearchParentObjectMapper { + override fun map(element: KtElement): KtElement? = element.getParentOfType(false) +} + +object ImplementationsParentObjectMapper : SearchParentObjectMapper { + override fun map(element: KtElement): KtElement? = + element.getParentOfTypes( + false, + KtClass::class.java, + KtObjectDeclaration::class.java, + KtProperty::class.java, + KtNamedFunction::class.java + ) +} diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/search/searchFilters.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/search/searchFilters.kt index dddacfacf..6842db603 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/search/searchFilters.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/search/searchFilters.kt @@ -20,14 +20,18 @@ import org.eclipse.jdt.core.IJavaElement import org.eclipse.jdt.core.IMethod import org.eclipse.jdt.core.search.IJavaSearchConstants import org.eclipse.jdt.ui.search.QuerySpecification -import org.jetbrains.kotlin.descriptors.SourceElement -import org.jetbrains.kotlin.psi.KtElement -import org.jetbrains.kotlin.psi.KtReferenceExpression -import org.jetbrains.kotlin.psi.KtSimpleNameExpression +import org.jetbrains.kotlin.core.resolve.KotlinAnalyzer +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.isImportDirectiveExpression +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.descriptorUtil.isSubclassOf +import org.jetbrains.kotlin.resolve.descriptorUtil.overriddenTreeUniqueAsSequence +import org.jetbrains.kotlin.ui.commands.findReferences.KotlinFindImplementationsInProjectAction import org.jetbrains.kotlin.ui.search.KotlinQueryParticipant.SearchElement import org.jetbrains.kotlin.ui.search.KotlinQueryParticipant.SearchElement.JavaSearchElement import org.jetbrains.kotlin.ui.search.KotlinQueryParticipant.SearchElement.KotlinSearchElement +import org.jetbrains.kotlin.utils.findCurrentDescriptor interface SearchFilter { fun isApplicable(jetElement: KtElement): Boolean @@ -35,10 +39,13 @@ interface SearchFilter { interface SearchFilterAfterResolve { fun isApplicable(sourceElement: KtElement, originElement: KtElement): Boolean - + fun isApplicable(sourceElement: IJavaElement, originElement: IJavaElement): Boolean - - fun isApplicable(sourceElements: List, originElement: SearchElement): Boolean { + + fun isApplicable( + sourceElements: List, + originElement: SearchElement + ): Boolean { val (javaElements, kotlinElements) = getJavaAndKotlinElements(sourceElements) return when (originElement) { is JavaSearchElement -> javaElements.any { isApplicable(it, originElement.javaElement) } @@ -47,54 +54,107 @@ interface SearchFilterAfterResolve { } } -fun getBeforeResolveFilters(querySpecification: QuerySpecification): List { - val filters = arrayListOf() - if (querySpecification.getLimitTo() == IJavaSearchConstants.REFERENCES) { - filters.add(NonImportFilter()) - filters.add(ReferenceFilter()) +fun getBeforeResolveFilters(querySpecification: QuerySpecification): List = + when (querySpecification.limitTo) { + IJavaSearchConstants.REFERENCES -> listOf(NonImportFilter, ElementWithPossibleReferencesFilter) + KotlinFindImplementationsInProjectAction.IMPLEMENTORS_LIMIT_TO -> listOf( + NonImportFilter, + PossibleOverridingMemberFilter + ) + else -> emptyList() + } + +fun getAfterResolveFilters(querySpecification: QuerySpecification): List = + when (querySpecification.limitTo) { + KotlinFindImplementationsInProjectAction.IMPLEMENTORS_LIMIT_TO -> listOf(InheritorsFilter) + else -> listOf(ResolvedReferenceFilter) } - - return filters + +object ElementWithPossibleReferencesFilter : SearchFilter { + override fun isApplicable(jetElement: KtElement): Boolean = + jetElement is KtReferenceExpression || (jetElement is KtPropertyDelegate) } -fun getAfterResolveFilters(): List = listOf(ResolvedReferenceFilter()) +object PossibleOverridingMemberFilter : SearchFilter { -class ReferenceFilter : SearchFilter { - override fun isApplicable(jetElement: KtElement): Boolean = jetElement is KtReferenceExpression + override fun isApplicable(jetElement: KtElement): Boolean { + return jetElement is KtClass || jetElement is KtNamedFunction || jetElement is KtProperty || jetElement is KtObjectDeclaration + } } -class NonImportFilter : SearchFilter { +object NonImportFilter : SearchFilter { override fun isApplicable(jetElement: KtElement): Boolean { return jetElement !is KtSimpleNameExpression || !jetElement.isImportDirectiveExpression() } } -class ResolvedReferenceFilter : SearchFilterAfterResolve { +object InheritorsFilter : SearchFilterAfterResolve { override fun isApplicable(sourceElement: KtElement, originElement: KtElement): Boolean { - return sourceElement == originElement + if (originElement is KtClass && (sourceElement !is KtClass && sourceElement !is KtObjectDeclaration)) return false + if (originElement is KtProperty && sourceElement !is KtProperty) return false + if (originElement is KtNamedFunction && sourceElement !is KtNamedFunction) return false + + val (tempSourceModuleDescriptor, tempSourceDescriptor) = sourceElement.tryGetDescriptor() + val (_, tempOriginDescriptor) = originElement.tryGetDescriptor() + + if (tempSourceDescriptor == null || tempOriginDescriptor == null) return false + + val tempCurrentOriginDescriptor = + tempSourceModuleDescriptor.findCurrentDescriptor(tempOriginDescriptor) ?: return false + val tempCurrentSourceDescriptor = + tempSourceModuleDescriptor.findCurrentDescriptor(tempSourceDescriptor) ?: return false + + if (tempCurrentOriginDescriptor == tempCurrentSourceDescriptor) return false + + return if (tempCurrentSourceDescriptor is ClassDescriptor && tempCurrentOriginDescriptor is ClassDescriptor) { + return tempCurrentSourceDescriptor.isSubclassOf(tempCurrentOriginDescriptor) + } else { + val tempOverriddenDescriptors = when (tempSourceDescriptor) { + is FunctionDescriptor -> tempSourceDescriptor.overriddenTreeUniqueAsSequence(false).toList() + is PropertyDescriptor -> tempSourceDescriptor.overriddenTreeUniqueAsSequence(false).toList() + else -> return false + }.mapNotNull { + tempSourceModuleDescriptor.findCurrentDescriptor(it) + } + + tempSourceModuleDescriptor.findCurrentDescriptor(tempOriginDescriptor) in tempOverriddenDescriptors + } } - + + private fun KtElement.tryGetDescriptor(): Pair { + val (bindingContext, moduleDescriptor) = KotlinAnalyzer.analyzeFile(containingKtFile).analysisResult + return Pair(moduleDescriptor, bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, this]) + } + + override fun isApplicable(sourceElement: IJavaElement, originElement: IJavaElement): Boolean = false +} + +object ResolvedReferenceFilter : SearchFilterAfterResolve { + override fun isApplicable(sourceElement: KtElement, originElement: KtElement): Boolean { + return sourceElement == originElement || InheritorsFilter.isApplicable(sourceElement, originElement) + } + override fun isApplicable(sourceElement: IJavaElement, originElement: IJavaElement): Boolean { - return referenceFilter(sourceElement, originElement) + return referenceFilter(sourceElement, originElement) } - + private fun referenceFilter(potentialElement: IJavaElement, originElement: IJavaElement): Boolean { return when { originElement.isConstructorCall() && potentialElement.isConstructorCall() -> { - (originElement as IMethod).getDeclaringType() == (potentialElement as IMethod).getDeclaringType() + (originElement as IMethod).declaringType == (potentialElement as IMethod).declaringType } - + originElement.isConstructorCall() -> { - (originElement as IMethod).getDeclaringType() == potentialElement + (originElement as IMethod).declaringType == potentialElement } - + potentialElement.isConstructorCall() -> { - originElement == (potentialElement as IMethod).getDeclaringType() + originElement == (potentialElement as IMethod).declaringType } - + else -> potentialElement == originElement } } - - private fun IJavaElement.isConstructorCall() = this is IMethod && this.isConstructor() -} \ No newline at end of file + + private fun IJavaElement.isConstructorCall() = this is IMethod && isConstructor +} diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/utils/DescriptorUtils.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/utils/DescriptorUtils.kt new file mode 100644 index 000000000..d4d41bee2 --- /dev/null +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/utils/DescriptorUtils.kt @@ -0,0 +1,47 @@ +package org.jetbrains.kotlin.utils + +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.renderer.DescriptorRenderer +import org.jetbrains.kotlin.resolve.descriptorUtil.classId +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter +import org.jetbrains.kotlin.resolve.scopes.MemberScope +import org.jetbrains.kotlin.resolve.scopes.MemberScope.Companion.ALL_NAME_FILTER + +private fun ModuleDescriptor.findCurrentDescriptorForMember(originalDescriptor: MemberDescriptor): DeclarationDescriptor? { + val containingDeclaration = findCurrentDescriptor(originalDescriptor.containingDeclaration) + val memberScope = containingDeclaration?.memberScope ?: return null + + val renderedOriginal: String = DescriptorRenderer.FQ_NAMES_IN_TYPES.render(originalDescriptor) + val descriptors: Collection = + if (originalDescriptor is ConstructorDescriptor && containingDeclaration is ClassDescriptor) { + containingDeclaration.constructors + } else { + memberScope.getContributedDescriptors(DescriptorKindFilter.ALL, ALL_NAME_FILTER) + } + for (member in descriptors) { + if (renderedOriginal == DescriptorRenderer.FQ_NAMES_IN_TYPES.render(member)) { + return member + } + } + return null +} + +fun ModuleDescriptor.findCurrentDescriptor(originalDescriptor: DeclarationDescriptor): DeclarationDescriptor? { + if (originalDescriptor is ClassDescriptor) { + val classId: ClassId = originalDescriptor.classId ?: return null + return findClassAcrossModuleDependencies(classId) + } + if (originalDescriptor is PackageFragmentDescriptor) { + return getPackage(originalDescriptor.fqName) + } + return if (originalDescriptor is MemberDescriptor) { + findCurrentDescriptorForMember(originalDescriptor) + } else null +} + +private val DeclarationDescriptor.memberScope: MemberScope? get() = when (this) { + is ClassDescriptor -> defaultType.memberScope + is PackageFragmentDescriptor -> getMemberScope() + else -> null +} diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/wizards/NewUnitWizard.java b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/wizards/NewUnitWizard.java index 22ab6f591..4ba0a733f 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/wizards/NewUnitWizard.java +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/wizards/NewUnitWizard.java @@ -47,9 +47,12 @@ public class NewUnitWizard extends AbstractWizard { private static final String PACKAGE_FORMAT = "package %s\n\n"; private final WizardType type; + + private boolean isDynamicType; public NewUnitWizard() { this(WizardType.NONE); + isDynamicType = true; } public NewUnitWizard(WizardType type) { @@ -59,7 +62,13 @@ public NewUnitWizard(WizardType type) { @Override public boolean performFinish() { NewUnitWizardPage wizardPage = getWizardPage(); - String contents = createPackageHeader() + createTypeBody(); + WizardType finalType; + if(isDynamicType) { + finalType = wizardPage.getType(); + } else { + finalType = type; + } + String contents = createPackageHeader() + createTypeBody(finalType); IFile kotlinSourceFile; try { @@ -108,7 +117,7 @@ protected String getPageTitle() { @Override protected NewUnitWizardPage createWizardPage() { return new NewUnitWizardPage(getPageTitle(), String.format(DESCRIPTION_FORMAT, - type.getWizardTypeName().toLowerCase()), DEFAULT_FILE_NAME, getStructuredSelection()); + type.getWizardTypeName().toLowerCase()), DEFAULT_FILE_NAME, getStructuredSelection(), isDynamicType); } @Nullable @@ -130,12 +139,12 @@ public static IFile createKotlinSourceFile( return operation.getResult(); } - private String createTypeBody() { - if (type == WizardType.NONE) { + private String createTypeBody(WizardType finalType) { + if (finalType == WizardType.NONE) { return DEFAULT_TYPE_BODY; } - return String.format(type.getFileBodyFormat(), FileCreationOp.getSimpleUnitName(getWizardPage().getUnitName())); + return String.format(finalType.getFileBodyFormat(), FileCreationOp.getSimpleUnitName(getWizardPage().getUnitName())); } private String createPackageHeader() { @@ -147,4 +156,4 @@ private String createPackageHeader() { return String.format(PACKAGE_FORMAT, pckg); } -} \ No newline at end of file +} diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/wizards/NewUnitWizardPage.java b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/wizards/NewUnitWizardPage.java index f6524769c..5a1e1ec4c 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/wizards/NewUnitWizardPage.java +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/wizards/NewUnitWizardPage.java @@ -16,75 +16,78 @@ *******************************************************************************/ package org.jetbrains.kotlin.wizards; -import static org.eclipse.jdt.internal.ui.refactoring.nls.SourceContainerDialog.getSourceContainer; -import static org.jetbrains.kotlin.wizards.FileCreationOp.fileExists; -import static org.jetbrains.kotlin.wizards.FileCreationOp.makeFile; -import static org.jetbrains.kotlin.wizards.SWTWizardUtils.createButton; -import static org.jetbrains.kotlin.wizards.SWTWizardUtils.createEmptySpace; -import static org.jetbrains.kotlin.wizards.SWTWizardUtils.createLabel; -import static org.jetbrains.kotlin.wizards.SWTWizardUtils.createSeparator; -import static org.jetbrains.kotlin.wizards.SWTWizardUtils.createText; - import org.eclipse.core.resources.IProject; -import org.eclipse.jdt.core.IJavaProject; -import org.eclipse.jdt.core.IPackageFragment; -import org.eclipse.jdt.core.IPackageFragmentRoot; -import org.eclipse.jdt.core.JavaCore; -import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.*; import org.eclipse.jdt.ui.JavaUI; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.swt.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.dialogs.SelectionDialog; import org.jetbrains.kotlin.core.log.KotlinLogger; +import java.util.Arrays; + +import static org.eclipse.jdt.internal.ui.refactoring.nls.SourceContainerDialog.getSourceContainer; +import static org.jetbrains.kotlin.wizards.FileCreationOp.fileExists; +import static org.jetbrains.kotlin.wizards.FileCreationOp.makeFile; +import static org.jetbrains.kotlin.wizards.SWTWizardUtils.*; + public class NewUnitWizardPage extends AbstractWizardPage { - + private static final String DEFAULT_SOURCE_FOLDER = ""; private static final String DEFAULT_PACKAGE = ""; - + private static final String NAME_LABEL_TITLE = "Na&me"; private static final String SOURCE_FOLDER_LABEL_TITLE = "Source fol&der"; private static final String PACKAGE_LABEL_TITLE = "Pac&kage"; - + private static final String ILLEGAL_UNIT_NAME_MESSAGE = "Please enter a legal compilation unit name"; private static final String SELECT_SOURCE_FOLDER_MESSAGE = "Please select a source folder"; private static final String ILLEGAL_PACKAGE_NAME_MESSAGE = "Please enter a legal package name"; private static final String UNIT_EXISTS_MESSAGE = "File already exists"; - + private static final String JAVA_IDENTIFIER_REGEXP = "[a-zA-Z_]\\w*"; - + private String unitName; private String packageName; private IPackageFragmentRoot sourceDir; private IPackageFragment packageFragment; + + private WizardType type = WizardType.NONE; + + private final boolean isDynamicType; private Text nameField = null; private final IStructuredSelection selection; - - protected NewUnitWizardPage(String title, String description, String unitName, IStructuredSelection selection) { + + protected NewUnitWizardPage(String title, String description, String unitName, IStructuredSelection selection, boolean isDynamicType) { super(title, description); - + this.selection = selection; this.unitName = unitName; + this.isDynamicType = isDynamicType; } - + public IPackageFragment getPackageFragment() { return packageFragment; } - + public IPackageFragmentRoot getSourceDir() { return sourceDir; } - + + public WizardType getType() { + return type; + } + public String getUnitName() { return unitName; } - + public IProject getProject() { if (sourceDir != null) { return sourceDir.getJavaProject().getProject(); @@ -92,17 +95,36 @@ public IProject getProject() { return null; } } - + @Override protected void createControls(Composite parent) { createSourceFolderField(parent); createPackageField(parent); - + createSeparator(parent); - + nameField = createNameField(parent); + if (isDynamicType) { + createDynamicTypeField(parent); + } } - + + private void createDynamicTypeField(Composite parent) { + createLabel(parent, "Type:"); + Combo tempCombo = new Combo(parent, SWT.READ_ONLY); + for (WizardType tempType : WizardType.values()) { + tempCombo.add(tempType.getWizardTypeName()); + } + tempCombo.select(0); + + tempCombo.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + type = Arrays.stream(WizardType.values()).filter(it -> it.getWizardTypeName().equals(tempCombo.getText())).findFirst().orElse(WizardType.CLASS); + } + }); + } + @Override public void setVisible(boolean visible) { super.setVisible(visible); @@ -117,12 +139,9 @@ private Text createNameField(Composite parent) { createLabel(parent, NAME_LABEL_TITLE); final Text name = createText(parent, unitName); - name.addModifyListener(new ModifyListener() { - @Override - public void modifyText(ModifyEvent e) { - unitName = name.getText(); - validate(); - } + name.addModifyListener(e -> { + unitName = name.getText(); + validate(); }); createEmptySpace(parent); @@ -146,7 +165,7 @@ private void setSourceDirByFolderName(String srcFolder) { } } - private Text createSourceFolderField(Composite parent) { + private void createSourceFolderField(Composite parent) { createLabel(parent, SOURCE_FOLDER_LABEL_TITLE); IPackageFragmentRoot srcFolder = WizardUtilsKt.getSourceFolderBySelection(selection); @@ -154,12 +173,9 @@ private Text createSourceFolderField(Composite parent) { sourceDir = srcFolder; final Text folder = createText(parent, sourceFolderFromSelection); - folder.addModifyListener(new ModifyListener() { - @Override - public void modifyText(ModifyEvent e) { - setSourceDirByFolderName(folder.getText()); - validate(); - } + folder.addModifyListener(e -> { + setSourceDirByFolderName(folder.getText()); + validate(); }); createButton(parent, BROWSE_BUTTON_TITLE, new SelectionAdapter() { @@ -176,11 +192,10 @@ public void widgetSelected(SelectionEvent e) { validate(); } }); - - return folder; + } - private Text createPackageField(Composite parent) { + private void createPackageField(Composite parent) { createLabel(parent, PACKAGE_LABEL_TITLE); IPackageFragment fragment = WizardUtilsKt.getPackageBySelection(selection); @@ -188,12 +203,9 @@ private Text createPackageField(Composite parent) { packageName = packageFromSelection; final Text pkg = createText(parent, packageFromSelection); - pkg.addModifyListener(new ModifyListener() { - @Override - public void modifyText(ModifyEvent e) { - packageName = pkg.getText(); - validate(); - } + pkg.addModifyListener(e -> { + packageName = pkg.getText(); + validate(); }); createButton(parent, BROWSE_BUTTON_TITLE, new SelectionAdapter() { @@ -226,8 +238,7 @@ public void widgetSelected(SelectionEvent e) { } } }); - - return pkg; + } @Override diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/wizards/WizardType.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/wizards/WizardType.kt index fc7e146a6..1a7378f92 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/wizards/WizardType.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/wizards/WizardType.kt @@ -22,12 +22,16 @@ import org.jetbrains.kotlin.lexer.KtToken enum class WizardType(val wizardTypeName: String, val fileBodyFormat: String = "") { NONE("Source"), CLASS("Class", buildFileBody(KtTokens.CLASS_KEYWORD)), + SEALED_CLASS("Sealed Class", buildFileBody(KtTokens.SEALED_KEYWORD, KtTokens.CLASS_KEYWORD)), INTERFACE("Interface", buildFileBody(KtTokens.INTERFACE_KEYWORD)), + SEALED_INTERFACE("Sealed Interface", buildFileBody(KtTokens.SEALED_KEYWORD, KtTokens.INTERFACE_KEYWORD)), OBJECT("Object", buildFileBody(KtTokens.OBJECT_KEYWORD)), - ENUM("Enum", buildFileBody(KtTokens.ENUM_KEYWORD, KtTokens.CLASS_KEYWORD)) + ENUM("Enum", buildFileBody(KtTokens.ENUM_KEYWORD, KtTokens.CLASS_KEYWORD)), + DATA("Data", buildFileBody(KtTokens.DATA_KEYWORD, KtTokens.CLASS_KEYWORD)), + ANNOTATION("Annotation", buildFileBody(KtTokens.ANNOTATION_KEYWORD, KtTokens.CLASS_KEYWORD)), } -private val NOT_EMPTY_BODY_FORMAT = "%s {\n}" +private const val NOT_EMPTY_BODY_FORMAT = "%s {\n}" private fun buildFileBody(vararg modifiers: KtToken): String = - "${modifiers.joinToString(separator = " ")} $NOT_EMPTY_BODY_FORMAT" \ No newline at end of file + "${modifiers.joinToString(separator = " ")} $NOT_EMPTY_BODY_FORMAT" diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/wizards/unitWizards.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/wizards/unitWizards.kt index af532377e..157c65e18 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/wizards/unitWizards.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/wizards/unitWizards.kt @@ -14,10 +14,10 @@ * limitations under the License. * *******************************************************************************/ +@file:Suppress("unused") + package org.jetbrains.kotlin.wizards -import org.eclipse.core.resources.IFile -import org.eclipse.core.runtime.IPath import org.eclipse.jface.viewers.IStructuredSelection import org.eclipse.ui.IWorkbench import org.eclipse.ui.dialogs.WizardNewFileCreationPage @@ -26,37 +26,41 @@ import org.eclipse.ui.wizards.newresource.BasicNewResourceWizard import org.jetbrains.kotlin.parsing.KotlinParserDefinition class NewClassWizard : NewUnitWizard(WizardType.CLASS) +class NewSealedClassWizard : NewUnitWizard(WizardType.SEALED_CLASS) class NewEnumWizard : NewUnitWizard(WizardType.ENUM) +class NewDataClassWizard : NewUnitWizard(WizardType.DATA) +class NewAnnotationWizard : NewUnitWizard(WizardType.ANNOTATION) class NewObjectWizard : NewUnitWizard(WizardType.OBJECT) class NewInterfaceWizard : NewUnitWizard(WizardType.INTERFACE) +class NewSealedInterfaceWizard : NewUnitWizard(WizardType.SEALED_INTERFACE) class NewScriptWizard : BasicNewResourceWizard() { companion object { private const val pageName = "New Kotlin Script" } - + private lateinit var mainPage: WizardNewFileCreationPage - + override fun addPages() { super.addPages() - + mainPage = WizardNewFileCreationPage(pageName, getSelection()).apply { - setFileExtension(KotlinParserDefinition.STD_SCRIPT_SUFFIX) - setTitle("Kotlin Script") - setDescription("Create a new Kotlin script") + fileExtension = KotlinParserDefinition.STD_SCRIPT_SUFFIX + title = "Kotlin Script" + description = "Create a new Kotlin script" } - + addPage(mainPage) } - + override fun init(workbench: IWorkbench, currentSelection: IStructuredSelection) { super.init(workbench, currentSelection) - setWindowTitle(pageName) + windowTitle = pageName } - + override fun performFinish(): Boolean { val file = mainPage.createNewFile() ?: return false - + selectAndReveal(file) workbench.activeWorkbenchWindow?.let { val page = it.activePage @@ -64,7 +68,7 @@ class NewScriptWizard : BasicNewResourceWizard() { IDE.openEditor(page, file, true) } } - + return true } -} \ No newline at end of file +} diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/wizards/wizardUtils.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/wizards/wizardUtils.kt index 6fe94e931..899c941fb 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/wizards/wizardUtils.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/wizards/wizardUtils.kt @@ -16,21 +16,15 @@ *******************************************************************************/ package org.jetbrains.kotlin.wizards +import org.eclipse.core.resources.IResource +import org.eclipse.jdt.core.* import org.eclipse.jface.viewers.IStructuredSelection import org.jetbrains.kotlin.core.utils.sourceFolders -import org.eclipse.jdt.core.IJavaProject -import org.eclipse.jdt.core.IPackageFragmentRoot -import org.eclipse.jdt.core.IPackageFragment -import org.eclipse.jdt.core.ICompilationUnit -import org.eclipse.core.resources.IFile -import org.eclipse.jdt.core.JavaCore -import org.eclipse.core.resources.IResource fun getSourceFolderBySelection(selection: IStructuredSelection): IPackageFragmentRoot? { if (selection.isEmpty) return null - - val element = selection.firstElement - return when (element) { + + return when (val element = selection.firstElement) { is IPackageFragmentRoot -> element is IJavaProject -> element.sourceFolders.firstOrNull() is IPackageFragment -> element.parent as IPackageFragmentRoot @@ -42,15 +36,14 @@ fun getSourceFolderBySelection(selection: IStructuredSelection): IPackageFragmen fun getPackageBySelection(selection: IStructuredSelection): IPackageFragment? { if (selection.isEmpty) return null - - val element = selection.firstElement - return when (element) { + + return when (val element = selection.firstElement) { is IPackageFragment -> element is ICompilationUnit -> element.parent as IPackageFragment is IResource -> { val javaProject = JavaCore.create(element.project) - javaProject.findPackageFragment(element.getFullPath().removeLastSegments(1)) + javaProject.findPackageFragment(element.fullPath.removeLastSegments(1)) } else -> null } -} \ No newline at end of file +}