From 690e0e1b31aef4c0574661234d6cc9a6c54b1c1e Mon Sep 17 00:00:00 2001 From: U534967 Date: Fri, 5 Nov 2021 10:11:59 +0100 Subject: [PATCH 01/31] enable nullability handling for scripts and make sure changes kotlin classes are deleted before recompiling them. Otherwise deleted classes wont be deleted in output folder. --- .../core/asJava/KotlinLightClassGeneration.kt | 4 +- .../kotlin/core/compiler/KotlinCompiler.kt | 85 ++--- .../filesystem/KotlinLightClassManager.java | 307 ------------------ .../filesystem/KotlinLightClassManager.kt | 261 +++++++++++++++ .../model/EclipseScriptDefinitionProvider.kt | 71 ++-- .../resolve/EclipseAnalyzerFacadeForJVM.kt | 10 +- .../core/resolve/KotlinPackagePartProvider.kt | 58 ++-- .../core/script/ScriptTemplateContribution.kt | 3 + .../codeassist/KotlinCompletionProcessor.kt | 18 +- .../KotlinReferenceVariantsHelper.kt | 21 +- .../ui/navigation/KotlinOpenEditor.java | 2 +- 11 files changed, 403 insertions(+), 437 deletions(-) delete mode 100644 kotlin-eclipse-core/src/org/jetbrains/kotlin/core/filesystem/KotlinLightClassManager.java create mode 100644 kotlin-eclipse-core/src/org/jetbrains/kotlin/core/filesystem/KotlinLightClassManager.kt 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/compiler/KotlinCompiler.kt b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/compiler/KotlinCompiler.kt index 1738d87ea..6581c6150 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,16 +4,17 @@ 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 @@ -22,50 +23,54 @@ import org.jetbrains.kotlin.core.model.KotlinEnvironment import org.jetbrains.kotlin.core.preferences.CompilerPlugin 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() @@ -88,7 +93,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 @@ -123,16 +128,16 @@ object KotlinCompiler { pluginOptions = pluginOptionsList.toTypedArray() classpath = ProjectUtils.collectClasspathWithDependenciesForLaunch(javaProject, jdkUndefined) - .joinToString(separator = System.getProperty("path.separator")) { it.absolutePath } + .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) @@ -165,8 +170,8 @@ object KotlinCompiler { add("-classpath") ProjectUtils.collectClasspathWithDependenciesForLaunch(javaProject, jdkUndefined) - .joinToString(separator = System.getProperty("path.separator")) { it.absolutePath } - .let { add(it) } + .joinToString(separator = System.getProperty("path.separator")) { it.absolutePath } + .let { add(it) } add("-d") add(outputDir) @@ -194,9 +199,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 +220,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/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/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/resolve/EclipseAnalyzerFacadeForJVM.kt b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/resolve/EclipseAnalyzerFacadeForJVM.kt index 0d220159d..fef9926d7 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 @@ -35,6 +35,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 @@ -136,10 +137,14 @@ object EclipseAnalyzerFacadeForJVM { ProjectUtils.getSourceFilesWithDependencies(environment.javaProject).toCollection(allFiles) + val tempSourceCode = KtFileScriptSource(scriptFile) + val tempRefinedConfig = environment.definition?.let { - refineScriptCompilationConfiguration(KtFileScriptSource(scriptFile), it, environment.project) + refineScriptCompilationConfiguration(tempSourceCode, it, environment.project) }?.valueOrNull()?.configuration + val tempContribution = EclipseScriptDefinitionProvider.getContribution(tempSourceCode) + val tempDefaultImports = tempRefinedConfig?.get(PropertiesCollection.Key("defaultImports", emptyList())) ?: emptyList() val tempImports = ArrayList(tempDefaultImports) @@ -182,9 +187,10 @@ object EclipseAnalyzerFacadeForJVM { val tempPackageName = "scriptParameters${scriptFile.virtualFilePath.hashCode().absoluteValue}" val tempContent = "package $tempPackageName\n" + tempProperties.entries.joinToString(separator = "\n") { (key, value) -> + val isNullable = tempContribution?.isNullable(key, tempRefinedConfig) ?: true """ |@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 + |val $key: ${value.typeName}${if(isNullable) "? = null" else " = TODO()"} """.trimMargin("|") } 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/script/ScriptTemplateContribution.kt b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/script/ScriptTemplateContribution.kt index acdd48212..050c9f7bf 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 @@ -2,6 +2,7 @@ package org.jetbrains.kotlin.core.script import java.io.File import kotlin.reflect.KClass +import kotlin.script.experimental.api.ScriptCompilationConfiguration abstract class ScriptTemplateContribution { open val priority = 0 @@ -10,6 +11,8 @@ abstract class ScriptTemplateContribution { 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-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..82cc911bc 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 @@ -26,14 +26,7 @@ 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 @@ -50,7 +43,6 @@ 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 abstract class KotlinCompletionProcessor( val editor: KotlinEditor, @@ -126,7 +118,7 @@ abstract class KotlinCompletionProcessor( if (assistant != null) { configureContentAssistant(assistant) } - + val generatedProposals = generateCompletionProposals(viewer, offset).let { if (needSorting) sortProposals(it) else it } @@ -135,11 +127,7 @@ abstract class KotlinCompletionProcessor( } 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) { 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..91b35ec3d 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 @@ -21,6 +21,8 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastManager import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension import org.jetbrains.kotlin.resolve.scopes.* +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 @@ -30,7 +32,6 @@ import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.expressions.DoubleColonLHS import org.jetbrains.kotlin.types.typeUtil.isUnit import org.jetbrains.kotlin.ui.refactorings.extract.parentsWithSelf -import java.util.* class KotlinReferenceVariantsHelper( val bindingContext: BindingContext, @@ -52,19 +53,11 @@ class KotlinReferenceVariantsHelper( ) } - ShadowedDeclarationsFilter.create(bindingContext, resolutionFacade, simpleNameExpression, callTypeAndReceiver) - ?.let { + ShadowedDeclarationsFilter.create(bindingContext, resolutionFacade, simpleNameExpression, callTypeAndReceiver)?.let { variants = it.filter(variants) - } - if (kindFilter.kindMask.and(DescriptorKindFilter.FUNCTIONS_MASK) != 0) { - variants = filterOutJavaGettersAndSetters(variants) - } - - if (kindFilter.kindMask.and(DescriptorKindFilter.VARIABLES_MASK) != 0) { - variants = excludeNonInitializedVariable(variants, simpleNameExpression) } - return variants + return variants.filter { kindFilter.accepts(it) } } private fun getVariantsForImportOrPackageDirective( @@ -357,7 +350,7 @@ class KotlinReferenceVariantsHelper( ) // 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) } @@ -401,7 +394,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 +404,7 @@ class KotlinReferenceVariantsHelper( } } - if (kindFilter.acceptsKinds(DescriptorKindFilter.FUNCTIONS_MASK)) { + if (kindFilter.acceptsKinds(FUNCTIONS_MASK)) { for (syntheticMember in syntheticScopes.collectSyntheticMemberFunctions(receiverTypes)) { process(syntheticMember) } 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); From c3663014595075be3b915284bb9ad9d662473f68 Mon Sep 17 00:00:00 2001 From: Julian Hahn Date: Sat, 6 Nov 2021 18:53:02 +0100 Subject: [PATCH 02/31] improve gradle build --- kotlin-bundled-compiler/build.gradle.kts | 2 +- .../buildsupport/resolve/http/HttpArtifactsResolver.groovy | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kotlin-bundled-compiler/build.gradle.kts b/kotlin-bundled-compiler/build.gradle.kts index b1b9c6fbc..c6d0d80c3 100644 --- a/kotlin-bundled-compiler/build.gradle.kts +++ b/kotlin-bundled-compiler/build.gradle.kts @@ -29,7 +29,7 @@ val testDataDir = file("${projectDir.parentFile}/kotlin-eclipse-ui-test/common_t val testModuleLibDir = file("${projectDir.parentFile}/kotlin-eclipse-ui-test/lib") //TODO later refactor to the proper project dir -val downloadDirName = "downloads" +val downloadDirName = "downloads$ideaVersion-$kotlinCompilerVersion" val teamCityWorkingDir = project.findProperty("teamcity.buildsupport.workingDir") val libDir = if (teamCityWorkingDir != null) file("$teamCityWorkingDir/lib") else file("lib") 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 { From 7d02ac3874296f66b84449b64fa624ed1525a8b2 Mon Sep 17 00:00:00 2001 From: Julian Hahn Date: Sat, 6 Nov 2021 18:53:02 +0100 Subject: [PATCH 03/31] improve gradle build --- kotlin-bundled-compiler/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/kotlin-bundled-compiler/build.gradle.kts b/kotlin-bundled-compiler/build.gradle.kts index c6d0d80c3..591aeaf98 100644 --- a/kotlin-bundled-compiler/build.gradle.kts +++ b/kotlin-bundled-compiler/build.gradle.kts @@ -300,6 +300,7 @@ val repackageIdeaAndKotlinCompilerSources by tasks.registering(Zip::class) { } val downloadBundled by tasks.registering { + libDir.listFiles()?.filter { it.isFile }?.forEach { it.deleteRecursively() } if (localTCArtifacts) { dependsOn(downloadKotlinCompilerPluginAndExtractSelectedJars, extractPackagesFromPlugin, From 99fe1030acd9a8e8ddac8253b7916c9dadda0d4b Mon Sep 17 00:00:00 2001 From: Julian Hahn Date: Sun, 7 Nov 2021 11:23:38 +0100 Subject: [PATCH 04/31] improve gradle build --- kotlin-bundled-compiler/build.gradle.kts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/kotlin-bundled-compiler/build.gradle.kts b/kotlin-bundled-compiler/build.gradle.kts index 591aeaf98..180a78183 100644 --- a/kotlin-bundled-compiler/build.gradle.kts +++ b/kotlin-bundled-compiler/build.gradle.kts @@ -29,7 +29,7 @@ val testDataDir = file("${projectDir.parentFile}/kotlin-eclipse-ui-test/common_t val testModuleLibDir = file("${projectDir.parentFile}/kotlin-eclipse-ui-test/lib") //TODO later refactor to the proper project dir -val downloadDirName = "downloads$ideaVersion-$kotlinCompilerVersion" +val downloadDirName = "downloads" val teamCityWorkingDir = project.findProperty("teamcity.buildsupport.workingDir") val libDir = if (teamCityWorkingDir != null) file("$teamCityWorkingDir/lib") else file("lib") @@ -106,9 +106,10 @@ val downloadTestFrameworkDependencies by tasks.registering(Copy::class) { } val downloadKotlinCompilerPluginAndExtractSelectedJars by tasks.registering { + val kotlinDownloadDir = file("$downloadDir/$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 +173,8 @@ val extractPackagesFromPlugin by tasks.registering(Jar::class) { } val downloadIntellijCoreAndExtractSelectedJars by tasks.registering { - val locallyDownloadedIntellijCoreFile by extra { file("$downloadDir/intellij-core.zip") } + val ideaDownloadDir = file("$downloadDir/$ideaVersion") + val locallyDownloadedIntellijCoreFile by extra { file("$ideaDownloadDir/intellij-core.zip") } doLast { if(!locallyDownloadedIntellijCoreFile.exists()) { @@ -191,7 +193,8 @@ val downloadIntellijCoreAndExtractSelectedJars by tasks.registering { } val downloadIdeaDistributionZipAndExtractSelectedJars by tasks.registering { - val locallyDownloadedIdeaZipFile by extra { file("$downloadDir/ideaIC.zip") } + val ideaDownloadDir = file("$downloadDir/$ideaVersion") + val locallyDownloadedIdeaZipFile by extra { file("$ideaDownloadDir/ideaIC.zip") } val chosenJars by extra { setOf(//"openapi", "platform-util-ui", "util", From 19e5f779894e98414e66bd6208f1de6623877046 Mon Sep 17 00:00:00 2001 From: U534967 Date: Mon, 8 Nov 2021 09:17:10 +0100 Subject: [PATCH 05/31] improve gradle build --- kotlin-bundled-compiler/build.gradle.kts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/kotlin-bundled-compiler/build.gradle.kts b/kotlin-bundled-compiler/build.gradle.kts index 180a78183..8a81a81ab 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") @@ -106,7 +105,7 @@ val downloadTestFrameworkDependencies by tasks.registering(Copy::class) { } val downloadKotlinCompilerPluginAndExtractSelectedJars by tasks.registering { - val kotlinDownloadDir = file("$downloadDir/$kotlinCompilerVersion/$kotlinIdeaCompatibleVersionMinor") + val kotlinDownloadDir = file("$downloadDir/kotlin-$kotlinCompilerVersion/$kotlinIdeaCompatibleVersionMinor") val locallyDownloadedCompilerFile by extra { file(kotlinDownloadDir).listFiles()?.firstOrNull { it.name.startsWith("kotlin-plugin-") } ?: file("$kotlinDownloadDir/kotlin-plugin.zip") @@ -173,7 +172,7 @@ val extractPackagesFromPlugin by tasks.registering(Jar::class) { } val downloadIntellijCoreAndExtractSelectedJars by tasks.registering { - val ideaDownloadDir = file("$downloadDir/$ideaVersion") + val ideaDownloadDir = file("$downloadDir/idea-$ideaVersion") val locallyDownloadedIntellijCoreFile by extra { file("$ideaDownloadDir/intellij-core.zip") } doLast { @@ -193,7 +192,7 @@ val downloadIntellijCoreAndExtractSelectedJars by tasks.registering { } val downloadIdeaDistributionZipAndExtractSelectedJars by tasks.registering { - val ideaDownloadDir = file("$downloadDir/$ideaVersion") + val ideaDownloadDir = file("$downloadDir/idea-$ideaVersion") val locallyDownloadedIdeaZipFile by extra { file("$ideaDownloadDir/ideaIC.zip") } val chosenJars by extra { setOf(//"openapi", "platform-util-ui", From e73e04636064816f94e9c1d9c8bf15879bbc90bf Mon Sep 17 00:00:00 2001 From: U534967 Date: Mon, 8 Nov 2021 09:57:26 +0100 Subject: [PATCH 06/31] improve gradle build --- kotlin-bundled-compiler/build.gradle.kts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/kotlin-bundled-compiler/build.gradle.kts b/kotlin-bundled-compiler/build.gradle.kts index 8a81a81ab..b52150e8f 100644 --- a/kotlin-bundled-compiler/build.gradle.kts +++ b/kotlin-bundled-compiler/build.gradle.kts @@ -77,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){ @@ -105,6 +111,8 @@ 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(kotlinDownloadDir).listFiles()?.firstOrNull { it.name.startsWith("kotlin-plugin-") } @@ -172,6 +180,7 @@ val extractPackagesFromPlugin by tasks.registering(Jar::class) { } val downloadIntellijCoreAndExtractSelectedJars by tasks.registering { + dependsOn(deleteLibrariesFromLibFolder) val ideaDownloadDir = file("$downloadDir/idea-$ideaVersion") val locallyDownloadedIntellijCoreFile by extra { file("$ideaDownloadDir/intellij-core.zip") } @@ -192,6 +201,7 @@ val downloadIntellijCoreAndExtractSelectedJars by tasks.registering { } val downloadIdeaDistributionZipAndExtractSelectedJars by tasks.registering { + dependsOn(deleteLibrariesFromLibFolder) val ideaDownloadDir = file("$downloadDir/idea-$ideaVersion") val locallyDownloadedIdeaZipFile by extra { file("$ideaDownloadDir/ideaIC.zip") } val chosenJars by extra { setOf(//"openapi", @@ -302,16 +312,13 @@ val repackageIdeaAndKotlinCompilerSources by tasks.registering(Zip::class) { } val downloadBundled by tasks.registering { - libDir.listFiles()?.filter { it.isFile }?.forEach { it.deleteRecursively() } if (localTCArtifacts) { - dependsOn(downloadKotlinCompilerPluginAndExtractSelectedJars, - extractPackagesFromPlugin, + dependsOn(extractPackagesFromPlugin, downloadIntellijCoreAndExtractSelectedJars, createIdeDependenciesJar, downloadKotlinxLibraries) } else { - dependsOn(downloadKotlinCompilerPluginAndExtractSelectedJars, - extractPackagesFromPlugin, + dependsOn(extractPackagesFromPlugin, downloadIntellijCoreAndExtractSelectedJars, createIdeDependenciesJar, downloadKotlinxLibraries) From 6e9fe0605480f8899a9aa912896fbd5b55eff6e0 Mon Sep 17 00:00:00 2001 From: U534967 Date: Mon, 8 Nov 2021 10:29:24 +0100 Subject: [PATCH 07/31] fix warning --- .../src/org/jetbrains/kotlin/core/compiler/KotlinCompiler.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 6581c6150..4c6d26824 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 @@ -81,7 +81,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() } From 821f3cbeb450c9441416e27d1aa924e324660225 Mon Sep 17 00:00:00 2001 From: Julian Hahn Date: Sun, 21 Nov 2021 15:36:59 +0100 Subject: [PATCH 08/31] fix provided properties in scripts not with imports but with added properties declarations. --- .../resolve/EclipseAnalyzerFacadeForJVM.kt | 198 ++++++++++-------- 1 file changed, 107 insertions(+), 91 deletions(-) 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 fef9926d7..d61fbc5ae 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 @@ -45,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 @@ -63,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 @@ -104,113 +108,121 @@ 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 + + val tempNewClasspath = tempOrigClasspath + environment.definitionClasspath.map { + JavaCore.newLibraryEntry(Path(it.absolutePath), null, null) } - ProjectUtils.getSourceFilesWithDependencies(environment.javaProject).toCollection(allFiles) + javaProject.setRawClasspath(tempNewClasspath, null) + + try { + + 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) - val tempSourceCode = KtFileScriptSource(scriptFile) + val tempSourceCode = KtFileScriptSource(scriptFile) - val tempRefinedConfig = environment.definition?.let { - refineScriptCompilationConfiguration(tempSourceCode, it, environment.project) - }?.valueOrNull()?.configuration + val tempRefinedConfig = environment.definition?.let { + refineScriptCompilationConfiguration(tempSourceCode, it, environment.project) + }?.valueOrNull()?.configuration - val tempContribution = EclipseScriptDefinitionProvider.getContribution(tempSourceCode) + val tempContribution = EclipseScriptDefinitionProvider.getContribution(tempSourceCode) - val tempDefaultImports = - tempRefinedConfig?.get(PropertiesCollection.Key("defaultImports", emptyList())) ?: emptyList() - val tempImports = ArrayList(tempDefaultImports) + val tempDefaultImports = + tempRefinedConfig?.get(PropertiesCollection.Key("defaultImports", emptyList())) ?: emptyList() + val tempImports = ArrayList(tempDefaultImports) - val analyzerService = object : PlatformDependentAnalyzerServices() { + val analyzerService = object : PlatformDependentAnalyzerServices() { - override val defaultLowPriorityImports: List = listOf(ImportPath.fromString("java.lang.*")) + override val defaultLowPriorityImports: List = listOf(ImportPath.fromString("java.lang.*")) - override val platformConfigurator: PlatformConfigurator = JvmPlatformConfigurator + override val platformConfigurator: PlatformConfigurator = JvmPlatformConfigurator - override fun computePlatformSpecificDefaultImports( - storageManager: StorageManager, - result: MutableList - ) { - result.add(ImportPath.fromString("kotlin.jvm.*")) - tempImports.map(ImportPath::fromString).toCollection(result) + 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)) + 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) -> - val isNullable = tempContribution?.isNullable(key, tempRefinedConfig) ?: true - """ - |@Deprecated(message = "Do not import this explicitly! Used only in eclipse as workaround for providedProperties in Scripts!", level = DeprecationLevel.WARNING) - |val $key: ${value.typeName}${if(isNullable) "? = null" else " = TODO()"} - """.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) - tempImports.add("$tempPackageName.*") + override fun getClassMemberDeclarationProvider(classLikeInfo: KtClassLikeInfo): ClassMemberDeclarationProvider { + if (classLikeInfo is KtScriptInfo) { + return object : AbstractPsiBasedDeclarationProvider(storageManager), + ClassMemberDeclarationProvider { + override val ownerInfo: KtClassLikeInfo = classLikeInfo - val tempKtFile = PsiFileFactory.getInstance(environment.project) - .createFileFromText("scriptParameters.kt", KotlinLanguage.INSTANCE, tempContent) as? KtFile + override fun doCreateIndex(index: Index) { + val tempProvidedProperties = tempProperties?.entries?.map { (key, value) -> + val isNullable = tempContribution?.isNullable(key, tempRefinedConfig) ?: true + val tempText = + """val $key: ${value.typeName}${'$'}${if (isNullable) "? = null" else " = TODO()"}""" + factory.createProperty(tempText) + } ?: emptyList() - if (tempKtFile != null) { - allFiles.add(tempKtFile) + val tempTestProp = factory.createProperty("val test: String = TODO()") + + val tempDeclarations = ownerInfo.declarations + + ownerInfo.primaryConstructorParameters + + tempProvidedProperties + + tempTestProp + + tempDeclarations.forEach(index::putToIndex) + } + } + } + return super.getClassMemberDeclarationProvider(classLikeInfo) + } + } } - } - 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( @@ -219,14 +231,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) From 7f975dec425b97152cc68b8cd87a7526b823952a Mon Sep 17 00:00:00 2001 From: Julian Hahn Date: Sun, 21 Nov 2021 16:41:49 +0100 Subject: [PATCH 09/31] remove test property --- .../kotlin/core/resolve/EclipseAnalyzerFacadeForJVM.kt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) 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 d61fbc5ae..4253fdc62 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 @@ -196,12 +196,9 @@ object EclipseAnalyzerFacadeForJVM { factory.createProperty(tempText) } ?: emptyList() - val tempTestProp = factory.createProperty("val test: String = TODO()") - val tempDeclarations = ownerInfo.declarations + ownerInfo.primaryConstructorParameters + - tempProvidedProperties + - tempTestProp + tempProvidedProperties tempDeclarations.forEach(index::putToIndex) } From 5d20665669f3bd075a32b7e1bc40ae9ed111c962 Mon Sep 17 00:00:00 2001 From: Julian Hahn Date: Sun, 21 Nov 2021 16:42:56 +0100 Subject: [PATCH 10/31] only unique classpath entries --- .../kotlin/core/resolve/EclipseAnalyzerFacadeForJVM.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 4253fdc62..b7ed400fe 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 @@ -116,7 +116,7 @@ object EclipseAnalyzerFacadeForJVM { JavaCore.newLibraryEntry(Path(it.absolutePath), null, null) } - javaProject.setRawClasspath(tempNewClasspath, null) + javaProject.setRawClasspath(tempNewClasspath.toSet().toTypedArray(), null) try { From cc89e17452d60e1e18338e8b3a2bc824195d6c51 Mon Sep 17 00:00:00 2001 From: U534967 Date: Tue, 23 Nov 2021 08:38:23 +0100 Subject: [PATCH 11/31] Make the ScriptingContribution capable of deciding which classpath to take to analyze the script. Dont throw exceptions if project classpath contains missing entries. Analyze anyways and show possible errors from unknown references. Improve handling of changed resources like projects and files, especially if new projects gets added or old ones removed. --- .../kotlin/core/compiler/KotlinCompiler.kt | 21 +++- .../kotlin/core/model/CachedEnvironment.kt | 7 ++ .../core/model/KotlinCommonEnvironment.kt | 5 + .../kotlin/core/model/KotlinEnvironment.kt | 41 ++++++- .../resolve/EclipseAnalyzerFacadeForJVM.kt | 19 +-- .../core/script/ScriptTemplateContribution.kt | 4 + .../ProjectScriptTemplateContribution.kt | 22 +++- .../core/utils/DependencyResolverException.kt | 5 + .../kotlin/core/utils/ProjectUtils.kt | 75 ++++++++---- .../ui/builder/ResourceChangeListener.kt | 109 ++++++++++-------- 10 files changed, 211 insertions(+), 97 deletions(-) create mode 100644 kotlin-eclipse-core/src/org/jetbrains/kotlin/core/utils/DependencyResolverException.kt 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 4c6d26824..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 @@ -21,6 +21,7 @@ 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.* @@ -127,10 +128,13 @@ 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( @@ -169,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) 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/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/resolve/EclipseAnalyzerFacadeForJVM.kt b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/resolve/EclipseAnalyzerFacadeForJVM.kt index b7ed400fe..1c5589400 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 @@ -112,13 +112,15 @@ object EclipseAnalyzerFacadeForJVM { val tempOrigClasspath = javaProject.rawClasspath - val tempNewClasspath = tempOrigClasspath + environment.definitionClasspath.map { - JavaCore.newLibraryEntry(Path(it.absolutePath), null, null) - } + try { + val tempSourceCode = KtFileScriptSource(scriptFile) + val tempContribution = EclipseScriptDefinitionProvider.getContribution(tempSourceCode) - javaProject.setRawClasspath(tempNewClasspath.toSet().toTypedArray(), null) + val tempNewClasspath = tempContribution?.createClasspath(environment) ?: (tempOrigClasspath + environment.definitionClasspath.map { + JavaCore.newLibraryEntry(Path(it.absolutePath), null, null) + }).distinctBy { it.path }.toTypedArray() - try { + javaProject.setRawClasspath(tempNewClasspath, null) val allFiles = LinkedHashSet().run { add(scriptFile) @@ -131,13 +133,10 @@ object EclipseAnalyzerFacadeForJVM { ProjectUtils.getSourceFilesWithDependencies(environment.javaProject).toCollection(allFiles) - val tempSourceCode = KtFileScriptSource(scriptFile) - val tempRefinedConfig = environment.definition?.let { refineScriptCompilationConfiguration(tempSourceCode, it, environment.project) }?.valueOrNull()?.configuration - val tempContribution = EclipseScriptDefinitionProvider.getContribution(tempSourceCode) val tempDefaultImports = tempRefinedConfig?.get(PropertiesCollection.Key("defaultImports", emptyList())) ?: emptyList() @@ -192,7 +191,9 @@ object EclipseAnalyzerFacadeForJVM { val tempProvidedProperties = tempProperties?.entries?.map { (key, value) -> val isNullable = tempContribution?.isNullable(key, tempRefinedConfig) ?: true val tempText = - """val $key: ${value.typeName}${'$'}${if (isNullable) "? = null" else " = TODO()"}""" + """ + /** Provided property '$key' of type: ${value.typeName} */ + val $key: ${value.typeName}${'$'}${if (isNullable) "? = null" else " = TODO()"}""".trimIndent() factory.createProperty(tempText) } ?: emptyList() 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 050c9f7bf..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,5 +1,7 @@ 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 @@ -9,6 +11,8 @@ abstract class ScriptTemplateContribution { 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 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/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 From 95657d29a296424e616857de7ab3354d9da35af4 Mon Sep 17 00:00:00 2001 From: U534967 Date: Sat, 18 Dec 2021 14:08:14 +0100 Subject: [PATCH 12/31] create console perproject as otherwise an error in the first project wont be visible at the end of compilation, as other projects may depend on that one and have errors as well and override the console output. --- .../core/compiler/KotlinCompilerUtils.java | 46 ------- .../core/compiler/KotlinCompilerUtils.kt | 43 ++++++ .../ui/builder/BaseKotlinBuilderElement.kt | 50 +++++++ .../IncrementalKotlinBuilderElement.kt | 48 +------ .../kotlin/ui/builder/KotlinBuilderElement.kt | 48 +------ .../ui/launch/CompilerStatusHandler.java | 125 ------------------ .../kotlin/ui/launch/CompilerStatusHandler.kt | 96 ++++++++++++++ .../kotlin/ui/launch/kotlinConsole.kt | 27 ++-- 8 files changed, 214 insertions(+), 269 deletions(-) delete mode 100644 kotlin-eclipse-core/src/org/jetbrains/kotlin/core/compiler/KotlinCompilerUtils.java create mode 100644 kotlin-eclipse-core/src/org/jetbrains/kotlin/core/compiler/KotlinCompilerUtils.kt delete mode 100644 kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/launch/CompilerStatusHandler.java create mode 100644 kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/launch/CompilerStatusHandler.kt 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-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/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 From b3c77e40a1f5e74b59efcd7db8128f3aa53e24fa Mon Sep 17 00:00:00 2001 From: U534967 Date: Mon, 21 Mar 2022 12:46:34 +0100 Subject: [PATCH 13/31] Fixed search references did not find import alias references. Fix code completion did not find top level functions and properties that were not already imported in the file. MAke coroutines available for use in plugin code. update gradle version --- kotlin-bundled-compiler/META-INF/MANIFEST.MF | 1 + kotlin-bundled-compiler/buildSrc/build.gradle | 10 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../kotlin/core/model/KotlinJavaManager.kt | 82 +++-- .../eclipse/ui/utils/KotlinEclipseScope.kt | 7 + .../eclipse/ui/utils/KotlinImageProvider.kt | 24 +- .../codeassist/KotlinCompletionProcessor.kt | 212 ++++++++----- .../codeassist/KotlinCompletionProposal.kt | 36 ++- .../KotlinFunctionCompletionProposal.kt | 3 +- .../KotlinFunctionParameterInfoAssist.kt | 74 ++--- .../completion/KotlinCompletionUtils.kt | 30 +- .../KotlinReferenceVariantsHelper.kt | 297 +++++++++++++++--- .../ui/search/KotlinQueryParticipant.kt | 155 ++++----- 13 files changed, 605 insertions(+), 328 deletions(-) create mode 100644 kotlin-eclipse-ui/src/org/jetbrains/kotlin/eclipse/ui/utils/KotlinEclipseScope.kt 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/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/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/model/KotlinJavaManager.kt b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/model/KotlinJavaManager.kt index af23d90b9..06f1773ae 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,53 +16,36 @@ *******************************************************************************/ 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() @@ -77,16 +60,16 @@ public object KotlinJavaManager { 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 { + fun hasLinkedKotlinBinFolder(project: IProject): Boolean { val folder = project.getFolder(KotlinJavaManager.KOTLIN_BIN_FOLDER) - return folder.isLinked() && KotlinFileSystem.SCHEME == folder.getLocationURI().getScheme() + return folder.isLinked && KotlinFileSystem.SCHEME == folder.locationURI.scheme } private fun findMembersIn(eclipseType: IType, declaration: KtDeclaration, klass: Class): List { @@ -95,14 +78,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 +102,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-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/editors/codeassist/KotlinCompletionProcessor.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/codeassist/KotlinCompletionProcessor.kt index 82cc911bc..93e9bdcf5 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 @@ -44,10 +44,21 @@ import org.jetbrains.kotlin.ui.editors.templates.KotlinApplicableTemplateContext import org.jetbrains.kotlin.ui.editors.templates.KotlinDocumentTemplateContext import org.jetbrains.kotlin.ui.editors.templates.KotlinTemplateManager +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() @@ -55,7 +66,8 @@ abstract class KotlinCompletionProcessor( 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, @@ -80,7 +92,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) { @@ -109,11 +125,11 @@ abstract class KotlinCompletionProcessor( } ) } - + private val kotlinParameterValidator by lazy { KotlinParameterListValidator(editor) } - + override fun computeCompletionProposals(viewer: ITextViewer, offset: Int): Array { if (assistant != null) { configureContentAssistant(assistant) @@ -122,14 +138,14 @@ abstract class KotlinCompletionProcessor( val generatedProposals = generateCompletionProposals(viewer, offset).let { if (needSorting) sortProposals(it) else it } - + return generatedProposals.toTypedArray() } private fun sortProposals(proposals: List): List { return proposals.sortedWith(KotlinCompletionSorter::compare) } - + private fun configureContentAssistant(contentAssistant: ContentAssistant) { contentAssistant.setEmptyMessage("No Default Proposals") contentAssistant.setSorter(KotlinCompletionSorter) @@ -152,105 +168,131 @@ 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 presentableString = DescriptorRenderer.ONLY_NAMES_WITH_SHORT_TYPES.render(descriptor) + val containmentPresentableString = descriptor.containingDeclaration?.let { + DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.render(it) + } + + val proposal = KotlinCompletionProposal( + completion, + image, + presentableString, + containmentPresentableString, + null, + completion, + part + ) + + withKotlinInsertHandler(descriptor, proposal, part) + } + 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) } } @@ -275,26 +317,26 @@ 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() - + 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.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..25a7f0982 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 @@ -31,12 +31,17 @@ 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.core.imports.FunctionCandidate +import org.jetbrains.kotlin.core.imports.TypeCandidate +import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.resolve.calls.util.getValueParametersCountFromFunctionType +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull 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, @@ -79,7 +84,7 @@ fun getIdentifierInfo(document: IDocument, offset: Int): IdentifierInfo { data class IdentifierInfo(val identifierPart: String, val identifierStart: Int) -open class KotlinCompletionProposal( +open class KotlinCompletionProposal constructor( val replacementString: String, val img: Image?, val presentableString: String, @@ -139,7 +144,7 @@ open class KotlinCompletionProposal( } } -class KotlinImportCompletionProposal(val typeName: TypeNameMatch, image: Image?, val file: IFile, identifierPart: String) : +class KotlinImportTypeCompletionProposal(val typeName: TypeNameMatch, image: Image?, val file: IFile, identifierPart: String) : KotlinCompletionProposal( typeName.simpleTypeName, image, @@ -164,6 +169,29 @@ class KotlinImportCompletionProposal(val typeName: TypeNameMatch, image: Image?, } } +class KotlinImportCallableCompletionProposal(val descriptor: CallableDescriptor, image: Image?, val file: IFile, identifierPart: String) : + KotlinCompletionProposal( + "${descriptor.name.identifier}${if(descriptor is PropertyDescriptor) "" else "()"}", + image, + DescriptorRenderer.ONLY_NAMES_WITH_SHORT_TYPES.render(descriptor), + DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.render(descriptor.containingDeclaration), + 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) @@ -175,4 +203,4 @@ private fun createStyledString(simpleName: String, containingDeclaration: String append(containingDeclaration, StyledString.QUALIFIER_STYLER) } } -} \ No newline at end of file +} 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..d46f9b5a2 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 @@ -44,6 +44,7 @@ public class KotlinFunctionCompletionProposal( proposal.replacementString, proposal.img, proposal.presentableString, + proposal.containmentPresentableString, identifierPart = identifierPart) { init { @@ -115,4 +116,4 @@ public class KotlinFunctionCompletionProposal( 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..34c55e1ab 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() } @@ -73,16 +75,16 @@ fun getCallSimpleNameExpression(editor: KotlinEditor, offset: Int): KtSimpleName val argumentList = PsiTreeUtil.getParentOfType(psiElement, KtValueArgumentList::class.java) if (argumentList == null) return null - val argumentListParent = argumentList.getParent() + val argumentListParent = argumentList.parent return if (argumentListParent is KtCallElement) argumentListParent.getCallNameExpression() else null } -public class KotlinFunctionParameterContextInformation(descriptor: FunctionDescriptor) : IContextInformation { +class KotlinFunctionParameterContextInformation(descriptor: FunctionDescriptor) : IContextInformation { val displayString = DescriptorRenderer.ONLY_NAMES_WITH_SHORT_TYPES.render(descriptor) - val renderedParameters = descriptor.getValueParameters().map { renderParameter(it) } + val renderedParameters = descriptor.valueParameters.map { renderParameter(it) } val informationString = renderedParameters.joinToString(", ") val displayImage = KotlinImageProvider.getImage(descriptor) - val name = if (descriptor is ConstructorDescriptor) descriptor.getContainingDeclaration().getName() else descriptor.getName() + val name = if (descriptor is ConstructorDescriptor) descriptor.containingDeclaration.name else descriptor.name override fun getContextDisplayString(): String = displayString @@ -94,7 +96,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 +113,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 +122,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/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 91b35ec3d..ca375424e 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,11 +1,22 @@ 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 @@ -31,7 +42,12 @@ 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.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.* +import kotlin.time.Duration +import kotlin.time.ExperimentalTime class KotlinReferenceVariantsHelper( val bindingContext: BindingContext, @@ -42,22 +58,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 - ) - } + var variants: Collection = + getReferenceVariants( + simpleNameExpression, + callTypeAndReceiver, + kindFilter, + nameFilter, + javaProject, + ktFile, + file, + identifierPart + ).filter { + !resolutionFacade.frontendService().isHiddenInResolution(it.descriptor) && + visibilityFilter(it.descriptor) + } - ShadowedDeclarationsFilter.create(bindingContext, resolutionFacade, simpleNameExpression, callTypeAndReceiver)?.let { - variants = it.filter(variants) + 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.filter { kindFilter.accepts(it) } + return variants.filter { kindFilter.accepts(it.descriptor) } } private fun getVariantsForImportOrPackageDirective( @@ -103,8 +138,8 @@ class KotlinReferenceVariantsHelper( useReceiverType: KotlinType?, kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean - ): Collection { - val descriptors = LinkedHashSet() + ): Collection { + val descriptors = LinkedHashSet() val resolutionScope = contextElement.getResolutionScope(bindingContext, resolutionFacade) @@ -136,7 +171,10 @@ class KotlinReferenceVariantsHelper( if (isStatic) { explicitReceiverTypes .mapNotNull { (it.constructor.declarationDescriptor as? ClassDescriptor)?.staticScope } - .flatMapTo(descriptors) { it.collectStaticMembers(resolutionFacade, kindFilter, nameFilter) } + .flatMapTo(descriptors) { + it.collectStaticMembers(resolutionFacade, kindFilter, nameFilter) + .map { KotlinBasicCompletionProposal.Descriptor(it) } + } } } else { descriptors.addNonExtensionCallablesAndConstructors( @@ -152,8 +190,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") @@ -163,10 +205,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 -> { @@ -175,7 +219,7 @@ class KotlinReferenceVariantsHelper( simpleNameExpression, kindFilter, nameFilter - ) + ).map { KotlinBasicCompletionProposal.Descriptor(it) } } is CallTypeAndReceiver.ANNOTATION -> { @@ -184,7 +228,7 @@ class KotlinReferenceVariantsHelper( simpleNameExpression, kindFilter, nameFilter - ) + ).map { KotlinBasicCompletionProposal.Descriptor(it) } } is CallTypeAndReceiver.CALLABLE_REFERENCE -> { @@ -227,7 +271,7 @@ class KotlinReferenceVariantsHelper( ) }.toSet() - val descriptors = LinkedHashSet() + val descriptors = LinkedHashSet() val filterWithoutExtensions = kindFilter exclude DescriptorKindExclude.Extensions if (receiverExpression != null) { @@ -238,7 +282,7 @@ class KotlinReferenceVariantsHelper( resolutionFacade, filterWithoutExtensions, nameFilter - ) + ).map { KotlinBasicCompletionProposal.Descriptor(it) } ) } @@ -256,7 +300,12 @@ class KotlinReferenceVariantsHelper( resolutionScope, callType, kindFilter, - nameFilter + nameFilter, + javaProject, + ktFile, + file, + identifierPart, + false ) } else { descriptors.processAll( @@ -265,7 +314,12 @@ class KotlinReferenceVariantsHelper( resolutionScope, callType, kindFilter, - nameFilter + nameFilter, + javaProject, + ktFile, + file, + identifierPart, + true ) descriptors.addAll( @@ -273,50 +327,193 @@ 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()) { + if (it.descriptor is CallableMemberDescriptor && it.descriptor.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) { + it.descriptor.overriddenDescriptors.map { KotlinBasicCompletionProposal.Descriptor(it) } + } else { + listOf(it) + } + } } - return descriptors + return descriptors.distinctBy { 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() + var tempJob = KotlinEclipseScope.launch { + addNonExtensionMembers(receiverTypes, kindFilter, nameFilter, constructorFilter = { it.isInner }) + } + tempJobs += tempJob + tempJob = KotlinEclipseScope.launch { + addMemberExtensions(implicitReceiverTypes, receiverTypes, callType, kindFilter, nameFilter) + } + tempJobs += tempJob + tempJob = KotlinEclipseScope.launch { + addNotImportedTopLevelCallables( + receiverTypes, + kindFilter, + nameFilter, + javaProject, + ktFile, + file, + identifierPart, + allowNoReceiver + ) + println("Finished!") + } + tempJobs += tempJob + tempJob = KotlinEclipseScope.launch { + addScopeAndSyntheticExtensions(resolutionScope, receiverTypes, callType, kindFilter, nameFilter) + } + tempJobs += tempJob + tempJobs.joinAll() + } + } + + @OptIn(ExperimentalTime::class) + 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 tempMethodNamePackages = mutableSetOf() + + val collector = object : MethodNameMatchRequestor() { + override fun acceptMethodNameMatch(match: MethodNameMatch) { + if (Flags.isPublic(match.modifiers) && Flags.isStatic(match.modifiers)) { + tempMethodNamePackages.add(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 + ) + + searchEngine.searchAllMethodNames( + null, + SearchPattern.R_EXACT_MATCH, + null, + SearchPattern.R_EXACT_MATCH, + null, + SearchPattern.R_EXACT_MATCH, + "get${identifierPart.capitalize()}".toCharArray(), + SearchPattern.R_PREFIX_MATCH, + javaProjectSearchScope, + collector, + IJavaSearchConstants.FORCE_IMMEDIATE_SEARCH, + null + ) + + val tempPackages = tempMethodNamePackages.map { + resolutionFacade.moduleDescriptor.getPackage(FqName(it)) + } + + val importsSet = ktFile.importDirectives + .mapNotNull { it.importedFqName?.asString() } + .toSet() + + val originPackage = ktFile.packageFqName.asString() + + val tempDeferreds = tempPackages.map { packageDesc -> + KotlinEclipseScope.async { + packageDesc.memberScope.getDescriptorsFiltered( + kindFilter.intersect(DescriptorKindFilter.CALLABLES), + nameFilter + ).asSequence().filterIsInstance() + .filter { callDesc -> + val tempFuzzy = callDesc.fuzzyExtensionReceiverType() + (allowNoReceiver && tempFuzzy == null) || (tempFuzzy != null && receiverTypes.any { receiverType -> + tempFuzzy.checkIsSuperTypeOf(receiverType) != null + }) + } + .filter { callDesc -> + callDesc.importableFqName?.asString() !in importsSet && + callDesc.importableFqName?.parent()?.asString() != originPackage + }.toList() + } + } + + val tempDescriptors = tempDeferreds.awaitAll().flatten() + + tempDescriptors + .map { + KotlinBasicCompletionProposal.Proposal( + KotlinImportCallableCompletionProposal( + it, + KotlinImageProvider.getImage(it), + file, + identifierPart + ), it + ) + }.toCollection(this) + } } - private fun MutableSet.addMemberExtensions( + 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, @@ -338,7 +535,7 @@ class KotlinReferenceVariantsHelper( } } - private fun MutableSet.addNonExtensionCallablesAndConstructors( + private fun MutableSet.addNonExtensionCallablesAndConstructors( scope: HierarchicalScope, kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean, @@ -358,14 +555,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<*>, @@ -378,9 +576,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)) } } } @@ -465,10 +665,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/search/KotlinQueryParticipant.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/search/KotlinQueryParticipant.kt index 6060a426b..9e530eb83 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 @@ -44,20 +44,18 @@ 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.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.lexer.KtSingleValueToken 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.calls.callUtil.getCall 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 @@ -74,12 +72,12 @@ class KotlinQueryParticipant : IQueryParticipant { override fun run() { val searchElements = getSearchElements(querySpecification) if (searchElements.isEmpty()) return - + if (querySpecification is KotlinAndJavaSearchable) { runCompositeSearch(searchElements, requestor, querySpecification, monitor) return } - + val kotlinFiles = getKotlinFilesByScope(querySpecification) if (kotlinFiles.isEmpty()) return if (monitor?.isCanceled == true) return @@ -87,32 +85,44 @@ 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) + val matchedReferences = resolveElementsAndMatch(elements, searchElement, querySpecification, monitor) 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) { + while (tempFunction?.isLocal == true) { tempFunction = PsiTreeUtil.getParentOfType(tempFunction, KtFunction::class.java) } - val tempClassObjectOrFileName = PsiTreeUtil.getNonStrictParentOfType(ktElement, KtClassOrObject::class.java)?.name - ?: ktElement.containingKtFile.name + val tempClassObjectOrFileName = + PsiTreeUtil.getNonStrictParentOfType(ktElement, KtClassOrObject::class.java)?.name + ?: ktElement.containingKtFile.name val tempLabel = buildString { append(tempClassObjectOrFileName) - if(tempFunction != null) { + if (tempFunction != null) { append("#") append(tempFunction.name) } - if(isNotEmpty()) { + if (isNotEmpty()) { append(": ") } append(tempElement) @@ -121,80 +131,80 @@ class KotlinQueryParticipant : IQueryParticipant { requestor.reportMatch(KotlinElementMatch(ktElement, tempLabel)) } } - + 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 reportSearchResults(result: AbstractJavaSearchResult) { for (searchElement in result.elements) { result.getMatches(searchElement).forEach { requestor.reportMatch(it) } } } - - 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 { + } 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,7 +212,7 @@ class KotlinQueryParticipant : IQueryParticipant { else -> emptyList() } } - + private fun searchTextOccurrences(searchElement: SearchElement, filesScope: List): ISearchResult? { var searchText = searchElement.getSearchText() ?: return null var asRegex = false @@ -226,38 +236,39 @@ class KotlinQueryParticipant : IQueryParticipant { } } } - + 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 resolveElementsAndMatch( elements: List, searchElement: SearchElement, - querySpecification: QuerySpecification + querySpecification: QuerySpecification, + monitor: IProgressMonitor? ): List { val beforeResolveFilters = getBeforeResolveFilters(querySpecification) val afterResolveFilters = getAfterResolveFilters() - + // This is important for optimization: // we will consequentially cache files one by one which are containing these references val sortedByFileNameElements = elements.sortedBy { it.containingKtFile.name } - - return sortedByFileNameElements.mapNotNull { element -> + + return sortedByFileNameElements.flatMap { element -> var tempElement: KtElement? = element var beforeResolveCheck = beforeResolveFilters.all { it.isApplicable(tempElement!!) } if (!beforeResolveCheck) { @@ -266,28 +277,28 @@ class KotlinQueryParticipant : IQueryParticipant { if (tempElement != null) { beforeResolveCheck = beforeResolveFilters.all { it.isApplicable(tempElement) } } - if (!beforeResolveCheck) return@mapNotNull null - + if (!beforeResolveCheck) return@flatMap emptyList() + val sourceElements = tempElement!!.resolveToSourceDeclaration() - if (sourceElements.isEmpty()) return@mapNotNull null - + 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 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 +306,10 @@ class KotlinQueryParticipant : IQueryParticipant { } .filterNotNullTo(elements) } - + return elements } - + private fun getKotlinFilesByScope(querySpecification: QuerySpecification): List { return when (querySpecification) { is ElementQuerySpecification, @@ -317,34 +328,34 @@ fun getContainingClassOrObjectForConstructor(sourceElements: List return@mapNotNull KotlinSourceElement(psi.getContainingClassOrObject()) } } - + null } } 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 -> + val kotlinElements = sourceElementsToKotlinElements(sourceElements).filterNot { kotlinElement -> (kotlinElement !is KtFunction || !kotlinElement.hasModifier(KtTokens.OPERATOR_KEYWORD)) && javaElements.any { it.elementName == kotlinElement.name } } - + 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 { @@ -378,4 +389,4 @@ class KotlinAdaptableElement(val jetElement: KtElement, val label: String) : IAd else -> null } } -} \ No newline at end of file +} From 05e9801c626d8c671b0fd59d02f9291243038c8e Mon Sep 17 00:00:00 2001 From: U534967 Date: Mon, 21 Mar 2022 15:17:52 +0100 Subject: [PATCH 14/31] Also find functions and properties in objects and companion objects. --- .../KotlinReferenceVariantsHelper.kt | 70 +++++++++++++------ 1 file changed, 47 insertions(+), 23 deletions(-) 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 ca375424e..9a9902609 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 @@ -32,6 +32,7 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastManager import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver 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 @@ -45,8 +46,6 @@ 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.* -import kotlin.time.Duration import kotlin.time.ExperimentalTime class KotlinReferenceVariantsHelper( @@ -412,12 +411,13 @@ class KotlinReferenceVariantsHelper( val javaProjectSearchScope = JavaSearchScopeFactory.getInstance().createJavaSearchScope(dependencyProjects.toTypedArray(), false) - val tempMethodNamePackages = mutableSetOf() + val tempClassNames = hashMapOf() val collector = object : MethodNameMatchRequestor() { override fun acceptMethodNameMatch(match: MethodNameMatch) { - if (Flags.isPublic(match.modifiers) && Flags.isStatic(match.modifiers)) { - tempMethodNamePackages.add(match.method.declaringType.packageFragment.elementName) + if (Flags.isPublic(match.modifiers)) { + tempClassNames[match.method.declaringType.typeQualifiedName] = + match.method.declaringType.packageFragment.elementName } } } @@ -452,34 +452,58 @@ class KotlinReferenceVariantsHelper( null ) - val tempPackages = tempMethodNamePackages.map { + val tempPackages = tempClassNames.values.map { resolutionFacade.moduleDescriptor.getPackage(FqName(it)) } + val tempClasses = tempClassNames.mapNotNull { (className, packageName) -> + val tempPackage = resolutionFacade.moduleDescriptor.getPackage(FqName(packageName)) + if (className.contains('$')) { + val tempNames = className.split('$') + val topLevelClass = tempNames.first() + var tempClassDescriptor = tempPackage.memberScope.getDescriptorsFiltered { + !it.isSpecial && it.identifier == topLevelClass + }.filterIsInstance().singleOrNull() ?: return@mapNotNull null + + tempNames.drop(1).forEach { subName -> + tempClassDescriptor = tempClassDescriptor.unsubstitutedMemberScope.getDescriptorsFiltered { + !it.isSpecial && it.identifier == subName + }.filterIsInstance().singleOrNull() ?: return@mapNotNull null + } + + tempClassDescriptor + } else { + tempPackage.memberScope.getDescriptorsFiltered { + !it.isSpecial && it.identifier == className + }.filterIsInstance().singleOrNull() + } + } + val importsSet = ktFile.importDirectives .mapNotNull { it.importedFqName?.asString() } .toSet() val originPackage = ktFile.packageFqName.asString() - val tempDeferreds = tempPackages.map { packageDesc -> - KotlinEclipseScope.async { - packageDesc.memberScope.getDescriptorsFiltered( - kindFilter.intersect(DescriptorKindFilter.CALLABLES), - nameFilter - ).asSequence().filterIsInstance() - .filter { callDesc -> - val tempFuzzy = callDesc.fuzzyExtensionReceiverType() - (allowNoReceiver && tempFuzzy == null) || (tempFuzzy != null && receiverTypes.any { receiverType -> - tempFuzzy.checkIsSuperTypeOf(receiverType) != null - }) - } - .filter { callDesc -> - callDesc.importableFqName?.asString() !in importsSet && - callDesc.importableFqName?.parent()?.asString() != originPackage - }.toList() + fun MemberScope.filterDescriptors() = getDescriptorsFiltered( + kindFilter.intersect(CALLABLES), + nameFilter + ).asSequence() + .filterIsInstance() + .filter { callDesc -> + val tempFuzzy = callDesc.fuzzyExtensionReceiverType() + (allowNoReceiver && tempFuzzy == null) || (tempFuzzy != null && receiverTypes.any { receiverType -> + tempFuzzy.checkIsSuperTypeOf(receiverType) != null + }) } - } + .filter { callDesc -> + callDesc.importableFqName?.asString() !in importsSet && + callDesc.importableFqName?.parent()?.asString() != originPackage + }.toList() + + val tempDeferreds = + tempPackages.map { desc -> KotlinEclipseScope.async { desc.memberScope.filterDescriptors() } } + + tempClasses.map { desc -> KotlinEclipseScope.async { desc.unsubstitutedMemberScope.filterDescriptors() } } val tempDescriptors = tempDeferreds.awaitAll().flatten() From b9ae950f204cf4ac506f2a811f071efe511148c0 Mon Sep 17 00:00:00 2001 From: U534967 Date: Wed, 23 Mar 2022 19:11:11 +0100 Subject: [PATCH 15/31] enable find references to find delegated properties as reference to operator functions for references. Also enable to navigate from the by keyword to the getValue and / or setValue methods of the delegate. --- .../kotlin/core/references/KotlinReference.kt | 63 ++++++--- .../kotlin/core/references/referenceUtils.kt | 123 ++++++++---------- .../KotlinStepIntoSelectionHandler.kt | 31 ++--- .../ui/editors/KotlinElementHyperlink.kt | 52 ++++++-- .../editors/KotlinElementHyperlinkDetector.kt | 34 +++-- .../navigation/KotlinOpenDeclarationAction.kt | 48 ++++--- .../ui/search/KotlinQueryParticipant.kt | 31 +++-- .../kotlin/ui/search/searchFilters.kt | 11 +- 8 files changed, 232 insertions(+), 161 deletions(-) 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..2f834faf5 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,39 @@ 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() + return tempDescriptor.accessors.mapNotNull { + context[BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, it]?.candidateDescriptor + } + } + + 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 +131,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 +148,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 +176,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 +201,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..3e19cc2cd 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,108 @@ *******************************************************************************/ 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.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() + + val javaProject = JavaCore.create(KotlinPsiManager.getEclipseFile(ktFile)?.project) + ?: ktFile.getUserData(FILE_PROJECT) ?: return emptyList() return resolveToSourceElements( - KotlinAnalyzer.analyzeFile(jetFile).analysisResult.bindingContext, - javaProject) + KotlinAnalyzer.analyzeFile(ktFile).analysisResult.bindingContext, + 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-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/KotlinElementHyperlink.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/KotlinElementHyperlink.kt index b0c0d6fc0..93ba704fe 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,13 +18,19 @@ 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.VariableDescriptorWithAccessors +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny +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, - private val region: IRegion, - private val refExpression: KtReferenceExpression? = null + private val region: IRegion ) : IHyperlink { override fun getHyperlinkRegion(): IRegion = region @@ -32,11 +38,39 @@ class KotlinElementHyperlink( override fun getHyperlinkText(): String = HYPERLINK_TEXT - override fun open() { - refExpression?.let { openAction.run(it) } ?: openAction.run() - } + override fun open() = openAction.run() +} - companion object { - private const val HYPERLINK_TEXT = "Open Kotlin Declaration" +fun KtPropertyDelegate.doOpenDelegateFun(editor: KotlinEditor, openSetter: Boolean) { + val property = getParentOfType(false) ?: return + val javaProject = editor.javaProject ?: return + + val context = property.getBindingContext() + val tempDescriptor = property.resolveToDescriptorIfAny() as? VariableDescriptorWithAccessors ?: return + val tempAccessor = if (openSetter) { + tempDescriptor.setter + } else { + tempDescriptor.getter } -} \ No newline at end of file + val tempTargetDescriptor = + context[BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, tempAccessor]?.candidateDescriptor ?: return + + gotoElement(tempTargetDescriptor, property, editor, javaProject) +} + +class KotlinKTDelegateHyperLink( + private val region: IRegion, + private val property: KtPropertyDelegate, + private val isSetter: Boolean, + private val editor: KotlinEditor +) : IHyperlink { + override fun getHyperlinkRegion(): IRegion = region + + override fun getTypeLabel(): String? = null + + override fun getHyperlinkText(): String = "$HYPERLINK_TEXT (${if (isSetter) "set" else "get"})" + + override fun open() = property.doOpenDelegateFun(editor, isSetter) +} + +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..bff5e530a 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 @@ -25,10 +25,8 @@ import org.eclipse.jface.text.hyperlink.IHyperlink import org.eclipse.ui.texteditor.ITextEditor 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.psi.* +import org.jetbrains.kotlin.ui.editors.codeassist.getParentOfType import org.jetbrains.kotlin.ui.editors.navigation.KotlinOpenDeclarationAction import org.jetbrains.kotlin.ui.editors.navigation.KotlinOpenDeclarationAction.Companion.OPEN_EDITOR_TEXT @@ -60,17 +58,31 @@ 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 (textEditor.javaProject != null && KotlinOpenDeclarationAction.getNavigationData( + tempReferenceExpression, + textEditor.javaProject!! + ) != 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 tempRef = tempReferenceExpression ?: EditorUtil.getReferenceExpression(textEditor, region.offset) + if (tempRef != null) { + return arrayOf(KotlinElementHyperlink(openAction, wordRegion)) + } else { + val tempElement = EditorUtil.getJetElement(textEditor, region.offset) + if (tempElement is KtPropertyDelegate) { + val isVar = tempElement.getParentOfType(false)?.isVar ?: return null + val tempGetLink = KotlinKTDelegateHyperLink(wordRegion, tempElement, false, textEditor) + val tempSetLink = + if (isVar) KotlinKTDelegateHyperLink(wordRegion, tempElement, true, textEditor) else null + return listOfNotNull(tempGetLink, tempSetLink).toTypedArray() + } + } + return null } -} \ 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..5acf040e2 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,8 +26,10 @@ 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.KtPropertyDelegate import org.jetbrains.kotlin.psi.KtReferenceExpression import org.jetbrains.kotlin.ui.editors.KotlinEditor +import org.jetbrains.kotlin.ui.editors.doOpenDelegateFun class KotlinOpenDeclarationAction(val editor: KotlinEditor) : SelectionDispatchAction(editor.javaEditor.site) { companion object { @@ -47,16 +49,22 @@ 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) ?: kotlin.run { + val tempElement = EditorUtil.getJetElement(editor, selection.offset) + if (tempElement is KtPropertyDelegate) { + tempElement.doOpenDelegateFun(editor, false) + } + return + } val javaProject = editor.javaProject ?: return - + val data = getNavigationData(selectedExpression, javaProject) ?: return gotoElement(data.sourceElement, data.descriptor, selectedExpression, editor, javaProject) @@ -68,4 +76,4 @@ class KotlinOpenDeclarationAction(val editor: KotlinEditor) : SelectionDispatchA 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/search/KotlinQueryParticipant.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/search/KotlinQueryParticipant.kt index 9e530eb83..2f66ce709 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 @@ -94,9 +94,12 @@ class KotlinQueryParticipant : IQueryParticipant { val elements = obtainElements(searchResult as FileSearchResult, kotlinFiles).flatMap { val tempImportAlias = it.getParentOfType(false)?.alias - if(tempImportAlias != null) { + if (tempImportAlias != null) { val tempEclipseFile = KotlinPsiManager.getEclipseFile(tempImportAlias.containingKtFile)!! - val tempResult = searchTextOccurrences(SearchElement.KotlinSearchElement(tempImportAlias), listOf(tempEclipseFile)) + val tempResult = searchTextOccurrences( + SearchElement.KotlinSearchElement(tempImportAlias), + listOf(tempEclipseFile) + ) return@flatMap obtainElements(tempResult as FileSearchResult, listOf(tempEclipseFile)) + it } @@ -227,6 +230,7 @@ class KotlinQueryParticipant : IQueryParticipant { "set" -> "\\[.*?]\\s*?=" "invoke" -> "\\(.*?\\)" "contains" -> "in|!in" + "getValue", "setValue" -> "by" else -> null } if (tempOperationSymbol != null) { @@ -265,21 +269,13 @@ class KotlinQueryParticipant : IQueryParticipant { val afterResolveFilters = getAfterResolveFilters() // 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.flatMap { 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@flatMap emptyList() + val tempElement = findApplicableElement(element, beforeResolveFilters) ?: return@flatMap emptyList() - val sourceElements = tempElement!!.resolveToSourceDeclaration() + val sourceElements = tempElement.resolveToSourceDeclaration() if (sourceElements.isEmpty()) return@flatMap emptyList() val additionalElements = getContainingClassOrObjectForConstructor(sourceElements) @@ -292,6 +288,15 @@ class KotlinQueryParticipant : IQueryParticipant { } } + private fun findApplicableElement( + element: KtElement, beforeResolveFilters: List + ): KtElement? { + if(beforeResolveFilters.all { it.isApplicable(element) }) return element + return element.getParentOfType(false)?.takeIf { refExp -> + beforeResolveFilters.all { it.isApplicable(refExp) } + } + } + private fun obtainElements(searchResult: FileSearchResult, files: List): List { val elements = ArrayList() for (file in files) { 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..276c8c945 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 @@ -22,6 +22,7 @@ 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.KtPropertyDelegate import org.jetbrains.kotlin.psi.KtReferenceExpression import org.jetbrains.kotlin.psi.KtSimpleNameExpression import org.jetbrains.kotlin.psi.psiUtil.isImportDirectiveExpression @@ -49,9 +50,9 @@ interface SearchFilterAfterResolve { fun getBeforeResolveFilters(querySpecification: QuerySpecification): List { val filters = arrayListOf() - if (querySpecification.getLimitTo() == IJavaSearchConstants.REFERENCES) { + if (querySpecification.limitTo == IJavaSearchConstants.REFERENCES) { filters.add(NonImportFilter()) - filters.add(ReferenceFilter()) + filters.add(ElementWithPossibleReferencesFilter()) } return filters @@ -59,8 +60,8 @@ fun getBeforeResolveFilters(querySpecification: QuerySpecification): List = listOf(ResolvedReferenceFilter()) -class ReferenceFilter : SearchFilter { - override fun isApplicable(jetElement: KtElement): Boolean = jetElement is KtReferenceExpression +class ElementWithPossibleReferencesFilter : SearchFilter { + override fun isApplicable(jetElement: KtElement): Boolean = jetElement is KtReferenceExpression || (jetElement is KtPropertyDelegate) } class NonImportFilter : SearchFilter { @@ -97,4 +98,4 @@ class ResolvedReferenceFilter : SearchFilterAfterResolve { } private fun IJavaElement.isConstructorCall() = this is IMethod && this.isConstructor() -} \ No newline at end of file +} From 1640594c8f119942dcab70f1153ade6d5a18bfc6 Mon Sep 17 00:00:00 2001 From: U534967 Date: Thu, 24 Mar 2022 10:43:55 +0100 Subject: [PATCH 16/31] adjust presentation of items, enable search for and reference resolution for provideDelegate methods. --- .../kotlin/core/references/KotlinReference.kt | 3 +- .../kotlin/core/references/referenceUtils.kt | 6 +- .../ui/editors/KotlinElementHyperlink.kt | 23 +++++-- .../editors/KotlinElementHyperlinkDetector.kt | 52 ++++++++++----- .../codeassist/KotlinCompletionProcessor.kt | 10 ++- .../codeassist/KotlinCompletionProposal.kt | 4 +- .../KotlinReferenceVariantsHelper.kt | 24 +++---- .../KotlinSemanticHighlightingVisitor.kt | 4 +- .../ui/search/KotlinQueryParticipant.kt | 66 +++++++++++-------- 9 files changed, 116 insertions(+), 76 deletions(-) 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 2f834faf5..ae535fd40 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 @@ -91,9 +91,10 @@ class KotlinKtPropertyDelegateReference(override val expression: KtPropertyDeleg val tempProperty = expression.getParentOfType(false) ?: return emptyList() val tempDescriptor = tempProperty.resolveToDescriptorIfAny() as? VariableDescriptorWithAccessors ?: return emptyList() + val tempDelegateProvider = context[BindingContext.PROVIDE_DELEGATE_RESOLVED_CALL, tempDescriptor]?.candidateDescriptor return tempDescriptor.accessors.mapNotNull { context[BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, it]?.candidateDescriptor - } + } + (tempDelegateProvider?.let { arrayOf(it) } ?: emptyArray()) } override val resolvesByNames: Collection 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 3e19cc2cd..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 @@ -24,6 +24,7 @@ import org.eclipse.jdt.core.JavaCore import org.jetbrains.kotlin.core.builder.KotlinPsiManager 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 @@ -49,10 +50,7 @@ fun List>.resolveToSourceElements(ktFile: KtFile): List>.resolveToSourceElements( 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 93ba704fe..ae8d48026 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 @@ -19,8 +19,11 @@ package org.jetbrains.kotlin.ui.editors import org.eclipse.jface.text.IRegion import org.eclipse.jface.text.hyperlink.IHyperlink 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.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 @@ -58,19 +61,25 @@ fun KtPropertyDelegate.doOpenDelegateFun(editor: KotlinEditor, openSetter: Boole gotoElement(tempTargetDescriptor, property, editor, javaProject) } -class KotlinKTDelegateHyperLink( +class KTGenericHyperLink( private val region: IRegion, - private val property: KtPropertyDelegate, - private val isSetter: Boolean, - private val editor: KotlinEditor -) : 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 (${if (isSetter) "set" else "get"})" + override fun getHyperlinkText(): String = label + + override fun open() { + val tempPrj = editor.javaProject ?: return + gotoElement(targetDescriptor, fromElement, editor, tempPrj) + } - override fun open() = property.doOpenDelegateFun(editor, isSetter) } 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 bff5e530a..b7618598b 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 @@ -16,6 +16,7 @@ */ package org.jetbrains.kotlin.ui.editors +import org.eclipse.jdt.core.IJavaProject import org.eclipse.jdt.internal.ui.text.JavaWordFinder import org.eclipse.jface.text.IRegion import org.eclipse.jface.text.ITextViewer @@ -23,9 +24,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.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors import org.jetbrains.kotlin.eclipse.ui.utils.EditorUtil import org.jetbrains.kotlin.eclipse.ui.utils.LineEndUtil +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.renderer.DescriptorRenderer +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.KotlinOpenDeclarationAction.Companion.OPEN_EDITOR_TEXT @@ -40,6 +48,8 @@ class KotlinElementHyperlinkDetector : AbstractHyperlinkDetector() { val textEditor = getAdapter(ITextEditor::class.java) if (region == null || textEditor !is KotlinEditor) return null + val tempProject = textEditor.javaProject ?: return null + val openAction = textEditor.getAction(OPEN_EDITOR_TEXT) as? KotlinOpenDeclarationAction ?: return null @@ -59,30 +69,36 @@ class KotlinElementHyperlinkDetector : AbstractHyperlinkDetector() { 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 - ) { + if (KotlinOpenDeclarationAction.getNavigationData(tempReferenceExpression, tempProject) != null) { val tempOffset = LineEndUtil.convertLfOffsetForMixedDocument(tempDocument, tempReferenceExpression.textOffset) wordRegion = Region(tempOffset, tempReferenceExpression.textLength) } } } - val tempRef = tempReferenceExpression ?: EditorUtil.getReferenceExpression(textEditor, region.offset) - if (tempRef != null) { - return arrayOf(KotlinElementHyperlink(openAction, wordRegion)) - } else { - val tempElement = EditorUtil.getJetElement(textEditor, region.offset) - if (tempElement is KtPropertyDelegate) { - val isVar = tempElement.getParentOfType(false)?.isVar ?: return null - val tempGetLink = KotlinKTDelegateHyperLink(wordRegion, tempElement, false, textEditor) - val tempSetLink = - if (isVar) KotlinKTDelegateHyperLink(wordRegion, tempElement, true, textEditor) else null - return listOfNotNull(tempGetLink, tempSetLink).toTypedArray() - } + + val tempRenderer = DescriptorRenderer.SHORT_NAMES_IN_TYPES.withOptions { + modifiers = emptySet() + includeAdditionalModifiers = false } - return null + + 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() } } 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 93e9bdcf5..ec8d3e4d1 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 @@ -214,11 +214,15 @@ abstract class KotlinCompletionProcessor( val descriptor = basicDescriptor.descriptor val completion = descriptor.name.identifier val image = KotlinImageProvider.getImage(descriptor) - val presentableString = DescriptorRenderer.ONLY_NAMES_WITH_SHORT_TYPES.render(descriptor) - val containmentPresentableString = descriptor.containingDeclaration?.let { - DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.render(it) + + 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, 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 25a7f0982..8319720fb 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 @@ -173,8 +173,8 @@ class KotlinImportCallableCompletionProposal(val descriptor: CallableDescriptor, KotlinCompletionProposal( "${descriptor.name.identifier}${if(descriptor is PropertyDescriptor) "" else "()"}", image, - DescriptorRenderer.ONLY_NAMES_WITH_SHORT_TYPES.render(descriptor), - DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.render(descriptor.containingDeclaration), + DescriptorRenderer.SHORT_NAMES_IN_TYPES.render(descriptor), + null, identifierPart = identifierPart) { private var importShift = -1 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 9a9902609..d6bd706db 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 @@ -30,9 +30,11 @@ import org.jetbrains.kotlin.resolve.BindingContext 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.fqNameOrNull 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.CLASSIFIERS 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 @@ -416,7 +418,7 @@ class KotlinReferenceVariantsHelper( val collector = object : MethodNameMatchRequestor() { override fun acceptMethodNameMatch(match: MethodNameMatch) { if (Flags.isPublic(match.modifiers)) { - tempClassNames[match.method.declaringType.typeQualifiedName] = + tempClassNames[match.method.declaringType.getTypeQualifiedName('$')] = match.method.declaringType.packageFragment.elementName } } @@ -461,21 +463,21 @@ class KotlinReferenceVariantsHelper( if (className.contains('$')) { val tempNames = className.split('$') val topLevelClass = tempNames.first() - var tempClassDescriptor = tempPackage.memberScope.getDescriptorsFiltered { + var tempClassDescriptor = tempPackage.memberScope.getDescriptorsFiltered(CLASSIFIERS) { !it.isSpecial && it.identifier == topLevelClass - }.filterIsInstance().singleOrNull() ?: return@mapNotNull null + }.filterIsInstance().distinctBy { it.fqNameOrNull()?.asString() ?: it.toString() }.singleOrNull() ?: return@mapNotNull null tempNames.drop(1).forEach { subName -> - tempClassDescriptor = tempClassDescriptor.unsubstitutedMemberScope.getDescriptorsFiltered { + tempClassDescriptor = tempClassDescriptor.unsubstitutedMemberScope.getDescriptorsFiltered(CLASSIFIERS) { !it.isSpecial && it.identifier == subName - }.filterIsInstance().singleOrNull() ?: return@mapNotNull null + }.filterIsInstance().distinctBy { it.fqNameOrNull()?.asString() ?: it.toString() }.singleOrNull() ?: return@mapNotNull null } tempClassDescriptor } else { - tempPackage.memberScope.getDescriptorsFiltered { + tempPackage.memberScope.getDescriptorsFiltered(CLASSIFIERS) { !it.isSpecial && it.identifier == className - }.filterIsInstance().singleOrNull() + }.filterIsInstance().distinctBy { it.fqNameOrNull()?.asString() ?: it.toString() }.singleOrNull() } } @@ -485,7 +487,7 @@ class KotlinReferenceVariantsHelper( val originPackage = ktFile.packageFqName.asString() - fun MemberScope.filterDescriptors() = getDescriptorsFiltered( + fun MemberScope.filterByKindAndName() = getDescriptorsFiltered( kindFilter.intersect(CALLABLES), nameFilter ).asSequence() @@ -502,8 +504,8 @@ class KotlinReferenceVariantsHelper( }.toList() val tempDeferreds = - tempPackages.map { desc -> KotlinEclipseScope.async { desc.memberScope.filterDescriptors() } } + - tempClasses.map { desc -> KotlinEclipseScope.async { desc.unsubstitutedMemberScope.filterDescriptors() } } + tempPackages.map { desc -> KotlinEclipseScope.async { desc.memberScope.filterByKindAndName() } } + + tempClasses.map { desc -> KotlinEclipseScope.async { desc.unsubstitutedMemberScope.filterByKindAndName() } } val tempDescriptors = tempDeferreds.awaitAll().flatten() @@ -566,7 +568,7 @@ class KotlinReferenceVariantsHelper( 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 ) 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/search/KotlinQueryParticipant.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/search/KotlinQueryParticipant.kt index 2f66ce709..9fac28f88 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 @@ -46,14 +46,17 @@ import org.jetbrains.kotlin.core.log.KotlinLogger import org.jetbrains.kotlin.core.model.sourceElementsToLightElements import org.jetbrains.kotlin.core.references.resolveToSourceDeclaration import org.jetbrains.kotlin.core.utils.getBindingContext +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.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.psi.psiUtil.getParentOfType +import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.resolve.calls.callUtil.getCall import org.jetbrains.kotlin.resolve.source.KotlinSourceElement import org.jetbrains.kotlin.types.expressions.OperatorConventions @@ -111,24 +114,15 @@ class KotlinQueryParticipant : IQueryParticipant { 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 tempParentDescriptor = PsiTreeUtil.getParentOfType(ktElement, KtDeclaration::class.java)?.resolveToDescriptorIfAny() val tempLabel = buildString { - append(tempClassObjectOrFileName) - if (tempFunction != null) { - append("#") - append(tempFunction.name) - } - if (isNotEmpty()) { - append(": ") - } append(tempElement) + if(tempParentDescriptor != null) { + append(" in ") + append(DescriptorRenderer.SHORT_NAMES_IN_TYPES.render(tempParentDescriptor)) + } } requestor.reportMatch(KotlinElementMatch(ktElement, tempLabel)) @@ -222,20 +216,17 @@ class KotlinQueryParticipant : IQueryParticipant { 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" - "getValue", "setValue" -> "by" - 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 } } } @@ -260,6 +251,25 @@ class KotlinQueryParticipant : IQueryParticipant { 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, From 1cb5c05b44fe32d9aa7b5a4e06653942c05a4384 Mon Sep 17 00:00:00 2001 From: U534967 Date: Thu, 24 Mar 2022 16:06:05 +0100 Subject: [PATCH 17/31] extract context assist to separate processor as currently it gets shown 4 times and requires 4 times the time to calculate. add assist for automatically insert lambda parameters on function insertion. --- .../ui/editors/FileEditorConfiguration.kt | 7 +- .../codeassist/KotlinCompletionProcessor.kt | 21 +- .../codeassist/KotlinCompletionProposal.kt | 180 +++++++++++------- ...KotlinContextInfoContentAssistProcessor.kt | 32 ++++ .../KotlinFunctionCompletionProposal.kt | 118 +++++------- .../KotlinFunctionParameterInfoAssist.kt | 13 +- .../KotlinParameterListValidator.kt | 66 +++---- .../KotlinReferenceVariantsHelper.kt | 47 ++--- 8 files changed, 269 insertions(+), 215 deletions(-) create mode 100644 kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/codeassist/KotlinContextInfoContentAssistProcessor.kt 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..8b752556e 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,10 +50,12 @@ 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().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/codeassist/KotlinCompletionProcessor.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/codeassist/KotlinCompletionProcessor.kt index ec8d3e4d1..49c862c43 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 @@ -29,7 +29,6 @@ import org.eclipse.jface.text.Region 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 @@ -37,14 +36,13 @@ 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 -sealed class KotlinBasicCompletionProposal() { +sealed class KotlinBasicCompletionProposal { abstract val descriptor: DeclarationDescriptor @@ -62,7 +60,6 @@ abstract class KotlinCompletionProcessor( companion object { private val VALID_PROPOSALS_CHARS = charArrayOf() - private val VALID_INFO_CHARS = charArrayOf('(', ',') fun createKotlinCompletionProcessors( editor: KotlinEditor, assistant: ContentAssistant? = null, @@ -126,10 +123,6 @@ abstract class KotlinCompletionProcessor( ) } - private val kotlinParameterValidator by lazy { - KotlinParameterListValidator(editor) - } - override fun computeCompletionProposals(viewer: ITextViewer, offset: Int): Array { if (assistant != null) { configureContentAssistant(assistant) @@ -207,7 +200,7 @@ abstract class KotlinCompletionProcessor( protected fun collectCompletionProposals( descriptors: Collection, part: String - ): List { + ): List { return descriptors.map { basicDescriptor -> when (basicDescriptor) { is KotlinBasicCompletionProposal.Descriptor -> { @@ -233,7 +226,7 @@ abstract class KotlinCompletionProcessor( part ) - withKotlinInsertHandler(descriptor, proposal, part) + withKotlinInsertHandler(descriptor, proposal) } is KotlinBasicCompletionProposal.Proposal -> basicDescriptor.proposal } @@ -303,17 +296,15 @@ abstract class KotlinCompletionProcessor( }.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?) { } 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 8319720fb..2021dc91c 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 @@ -33,39 +33,42 @@ import org.jetbrains.kotlin.builtins.isExtensionFunctionType import org.jetbrains.kotlin.builtins.isFunctionType import org.jetbrains.kotlin.core.imports.FunctionCandidate import org.jetbrains.kotlin.core.imports.TypeCandidate -import org.jetbrains.kotlin.descriptors.CallableDescriptor -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.descriptors.PropertyDescriptor +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.resolve.descriptorUtil.fqNameOrNull +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 -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) } } @@ -73,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 @@ -85,36 +112,35 @@ fun getIdentifierInfo(document: IDocument, offset: Int): IdentifierInfo { data class IdentifierInfo(val identifierPart: String, val identifierStart: Int) open class KotlinCompletionProposal constructor( - 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 - + 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 +) : ICompletionProposal, ICompletionProposalExtension2, ICompletionProposalExtension6 { + + private var selectedOffset = -1 + open 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 @@ -124,58 +150,70 @@ open class KotlinCompletionProposal constructor( 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 KotlinImportTypeCompletionProposal(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) : +class KotlinImportCallableCompletionProposal( + val descriptor: CallableDescriptor, + image: Image?, + val file: IFile, + identifierPart: String +) : KotlinCompletionProposal( - "${descriptor.name.identifier}${if(descriptor is PropertyDescriptor) "" else "()"}", + "${descriptor.name.identifier}${if (descriptor is PropertyDescriptor) "" else "()"}", image, DescriptorRenderer.SHORT_NAMES_IN_TYPES.render(descriptor), null, - identifierPart = identifierPart) { + identifierPart = identifierPart + ) { private var importShift = -1 @@ -193,10 +231,10 @@ class KotlinImportCallableCompletionProposal(val descriptor: CallableDescriptor, } 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) 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 d46f9b5a2..b0e2970b2 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,104 @@ /******************************************************************************* -* 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, - proposal.containmentPresentableString, - 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 { + 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 } } 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 34c55e1ab..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 @@ -72,18 +72,19 @@ 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 argumentList = PsiTreeUtil.getParentOfType(psiElement, KtValueArgumentList::class.java) ?: return null + val argumentListParent = argumentList.parent return if (argumentListParent is KtCallElement) argumentListParent.getCallNameExpression() else null } class KotlinFunctionParameterContextInformation(descriptor: FunctionDescriptor) : IContextInformation { - val displayString = DescriptorRenderer.ONLY_NAMES_WITH_SHORT_TYPES.render(descriptor) + private val displayString = DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.withOptions { + this.withDefinedIn = false + }.render(descriptor) val renderedParameters = descriptor.valueParameters.map { renderParameter(it) } - val informationString = renderedParameters.joinToString(", ") - val displayImage = KotlinImageProvider.getImage(descriptor) + 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 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/KotlinReferenceVariantsHelper.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/completion/KotlinReferenceVariantsHelper.kt index d6bd706db..664984b80 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 @@ -48,7 +48,7 @@ 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 kotlin.time.ExperimentalTime +import java.util.* class KotlinReferenceVariantsHelper( val bindingContext: BindingContext, @@ -172,8 +172,8 @@ 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) } } } @@ -334,11 +334,11 @@ class KotlinReferenceVariantsHelper( if (callType == CallType.SUPER_MEMBERS) { // we need to unwrap fake overrides in case of "super." because ShadowedDeclarationsFilter does not work correctly return descriptors.filterIsInstance() - .flatMapTo(LinkedHashSet()) { - if (it.descriptor is CallableMemberDescriptor && it.descriptor.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) { - it.descriptor.overriddenDescriptors.map { KotlinBasicCompletionProposal.Descriptor(it) } + .flatMapTo(LinkedHashSet()) { descriptor -> + if (descriptor.descriptor is CallableMemberDescriptor && descriptor.descriptor.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) { + descriptor.descriptor.overriddenDescriptors.map { KotlinBasicCompletionProposal.Descriptor(it) } } else { - listOf(it) + listOf(descriptor) } } } @@ -361,15 +361,13 @@ class KotlinReferenceVariantsHelper( ) { runBlocking { val tempJobs = mutableListOf() - var tempJob = KotlinEclipseScope.launch { + tempJobs += KotlinEclipseScope.launch { addNonExtensionMembers(receiverTypes, kindFilter, nameFilter, constructorFilter = { it.isInner }) } - tempJobs += tempJob - tempJob = KotlinEclipseScope.launch { + tempJobs += KotlinEclipseScope.launch { addMemberExtensions(implicitReceiverTypes, receiverTypes, callType, kindFilter, nameFilter) } - tempJobs += tempJob - tempJob = KotlinEclipseScope.launch { + tempJobs += KotlinEclipseScope.launch { addNotImportedTopLevelCallables( receiverTypes, kindFilter, @@ -382,16 +380,13 @@ class KotlinReferenceVariantsHelper( ) println("Finished!") } - tempJobs += tempJob - tempJob = KotlinEclipseScope.launch { + tempJobs += KotlinEclipseScope.launch { addScopeAndSyntheticExtensions(resolutionScope, receiverTypes, callType, kindFilter, nameFilter) } - tempJobs += tempJob tempJobs.joinAll() } } - @OptIn(ExperimentalTime::class) private suspend fun MutableSet.addNotImportedTopLevelCallables( receiverTypes: Collection, kindFilter: DescriptorKindFilter, @@ -439,6 +434,9 @@ class KotlinReferenceVariantsHelper( null ) + val tempCapitalIdentifier = + identifierPart.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() } + searchEngine.searchAllMethodNames( null, SearchPattern.R_EXACT_MATCH, @@ -446,7 +444,7 @@ class KotlinReferenceVariantsHelper( SearchPattern.R_EXACT_MATCH, null, SearchPattern.R_EXACT_MATCH, - "get${identifierPart.capitalize()}".toCharArray(), + "get$tempCapitalIdentifier".toCharArray(), SearchPattern.R_PREFIX_MATCH, javaProjectSearchScope, collector, @@ -465,19 +463,24 @@ class KotlinReferenceVariantsHelper( val topLevelClass = tempNames.first() var tempClassDescriptor = tempPackage.memberScope.getDescriptorsFiltered(CLASSIFIERS) { !it.isSpecial && it.identifier == topLevelClass - }.filterIsInstance().distinctBy { it.fqNameOrNull()?.asString() ?: it.toString() }.singleOrNull() ?: return@mapNotNull null + }.filterIsInstance().distinctBy { it.fqNameOrNull()?.asString() ?: it.toString() } + .singleOrNull() ?: return@mapNotNull null tempNames.drop(1).forEach { subName -> - tempClassDescriptor = tempClassDescriptor.unsubstitutedMemberScope.getDescriptorsFiltered(CLASSIFIERS) { - !it.isSpecial && it.identifier == subName - }.filterIsInstance().distinctBy { it.fqNameOrNull()?.asString() ?: it.toString() }.singleOrNull() ?: return@mapNotNull null + tempClassDescriptor = + tempClassDescriptor.unsubstitutedMemberScope.getDescriptorsFiltered(CLASSIFIERS) { + !it.isSpecial && it.identifier == subName + }.filterIsInstance() + .distinctBy { it.fqNameOrNull()?.asString() ?: it.toString() }.singleOrNull() + ?: return@mapNotNull null } tempClassDescriptor } else { tempPackage.memberScope.getDescriptorsFiltered(CLASSIFIERS) { !it.isSpecial && it.identifier == className - }.filterIsInstance().distinctBy { it.fqNameOrNull()?.asString() ?: it.toString() }.singleOrNull() + }.filterIsInstance().distinctBy { it.fqNameOrNull()?.asString() ?: it.toString() } + .singleOrNull() } } From 593dbad94952a13e146c5027f005b55cbec1c456 Mon Sep 17 00:00:00 2001 From: U534967 Date: Thu, 24 Mar 2022 16:27:41 +0100 Subject: [PATCH 18/31] remove unused code --- .../ui/editors/KotlinElementHyperlinkDetector.kt | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) 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 b7618598b..41458085d 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 @@ -16,7 +16,6 @@ */ package org.jetbrains.kotlin.ui.editors -import org.eclipse.jdt.core.IJavaProject import org.eclipse.jdt.internal.ui.text.JavaWordFinder import org.eclipse.jface.text.IRegion import org.eclipse.jface.text.ITextViewer @@ -26,17 +25,14 @@ 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.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors import org.jetbrains.kotlin.eclipse.ui.utils.EditorUtil import org.jetbrains.kotlin.eclipse.ui.utils.LineEndUtil -import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny -import org.jetbrains.kotlin.psi.* +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.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.KotlinOpenDeclarationAction.Companion.OPEN_EDITOR_TEXT @Suppress("unused") class KotlinElementHyperlinkDetector : AbstractHyperlinkDetector() { @@ -50,9 +46,6 @@ class KotlinElementHyperlinkDetector : AbstractHyperlinkDetector() { val tempProject = textEditor.javaProject ?: return null - val openAction = textEditor.getAction(OPEN_EDITOR_TEXT) as? KotlinOpenDeclarationAction - ?: return null - val tempDocument = textEditor.documentProvider.getDocument(textEditor.editorInput) var wordRegion = JavaWordFinder.findWord(tempDocument, region.offset) From 7a7db1835ca68925fe0c7ccd816dc1b42b62791b Mon Sep 17 00:00:00 2001 From: U534967 Date: Thu, 24 Mar 2022 17:02:21 +0100 Subject: [PATCH 19/31] its just called fromone point where we can only open one possibility anyways. So open in the following order: provideDelegate, getValue, setValue --- .../ui/editors/KotlinElementHyperlink.kt | 31 ------------------- .../navigation/KotlinOpenDeclarationAction.kt | 26 +++++++++++++--- 2 files changed, 21 insertions(+), 36 deletions(-) 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 ae8d48026..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 @@ -20,7 +20,6 @@ import org.eclipse.jface.text.IRegion import org.eclipse.jface.text.hyperlink.IHyperlink 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.idea.caches.resolve.resolveToDescriptorIfAny import org.jetbrains.kotlin.psi.KtElement @@ -31,36 +30,6 @@ 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, - private val region: IRegion -) : IHyperlink { - override fun getHyperlinkRegion(): IRegion = region - - override fun getTypeLabel(): String? = null - - override fun getHyperlinkText(): String = HYPERLINK_TEXT - - override fun open() = openAction.run() -} - -fun KtPropertyDelegate.doOpenDelegateFun(editor: KotlinEditor, openSetter: Boolean) { - val property = getParentOfType(false) ?: return - val javaProject = editor.javaProject ?: return - - val context = property.getBindingContext() - val tempDescriptor = property.resolveToDescriptorIfAny() as? VariableDescriptorWithAccessors ?: return - val tempAccessor = if (openSetter) { - tempDescriptor.setter - } else { - tempDescriptor.getter - } - val tempTargetDescriptor = - context[BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, tempAccessor]?.candidateDescriptor ?: return - - gotoElement(tempTargetDescriptor, property, editor, javaProject) -} - class KTGenericHyperLink( private val region: IRegion, private val label: String, 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 5acf040e2..3811171f4 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 @@ -25,11 +25,15 @@ import org.jetbrains.kotlin.core.references.createReferences 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.eclipse.ui.utils.EditorUtil +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny +import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.psi.KtPropertyDelegate import org.jetbrains.kotlin.psi.KtReferenceExpression +import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.ui.editors.KotlinEditor -import org.jetbrains.kotlin.ui.editors.doOpenDelegateFun +import org.jetbrains.kotlin.ui.editors.codeassist.getParentOfType class KotlinOpenDeclarationAction(val editor: KotlinEditor) : SelectionDispatchAction(editor.javaEditor.site) { companion object { @@ -59,7 +63,7 @@ class KotlinOpenDeclarationAction(val editor: KotlinEditor) : SelectionDispatchA val selectedExpression = EditorUtil.getReferenceExpression(editor, selection.offset) ?: kotlin.run { val tempElement = EditorUtil.getJetElement(editor, selection.offset) if (tempElement is KtPropertyDelegate) { - tempElement.doOpenDelegateFun(editor, false) + tempElement.doOpenDelegateFun() } return } @@ -70,10 +74,22 @@ class KotlinOpenDeclarationAction(val editor: KotlinEditor) : SelectionDispatchA gotoElement(data.sourceElement, data.descriptor, selectedExpression, editor, javaProject) } - internal fun run(refElement: KtReferenceExpression) { + private fun KtPropertyDelegate.doOpenDelegateFun() { + val property = getParentOfType(false) ?: return val javaProject = editor.javaProject ?: return - val data = getNavigationData(refElement, javaProject) ?: return - gotoElement(data.sourceElement, data.descriptor, refElement, editor, javaProject) + val context = property.getBindingContext() + val tempDescriptor = property.resolveToDescriptorIfAny() as? VariableDescriptorWithAccessors ?: return + + var tempTargetDescriptor = + context[BindingContext.PROVIDE_DELEGATE_RESOLVED_CALL, tempDescriptor]?.candidateDescriptor + + if (tempTargetDescriptor == null) { + val tempAccessor = tempDescriptor.getter ?: tempDescriptor.setter + tempTargetDescriptor = + context[BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, tempAccessor]?.candidateDescriptor ?: return + } + + gotoElement(tempTargetDescriptor, property, editor, javaProject) } } From dbe381bf5a96d19e3f6cb08cc7fa02bd65a88899 Mon Sep 17 00:00:00 2001 From: U534967 Date: Thu, 24 Mar 2022 17:31:03 +0100 Subject: [PATCH 20/31] we don't need that function at all, we just need to order the returned references. --- .../kotlin/core/references/KotlinReference.kt | 9 ++-- .../navigation/KotlinOpenDeclarationAction.kt | 42 +++---------------- 2 files changed, 11 insertions(+), 40 deletions(-) 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 ae535fd40..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 @@ -91,10 +91,11 @@ class KotlinKtPropertyDelegateReference(override val expression: KtPropertyDeleg val tempProperty = expression.getParentOfType(false) ?: return emptyList() val tempDescriptor = tempProperty.resolveToDescriptorIfAny() as? VariableDescriptorWithAccessors ?: return emptyList() - val tempDelegateProvider = context[BindingContext.PROVIDE_DELEGATE_RESOLVED_CALL, tempDescriptor]?.candidateDescriptor - return tempDescriptor.accessors.mapNotNull { - context[BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, it]?.candidateDescriptor - } + (tempDelegateProvider?.let { arrayOf(it) } ?: emptyArray()) + 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 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 3811171f4..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 @@ -25,23 +25,17 @@ import org.jetbrains.kotlin.core.references.createReferences 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.eclipse.ui.utils.EditorUtil -import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny -import org.jetbrains.kotlin.psi.KtProperty -import org.jetbrains.kotlin.psi.KtPropertyDelegate -import org.jetbrains.kotlin.psi.KtReferenceExpression -import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.ui.editors.KotlinEditor -import org.jetbrains.kotlin.ui.editors.codeassist.getParentOfType 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 -> @@ -60,36 +54,12 @@ class KotlinOpenDeclarationAction(val editor: KotlinEditor) : SelectionDispatchA } override fun run(selection: ITextSelection) { - val selectedExpression = EditorUtil.getReferenceExpression(editor, selection.offset) ?: kotlin.run { - val tempElement = EditorUtil.getJetElement(editor, selection.offset) - if (tempElement is KtPropertyDelegate) { - tempElement.doOpenDelegateFun() - } - 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) } - - private fun KtPropertyDelegate.doOpenDelegateFun() { - val property = getParentOfType(false) ?: return - val javaProject = editor.javaProject ?: return - - val context = property.getBindingContext() - val tempDescriptor = property.resolveToDescriptorIfAny() as? VariableDescriptorWithAccessors ?: return - - var tempTargetDescriptor = - context[BindingContext.PROVIDE_DELEGATE_RESOLVED_CALL, tempDescriptor]?.candidateDescriptor - - if (tempTargetDescriptor == null) { - val tempAccessor = tempDescriptor.getter ?: tempDescriptor.setter - tempTargetDescriptor = - context[BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, tempAccessor]?.candidateDescriptor ?: return - } - - gotoElement(tempTargetDescriptor, property, editor, javaProject) - } } From 0a426795c88e2be886b16f14f534b8f8bb7475a5 Mon Sep 17 00:00:00 2001 From: U534967 Date: Fri, 25 Mar 2022 13:06:04 +0100 Subject: [PATCH 21/31] Add open super implementation to context popup menu. Some refactorings --- .../kotlin/core/model/KotlinJavaManager.kt | 12 +- kotlin-eclipse-ui/plugin.xml | 23 ++- .../KotlinFindReferencesAction.kt | 97 +++++----- .../editors/KotlinElementHyperlinkDetector.kt | 4 +- .../KotlinOpenSuperImplementationAction.kt | 169 +++++++++++------- .../ui/search/KotlinQueryParticipant.kt | 24 +-- .../kotlin/ui/search/SearchResultRenderer.kt | 40 +++++ .../kotlin/ui/search/searchFilters.kt | 55 +++--- 8 files changed, 243 insertions(+), 181 deletions(-) create mode 100644 kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/search/SearchResultRenderer.kt 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 06f1773ae..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 @@ -52,12 +52,10 @@ object KotlinJavaManager { 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.declaringType, declaration, klass) @@ -68,7 +66,7 @@ object KotlinJavaManager { } fun hasLinkedKotlinBinFolder(project: IProject): Boolean { - val folder = project.getFolder(KotlinJavaManager.KOTLIN_BIN_FOLDER) + val folder = project.getFolder(KOTLIN_BIN_FOLDER) return folder.isLinked && KotlinFileSystem.SCHEME == folder.locationURI.scheme } diff --git a/kotlin-eclipse-ui/plugin.xml b/kotlin-eclipse-ui/plugin.xml index d2dffc685..f0a57e80d 100644 --- a/kotlin-eclipse-ui/plugin.xml +++ b/kotlin-eclipse-ui/plugin.xml @@ -311,6 +311,12 @@ id="org.jetbrains.kotlin.ui.commands.findReferences.findReferencesInWorkspace" name="Find References In Workspace"> + + @@ -370,14 +376,24 @@ label="References"> + + + + @@ -600,8 +616,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/ui/commands/findReferences/KotlinFindReferencesAction.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/commands/findReferences/KotlinFindReferencesAction.kt index c4877eac3..057d9326f 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,79 @@ class KotlinFindReferencesInProjectHandler : KotlinFindReferencesHandler() { } } + class KotlinFindReferencesInWorkspaceHandler : KotlinFindReferencesHandler() { override fun getAction(editor: KotlinCommonEditor): KotlinFindReferencesAction { return KotlinFindReferencesInWorkspaceAction(editor) } } -public class KotlinFindReferencesInProjectAction(editor: KotlinCommonEditor) : KotlinFindReferencesAction(editor) { +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/editors/KotlinElementHyperlinkDetector.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/KotlinElementHyperlinkDetector.kt index 41458085d..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 @@ -40,7 +40,7 @@ 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 @@ -92,6 +92,6 @@ class KotlinElementHyperlinkDetector : AbstractHyperlinkDetector() { it, tempRef ) - }.toTypedArray() + }.toTypedArray().takeIf { it.isNotEmpty() } } } 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/search/KotlinQueryParticipant.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/search/KotlinQueryParticipant.kt index 9fac28f88..e768c7988 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 @@ -45,7 +45,6 @@ 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.references.resolveToSourceDeclaration -import org.jetbrains.kotlin.core.utils.getBindingContext import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.SourceElement import org.jetbrains.kotlin.eclipse.ui.utils.EditorUtil @@ -56,8 +55,6 @@ 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.renderer.DescriptorRenderer -import org.jetbrains.kotlin.resolve.calls.callUtil.getCall import org.jetbrains.kotlin.resolve.source.KotlinSourceElement import org.jetbrains.kotlin.types.expressions.OperatorConventions import org.jetbrains.kotlin.ui.commands.findReferences.KotlinAndJavaSearchable @@ -110,22 +107,12 @@ class KotlinQueryParticipant : IQueryParticipant { } if (monitor?.isCanceled == true) return - val matchedReferences = resolveElementsAndMatch(elements, searchElement, querySpecification, monitor) + val matchedReferences = resolveElementsAndMatch(elements, searchElement, querySpecification) if (monitor?.isCanceled == true) return matchedReferences.forEach { ktElement -> - val tempElement = ktElement.getCall(ktElement.getBindingContext())?.toString() ?: ktElement.text + val tempRenderer = SearchResultRenderer.getResultRenderer(querySpecification) - val tempParentDescriptor = PsiTreeUtil.getParentOfType(ktElement, KtDeclaration::class.java)?.resolveToDescriptorIfAny() - - val tempLabel = buildString { - append(tempElement) - if(tempParentDescriptor != null) { - append(" in ") - append(DescriptorRenderer.SHORT_NAMES_IN_TYPES.render(tempParentDescriptor)) - } - } - - requestor.reportMatch(KotlinElementMatch(ktElement, tempLabel)) + requestor.reportMatch(KotlinElementMatch(ktElement, tempRenderer.render(ktElement))) } } @@ -272,11 +259,10 @@ class KotlinQueryParticipant : IQueryParticipant { private fun resolveElementsAndMatch( elements: List, searchElement: SearchElement, - querySpecification: QuerySpecification, - monitor: IProgressMonitor? + querySpecification: QuerySpecification ): List { val beforeResolveFilters = getBeforeResolveFilters(querySpecification) - val afterResolveFilters = getAfterResolveFilters() + val afterResolveFilters = getAfterResolveFilters(querySpecification) // This is important for optimization: // we will consequentially cache files one by one which do contain these references diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/search/SearchResultRenderer.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/search/SearchResultRenderer.kt new file mode 100644 index 000000000..0b2138932 --- /dev/null +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/search/SearchResultRenderer.kt @@ -0,0 +1,40 @@ +package org.jetbrains.kotlin.ui.search + +import com.intellij.psi.util.PsiTreeUtil +import org.eclipse.jdt.ui.search.QuerySpecification +import org.jetbrains.kotlin.core.utils.getBindingContext +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.renderer.DescriptorRenderer +import org.jetbrains.kotlin.resolve.calls.callUtil.getCall + +interface SearchResultRenderer { + + fun render(element: KtElement): String + + companion object { + fun getResultRenderer(querySpecification: QuerySpecification) = when (querySpecification.limitTo) { + else -> BasicSearchResultRenderer + } + } +} + +object BasicSearchResultRenderer : SearchResultRenderer { + + override 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) + if (tempParentDescriptor != null) { + append(" in ") + append(DescriptorRenderer.SHORT_NAMES_IN_TYPES.render(tempParentDescriptor)) + } + } + } + +} 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 276c8c945..6e495f0ec 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 @@ -21,10 +21,7 @@ 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.KtPropertyDelegate -import org.jetbrains.kotlin.psi.KtReferenceExpression -import org.jetbrains.kotlin.psi.KtSimpleNameExpression +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.isImportDirectiveExpression import org.jetbrains.kotlin.ui.search.KotlinQueryParticipant.SearchElement import org.jetbrains.kotlin.ui.search.KotlinQueryParticipant.SearchElement.JavaSearchElement @@ -36,9 +33,9 @@ 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 { val (javaElements, kotlinElements) = getJavaAndKotlinElements(sourceElements) return when (originElement) { @@ -48,54 +45,52 @@ interface SearchFilterAfterResolve { } } -fun getBeforeResolveFilters(querySpecification: QuerySpecification): List { - val filters = arrayListOf() - if (querySpecification.limitTo == IJavaSearchConstants.REFERENCES) { - filters.add(NonImportFilter()) - filters.add(ElementWithPossibleReferencesFilter()) +fun getBeforeResolveFilters(querySpecification: QuerySpecification): List = + when (querySpecification.limitTo) { + IJavaSearchConstants.REFERENCES -> listOf(NonImportFilter, ElementWithPossibleReferencesFilter) + else -> emptyList() } - - return filters -} -fun getAfterResolveFilters(): List = listOf(ResolvedReferenceFilter()) +fun getAfterResolveFilters(querySpecification: QuerySpecification): List = + listOf(ResolvedReferenceFilter) -class ElementWithPossibleReferencesFilter : SearchFilter { - override fun isApplicable(jetElement: KtElement): Boolean = jetElement is KtReferenceExpression || (jetElement is KtPropertyDelegate) +object ElementWithPossibleReferencesFilter : SearchFilter { + override fun isApplicable(jetElement: KtElement): Boolean = + jetElement is KtReferenceExpression || (jetElement is KtPropertyDelegate) } -class NonImportFilter : SearchFilter { +object NonImportFilter : SearchFilter { override fun isApplicable(jetElement: KtElement): Boolean { return jetElement !is KtSimpleNameExpression || !jetElement.isImportDirectiveExpression() } } -class ResolvedReferenceFilter : SearchFilterAfterResolve { +object ResolvedReferenceFilter : SearchFilterAfterResolve { override fun isApplicable(sourceElement: KtElement, originElement: KtElement): Boolean { return 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() + + private fun IJavaElement.isConstructorCall() = this is IMethod && isConstructor } From 3425fee5c9a4dbe7b8960ce5889b1798f1751833 Mon Sep 17 00:00:00 2001 From: U534967 Date: Fri, 25 Mar 2022 15:11:25 +0100 Subject: [PATCH 22/31] Optimize code --- .../KotlinReferenceVariantsHelper.kt | 46 ++++++++----------- 1 file changed, 20 insertions(+), 26 deletions(-) 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 664984b80..0d8f110a2 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 @@ -23,6 +23,7 @@ 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.* @@ -452,36 +453,29 @@ class KotlinReferenceVariantsHelper( null ) - val tempPackages = tempClassNames.values.map { - resolutionFacade.moduleDescriptor.getPackage(FqName(it)) - } + val tempPackages = mutableListOf() val tempClasses = tempClassNames.mapNotNull { (className, packageName) -> val tempPackage = resolutionFacade.moduleDescriptor.getPackage(FqName(packageName)) - if (className.contains('$')) { - val tempNames = className.split('$') - val topLevelClass = tempNames.first() - var tempClassDescriptor = tempPackage.memberScope.getDescriptorsFiltered(CLASSIFIERS) { - !it.isSpecial && it.identifier == topLevelClass - }.filterIsInstance().distinctBy { it.fqNameOrNull()?.asString() ?: it.toString() } - .singleOrNull() ?: return@mapNotNull null - - tempNames.drop(1).forEach { subName -> - tempClassDescriptor = - tempClassDescriptor.unsubstitutedMemberScope.getDescriptorsFiltered(CLASSIFIERS) { - !it.isSpecial && it.identifier == subName - }.filterIsInstance() - .distinctBy { it.fqNameOrNull()?.asString() ?: it.toString() }.singleOrNull() - ?: return@mapNotNull null - } - - tempClassDescriptor - } else { - tempPackage.memberScope.getDescriptorsFiltered(CLASSIFIERS) { - !it.isSpecial && it.identifier == className - }.filterIsInstance().distinctBy { it.fqNameOrNull()?.asString() ?: it.toString() } - .singleOrNull() + val tempNames = className.split('$') + val topLevelClass = tempNames.first() + var tempClassDescriptor = + moduleDescriptor.findClassAcrossModuleDependencies(ClassId.topLevel(FqName(tempPackage.fqName.asString() + "." + topLevelClass))) + ?: run { + tempPackages.add(tempPackage) + return@mapNotNull null + } + + tempNames.drop(1).forEach { subName -> + tempClassDescriptor = + tempClassDescriptor.unsubstitutedMemberScope.getDescriptorsFiltered(CLASSIFIERS) { + !it.isSpecial && it.identifier == subName + }.filterIsInstance() + .distinctBy { it.fqNameOrNull()?.asString() ?: it.toString() }.singleOrNull() + ?: return@mapNotNull null } + + tempClassDescriptor } val importsSet = ktFile.importDirectives From 16116d961f418678ad7c9573f124fc89007308bd Mon Sep 17 00:00:00 2001 From: U534967 Date: Sat, 26 Mar 2022 12:42:45 +0100 Subject: [PATCH 23/31] add ability to search for implementations of classes / interfcaes, properties and methods. --- .../structure/EclipseJavaElementUtil.java | 16 +++- kotlin-eclipse-ui/plugin.xml | 15 +++ .../KotlinFindReferencesAction.kt | 60 ++++++++++++ .../findReferences/querySpecifications.kt | 49 +++++----- .../KotlinReferenceVariantsHelper.kt | 27 +----- .../occurrences/KotlinMarkOccurrences.kt | 38 ++++---- .../rename/KotlinRenameParticipant.kt | 20 ++-- .../refactorings/rename/refactoringUtils.kt | 4 +- .../ui/search/KotlinElementMatchCreator.kt | 69 ++++++++++++++ .../ui/search/KotlinQueryParticipant.kt | 95 ++++++++++++++----- .../KotlinReferenceMatchPresentation.kt | 64 +++++-------- .../ui/search/SearchParentObjectMapper.kt | 39 ++++++++ .../kotlin/ui/search/SearchResultRenderer.kt | 40 -------- .../kotlin/ui/search/searchFilters.kt | 72 +++++++++++++- .../jetbrains/kotlin/utils/DescriptorUtils.kt | 47 +++++++++ 15 files changed, 461 insertions(+), 194 deletions(-) create mode 100644 kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/search/KotlinElementMatchCreator.kt create mode 100644 kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/search/SearchParentObjectMapper.kt delete mode 100644 kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/search/SearchResultRenderer.kt create mode 100644 kotlin-eclipse-ui/src/org/jetbrains/kotlin/utils/DescriptorUtils.kt 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-ui/plugin.xml b/kotlin-eclipse-ui/plugin.xml index f0a57e80d..e0c8e24e8 100644 --- a/kotlin-eclipse-ui/plugin.xml +++ b/kotlin-eclipse-ui/plugin.xml @@ -310,6 +310,16 @@ description="Find References In Workspace" id="org.jetbrains.kotlin.ui.commands.findReferences.findReferencesInWorkspace" name="Find References In Workspace"> + + + + + + 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 057d9326f..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 @@ -67,6 +67,66 @@ class KotlinFindReferencesInWorkspaceHandler : KotlinFindReferencesHandler() { } } +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 { actionDefinitionId = IJavaEditorActionDefinitionIds.SEARCH_REFERENCES_IN_PROJECT 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/editors/completion/KotlinReferenceVariantsHelper.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/completion/KotlinReferenceVariantsHelper.kt index 0d8f110a2..b425c079c 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 @@ -31,11 +31,9 @@ import org.jetbrains.kotlin.resolve.BindingContext 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.fqNameOrNull 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.CLASSIFIERS 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 @@ -414,7 +412,7 @@ class KotlinReferenceVariantsHelper( val collector = object : MethodNameMatchRequestor() { override fun acceptMethodNameMatch(match: MethodNameMatch) { if (Flags.isPublic(match.modifiers)) { - tempClassNames[match.method.declaringType.getTypeQualifiedName('$')] = + tempClassNames[match.method.declaringType.getTypeQualifiedName('.')] = match.method.declaringType.packageFragment.elementName } } @@ -456,26 +454,11 @@ class KotlinReferenceVariantsHelper( val tempPackages = mutableListOf() val tempClasses = tempClassNames.mapNotNull { (className, packageName) -> - val tempPackage = resolutionFacade.moduleDescriptor.getPackage(FqName(packageName)) - val tempNames = className.split('$') - val topLevelClass = tempNames.first() - var tempClassDescriptor = - moduleDescriptor.findClassAcrossModuleDependencies(ClassId.topLevel(FqName(tempPackage.fqName.asString() + "." + topLevelClass))) - ?: run { - tempPackages.add(tempPackage) - return@mapNotNull null - } - - tempNames.drop(1).forEach { subName -> - tempClassDescriptor = - tempClassDescriptor.unsubstitutedMemberScope.getDescriptorsFiltered(CLASSIFIERS) { - !it.isSpecial && it.identifier == subName - }.filterIsInstance() - .distinctBy { it.fqNameOrNull()?.asString() ?: it.toString() }.singleOrNull() - ?: return@mapNotNull null + val tempClassId = ClassId(FqName(packageName), FqName(className), false) + moduleDescriptor.findClassAcrossModuleDependencies(tempClassId) ?: run { + tempPackages.add(resolutionFacade.moduleDescriptor.getPackage(FqName(packageName))) + null } - - tempClassDescriptor } val importsSet = ktFile.importDirectives 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/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 e768c7988..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 @@ -45,6 +47,7 @@ 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.references.resolveToSourceDeclaration +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 @@ -57,12 +60,34 @@ import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getParentOfType import org.jetbrains.kotlin.resolve.source.KotlinSourceElement 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, @@ -70,11 +95,16 @@ 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 } @@ -110,9 +140,8 @@ class KotlinQueryParticipant : IQueryParticipant { val matchedReferences = resolveElementsAndMatch(elements, searchElement, querySpecification) if (monitor?.isCanceled == true) return matchedReferences.forEach { ktElement -> - val tempRenderer = SearchResultRenderer.getResultRenderer(querySpecification) - - requestor.reportMatch(KotlinElementMatch(ktElement, tempRenderer.render(ktElement))) + val tempCreator = KotlinElementMatchCreator.getMatchCreator(querySpecification) + tempCreator.createMatch(ktElement)?.let { tempRequestor.reportMatch(it) } } } @@ -131,9 +160,19 @@ class KotlinQueryParticipant : IQueryParticipant { 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) + } } } @@ -159,7 +198,14 @@ class KotlinQueryParticipant : IQueryParticipant { for (specification in specifications) { if (specification is KotlinScoped) { - KotlinQueryParticipant().search({ requestor.reportMatch(it) }, specification, monitor) + 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) @@ -208,9 +254,9 @@ class KotlinQueryParticipant : IQueryParticipant { val pair = getSearchTextAsRegex(searchText) asRegex = pair.first searchText = pair.second - } else if(searchElement.kotlinElement.hasModifier(KtTokens.OVERRIDE_KEYWORD)) { + } else if (searchElement.kotlinElement.hasModifier(KtTokens.OVERRIDE_KEYWORD)) { val tempDescriptor = searchElement.kotlinElement.resolveToDescriptorIfAny() as? FunctionDescriptor - if(tempDescriptor?.isOperator == true) { + if (tempDescriptor?.isOperator == true) { val pair = getSearchTextAsRegex(searchText) asRegex = pair.first searchText = pair.second @@ -263,13 +309,14 @@ class KotlinQueryParticipant : IQueryParticipant { ): List { val beforeResolveFilters = getBeforeResolveFilters(querySpecification) val afterResolveFilters = getAfterResolveFilters(querySpecification) + val mapper = SearchParentObjectMapper.getMapper(querySpecification) // This is important for optimization: // we will consequentially cache files one by one which do contain these references val sortedByFileNameElements = elements.sortedBy { it.containingKtFile.name } return sortedByFileNameElements.flatMap { element -> - val tempElement = findApplicableElement(element, beforeResolveFilters) ?: return@flatMap emptyList() + val tempElement = findApplicableElement(element, beforeResolveFilters, mapper) ?: return@flatMap emptyList() val sourceElements = tempElement.resolveToSourceDeclaration() if (sourceElements.isEmpty()) return@flatMap emptyList() @@ -285,10 +332,10 @@ class KotlinQueryParticipant : IQueryParticipant { } private fun findApplicableElement( - element: KtElement, beforeResolveFilters: List + element: KtElement, beforeResolveFilters: List, mapper: SearchParentObjectMapper ): KtElement? { - if(beforeResolveFilters.all { it.isApplicable(element) }) return element - return element.getParentOfType(false)?.takeIf { refExp -> + if (beforeResolveFilters.all { it.isApplicable(element) }) return element + return mapper.map(element)?.takeIf { refExp -> beforeResolveFilters.all { it.isApplicable(refExp) } } } @@ -334,15 +381,11 @@ fun getContainingClassOrObjectForConstructor(sourceElements: List } } -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) } @@ -366,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") 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/SearchResultRenderer.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/search/SearchResultRenderer.kt deleted file mode 100644 index 0b2138932..000000000 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/search/SearchResultRenderer.kt +++ /dev/null @@ -1,40 +0,0 @@ -package org.jetbrains.kotlin.ui.search - -import com.intellij.psi.util.PsiTreeUtil -import org.eclipse.jdt.ui.search.QuerySpecification -import org.jetbrains.kotlin.core.utils.getBindingContext -import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny -import org.jetbrains.kotlin.psi.KtDeclaration -import org.jetbrains.kotlin.psi.KtElement -import org.jetbrains.kotlin.renderer.DescriptorRenderer -import org.jetbrains.kotlin.resolve.calls.callUtil.getCall - -interface SearchResultRenderer { - - fun render(element: KtElement): String - - companion object { - fun getResultRenderer(querySpecification: QuerySpecification) = when (querySpecification.limitTo) { - else -> BasicSearchResultRenderer - } - } -} - -object BasicSearchResultRenderer : SearchResultRenderer { - - override 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) - if (tempParentDescriptor != null) { - append(" in ") - append(DescriptorRenderer.SHORT_NAMES_IN_TYPES.render(tempParentDescriptor)) - } - } - } - -} 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 6e495f0ec..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,12 +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.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 @@ -36,7 +42,10 @@ interface SearchFilterAfterResolve { 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) } @@ -48,26 +57,81 @@ interface SearchFilterAfterResolve { 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 = - listOf(ResolvedReferenceFilter) + when (querySpecification.limitTo) { + KotlinFindImplementationsInProjectAction.IMPLEMENTORS_LIMIT_TO -> listOf(InheritorsFilter) + else -> listOf(ResolvedReferenceFilter) + } object ElementWithPossibleReferencesFilter : SearchFilter { override fun isApplicable(jetElement: KtElement): Boolean = jetElement is KtReferenceExpression || (jetElement is KtPropertyDelegate) } +object PossibleOverridingMemberFilter : SearchFilter { + + override fun isApplicable(jetElement: KtElement): Boolean { + return jetElement is KtClass || jetElement is KtNamedFunction || jetElement is KtProperty || jetElement is KtObjectDeclaration + } +} + object NonImportFilter : SearchFilter { override fun isApplicable(jetElement: KtElement): Boolean { return jetElement !is KtSimpleNameExpression || !jetElement.isImportDirectiveExpression() } } +object InheritorsFilter : SearchFilterAfterResolve { + override fun isApplicable(sourceElement: KtElement, originElement: KtElement): Boolean { + 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 + return sourceElement == originElement || InheritorsFilter.isApplicable(sourceElement, originElement) } override fun isApplicable(sourceElement: IJavaElement, originElement: IJavaElement): Boolean { 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 +} From 884a7c3c92be3333b1cfcb682ade3161e32af54d Mon Sep 17 00:00:00 2001 From: U534967 Date: Sun, 27 Mar 2022 12:24:57 +0200 Subject: [PATCH 24/31] Add new wizards for data and annotation classes and for sealed classes and interfaces. --- kotlin-eclipse-ui/plugin.xml | 60 ++++++++- .../kotlin/wizards/NewUnitWizard.java | 21 ++- .../kotlin/wizards/NewUnitWizardPage.java | 123 ++++++++++-------- .../jetbrains/kotlin/wizards/WizardType.kt | 10 +- .../jetbrains/kotlin/wizards/unitWizards.kt | 34 ++--- .../jetbrains/kotlin/wizards/wizardUtils.kt | 23 ++-- 6 files changed, 174 insertions(+), 97 deletions(-) diff --git a/kotlin-eclipse-ui/plugin.xml b/kotlin-eclipse-ui/plugin.xml index e0c8e24e8..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 + + { 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 +} From 71f0229146a47d81a980cfb3f943d8445eb56622 Mon Sep 17 00:00:00 2001 From: U534967 Date: Wed, 11 May 2022 11:49:15 +0200 Subject: [PATCH 25/31] fix type name for provided properties preferes the actual kotlin type, not the translated java type if possible. --- .../kotlin/core/resolve/EclipseAnalyzerFacadeForJVM.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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 1c5589400..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 @@ -190,10 +190,11 @@ object EclipseAnalyzerFacadeForJVM { 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: ${value.typeName} */ - val $key: ${value.typeName}${'$'}${if (isNullable) "? = null" else " = TODO()"}""".trimIndent() + /** Provided property '$key' of type: $tempTypeName */ + val $key: $tempTypeName${'$'}${if (isNullable) "? = null" else " = TODO()"}""".trimIndent() factory.createProperty(tempText) } ?: emptyList() @@ -376,4 +377,4 @@ object EclipseAnalyzerFacadeForJVM { } } } -} \ No newline at end of file +} From 31630b57629ec45867de852b25450c67d2938609 Mon Sep 17 00:00:00 2001 From: Julian Hahn Date: Mon, 23 May 2022 08:09:30 +0200 Subject: [PATCH 26/31] sort completion proposals by machting name and after that by theire type (member, member_extension, top_level, others). --- .../codeassist/KotlinCompletionProcessor.kt | 14 +++++++--- .../codeassist/KotlinCompletionProposal.kt | 18 +++++++++---- .../KotlinFunctionCompletionProposal.kt | 3 ++- .../KotlinReferenceVariantsHelper.kt | 27 ++++++++++--------- 4 files changed, 40 insertions(+), 22 deletions(-) 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 49c862c43..f3981afcb 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 @@ -49,7 +49,14 @@ sealed class KotlinBasicCompletionProposal { class Proposal(val proposal: KotlinCompletionProposal, override val descriptor: DeclarationDescriptor) : KotlinBasicCompletionProposal() - class Descriptor(override val descriptor: DeclarationDescriptor) : KotlinBasicCompletionProposal() + class Descriptor(override val descriptor: DeclarationDescriptor, val type: DescriptorType) : KotlinBasicCompletionProposal() +} + +enum class DescriptorType { + OTHER, + TOP_LEVEL, + MEMBER_EXTENSION, + MEMBER } abstract class KotlinCompletionProcessor( @@ -223,7 +230,8 @@ abstract class KotlinCompletionProcessor( containmentPresentableString, null, completion, - part + part, + basicDescriptor.type ) withKotlinInsertHandler(descriptor, proposal) @@ -332,6 +340,6 @@ private object KotlinCompletionSorter : ICompletionProposalSorter { } private fun ICompletionProposal.relevance(): Int { - return if (this is KotlinCompletionProposal) this.getRelevance() else 0 + return if (this is KotlinRelevanceCompletionProposal) this.getRelevance() else 0 } } 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 2021dc91c..07d441ad9 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 @@ -111,20 +111,28 @@ fun getIdentifierInfo(document: IDocument, offset: Int): IdentifierInfo { data class IdentifierInfo(val identifierPart: String, val identifierStart: Int) -open class KotlinCompletionProposal constructor( +interface KotlinRelevanceCompletionProposal { + fun getRelevance(): Int +} + +open class KotlinCompletionProposal( 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 -) : ICompletionProposal, ICompletionProposalExtension2, ICompletionProposalExtension6 { + @Volatile private var identifierPart: String, + private val type: DescriptorType? = null +) : ICompletionProposal, ICompletionProposalExtension2, ICompletionProposalExtension6, KotlinRelevanceCompletionProposal { private var selectedOffset = -1 - open fun getRelevance(): Int { - return computeCaseMatchingRelevance(identifierPart.toCharArray(), replacementString.toCharArray()) + override fun getRelevance(): Int { + //Case Matching takes precedence always. So multiply it with a high number. + val tempCaseMatchingRelevance = computeCaseMatchingRelevance(identifierPart.toCharArray(), replacementString.toCharArray()) * 10000 + //If available we then sort by type, but only after sorting by case matching. So multiply it with a lower number. + return type?.let { tempCaseMatchingRelevance + (it.ordinal * 1000) } ?: tempCaseMatchingRelevance } override fun apply(viewer: ITextViewer, trigger: Char, stateMask: Int, offset: Int) { 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 b0e2970b2..0c4b43386 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 @@ -33,7 +33,8 @@ class KotlinFunctionCompletionProposal( private val caretPosition: CaretPosition, private val hasLambda: Boolean, private val lambdaParamNames: String = "" -) : ICompletionProposal by proposal, ICompletionProposalExtension2 by proposal, ICompletionProposalExtension6 by proposal { +) : ICompletionProposal by proposal, ICompletionProposalExtension2 by proposal, + ICompletionProposalExtension6 by proposal, KotlinRelevanceCompletionProposal by proposal { init { if (caretPosition == CaretPosition.AFTER_BRACKETS && hasLambda) { 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 b425c079c..3bb91ecf8 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 @@ -44,6 +44,7 @@ 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.isUnit +import org.jetbrains.kotlin.ui.editors.codeassist.DescriptorType import org.jetbrains.kotlin.ui.editors.codeassist.KotlinBasicCompletionProposal import org.jetbrains.kotlin.ui.editors.codeassist.KotlinImportCallableCompletionProposal import org.jetbrains.kotlin.ui.refactorings.extract.parentsWithSelf @@ -173,7 +174,7 @@ class KotlinReferenceVariantsHelper( .mapNotNull { (it.constructor.declarationDescriptor as? ClassDescriptor)?.staticScope } .flatMapTo(descriptors) { scope -> scope.collectStaticMembers(resolutionFacade, kindFilter, nameFilter) - .map { KotlinBasicCompletionProposal.Descriptor(it) } + .map { KotlinBasicCompletionProposal.Descriptor(it, DescriptorType.OTHER) } } } } else { @@ -205,12 +206,12 @@ class KotlinReferenceVariantsHelper( when (callTypeAndReceiver) { is CallTypeAndReceiver.IMPORT_DIRECTIVE -> { return getVariantsForImportOrPackageDirective(callTypeAndReceiver.receiver, kindFilter, nameFilter) - .map { KotlinBasicCompletionProposal.Descriptor(it) } + .map { KotlinBasicCompletionProposal.Descriptor(it, DescriptorType.OTHER) } } is CallTypeAndReceiver.PACKAGE_DIRECTIVE -> { return getVariantsForImportOrPackageDirective(callTypeAndReceiver.receiver, kindFilter, nameFilter) - .map { KotlinBasicCompletionProposal.Descriptor(it) } + .map { KotlinBasicCompletionProposal.Descriptor(it, DescriptorType.OTHER) } } is CallTypeAndReceiver.TYPE -> { @@ -219,7 +220,7 @@ class KotlinReferenceVariantsHelper( simpleNameExpression, kindFilter, nameFilter - ).map { KotlinBasicCompletionProposal.Descriptor(it) } + ).map { KotlinBasicCompletionProposal.Descriptor(it, DescriptorType.OTHER) } } is CallTypeAndReceiver.ANNOTATION -> { @@ -228,7 +229,7 @@ class KotlinReferenceVariantsHelper( simpleNameExpression, kindFilter, nameFilter - ).map { KotlinBasicCompletionProposal.Descriptor(it) } + ).map { KotlinBasicCompletionProposal.Descriptor(it, DescriptorType.OTHER) } } is CallTypeAndReceiver.CALLABLE_REFERENCE -> { @@ -282,7 +283,7 @@ class KotlinReferenceVariantsHelper( resolutionFacade, filterWithoutExtensions, nameFilter - ).map { KotlinBasicCompletionProposal.Descriptor(it) } + ).map { KotlinBasicCompletionProposal.Descriptor(it, DescriptorType.OTHER) } ) } @@ -327,7 +328,7 @@ class KotlinReferenceVariantsHelper( filterWithoutExtensions, nameFilter, changeNamesForAliased = true - ).map { KotlinBasicCompletionProposal.Descriptor(it) } + ).map { KotlinBasicCompletionProposal.Descriptor(it, DescriptorType.OTHER) } ) } @@ -335,7 +336,7 @@ class KotlinReferenceVariantsHelper( 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) } + descriptor.descriptor.overriddenDescriptors.map { KotlinBasicCompletionProposal.Descriptor(it, descriptor.type) } } else { listOf(descriptor) } @@ -514,7 +515,7 @@ class KotlinReferenceVariantsHelper( for (dispatchReceiverType in dispatchReceiverTypes) { for (member in dispatchReceiverType.memberScope.getDescriptorsFiltered(memberFilter, nameFilter)) { addAll((member as CallableDescriptor).substituteExtensionIfCallable(extensionReceiverTypes, callType) - .map { KotlinBasicCompletionProposal.Descriptor(it) }) + .map { KotlinBasicCompletionProposal.Descriptor(it, DescriptorType.MEMBER_EXTENSION) }) } } } @@ -561,10 +562,10 @@ class KotlinReferenceVariantsHelper( if (descriptor is ClassDescriptor) { if (descriptor.modality == Modality.ABSTRACT || descriptor.modality == Modality.SEALED) continue if (!constructorFilter(descriptor)) continue - descriptor.constructors.map { KotlinBasicCompletionProposal.Descriptor(it) } + descriptor.constructors.map { KotlinBasicCompletionProposal.Descriptor(it, DescriptorType.MEMBER) } .filterTo(this) { kindFilter.accepts(it.descriptor) } } else if (!classesOnly && kindFilter.accepts(descriptor)) { - this.add(KotlinBasicCompletionProposal.Descriptor(descriptor)) + this.add(KotlinBasicCompletionProposal.Descriptor(descriptor, DescriptorType.MEMBER)) } } } @@ -584,9 +585,9 @@ class KotlinReferenceVariantsHelper( if (extensionOrSyntheticMember.isExtension) { addAll( extensionOrSyntheticMember.substituteExtensionIfCallable(receiverTypes, callType) - .map { KotlinBasicCompletionProposal.Descriptor(it) }) + .map { KotlinBasicCompletionProposal.Descriptor(it, DescriptorType.MEMBER_EXTENSION) }) } else { - add(KotlinBasicCompletionProposal.Descriptor(extensionOrSyntheticMember)) + add(KotlinBasicCompletionProposal.Descriptor(extensionOrSyntheticMember, DescriptorType.TOP_LEVEL)) } } } From 24ae76fe9f32b1485c3818422d2f570654177169 Mon Sep 17 00:00:00 2001 From: Julian Hahn Date: Mon, 23 May 2022 08:17:42 +0200 Subject: [PATCH 27/31] Revert "sort completion proposals by machting name and after that by theire type (member, member_extension, top_level, others)." This reverts commit 31630b57629ec45867de852b25450c67d2938609. --- .../codeassist/KotlinCompletionProcessor.kt | 14 +++------- .../codeassist/KotlinCompletionProposal.kt | 18 ++++--------- .../KotlinFunctionCompletionProposal.kt | 3 +-- .../KotlinReferenceVariantsHelper.kt | 27 +++++++++---------- 4 files changed, 22 insertions(+), 40 deletions(-) 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 f3981afcb..49c862c43 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 @@ -49,14 +49,7 @@ sealed class KotlinBasicCompletionProposal { class Proposal(val proposal: KotlinCompletionProposal, override val descriptor: DeclarationDescriptor) : KotlinBasicCompletionProposal() - class Descriptor(override val descriptor: DeclarationDescriptor, val type: DescriptorType) : KotlinBasicCompletionProposal() -} - -enum class DescriptorType { - OTHER, - TOP_LEVEL, - MEMBER_EXTENSION, - MEMBER + class Descriptor(override val descriptor: DeclarationDescriptor) : KotlinBasicCompletionProposal() } abstract class KotlinCompletionProcessor( @@ -230,8 +223,7 @@ abstract class KotlinCompletionProcessor( containmentPresentableString, null, completion, - part, - basicDescriptor.type + part ) withKotlinInsertHandler(descriptor, proposal) @@ -340,6 +332,6 @@ private object KotlinCompletionSorter : ICompletionProposalSorter { } private fun ICompletionProposal.relevance(): Int { - return if (this is KotlinRelevanceCompletionProposal) this.getRelevance() else 0 + return if (this is KotlinCompletionProposal) this.getRelevance() else 0 } } 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 07d441ad9..2021dc91c 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 @@ -111,28 +111,20 @@ fun getIdentifierInfo(document: IDocument, offset: Int): IdentifierInfo { data class IdentifierInfo(val identifierPart: String, val identifierStart: Int) -interface KotlinRelevanceCompletionProposal { - fun getRelevance(): Int -} - -open class KotlinCompletionProposal( +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, - private val type: DescriptorType? = null -) : ICompletionProposal, ICompletionProposalExtension2, ICompletionProposalExtension6, KotlinRelevanceCompletionProposal { + @Volatile private var identifierPart: String +) : ICompletionProposal, ICompletionProposalExtension2, ICompletionProposalExtension6 { private var selectedOffset = -1 - override fun getRelevance(): Int { - //Case Matching takes precedence always. So multiply it with a high number. - val tempCaseMatchingRelevance = computeCaseMatchingRelevance(identifierPart.toCharArray(), replacementString.toCharArray()) * 10000 - //If available we then sort by type, but only after sorting by case matching. So multiply it with a lower number. - return type?.let { tempCaseMatchingRelevance + (it.ordinal * 1000) } ?: tempCaseMatchingRelevance + open fun getRelevance(): Int { + return computeCaseMatchingRelevance(identifierPart.toCharArray(), replacementString.toCharArray()) } override fun apply(viewer: ITextViewer, trigger: Char, stateMask: Int, offset: Int) { 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 0c4b43386..b0e2970b2 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 @@ -33,8 +33,7 @@ class KotlinFunctionCompletionProposal( private val caretPosition: CaretPosition, private val hasLambda: Boolean, private val lambdaParamNames: String = "" -) : ICompletionProposal by proposal, ICompletionProposalExtension2 by proposal, - ICompletionProposalExtension6 by proposal, KotlinRelevanceCompletionProposal by proposal { +) : ICompletionProposal by proposal, ICompletionProposalExtension2 by proposal, ICompletionProposalExtension6 by proposal { init { if (caretPosition == CaretPosition.AFTER_BRACKETS && hasLambda) { 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 3bb91ecf8..b425c079c 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 @@ -44,7 +44,6 @@ 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.isUnit -import org.jetbrains.kotlin.ui.editors.codeassist.DescriptorType import org.jetbrains.kotlin.ui.editors.codeassist.KotlinBasicCompletionProposal import org.jetbrains.kotlin.ui.editors.codeassist.KotlinImportCallableCompletionProposal import org.jetbrains.kotlin.ui.refactorings.extract.parentsWithSelf @@ -174,7 +173,7 @@ class KotlinReferenceVariantsHelper( .mapNotNull { (it.constructor.declarationDescriptor as? ClassDescriptor)?.staticScope } .flatMapTo(descriptors) { scope -> scope.collectStaticMembers(resolutionFacade, kindFilter, nameFilter) - .map { KotlinBasicCompletionProposal.Descriptor(it, DescriptorType.OTHER) } + .map { KotlinBasicCompletionProposal.Descriptor(it) } } } } else { @@ -206,12 +205,12 @@ class KotlinReferenceVariantsHelper( when (callTypeAndReceiver) { is CallTypeAndReceiver.IMPORT_DIRECTIVE -> { return getVariantsForImportOrPackageDirective(callTypeAndReceiver.receiver, kindFilter, nameFilter) - .map { KotlinBasicCompletionProposal.Descriptor(it, DescriptorType.OTHER) } + .map { KotlinBasicCompletionProposal.Descriptor(it) } } is CallTypeAndReceiver.PACKAGE_DIRECTIVE -> { return getVariantsForImportOrPackageDirective(callTypeAndReceiver.receiver, kindFilter, nameFilter) - .map { KotlinBasicCompletionProposal.Descriptor(it, DescriptorType.OTHER) } + .map { KotlinBasicCompletionProposal.Descriptor(it) } } is CallTypeAndReceiver.TYPE -> { @@ -220,7 +219,7 @@ class KotlinReferenceVariantsHelper( simpleNameExpression, kindFilter, nameFilter - ).map { KotlinBasicCompletionProposal.Descriptor(it, DescriptorType.OTHER) } + ).map { KotlinBasicCompletionProposal.Descriptor(it) } } is CallTypeAndReceiver.ANNOTATION -> { @@ -229,7 +228,7 @@ class KotlinReferenceVariantsHelper( simpleNameExpression, kindFilter, nameFilter - ).map { KotlinBasicCompletionProposal.Descriptor(it, DescriptorType.OTHER) } + ).map { KotlinBasicCompletionProposal.Descriptor(it) } } is CallTypeAndReceiver.CALLABLE_REFERENCE -> { @@ -283,7 +282,7 @@ class KotlinReferenceVariantsHelper( resolutionFacade, filterWithoutExtensions, nameFilter - ).map { KotlinBasicCompletionProposal.Descriptor(it, DescriptorType.OTHER) } + ).map { KotlinBasicCompletionProposal.Descriptor(it) } ) } @@ -328,7 +327,7 @@ class KotlinReferenceVariantsHelper( filterWithoutExtensions, nameFilter, changeNamesForAliased = true - ).map { KotlinBasicCompletionProposal.Descriptor(it, DescriptorType.OTHER) } + ).map { KotlinBasicCompletionProposal.Descriptor(it) } ) } @@ -336,7 +335,7 @@ class KotlinReferenceVariantsHelper( 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, descriptor.type) } + descriptor.descriptor.overriddenDescriptors.map { KotlinBasicCompletionProposal.Descriptor(it) } } else { listOf(descriptor) } @@ -515,7 +514,7 @@ class KotlinReferenceVariantsHelper( for (dispatchReceiverType in dispatchReceiverTypes) { for (member in dispatchReceiverType.memberScope.getDescriptorsFiltered(memberFilter, nameFilter)) { addAll((member as CallableDescriptor).substituteExtensionIfCallable(extensionReceiverTypes, callType) - .map { KotlinBasicCompletionProposal.Descriptor(it, DescriptorType.MEMBER_EXTENSION) }) + .map { KotlinBasicCompletionProposal.Descriptor(it) }) } } } @@ -562,10 +561,10 @@ class KotlinReferenceVariantsHelper( if (descriptor is ClassDescriptor) { if (descriptor.modality == Modality.ABSTRACT || descriptor.modality == Modality.SEALED) continue if (!constructorFilter(descriptor)) continue - descriptor.constructors.map { KotlinBasicCompletionProposal.Descriptor(it, DescriptorType.MEMBER) } + descriptor.constructors.map { KotlinBasicCompletionProposal.Descriptor(it) } .filterTo(this) { kindFilter.accepts(it.descriptor) } } else if (!classesOnly && kindFilter.accepts(descriptor)) { - this.add(KotlinBasicCompletionProposal.Descriptor(descriptor, DescriptorType.MEMBER)) + this.add(KotlinBasicCompletionProposal.Descriptor(descriptor)) } } } @@ -585,9 +584,9 @@ class KotlinReferenceVariantsHelper( if (extensionOrSyntheticMember.isExtension) { addAll( extensionOrSyntheticMember.substituteExtensionIfCallable(receiverTypes, callType) - .map { KotlinBasicCompletionProposal.Descriptor(it, DescriptorType.MEMBER_EXTENSION) }) + .map { KotlinBasicCompletionProposal.Descriptor(it) }) } else { - add(KotlinBasicCompletionProposal.Descriptor(extensionOrSyntheticMember, DescriptorType.TOP_LEVEL)) + add(KotlinBasicCompletionProposal.Descriptor(extensionOrSyntheticMember)) } } } From d9d6a5c7e446e62bb04ca4e040c03142ba8fc6b4 Mon Sep 17 00:00:00 2001 From: Julian Hahn Date: Mon, 31 May 2021 20:50:21 +0200 Subject: [PATCH 28/31] fixed autocompletion and merged sorting from newer branch into older version. --- .../ui/editors/FileEditorConfiguration.kt | 4 +-- .../codeassist/CompletionElementType.kt | 34 +++++++++++++++++++ .../codeassist/KotlinCompletionProcessor.kt | 26 +++++++++----- .../codeassist/KotlinCompletionProposal.kt | 17 +++++++--- .../KotlinFunctionCompletionProposal.kt | 4 ++- .../KotlinReferenceVariantsHelper.kt | 16 +++++++-- 6 files changed, 83 insertions(+), 18 deletions(-) create mode 100644 kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/codeassist/CompletionElementType.kt 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 8b752556e..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 @@ -50,13 +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) 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 49c862c43..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 @@ -223,9 +223,9 @@ abstract class KotlinCompletionProcessor( containmentPresentableString, null, completion, - part + part, + CompletionElementType.from(descriptor) ) - withKotlinInsertHandler(descriptor, proposal) } is KotlinBasicCompletionProposal.Proposal -> basicDescriptor.proposal @@ -317,9 +317,10 @@ abstract class KotlinCompletionProcessor( 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 @@ -327,11 +328,18 @@ private object KotlinCompletionSorter : ICompletionProposalSorter { } } - 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 { - return if (this is KotlinCompletionProposal) this.getRelevance() else 0 + 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 + } } } 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 2021dc91c..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 @@ -111,6 +111,15 @@ fun getIdentifierInfo(document: IDocument, offset: Int): IdentifierInfo { data class IdentifierInfo(val identifierPart: String, val identifierStart: Int) +interface KotlinRelevanceCompletionProposal { + fun getRelevance(): Int +} + +interface KotlinTypedCompletionProposal { + + val type: CompletionElementType +} + open class KotlinCompletionProposal constructor( val replacementString: String, private val img: Image?, @@ -118,12 +127,12 @@ open class KotlinCompletionProposal constructor( private val containmentPresentableString: String? = null, private val information: IContextInformation? = null, private val additionalInfo: String? = null, - @Volatile private var identifierPart: String -) : ICompletionProposal, ICompletionProposalExtension2, ICompletionProposalExtension6 { + @Volatile private var identifierPart: String, + override val type: CompletionElementType = CompletionElementType.UNKNOWN +) : ICompletionProposal, ICompletionProposalExtension2, ICompletionProposalExtension6, KotlinTypedCompletionProposal, KotlinRelevanceCompletionProposal { private var selectedOffset = -1 - - open fun getRelevance(): Int { + override fun getRelevance(): Int { return computeCaseMatchingRelevance(identifierPart.toCharArray(), replacementString.toCharArray()) } 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 b0e2970b2..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 @@ -33,7 +33,9 @@ class KotlinFunctionCompletionProposal( private val caretPosition: CaretPosition, private val hasLambda: Boolean, private val lambdaParamNames: String = "" -) : ICompletionProposal by proposal, ICompletionProposalExtension2 by proposal, ICompletionProposalExtension6 by proposal { +) : ICompletionProposal by proposal, ICompletionProposalExtension2 by proposal, + ICompletionProposalExtension6 by proposal, KotlinTypedCompletionProposal by proposal, + KotlinRelevanceCompletionProposal by proposal { init { if (caretPosition == CaretPosition.AFTER_BRACKETS && hasLambda) { 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 b425c079c..00ea71553 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 @@ -31,6 +31,7 @@ import org.jetbrains.kotlin.resolve.BindingContext 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 @@ -43,6 +44,7 @@ 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 @@ -474,9 +476,19 @@ class KotlinReferenceVariantsHelper( .filterIsInstance() .filter { callDesc -> val tempFuzzy = callDesc.fuzzyExtensionReceiverType() - (allowNoReceiver && tempFuzzy == null) || (tempFuzzy != null && receiverTypes.any { receiverType -> + //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 && From e78b500ac21f0d3e57e2a14d97e770de94fdf24b Mon Sep 17 00:00:00 2001 From: Julian Hahn Date: Mon, 23 May 2022 10:00:31 +0200 Subject: [PATCH 29/31] fixed double import suggestion. --- .../ui/editors/completion/KotlinReferenceVariantsHelper.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 00ea71553..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 @@ -28,6 +28,7 @@ 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 @@ -344,7 +345,7 @@ class KotlinReferenceVariantsHelper( } } - return descriptors.distinctBy { it.descriptor } + return descriptors.distinctBy { (it.descriptor as? ImportedFromObjectCallableDescriptor<*>)?.callableFromObject ?: it.descriptor } } private fun MutableSet.processAll( From a2786a1031856d8fcb41a0d54baeeaa50cf48397 Mon Sep 17 00:00:00 2001 From: U534967 Date: Thu, 16 Jun 2022 11:01:22 +0200 Subject: [PATCH 30/31] convert java to kotlin --- .../java/structure/EclipseJavaArrayType.java | 36 ------------------- .../java/structure/EclipseJavaArrayType.kt | 26 ++++++++++++++ 2 files changed, 26 insertions(+), 36 deletions(-) delete mode 100644 kotlin-eclipse-core/src/org/jetbrains/kotlin/core/resolve/lang/java/structure/EclipseJavaArrayType.java create mode 100644 kotlin-eclipse-core/src/org/jetbrains/kotlin/core/resolve/lang/java/structure/EclipseJavaArrayType.kt 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) +} From adf645cc49c656d39dc0df1e8a45f40761722c57 Mon Sep 17 00:00:00 2001 From: U534967 Date: Thu, 7 Jul 2022 14:41:48 +0200 Subject: [PATCH 31/31] fix warnings --- .../kotlin/core/asJava/LightClassBuilderFactory.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 +}