diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml
index a40708e9be5..07ae7dd56f7 100644
--- a/idea/src/META-INF/plugin.xml
+++ b/idea/src/META-INF/plugin.xml
@@ -40,6 +40,9 @@
org.jetbrains.kotlin.idea.project.KotlinCodeBlockModificationListener
+
+ org.jetbrains.kotlin.idea.facet.FacetConfigurator
+
@@ -743,6 +746,8 @@
+
+
org.jetbrains.kotlin.idea.intentions.FoldInitializerAndIfToElvisIntention
Kotlin
diff --git a/idea/src/org/jetbrains/kotlin/idea/facet/FacetConfigurator.kt b/idea/src/org/jetbrains/kotlin/idea/facet/FacetConfigurator.kt
new file mode 100644
index 00000000000..ebf39417b87
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/facet/FacetConfigurator.kt
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2010-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.idea.facet
+
+import com.intellij.codeHighlighting.TextEditorHighlightingPass
+import com.intellij.codeHighlighting.TextEditorHighlightingPassFactory
+import com.intellij.codeHighlighting.TextEditorHighlightingPassRegistrar
+import com.intellij.framework.detection.DetectedFrameworkDescription
+import com.intellij.framework.detection.DetectionExcludesConfiguration
+import com.intellij.framework.detection.impl.*
+import com.intellij.framework.detection.impl.exclude.DetectionExcludesConfigurationImpl
+import com.intellij.openapi.application.ApplicationManager
+import com.intellij.openapi.components.AbstractProjectComponent
+import com.intellij.openapi.editor.Editor
+import com.intellij.openapi.progress.ProgressIndicator
+import com.intellij.openapi.project.DumbService
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.roots.PlatformModifiableModelsProvider
+import com.intellij.openapi.roots.ui.configuration.DefaultModulesProvider
+import com.intellij.openapi.startup.StartupManager
+import com.intellij.openapi.vfs.VirtualFile
+import com.intellij.psi.PsiFile
+import com.intellij.psi.search.GlobalSearchScope
+import com.intellij.util.indexing.FileBasedIndex
+import com.intellij.util.ui.update.MergingUpdateQueue
+import com.intellij.util.ui.update.Update
+import java.util.*
+
+// Based on com.intellij.framework.detection.impl.FrameworkDetectionManager
+class FacetConfigurator(
+ project: Project,
+ highlightingPassRegistrar: TextEditorHighlightingPassRegistrar
+) : AbstractProjectComponent(project), FrameworkDetectionIndexListener, TextEditorHighlightingPassFactory {
+ private val myDetectionUpdate = object : Update("detection") {
+ override fun run() {
+ doRunDetection()
+ }
+ }
+ private @Volatile var shouldDetect = false
+ private var myDetectionQueue: MergingUpdateQueue? = null
+ private var myDetectedFrameworksData: DetectedFrameworksData? = null
+
+ init {
+ highlightingPassRegistrar.registerTextEditorHighlightingPass(this, TextEditorHighlightingPassRegistrar.Anchor.LAST, -1, false, false)
+ }
+
+ override fun initComponent() {
+ if (!myProject.isDefault && !ApplicationManager.getApplication().isUnitTestMode) {
+ doInitialize()
+ }
+ }
+
+ fun doInitialize() {
+ myDetectionQueue = MergingUpdateQueue("FrameworkDetectionQueue", 500, true, null, myProject)
+ if (ApplicationManager.getApplication().isUnitTestMode) {
+ myDetectionQueue!!.isPassThrough = false
+ myDetectionQueue!!.hideNotify()
+ }
+ myDetectedFrameworksData = DetectedFrameworksData(myProject)
+ FrameworkDetectionIndex.getInstance().addListener(this, myProject)
+ myProject.messageBus.connect().subscribe(
+ DumbService.DUMB_MODE,
+ object : DumbService.DumbModeListener {
+ override fun enteredDumbMode() = myDetectionQueue!!.suspend()
+ override fun exitDumbMode() = myDetectionQueue!!.resume()
+ }
+ )
+ }
+
+ override fun projectOpened() {
+ StartupManager.getInstance(myProject).registerPostStartupActivity {
+ shouldDetect = true
+ queueDetection()
+ }
+ }
+
+ override fun disposeComponent() {
+ doDispose()
+ }
+
+ fun doDispose() {
+ if (myDetectedFrameworksData != null) {
+ myDetectedFrameworksData!!.saveDetected()
+ myDetectedFrameworksData = null
+ }
+ }
+
+ override fun fileUpdated(file: VirtualFile, detectorId: Int) {
+ if (detectorId != KotlinFrameworkDetector.detectorIndex) return
+ shouldDetect = true
+ queueDetection()
+ }
+
+ private fun queueDetection() {
+ if (myDetectionQueue != null) {
+ myDetectionQueue!!.queue(myDetectionUpdate)
+ }
+ }
+
+ override fun createHighlightingPass(file: PsiFile, editor: Editor): TextEditorHighlightingPass? {
+ val detectors = FrameworkDetectorRegistry.getInstance().getDetectorIds(file.fileType)
+ if (!detectors.isEmpty()) {
+ return FrameworkDetectionHighlightingPass(editor, detectors)
+ }
+ return null
+ }
+
+ private fun doRunDetection() {
+ if (!shouldDetect) return
+ shouldDetect = false
+
+ val index = FileBasedIndex.getInstance()
+ val newDescriptions = ArrayList()
+ val oldDescriptions = ArrayList()
+ val excludesConfiguration = DetectionExcludesConfiguration.getInstance(myProject)
+
+ val id = KotlinFrameworkDetector.detectorIndex
+ val frameworks = runDetector(id, index, excludesConfiguration, true)
+ oldDescriptions.addAll(frameworks)
+ val updated = myDetectedFrameworksData!!.updateFrameworksList(id, frameworks)
+ newDescriptions.addAll(updated)
+ oldDescriptions.removeAll(updated)
+
+ if (FrameworkDetectionUtil.removeDisabled(newDescriptions, oldDescriptions).isEmpty()) return
+
+ val framework = getValidDetectedFramework() ?: return
+ FrameworkDetectionUtil.setupFrameworks(listOf(framework), PlatformModifiableModelsProvider(), DefaultModulesProvider(myProject))
+ myDetectedFrameworksData!!.putExistentFrameworkFiles(id, framework.relatedFiles)
+ }
+
+ private fun runDetector(detectorId: Int?,
+ index: FileBasedIndex,
+ excludesConfiguration: DetectionExcludesConfiguration,
+ processNewFilesOnly: Boolean): List {
+ val acceptedFiles = index.getContainingFiles(FrameworkDetectionIndex.NAME, detectorId!!, GlobalSearchScope.projectScope(myProject))
+ val filesToProcess: Collection
+ if (processNewFilesOnly) {
+ filesToProcess = myDetectedFrameworksData!!.retainNewFiles(detectorId, acceptedFiles)
+ }
+ else {
+ filesToProcess = ArrayList(acceptedFiles)
+ }
+ val detector = FrameworkDetectorRegistry.getInstance().getDetectorById(detectorId) as? KotlinFrameworkDetector ?: return emptyList()
+
+ (excludesConfiguration as DetectionExcludesConfigurationImpl).removeExcluded(filesToProcess, detector.frameworkType)
+ val frameworks: List
+ if (!filesToProcess.isEmpty()) {
+ frameworks = detector.internalDetect(filesToProcess, FrameworkDetectionContextImpl(myProject))
+ }
+ else {
+ frameworks = arrayListOf()
+ }
+ return frameworks
+ }
+
+ private fun getValidDetectedFramework(): DetectedFrameworkDescription? {
+ val id = myDetectedFrameworksData!!.detectorsForDetectedFrameworks.singleOrNull() ?: return null
+ val index = FileBasedIndex.getInstance()
+ val excludesConfiguration = DetectionExcludesConfiguration.getInstance(myProject)
+ val frameworks = runDetector(id, index, excludesConfiguration, false)
+ return FrameworkDetectionUtil.removeDisabled(frameworks).singleOrNull()
+ }
+
+ private fun ensureIndexIsUpToDate(detectors: Collection) {
+ for (detectorId in detectors) {
+ FileBasedIndex.getInstance().getValues(FrameworkDetectionIndex.NAME, detectorId, GlobalSearchScope.projectScope(myProject))
+ }
+ }
+
+ private inner class FrameworkDetectionHighlightingPass(
+ editor: Editor,
+ private val myDetectors: Collection
+ ) : TextEditorHighlightingPass(this@FacetConfigurator.myProject, editor.document, false) {
+
+ override fun doCollectInformation(progress: ProgressIndicator) {
+ ensureIndexIsUpToDate(myDetectors)
+ }
+
+ override fun doApplyInformationToEditor() {
+ }
+ }
+
+ companion object {
+ fun getInstance(project: Project) = project.getComponent(FacetConfigurator::class.java)!!
+ }
+}
\ No newline at end of file
diff --git a/idea/src/org/jetbrains/kotlin/idea/facet/KotlinFrameworkDetector.kt b/idea/src/org/jetbrains/kotlin/idea/facet/KotlinFrameworkDetector.kt
new file mode 100644
index 00000000000..c75e8645c7c
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/facet/KotlinFrameworkDetector.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2010-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.idea.facet
+
+import com.intellij.framework.detection.DetectedFrameworkDescription
+import com.intellij.framework.detection.FacetBasedFrameworkDetector
+import com.intellij.framework.detection.FileContentPattern
+import com.intellij.framework.detection.FrameworkDetectionContext
+import com.intellij.framework.detection.impl.FrameworkDetectorRegistry
+import com.intellij.openapi.vfs.VirtualFile
+import org.jetbrains.kotlin.idea.KotlinFileType
+
+class KotlinFrameworkDetector : FacetBasedFrameworkDetector(DETECTOR_ID) {
+ companion object {
+ val DETECTOR_ID = "kotlin"
+
+ val detectorIndex by lazy { FrameworkDetectorRegistry.getInstance().getDetectorId(KotlinFrameworkDetector()) }
+ }
+
+ override fun getFacetType() = KotlinFacetType.INSTANCE
+
+ override fun createSuitableFilePattern() = FileContentPattern.fileContent()
+
+ override fun getFileType() = KotlinFileType.INSTANCE
+
+ // Suppress detection to avoid notification via FrameworkDetectionManager (FacetConfigurator is used instead)
+ override fun detect(
+ newFiles: Collection,
+ context: FrameworkDetectionContext
+ ) = emptyList()
+
+ fun internalDetect(
+ newFiles: Collection,
+ context: FrameworkDetectionContext
+ ) = super.detect(newFiles, context)
+}
\ No newline at end of file