diff --git a/kotlin-bundled-compiler/.classpath b/kotlin-bundled-compiler/.classpath index 308dbf4c6..c956c5346 100644 --- a/kotlin-bundled-compiler/.classpath +++ b/kotlin-bundled-compiler/.classpath @@ -2,6 +2,8 @@ + + diff --git a/kotlin-bundled-compiler/META-INF/MANIFEST.MF b/kotlin-bundled-compiler/META-INF/MANIFEST.MF index 823267fec..068f144c8 100644 --- a/kotlin-bundled-compiler/META-INF/MANIFEST.MF +++ b/kotlin-bundled-compiler/META-INF/MANIFEST.MF @@ -17,8 +17,10 @@ Bundle-ClassPath: ., lib/kotlin-formatter.jar, lib/ide-dependencies.jar, lib/annotations-13.0.jar, - lib/kotlin-scripting-impl.jar -Export-Package: + lib/kotlin-scripting-impl.jar, + lib/kotlinx-coroutines-core.jar, + lib/kotlinx-coroutines-jdk8.jar +Export-Package: com.intellij, com.intellij.codeInsight, com.intellij.codeInsight.completion.scope, @@ -180,6 +182,9 @@ Export-Package: kotlin.collections, kotlin.comparisons, kotlin.concurrent, + kotlin.coroutines, + kotlin.coroutines.intrinsics, + kotlin.coroutines.jvm.internal, kotlin.internal;x-internal:=true, kotlin.io, kotlin.jvm, @@ -197,6 +202,7 @@ Export-Package: kotlin.script.templates.standard, kotlin.sequences, kotlin.text, + kotlinx.coroutines, org.jetbrains.annotations, org.jetbrains.kotlin, org.jetbrains.kotlin.analyzer, diff --git a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/model/KotlinAnalysisFileCache.kt b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/model/KotlinAnalysisFileCache.kt index d9d19cecf..69e3835cc 100644 --- a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/model/KotlinAnalysisFileCache.kt +++ b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/model/KotlinAnalysisFileCache.kt @@ -16,44 +16,51 @@ *******************************************************************************/ package org.jetbrains.kotlin.core.model -import org.eclipse.jdt.core.IJavaProject +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.async +import kotlinx.coroutines.runBlocking import org.jetbrains.kotlin.core.resolve.AnalysisResultWithProvider import org.jetbrains.kotlin.core.resolve.EclipseAnalyzerFacadeForJVM +import org.jetbrains.kotlin.core.resolve.KotlinAnalysisScope import org.jetbrains.kotlin.psi.KtFile -import org.jetbrains.kotlin.core.builder.KotlinPsiManager -data class FileAnalysisResults(val file: KtFile, val analysisResult: AnalysisResultWithProvider) +data class FileAnalysisResults(val file: KtFile, val analysisResult: Deferred) -public object KotlinAnalysisFileCache { - private @Volatile var lastAnalysedFileCache: FileAnalysisResults? = null +object KotlinAnalysisFileCache { - @Synchronized fun getAnalysisResult(file: KtFile): AnalysisResultWithProvider { - return getImmediatlyFromCache(file) ?: run { + @Volatile + private var lastAnalysedFileCache: FileAnalysisResults? = null + + fun getAnalysisResult(file: KtFile): AnalysisResultWithProvider { + return runBlocking { + getImmediatelyFromCache(file)?.await() + } ?: runBlocking { val environment = getEnvironment(file.project)!! - val analysisResult = resolve(file, environment) - - lastAnalysedFileCache = FileAnalysisResults(file, analysisResult) - lastAnalysedFileCache!!.analysisResult + val analysisResult = KotlinAnalysisScope.async { + resolve(file, environment) + } + saveLastResult(file, analysisResult).analysisResult.await() } } + + @Synchronized + private fun saveLastResult(file: KtFile, result: Deferred): FileAnalysisResults = + FileAnalysisResults(file, result).also { + lastAnalysedFileCache = it + } fun resetCache() { lastAnalysedFileCache = null } - private fun resolve(file: KtFile, environment: KotlinCommonEnvironment): AnalysisResultWithProvider { - return when (environment) { + private fun resolve(file: KtFile, environment: KotlinCommonEnvironment): AnalysisResultWithProvider = + when (environment) { is KotlinScriptEnvironment -> EclipseAnalyzerFacadeForJVM.analyzeScript(environment, file) is KotlinEnvironment -> EclipseAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(environment, listOf(file)) else -> throw IllegalArgumentException("Could not analyze file with environment: $environment") } - } @Synchronized - private fun getImmediatlyFromCache(file: KtFile): AnalysisResultWithProvider? { - return if (lastAnalysedFileCache != null && lastAnalysedFileCache!!.file == file) - lastAnalysedFileCache!!.analysisResult - else - null - } + private fun getImmediatelyFromCache (ktFile: KtFile) : Deferred? = + lastAnalysedFileCache?.takeIf { it.file == ktFile }?.analysisResult } \ No newline at end of file diff --git a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/model/KotlinAnalysisProjectCache.kt b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/model/KotlinAnalysisProjectCache.kt index 18abc7562..3fcc17f48 100644 --- a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/model/KotlinAnalysisProjectCache.kt +++ b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/model/KotlinAnalysisProjectCache.kt @@ -16,6 +16,9 @@ *******************************************************************************/ package org.jetbrains.kotlin.core.model +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.async +import kotlinx.coroutines.runBlocking import org.eclipse.core.resources.IFile import org.eclipse.core.resources.IProject import org.eclipse.core.resources.IResourceChangeEvent @@ -24,52 +27,52 @@ import org.eclipse.jdt.core.IJavaProject import org.eclipse.jdt.core.JavaCore import org.jetbrains.kotlin.analyzer.AnalysisResult import org.jetbrains.kotlin.core.builder.KotlinPsiManager -import org.jetbrains.kotlin.core.resolve.EclipseAnalyzerFacadeForJVM -import org.jetbrains.kotlin.core.utils.ProjectUtils -import java.util.concurrent.ConcurrentHashMap +import org.jetbrains.kotlin.core.resolve.AnalysisResultWithProvider +import org.jetbrains.kotlin.core.resolve.KotlinAnalysisScope +import org.jetbrains.kotlin.core.resolve.KotlinAnalyzer +import java.util.concurrent.* -public object KotlinAnalysisProjectCache : IResourceChangeListener { - private val cachedAnalysisResults = ConcurrentHashMap() +object KotlinAnalysisProjectCache : IResourceChangeListener { + private val cachedAnalysisResults = ConcurrentHashMap>() - public fun resetCache(project: IProject) { + fun resetCache(project: IProject) { synchronized(project) { - cachedAnalysisResults.remove(project) + cachedAnalysisResults.remove(project)?.cancel() } } - public fun resetAllCaches() { + fun resetAllCaches() { cachedAnalysisResults.keys.toList().forEach { resetCache(it) } } - public fun getAnalysisResult(javaProject: IJavaProject): AnalysisResult { - val project = javaProject.getProject() - return synchronized(project) { - val analysisResult = cachedAnalysisResults.get(project) ?: run { - EclipseAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration( - KotlinEnvironment.getEnvironment(project), - ProjectUtils.getSourceFiles(javaProject.getProject())).analysisResult - } - - cachedAnalysisResults.putIfAbsent(project, analysisResult) ?: analysisResult - } + fun cancelablePostAnalysis(javaProject: IJavaProject, post: (AnalysisResult) -> Unit) { + try { + resetCache(javaProject.project) + post(getAnalysisResult(javaProject).analysisResult) + } catch (e: CancellationException) {} } - public @Synchronized fun getAnalysisResultIfCached(project: IProject): AnalysisResult? { - return cachedAnalysisResults.get(project) + fun getAnalysisResult(javaProject: IJavaProject) : AnalysisResultWithProvider { + return runBlocking { + val project = javaProject.project + synchronized(this@KotlinAnalysisProjectCache) { + cachedAnalysisResults.getOrPut(project) { KotlinAnalyzer.analyzeProjectAsync(project) } + }.await() + } } override fun resourceChanged(event: IResourceChangeEvent) { - when (event.getType()) { + when (event.type) { IResourceChangeEvent.PRE_DELETE, IResourceChangeEvent.PRE_CLOSE, - IResourceChangeEvent.PRE_BUILD -> event.getDelta()?.accept { delta -> - val resource = delta.getResource() + IResourceChangeEvent.PRE_BUILD -> event.delta?.accept { delta -> + val resource = delta.resource if (resource is IFile) { val javaProject = JavaCore.create(resource.getProject()) if (KotlinPsiManager.isKotlinSourceFile(resource, javaProject)) { - cachedAnalysisResults.remove(resource.getProject()) + resetCache(resource.getProject()) } return@accept false @@ -79,4 +82,4 @@ public object KotlinAnalysisProjectCache : IResourceChangeListener { } } } -} \ No newline at end of file +} diff --git a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/resolve/EclipseAnalyzerFacadeForJVM.kt b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/resolve/EclipseAnalyzerFacadeForJVM.kt index 2b4646750..0a47d514b 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 @@ -70,7 +70,7 @@ object EclipseAnalyzerFacadeForJVM { } val allFiles = LinkedHashSet(filesSet) - val addedFiles = filesSet.map { getPath(it) }.filterNotNull().toSet() + val addedFiles = filesSet.mapNotNull { getPath(it) }.toSet() ProjectUtils.getSourceFilesWithDependencies(environment.javaProject).filterNotTo(allFiles) { getPath(it) in addedFiles } diff --git a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/resolve/KotlinAnalysisScope.kt b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/resolve/KotlinAnalysisScope.kt new file mode 100644 index 000000000..5cc8e11ef --- /dev/null +++ b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/resolve/KotlinAnalysisScope.kt @@ -0,0 +1,22 @@ +package org.jetbrains.kotlin.core.resolve + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.joinAll +import kotlinx.coroutines.runBlocking +import kotlin.coroutines.CoroutineContext + +object KotlinAnalysisScope : CoroutineScope { + + private val job: Job by lazy { SupervisorJob() } + + override val coroutineContext: CoroutineContext + get() = job + Dispatchers.Default + + @JvmStatic + fun join() = runBlocking { + job.children.toList().joinAll() + } +} \ No newline at end of file diff --git a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/resolve/KotlinAnalyzer.kt b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/resolve/KotlinAnalyzer.kt index 196811020..f3f0ae245 100644 --- a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/resolve/KotlinAnalyzer.kt +++ b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/resolve/KotlinAnalyzer.kt @@ -16,10 +16,13 @@ *******************************************************************************/ package org.jetbrains.kotlin.core.resolve +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.async import org.eclipse.core.resources.IProject import org.jetbrains.kotlin.core.model.KotlinAnalysisFileCache import org.jetbrains.kotlin.core.model.KotlinEnvironment import org.jetbrains.kotlin.core.model.getEnvironment +import org.jetbrains.kotlin.core.utils.ProjectUtils import org.jetbrains.kotlin.psi.KtFile object KotlinAnalyzer { @@ -34,27 +37,25 @@ object KotlinAnalyzer { files.size == 1 -> analyzeFile(files.single()) else -> { - val environment = getEnvironment(files.first().project) - if (environment == null) { + val environment = getEnvironment(files.first().project) ?: throw IllegalStateException("There is no environment for project: ${files.first().project}") - } - - if (environment !is KotlinEnvironment) { + + environment as? KotlinEnvironment ?: throw IllegalStateException("Only KotlinEnvironment can be used to analyze several files") - } - + analyzeFiles(environment, files) } } } - - fun analyzeProject(eclipseProject: IProject): AnalysisResultWithProvider { - val environment = KotlinEnvironment.getEnvironment(eclipseProject) - return analyzeFiles(environment, emptyList()) - } + + fun analyzeProjectAsync(project: IProject) : Deferred = + KotlinAnalysisScope.async { + analyzeFiles( + KotlinEnvironment.getEnvironment(project), + ProjectUtils.getSourceFiles(project)) + } private fun analyzeFiles(kotlinEnvironment: KotlinEnvironment, - filesToAnalyze: Collection): AnalysisResultWithProvider { - return EclipseAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(kotlinEnvironment, filesToAnalyze) - } + filesToAnalyze: Collection): AnalysisResultWithProvider = + EclipseAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(kotlinEnvironment, filesToAnalyze) } \ No newline at end of file diff --git a/kotlin-eclipse-test-framework/src/org/jetbrains/kotlin/testframework/editor/KotlinEditorWithAfterFileTestCase.java b/kotlin-eclipse-test-framework/src/org/jetbrains/kotlin/testframework/editor/KotlinEditorWithAfterFileTestCase.java deleted file mode 100644 index 1252051db..000000000 --- a/kotlin-eclipse-test-framework/src/org/jetbrains/kotlin/testframework/editor/KotlinEditorWithAfterFileTestCase.java +++ /dev/null @@ -1,181 +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.testframework.editor; - -import com.intellij.openapi.util.Condition; -import com.intellij.util.containers.ContainerUtil; -import org.jetbrains.kotlin.testframework.utils.KotlinTestUtils; -import org.jetbrains.kotlin.testframework.utils.SourceFileData; - -import java.io.File; -import java.io.IOException; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - -public abstract class KotlinEditorWithAfterFileTestCase extends KotlinEditorAutoTestCase { - - public enum AfterSuffixPosition { - BEFORE_DOT, AFTER_NAME - } - - protected AfterSuffixPosition getAfterPosition() { - return AfterSuffixPosition.AFTER_NAME; - } - - private static class WithAfterSourceFileData extends EditorSourceFileData { - - private static final Condition TARGET_PREDICATE = new Condition() { - @Override - public boolean value(WithAfterSourceFileData data) { - return data.contentAfter != null; - } - }; - - private static final String NO_TARGET_FILE_FOUND_ERROR_MESSAGE = "No target file found"; - private static final String NO_TARGET_FILE_FOUND_FOR_AFTER_FILE_ERROR_MESSAGE_FORMAT = "No target file found for \'%s\' file"; - - private String contentAfter = null; - - public WithAfterSourceFileData(File file) { - super(file); - } - - public String getContentAfter() { - return contentAfter; - } - - public static Collection getTestFiles(File testFolder) { - Map result = new HashMap(); - - File targetAfterFile = null; - for (File file : testFolder.listFiles()) { - String fileName = file.getName(); - - if (!fileName.endsWith(AFTER_FILE_EXTENSION)) { - result.put(fileName, new WithAfterSourceFileData(file)); - } else { - targetAfterFile = file; - } - } - - if (targetAfterFile == null) { - throw new RuntimeException(NO_TARGET_FILE_FOUND_ERROR_MESSAGE); - } - - WithAfterSourceFileData target = result.get(targetAfterFile.getName().replace(AFTER_FILE_EXTENSION, "")); - if (target == null) { - throw new RuntimeException(String.format(NO_TARGET_FILE_FOUND_FOR_AFTER_FILE_ERROR_MESSAGE_FORMAT, targetAfterFile.getAbsolutePath())); - } - - target.contentAfter = KotlinTestUtils.getText(targetAfterFile.getAbsolutePath()); - - return result.values(); - } - - public static WithAfterSourceFileData getTargetFile(Iterable files) { - return ContainerUtil.find(files, TARGET_PREDICATE); - } - } - - private TextEditorTest testEditor; - - protected abstract void performTest(String fileText, String expectedFileText); - - protected TextEditorTest getTestEditor() { - return testEditor; - } - - protected boolean loadFilesBeforeOpeningEditor() { - return false; - } - - @Override - protected void doSingleFileAutoTest(String testPath) { - String fileText = loadEditor(testPath); - - String afterTestPath; - AfterSuffixPosition afterPosition = getAfterPosition(); - if (afterPosition == AfterSuffixPosition.AFTER_NAME) { - afterTestPath = testPath + AFTER_FILE_EXTENSION; - } else { - afterTestPath = testPath.substring(0, testPath.length() - getExtension().length()) + AFTER_FILE_EXTENSION + getExtension(); - } - - performTest(fileText, KotlinTestUtils.getText(afterTestPath)); - } - - @Override - protected void doMultiFileAutoTest(File testFolder) { - Collection files = WithAfterSourceFileData.getTestFiles(testFolder); - - WithAfterSourceFileData target = WithAfterSourceFileData.getTargetFile(files); - - if (loadFilesBeforeOpeningEditor()) { - loadFiles(files, target); - testEditor = configureEditor(target.getFileName(), target.getContent(), target.getPackageName()); - } else { - testEditor = configureEditor(target.getFileName(), target.getContent(), target.getPackageName()); - loadFiles(files, target); - } - - performTest(target.getContent(), target.getContentAfter()); - } - - private void loadFiles(Collection files, WithAfterSourceFileData target) { - for (WithAfterSourceFileData file : files) { - if (file != target) { - createSourceFile(file.getPackageName(), file.getFileName(), file.getContent()); - } - } - } - - @Override - protected void doAutoTestWithDependencyFile(String mainTestPath, File dependencyFile) { - String fileText; - - if (loadFilesBeforeOpeningEditor()) { - loadDependencyFile(dependencyFile); - fileText = loadEditor(mainTestPath); - } else { - fileText = loadEditor(mainTestPath); - loadDependencyFile(dependencyFile); - } - - performTest(fileText, KotlinTestUtils.getText(mainTestPath + AFTER_FILE_EXTENSION)); - } - - private String loadEditor(String mainTestPath) { - String fileText = KotlinTestUtils.getText(mainTestPath); - testEditor = configureEditor(KotlinTestUtils.getNameByPath(mainTestPath), fileText, - WithAfterSourceFileData.getPackageFromContent(fileText)); - return fileText; - } - - private void loadDependencyFile(File dependencyFile) { - try { - SourceFileData dependencySourceFile = new SourceFileData(dependencyFile); - String fileName = dependencySourceFile.getFileName(); - String dependencyFileName = fileName.substring(0, fileName.indexOf(FILE_DEPENDENCY_SUFFIX)) + - "_dependency" + getExtension(); - createSourceFile(dependencySourceFile.getPackageName(), dependencyFileName, - dependencySourceFile.getContent()); - } catch (IOException e) { - throw new RuntimeException(e); - } - } -} diff --git a/kotlin-eclipse-test-framework/src/org/jetbrains/kotlin/testframework/editor/KotlinEditorWithAfterFileTestCase.kt b/kotlin-eclipse-test-framework/src/org/jetbrains/kotlin/testframework/editor/KotlinEditorWithAfterFileTestCase.kt new file mode 100644 index 000000000..963b42bfd --- /dev/null +++ b/kotlin-eclipse-test-framework/src/org/jetbrains/kotlin/testframework/editor/KotlinEditorWithAfterFileTestCase.kt @@ -0,0 +1,167 @@ +/******************************************************************************* + * 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.testframework.editor; + +import com.intellij.openapi.util.Condition +import com.intellij.util.containers.ContainerUtil +import kotlinx.coroutines.runBlocking +import org.jetbrains.kotlin.core.model.KotlinAnalysisFileCache +import org.jetbrains.kotlin.core.model.KotlinAnalysisProjectCache + +import org.jetbrains.kotlin.testframework.utils.KotlinTestUtils +import org.jetbrains.kotlin.testframework.utils.SourceFileData + +import java.io.File +import java.io.IOException + +abstract class KotlinEditorWithAfterFileTestCase: KotlinEditorAutoTestCase() { + + companion object { + private const val NO_TARGET_FILE_FOUND_ERROR_MESSAGE = "No target file found" + private const val NO_TARGET_FILE_FOUND_FOR_AFTER_FILE_ERROR_MESSAGE_FORMAT = "No target file found for \'%s\' file" + } + + enum class AfterSuffixPosition { + BEFORE_DOT, AFTER_NAME + } + + protected open fun getAfterPosition(): AfterSuffixPosition = + AfterSuffixPosition.AFTER_NAME + + private class WithAfterSourceFileData(file: File) : EditorSourceFileData(file) { + + var contentAfter: String? = null + private set + + companion object { + + private val TARGET_PREDICATE = + Condition { data -> data.contentAfter != null } + + fun getTargetFile(files: Iterable): WithAfterSourceFileData? = + ContainerUtil.find(files, TARGET_PREDICATE) + + fun getTestFiles(testFolder: File): MutableCollection { + val result = HashMap() + + var targetAfterFile: File? = null + testFolder.listFiles().forEach { file -> + if (!file.name.endsWith(AFTER_FILE_EXTENSION)) { + result[file.name] = WithAfterSourceFileData(file) + } else { + targetAfterFile = file + } + } + + targetAfterFile?.let { + result[it.name.replace(AFTER_FILE_EXTENSION, "")] + ?.apply { + contentAfter = KotlinTestUtils.getText(it.absolutePath) + } ?: throw RuntimeException(String.format(NO_TARGET_FILE_FOUND_FOR_AFTER_FILE_ERROR_MESSAGE_FORMAT, it.absolutePath)) + + } ?: throw RuntimeException(NO_TARGET_FILE_FOUND_ERROR_MESSAGE) + + return result.values + } + } + } + + protected lateinit var testEditor: TextEditorTest + + protected abstract fun performTest(fileText: String, expectedFileText: String) + + protected open fun loadFilesBeforeOpeningEditor() = false + + private fun waitForCache() { + runBlocking { + //KotlinAnalysisProjectCache.getAnalysisResult(testProject.javaProject) + //KotlinAnalysisFileCache.resetCache() + } + } + + override fun doSingleFileAutoTest(testPath: String) { + val fileText = loadEditor(testPath) + + val afterTestPath = if (getAfterPosition() == AfterSuffixPosition.AFTER_NAME) { + testPath + AFTER_FILE_EXTENSION + } else { + testPath.substring(0, testPath.length - extension.length) + AFTER_FILE_EXTENSION + extension + } + waitForCache() + performTest(fileText, KotlinTestUtils.getText(afterTestPath)) + } + + override fun doMultiFileAutoTest(testFolder: File) { + val files = WithAfterSourceFileData.getTestFiles(testFolder) + + val target = WithAfterSourceFileData.getTargetFile(files)!! + + if (loadFilesBeforeOpeningEditor()) { + loadFiles(files, target) + testEditor = configureEditor(target.fileName, target.content, target.packageName) + } else { + testEditor = configureEditor(target.fileName, target.content, target.packageName) + loadFiles(files, target) + } + waitForCache() + + performTest(target.content, target.contentAfter!!) + } + + private fun loadFiles(files: Collection, target: WithAfterSourceFileData ) = + files.forEach { file -> + if (file != target) { + createSourceFile(file.packageName, file.fileName, file.content) + } + } + + override fun doAutoTestWithDependencyFile(mainTestPath: String, dependencyFile: File) { + lateinit var fileText: String + + if (loadFilesBeforeOpeningEditor()) { + loadDependencyFile(dependencyFile) + fileText = loadEditor(mainTestPath) + } else { + fileText = loadEditor(mainTestPath) + loadDependencyFile(dependencyFile) + } + + waitForCache() + performTest(fileText, KotlinTestUtils.getText(mainTestPath + AFTER_FILE_EXTENSION)) + } + + private fun loadEditor(mainTestPath: String): String { + val fileText = KotlinTestUtils.getText(mainTestPath) + testEditor = configureEditor(KotlinTestUtils.getNameByPath(mainTestPath), fileText, + SourceFileData.getPackageFromContent(fileText)) + return fileText + } + + private fun loadDependencyFile(dependencyFile: File) { + try { + val dependencySourceFile = SourceFileData(dependencyFile) + val fileName = dependencySourceFile.fileName + val dependencyFileName = fileName.substring(0, fileName.indexOf(FILE_DEPENDENCY_SUFFIX)) + + "_dependency" + extension + createSourceFile(dependencySourceFile.packageName, dependencyFileName, + dependencySourceFile.content + ) + } catch (e: IOException) { + throw RuntimeException(e) + } + } +} diff --git a/kotlin-eclipse-ui-test/src/org/jetbrains/kotlin/ui/tests/editors/quickfix/autoimport/KotlinAutoImportTestCase.java b/kotlin-eclipse-ui-test/src/org/jetbrains/kotlin/ui/tests/editors/quickfix/autoimport/KotlinAutoImportTestCase.java index 57896886f..c9b2e13da 100644 --- a/kotlin-eclipse-ui-test/src/org/jetbrains/kotlin/ui/tests/editors/quickfix/autoimport/KotlinAutoImportTestCase.java +++ b/kotlin-eclipse-ui-test/src/org/jetbrains/kotlin/ui/tests/editors/quickfix/autoimport/KotlinAutoImportTestCase.java @@ -20,7 +20,7 @@ import java.util.List; import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor; -import org.jetbrains.kotlin.core.model.KotlinAnalysisFileCache; +import org.jetbrains.kotlin.core.resolve.KotlinAnalysisScope; import org.jetbrains.kotlin.testframework.editor.KotlinEditorWithAfterFileTestCase; import org.jetbrains.kotlin.testframework.utils.EditorTestUtils; import org.jetbrains.kotlin.testframework.utils.ExpectedCompletionUtils; @@ -46,23 +46,18 @@ protected boolean loadFilesBeforeOpeningEditor() { } private List createProposals() { - // TODO: find better solution than this - try { - Thread.sleep(500); - } catch (InterruptedException ignored) {} - KotlinAnalysisFileCache.INSTANCE.resetCache(); - return KotlinQuickFixTestCaseKt.getProposals(getTestEditor()); } @Override protected void performTest(String fileText, String content) { + KotlinAnalysisScope.INSTANCE.join(); List proposals = createProposals(); assertCount(proposals, fileText); assertExistence(proposals, fileText); if (!proposals.isEmpty()) { - proposals.get(0).apply(getTestEditor().getEditingFile());; + proposals.get(0).apply(getTestEditor().getEditingFile()); } EditorTestUtils.assertByEditor(getEditor(), content); @@ -72,10 +67,10 @@ protected void performTest(String fileText, String content) { protected String getTestDataRelativePath() { return AUTOIMPORT_TEST_DATA_PATH_SEGMENT; } - + private static void assertCount(List proposals, String fileText) { Integer expectedNumber = ExpectedCompletionUtils.numberOfItemsShouldPresent(fileText); - + if (expectedNumber != null) { Assert.assertEquals(COUNT_ASSERTION_ERROR_MESSAGE, expectedNumber.intValue(), proposals.size()); } diff --git a/kotlin-eclipse-ui-test/src/org/jetbrains/kotlin/ui/tests/editors/quickfix/autoimport/KotlinQuickFixTestCase.kt b/kotlin-eclipse-ui-test/src/org/jetbrains/kotlin/ui/tests/editors/quickfix/autoimport/KotlinQuickFixTestCase.kt index ea5efcde9..6475cda24 100644 --- a/kotlin-eclipse-ui-test/src/org/jetbrains/kotlin/ui/tests/editors/quickfix/autoimport/KotlinQuickFixTestCase.kt +++ b/kotlin-eclipse-ui-test/src/org/jetbrains/kotlin/ui/tests/editors/quickfix/autoimport/KotlinQuickFixTestCase.kt @@ -2,28 +2,11 @@ package org.jetbrains.kotlin.ui.tests.editors.quickfix.autoimport import org.jetbrains.kotlin.testframework.editor.KotlinEditorWithAfterFileTestCase import org.junit.Before -import org.jetbrains.kotlin.testframework.utils.KotlinTestUtils -import org.eclipse.ui.ide.IDE -import org.eclipse.core.resources.IMarker -import org.eclipse.core.resources.IResource -import org.eclipse.jface.text.source.ISourceViewer -import org.jetbrains.kotlin.diagnostics.Diagnostic -import org.jetbrains.kotlin.ui.editors.annotations.DiagnosticAnnotation -import org.jetbrains.kotlin.ui.editors.annotations.endOffset -import org.eclipse.jface.text.source.IAnnotationModel import org.jetbrains.kotlin.ui.editors.quickfix.KotlinMarkerResolutionGenerator -import org.jetbrains.kotlin.ui.editors.annotations.AnnotationManager -import org.jetbrains.kotlin.eclipse.ui.utils.getBindingContext import org.jetbrains.kotlin.ui.editors.KotlinFileEditor -import org.jetbrains.kotlin.core.builder.KotlinPsiManager import org.junit.Assert import org.jetbrains.kotlin.testframework.utils.EditorTestUtils -import org.jetbrains.kotlin.eclipse.ui.utils.LineEndUtil -import org.eclipse.jdt.core.IJavaProject -import org.eclipse.core.resources.IFile -import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.ui.editors.quickfix.KotlinMarkerResolution -import org.eclipse.jface.text.IDocument import org.jetbrains.kotlin.testframework.editor.TextEditorTest import org.jetbrains.kotlin.ui.editors.findDiagnosticsBy import org.eclipse.jface.text.source.TextInvocationContext @@ -39,8 +22,8 @@ abstract class KotlinQuickFixTestCase : KotlinEditorWithAfterFileTestCase() { override fun performTest(fileText: String, expectedFileText: String) { val firstLine = fileText.split("\n")[0] - val splittedLine = expectedQuickFixRegex.find(firstLine) - val expectedLabel = splittedLine!!.groups[1]!!.value + val splattedLine = expectedQuickFixRegex.find(firstLine) + val expectedLabel = splattedLine!!.groups[1]!!.value val foundProposals = getProposals(testEditor) val resolution = foundProposals.find { it.label == expectedLabel } @@ -49,14 +32,14 @@ abstract class KotlinQuickFixTestCase : KotlinEditorWithAfterFileTestCase() { "Expected proposal with label \"$expectedLabel\" wasn't found. Found proposals:\n${foundProposals.joinToString("\n") { it.label }}", resolution) - resolution!!.apply(testEditor.getEditingFile()) + resolution!!.apply(testEditor.editingFile) - EditorTestUtils.assertByEditor(testEditor.getEditor(), expectedFileText); + EditorTestUtils.assertByEditor(testEditor.editor, expectedFileText); } } fun getProposals(testEditor: TextEditorTest): List { - val editor = testEditor.getEditor() as KotlinFileEditor + val editor = testEditor.editor as KotlinFileEditor val diagnostics = findDiagnosticsBy(TextInvocationContext(editor.viewer, testEditor.caretOffset, 0), editor) return KotlinMarkerResolutionGenerator.getResolutions(diagnostics) diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/KotlinAnalysisJob.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/KotlinAnalysisJob.kt deleted file mode 100644 index a3fe329a7..000000000 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/KotlinAnalysisJob.kt +++ /dev/null @@ -1,100 +0,0 @@ -/******************************************************************************* -* 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.runtime.IProgressMonitor -import org.eclipse.core.runtime.IStatus -import org.eclipse.core.runtime.NullProgressMonitor -import org.eclipse.core.runtime.Status -import org.eclipse.core.runtime.jobs.IJobChangeEvent -import org.eclipse.core.runtime.jobs.Job -import org.eclipse.core.runtime.jobs.JobChangeAdapter -import org.eclipse.jdt.core.IJavaProject -import org.jetbrains.kotlin.analyzer.AnalysisResult -import org.jetbrains.kotlin.core.model.KotlinAnalysisProjectCache -import org.jetbrains.kotlin.progress.CompilationCanceledException -import org.jetbrains.kotlin.progress.CompilationCanceledStatus -import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus - -public class KotlinAnalysisJob(private val javaProject: IJavaProject) : Job("Kotlin Analysis") { - init { - setPriority(DECORATE) - setSystem(true) - } - - val familyIndicator = constructFamilyIndicator(javaProject) - - @Volatile var canceled = false - - override fun run(monitor: IProgressMonitor): IStatus { - try { - canceled = false - - ProgressIndicatorAndCompilationCanceledStatus.setCompilationCanceledStatus(object : CompilationCanceledStatus { - override fun checkCanceled() { - if (canceled) throw CompilationCanceledException() - } - }) - - if (!javaProject.isOpen) { - return Status.OK_STATUS - } - - val analysisResult = KotlinAnalysisProjectCache.getAnalysisResult(javaProject) - - return AnalysisResultStatus(Status.OK_STATUS, analysisResult) - } catch (e: CompilationCanceledException) { - return AnalysisResultStatus(Status.CANCEL_STATUS, AnalysisResult.EMPTY) - } finally { - ProgressIndicatorAndCompilationCanceledStatus.setCompilationCanceledStatus(null) - } - } - - override fun belongsTo(family: Any): Boolean = family == familyIndicator - - override fun canceling() { - super.canceling() - canceled = true - } - - class AnalysisResultStatus(val status: IStatus, val analysisResult: AnalysisResult): IStatus by status -} - -private fun constructFamilyIndicator(javaProject: IJavaProject): String { - return javaProject.getProject().getName() + "_kotlinAnalysisFamily" -} - -fun runCancellableAnalysisFor(javaProject: IJavaProject, postAnalysisTask: (AnalysisResult) -> Unit = {}) { - val family = constructFamilyIndicator(javaProject) - Job.getJobManager().cancel(family) - Job.getJobManager().join(family, NullProgressMonitor()) // It should be fast enough - - KotlinAnalysisProjectCache.resetCache(javaProject.project) - - val analysisJob = KotlinAnalysisJob(javaProject) - - analysisJob.addJobChangeListener(object : JobChangeAdapter() { - override fun done(event: IJobChangeEvent) { - val result = event.result - if (result is KotlinAnalysisJob.AnalysisResultStatus && result.isOK) { - postAnalysisTask(result.analysisResult) - } - } - }) - - analysisJob.schedule() -} \ No newline at end of file diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/KotlinBuilder.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/KotlinBuilder.kt index 266f4a6ec..6a1f9ad61 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/KotlinBuilder.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/KotlinBuilder.kt @@ -31,6 +31,8 @@ import org.eclipse.ui.PlatformUI import org.jetbrains.kotlin.core.asJava.KotlinLightClassGeneration import org.jetbrains.kotlin.core.builder.KotlinPsiManager import org.jetbrains.kotlin.core.compiler.KotlinCompilerUtils +import org.jetbrains.kotlin.core.model.KotlinAnalysisProjectCache +import org.jetbrains.kotlin.core.model.KotlinAnalysisProjectCache.cancelablePostAnalysis import org.jetbrains.kotlin.core.model.KotlinScriptEnvironment import org.jetbrains.kotlin.core.model.runJob import org.jetbrains.kotlin.core.resolve.KotlinAnalyzer @@ -97,14 +99,14 @@ class KotlinBuilder : IncrementalProjectBuilder() { val ktFiles = existingAffectedFiles.map { KotlinPsiManager.getParsedFile(it) } val analysisResultWithProvider = if (ktFiles.isEmpty()) - KotlinAnalyzer.analyzeProject(project) + KotlinAnalysisProjectCache.getAnalysisResult(javaProject) else KotlinAnalyzer.analyzeFiles(ktFiles) clearProblemAnnotationsFromOpenEditorsExcept(existingAffectedFiles) updateLineMarkers(analysisResultWithProvider.analysisResult.bindingContext.diagnostics, existingAffectedFiles) - runCancellableAnalysisFor(javaProject) { analysisResult -> + cancelablePostAnalysis(javaProject) { analysisResult -> val projectFiles = KotlinPsiManager.getFilesByProject(javaProject.project) updateLineMarkers( analysisResult.bindingContext.diagnostics, @@ -145,7 +147,7 @@ class KotlinBuilder : IncrementalProjectBuilder() { clearProblemAnnotationsFromOpenEditorsExcept(emptyList()) clearMarkersFromFiles(existingFiles) - runCancellableAnalysisFor(javaProject) { analysisResult -> + cancelablePostAnalysis(javaProject) { analysisResult -> updateLineMarkers(analysisResult.bindingContext.diagnostics, existingFiles) KotlinLightClassGeneration.updateLightClasses(javaProject.project, kotlinFiles) } @@ -160,10 +162,10 @@ class KotlinBuilder : IncrementalProjectBuilder() { } private fun isAllFromOutputFolder(files: Set, javaProject: IJavaProject): Boolean { - val workspaceLocation = ResourcesPlugin.getWorkspace().getRoot().getFullPath() + val workspaceLocation = ResourcesPlugin.getWorkspace().root.fullPath val outputLocation = javaProject.outputLocation for (file in files) { - val filePathLocation = file.getFullPath().makeRelativeTo(workspaceLocation) + val filePathLocation = file.fullPath.makeRelativeTo(workspaceLocation) if (!outputLocation.isPrefixOf(filePathLocation)) { return false } @@ -175,9 +177,9 @@ class KotlinBuilder : IncrementalProjectBuilder() { private fun getAllAffectedFiles(resourceDelta: IResourceDelta): Set { val affectedFiles = hashSetOf() resourceDelta.accept { delta -> - if (delta.getKind() == IResourceDelta.NO_CHANGE) return@accept false + if (delta.kind == IResourceDelta.NO_CHANGE) return@accept false - val resource = delta.getResource() + val resource = delta.resource if (resource is IFile) { affectedFiles.add(resource) } else { @@ -191,8 +193,8 @@ class KotlinBuilder : IncrementalProjectBuilder() { } private fun isBuildingForLaunch(): Boolean { - val launchDelegateFQName = LaunchConfigurationDelegate::class.java.getCanonicalName() - return Thread.currentThread().getStackTrace().find { it.className == launchDelegateFQName } != null + val launchDelegateFQName = LaunchConfigurationDelegate::class.java.canonicalName + return Thread.currentThread().stackTrace.find { it.className == launchDelegateFQName } != null } private fun compileKotlinFiles(javaProject: IJavaProject) { @@ -213,9 +215,9 @@ private fun clearMarkersFromFiles(files: List) { } private fun clearProblemAnnotationsFromOpenEditorsExcept(affectedFiles: List) { - for (window in PlatformUI.getWorkbench().getWorkbenchWindows()) { - for (page in window.getPages()) { - page.getEditorReferences() + for (window in PlatformUI.getWorkbench().workbenchWindows) { + for (page in window.pages) { + page.editorReferences .map { it.getEditor(false) } .filterIsInstance(KotlinFileEditor::class.java) .filterNot { it.eclipseFile in affectedFiles } @@ -246,10 +248,10 @@ object ScriptFileFilter : KotlinFileFilterForBuild { object FileFromOuputFolderFilter : KotlinFileFilterForBuild { override fun isApplicable(file: IFile, javaProject: IJavaProject): Boolean { - val workspaceLocation = ResourcesPlugin.getWorkspace().getRoot().getFullPath() + val workspaceLocation = ResourcesPlugin.getWorkspace().root.fullPath val outputLocation = javaProject.outputLocation - val filePathLocation = file.getFullPath().makeRelativeTo(workspaceLocation) + val filePathLocation = file.fullPath.makeRelativeTo(workspaceLocation) return outputLocation.isPrefixOf(filePathLocation) } } diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/KotlinCorrectionProcessor.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/KotlinCorrectionProcessor.kt index d89fc07e5..a44ae4d0f 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/KotlinCorrectionProcessor.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/editors/KotlinCorrectionProcessor.kt @@ -87,5 +87,5 @@ fun findDiagnosticsBy(invocationContext: IQuickAssistInvocationContext, editor: val range = it.psiElement.textRange range.startOffset <= offset && offset <= range.endOffset } - } ?: emptyList() + } ?: emptyList() } \ No newline at end of file