mirror of
https://github.com/jlengrand/kotlin.git
synced 2026-04-04 15:51:54 +00:00
Kotlin Facet: Implement automatic facet configuration
This commit is contained in:
@@ -40,6 +40,9 @@
|
||||
<component>
|
||||
<implementation-class>org.jetbrains.kotlin.idea.project.KotlinCodeBlockModificationListener</implementation-class>
|
||||
</component>
|
||||
<component>
|
||||
<implementation-class>org.jetbrains.kotlin.idea.facet.FacetConfigurator</implementation-class>
|
||||
</component>
|
||||
</project-components>
|
||||
|
||||
<application-components>
|
||||
@@ -743,6 +746,8 @@
|
||||
|
||||
<facetType implementation="org.jetbrains.kotlin.idea.facet.KotlinFacetType"/>
|
||||
|
||||
<framework.detector implementation="org.jetbrains.kotlin.idea.facet.KotlinFrameworkDetector"/>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.idea.intentions.FoldInitializerAndIfToElvisIntention</className>
|
||||
<category>Kotlin</category>
|
||||
|
||||
200
idea/src/org/jetbrains/kotlin/idea/facet/FacetConfigurator.kt
Normal file
200
idea/src/org/jetbrains/kotlin/idea/facet/FacetConfigurator.kt
Normal file
@@ -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<DetectedFrameworkDescription>()
|
||||
val oldDescriptions = ArrayList<DetectedFrameworkDescription>()
|
||||
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<DetectedFrameworkDescription> {
|
||||
val acceptedFiles = index.getContainingFiles(FrameworkDetectionIndex.NAME, detectorId!!, GlobalSearchScope.projectScope(myProject))
|
||||
val filesToProcess: Collection<VirtualFile>
|
||||
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<DetectedFrameworkDescription>
|
||||
if (!filesToProcess.isEmpty()) {
|
||||
frameworks = detector.internalDetect(filesToProcess, FrameworkDetectionContextImpl(myProject))
|
||||
}
|
||||
else {
|
||||
frameworks = arrayListOf<DetectedFrameworkDescription>()
|
||||
}
|
||||
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<Int>) {
|
||||
for (detectorId in detectors) {
|
||||
FileBasedIndex.getInstance().getValues(FrameworkDetectionIndex.NAME, detectorId, GlobalSearchScope.projectScope(myProject))
|
||||
}
|
||||
}
|
||||
|
||||
private inner class FrameworkDetectionHighlightingPass(
|
||||
editor: Editor,
|
||||
private val myDetectors: Collection<Int>
|
||||
) : 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)!!
|
||||
}
|
||||
}
|
||||
@@ -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<KotlinFacet, KotlinFacetConfiguration>(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<VirtualFile>,
|
||||
context: FrameworkDetectionContext
|
||||
) = emptyList<DetectedFrameworkDescription>()
|
||||
|
||||
fun internalDetect(
|
||||
newFiles: Collection<VirtualFile>,
|
||||
context: FrameworkDetectionContext
|
||||
) = super.detect(newFiles, context)
|
||||
}
|
||||
Reference in New Issue
Block a user