mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-238567 added 3 state auto-reload option
IDEA-238228 added storing of auto-reload option in workspace.xml GitOrigin-RevId: 0a4ce55c040f553fc65ef1e957ed281e2f4a46c6
This commit is contained in:
committed by
intellij-monorepo-bot
parent
8ca9c829a1
commit
2604c5a552
@@ -57,9 +57,13 @@ action.unignore.external.projects.description=Unignore selected {0} {1,choice,1#
|
||||
action.open.config.text=Open {0} Config
|
||||
action.open.config.description=Opens the {0} project file in the editor
|
||||
|
||||
action.refresh.project.auto.text=Auto-reload external changes
|
||||
action.refresh.project.auto.description.enable=Enable to automatically reload the project after VCS updates and changes to build files made outside IDE
|
||||
action.refresh.project.auto.description.disable=Disable to automatically reload the project after VCS updates and changes to build files made outside IDE
|
||||
action.refresh.project.auto.text=Toggle Auto-reload Mode
|
||||
action.refresh.project.auto.text.all=Enable auto-reload after any changes
|
||||
action.refresh.project.auto.text.selective=Enable auto-reload only after external changes
|
||||
action.refresh.project.auto.text.none=Disable auto-reload after all changes
|
||||
action.refresh.project.auto.description.all=Auto-reload the project after any changes to build files
|
||||
action.refresh.project.auto.description.selective=Auto-reload the project after VCS updates and changes to build files made outside IDE
|
||||
action.refresh.project.auto.description.none=Don't auto-reload project after any changes to build files
|
||||
|
||||
action.open.settings.text={0} Settings
|
||||
action.open.settings.description=Edit {0} settings for the current project
|
||||
|
||||
+1
@@ -11,6 +11,7 @@ interface ExternalSystemProjectTracker {
|
||||
/**
|
||||
* Enables/disables auto reload external changes for all projects
|
||||
*/
|
||||
@Deprecated("Use ExternalSystemProjectTrackerSettings.autoReloadType instead")
|
||||
var isAutoReloadExternalChanges: Boolean
|
||||
|
||||
/**
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.openapi.externalSystem.autoimport
|
||||
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.openapi.project.Project
|
||||
|
||||
interface ExternalSystemProjectTrackerSettings {
|
||||
|
||||
var autoReloadType: AutoReloadType
|
||||
|
||||
enum class AutoReloadType {
|
||||
/**
|
||||
* Auto-reload project after any changes to build files
|
||||
*/
|
||||
ALL,
|
||||
|
||||
/**
|
||||
* Auto-reload project after VCS updates and changes to build files made outside IDE
|
||||
*/
|
||||
SELECTIVE,
|
||||
|
||||
/**
|
||||
* Don't auto-reload project
|
||||
*/
|
||||
NONE
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun getInstance(project: Project): ExternalSystemProjectTrackerSettings {
|
||||
return ServiceManager.getService(project, ExternalSystemProjectTrackerSettings::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
+32
-17
@@ -10,11 +10,13 @@ import com.intellij.openapi.components.State
|
||||
import com.intellij.openapi.components.Storage
|
||||
import com.intellij.openapi.components.StoragePathMacros.CACHE_FILE
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.externalSystem.autoimport.ExternalSystemProjectTrackerSettings.AutoReloadType
|
||||
import com.intellij.openapi.extensions.ExtensionPointUtil
|
||||
import com.intellij.openapi.externalSystem.ExternalSystemManager
|
||||
import com.intellij.openapi.externalSystem.autoimport.ExternalSystemRefreshStatus.SUCCESS
|
||||
import com.intellij.openapi.externalSystem.autoimport.ProjectStatus.ModificationType
|
||||
import com.intellij.openapi.externalSystem.autoimport.ProjectStatus.ModificationType.EXTERNAL
|
||||
import com.intellij.openapi.externalSystem.autoimport.ProjectStatus.ModificationType.INTERNAL
|
||||
import com.intellij.openapi.externalSystem.autoimport.update.PriorityEatUpdate
|
||||
import com.intellij.openapi.externalSystem.model.ProjectSystemId
|
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
|
||||
@@ -22,12 +24,14 @@ import com.intellij.openapi.observable.operations.AnonymousParallelOperationTrac
|
||||
import com.intellij.openapi.observable.operations.CompoundParallelOperationTrace
|
||||
import com.intellij.openapi.observable.properties.AtomicBooleanProperty
|
||||
import com.intellij.openapi.observable.properties.BooleanProperty
|
||||
import com.intellij.openapi.observable.properties.PropertyView
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.util.LocalTimeCounter.currentTime
|
||||
import com.intellij.util.concurrency.AppExecutorUtil
|
||||
import com.intellij.util.ui.update.MergingUpdateQueue
|
||||
import com.intellij.util.ui.update.Update
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import kotlin.streams.asStream
|
||||
@@ -41,17 +45,21 @@ class AutoImportProjectTracker(private val project: Project) : ExternalSystemPro
|
||||
private val LOG = Logger.getInstance("#com.intellij.openapi.externalSystem.autoimport")
|
||||
private val AUTO_REPARSE_DELAY get() = DaemonCodeAnalyzerSettings.getInstance().autoReparseDelay
|
||||
|
||||
private val settings get() = ProjectTrackerSettings.getInstance(project)
|
||||
private val projectStates = ConcurrentHashMap<State.Id, State.Project>()
|
||||
private val projectDataMap = ConcurrentHashMap<ExternalSystemProjectId, ProjectData>()
|
||||
private val isDisabled = AtomicBooleanProperty(ApplicationManager.getApplication().isUnitTestMode)
|
||||
private val asyncChangesProcessingProperty = AtomicBooleanProperty(!ApplicationManager.getApplication().isHeadlessEnvironment)
|
||||
private val autoReloadExternalChangesProperty = AtomicBooleanProperty(true)
|
||||
private val projectChangeOperation = AnonymousParallelOperationTrace(debugName = "Project change operation")
|
||||
private val projectRefreshOperation = CompoundParallelOperationTrace<String>(debugName = "Project refresh operation")
|
||||
private val dispatcher = MergingUpdateQueue("AutoImportProjectTracker.dispatcher", AUTO_REPARSE_DELAY, false, null, project)
|
||||
private val backgroundExecutor = AppExecutorUtil.createBoundedApplicationPoolExecutor("AutoImportProjectTracker.backgroundExecutor", 1)
|
||||
|
||||
override var isAutoReloadExternalChanges by autoReloadExternalChangesProperty
|
||||
override var isAutoReloadExternalChanges by PropertyView(
|
||||
settings.autoReloadTypeProperty,
|
||||
{ it == AutoReloadType.SELECTIVE },
|
||||
{ if (it) AutoReloadType.SELECTIVE else AutoReloadType.NONE }
|
||||
)
|
||||
|
||||
var isAsyncChangesProcessing by asyncChangesProcessingProperty
|
||||
|
||||
@@ -75,7 +83,7 @@ class AutoImportProjectTracker(private val project: Project) : ExternalSystemPro
|
||||
}
|
||||
|
||||
override fun afterProjectRefresh(status: ExternalSystemRefreshStatus) {
|
||||
if (status != SUCCESS) projectData.status.markDirty(currentTime())
|
||||
if (status != SUCCESS) projectData.status.markBroken(currentTime())
|
||||
projectRefreshOperation.finishTask(id)
|
||||
}
|
||||
}
|
||||
@@ -97,15 +105,26 @@ class AutoImportProjectTracker(private val project: Project) : ExternalSystemPro
|
||||
fun scheduleChangeProcessing() {
|
||||
LOG.debug("Schedule change processing")
|
||||
dispatcher.queue(PriorityEatUpdate(1) {
|
||||
if (getModificationType() == EXTERNAL && isAutoReloadExternalChanges) {
|
||||
refreshProject(doImportDeactivatedProjects = false)
|
||||
}
|
||||
else {
|
||||
updateProjectNotification()
|
||||
}
|
||||
processChanges()
|
||||
})
|
||||
}
|
||||
|
||||
private fun processChanges() {
|
||||
when (settings.autoReloadType) {
|
||||
AutoReloadType.ALL -> when (getModificationType()) {
|
||||
INTERNAL -> refreshProject(doImportDeactivatedProjects = false)
|
||||
EXTERNAL -> refreshProject(doImportDeactivatedProjects = false)
|
||||
null -> updateProjectNotification()
|
||||
}
|
||||
AutoReloadType.SELECTIVE -> when (getModificationType()) {
|
||||
INTERNAL -> updateProjectNotification()
|
||||
EXTERNAL -> refreshProject(doImportDeactivatedProjects = false)
|
||||
null -> updateProjectNotification()
|
||||
}
|
||||
AutoReloadType.NONE -> updateProjectNotification()
|
||||
}
|
||||
}
|
||||
|
||||
private fun refreshProject(doImportDeactivatedProjects: Boolean) {
|
||||
LOG.debug("Incremental project refresh")
|
||||
if (isDisabled.get() || Registry.`is`("external.system.auto.import.disabled")) return
|
||||
@@ -221,11 +240,10 @@ class AutoImportProjectTracker(private val project: Project) : ExternalSystemPro
|
||||
val projectSettingsTrackerStates = projectDataMap.asSequence()
|
||||
.map { (id, data) -> id.getState() to data.getState() }
|
||||
.toMap()
|
||||
return State(isAutoReloadExternalChanges, projectSettingsTrackerStates)
|
||||
return State(projectSettingsTrackerStates)
|
||||
}
|
||||
|
||||
override fun loadState(state: State) {
|
||||
isAutoReloadExternalChanges = state.isAutoReloadExternalChanges
|
||||
projectStates.putAll(state.projectSettingsTrackerStates)
|
||||
projectDataMap.forEach { (id, data) -> loadState(id, data) }
|
||||
}
|
||||
@@ -270,13 +288,13 @@ class AutoImportProjectTracker(private val project: Project) : ExternalSystemPro
|
||||
val notificationAware = ProjectNotificationAware.getInstance(project)
|
||||
projectRefreshOperation.beforeOperation { LOG.debug("Project refresh started") }
|
||||
projectRefreshOperation.beforeOperation { notificationAware.notificationExpire() }
|
||||
projectRefreshOperation.afterOperation { scheduleProjectNotificationUpdate() }
|
||||
projectRefreshOperation.afterOperation { scheduleChangeProcessing() }
|
||||
projectRefreshOperation.afterOperation { LOG.debug("Project refresh finished") }
|
||||
projectChangeOperation.beforeOperation { LOG.debug("Project change started") }
|
||||
projectChangeOperation.beforeOperation { notificationAware.notificationExpire() }
|
||||
projectChangeOperation.afterOperation { scheduleChangeProcessing() }
|
||||
projectChangeOperation.afterOperation { LOG.debug("Project change finished") }
|
||||
autoReloadExternalChangesProperty.afterSet { scheduleProjectRefresh() }
|
||||
settings.autoReloadTypeProperty.afterChange { scheduleChangeProcessing() }
|
||||
asyncChangesProcessingProperty.afterChange { dispatcher.isPassThrough = !it }
|
||||
}
|
||||
|
||||
@@ -308,10 +326,7 @@ class AutoImportProjectTracker(private val project: Project) : ExternalSystemPro
|
||||
}
|
||||
}
|
||||
|
||||
data class State(
|
||||
var isAutoReloadExternalChanges: Boolean = true,
|
||||
var projectSettingsTrackerStates: Map<Id, Project> = emptyMap()
|
||||
) {
|
||||
data class State(var projectSettingsTrackerStates: Map<Id, Project> = emptyMap()) {
|
||||
data class Id(var systemId: String? = null, var externalProjectPath: String? = null)
|
||||
data class Project(
|
||||
var isDirty: Boolean = false,
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.openapi.externalSystem.autoimport
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.actionSystem.Toggleable
|
||||
import com.intellij.openapi.externalSystem.autoimport.ExternalSystemProjectTrackerSettings.AutoReloadType
|
||||
import com.intellij.openapi.externalSystem.autoimport.ToggleAutoReloadActionGroup.Companion.projectTrackerSettings
|
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemBundle
|
||||
import com.intellij.openapi.project.DumbAwareAction
|
||||
|
||||
class UseAllAutoReloadTypeAction : AutoReloadTypeAction(AutoReloadType.ALL)
|
||||
|
||||
class UseSelectiveAutoReloadTypeAction : AutoReloadTypeAction(AutoReloadType.SELECTIVE)
|
||||
|
||||
class UseNoneAutoReloadTypeAction : AutoReloadTypeAction(AutoReloadType.NONE)
|
||||
|
||||
abstract class AutoReloadTypeAction(val type: AutoReloadType) : DumbAwareAction(), Toggleable {
|
||||
override fun update(e: AnActionEvent) {
|
||||
val settings = e.projectTrackerSettings
|
||||
e.presentation.isEnabledAndVisible = settings != null
|
||||
val isSelected = settings?.autoReloadType == type
|
||||
Toggleable.setSelected(e.presentation, isSelected)
|
||||
}
|
||||
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
val settings = e.projectTrackerSettings ?: return
|
||||
settings.autoReloadType = type
|
||||
}
|
||||
|
||||
init {
|
||||
templatePresentation.text = when (type) {
|
||||
AutoReloadType.ALL -> ExternalSystemBundle.message("action.refresh.project.auto.text.all")
|
||||
AutoReloadType.SELECTIVE -> ExternalSystemBundle.message("action.refresh.project.auto.text.selective")
|
||||
AutoReloadType.NONE -> ExternalSystemBundle.message("action.refresh.project.auto.text.none")
|
||||
}
|
||||
templatePresentation.description = when (type) {
|
||||
AutoReloadType.ALL -> ExternalSystemBundle.message("action.refresh.project.auto.description.all")
|
||||
AutoReloadType.SELECTIVE -> ExternalSystemBundle.message("action.refresh.project.auto.description.selective")
|
||||
AutoReloadType.NONE -> ExternalSystemBundle.message("action.refresh.project.auto.description.none")
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
-2
@@ -201,7 +201,7 @@ class ProjectSettingsTracker(
|
||||
status.markModified(currentTime(), modificationType)
|
||||
}
|
||||
else {
|
||||
status.markDirty(currentTime())
|
||||
status.markDirty(currentTime(), modificationType)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,7 +215,12 @@ class ProjectSettingsTracker(
|
||||
if (!hasChanges(newSettingsFilesCRC)) {
|
||||
status.markReverted(currentTime())
|
||||
}
|
||||
projectTracker.scheduleChangeProcessing()
|
||||
if (applyChangesOperation.isOperationCompleted()) {
|
||||
projectTracker.scheduleChangeProcessing()
|
||||
}
|
||||
else {
|
||||
projectTracker.scheduleProjectNotificationUpdate()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+21
-2
@@ -17,14 +17,18 @@ class ProjectStatus(private val debugName: String? = null) {
|
||||
fun isDirty() = state.get() is Dirty
|
||||
|
||||
fun isUpToDate() = when (state.get()) {
|
||||
is Modified, is Dirty -> false
|
||||
is Modified, is Dirty, is Broken -> false
|
||||
is Synchronized, is Reverted -> true
|
||||
}
|
||||
|
||||
fun getModificationType() = when (val state = state.get()) {
|
||||
is Dirty -> state.type
|
||||
is Modified -> state.type
|
||||
is Synchronized, is Reverted -> null
|
||||
else -> null
|
||||
}
|
||||
|
||||
fun markBroken(stamp: Long): ProjectState {
|
||||
return update(Break(stamp))
|
||||
}
|
||||
|
||||
fun markDirty(stamp: Long, type: ModificationType = INTERNAL): ProjectState {
|
||||
@@ -58,24 +62,35 @@ class ProjectStatus(private val debugName: String? = null) {
|
||||
is Invalidate -> event.ifFuture(currentState) { Dirty(it, event.type) }
|
||||
is Modify -> event.ifFuture(currentState) { Modified(it, event.type) }
|
||||
is Revert -> event.ifFuture(currentState, ::Reverted)
|
||||
is Break -> event.ifFuture(currentState, ::Broken)
|
||||
}
|
||||
is Dirty -> when (event) {
|
||||
is Synchronize -> event.ifFuture(currentState, ::Synchronized)
|
||||
is Invalidate -> event.withFuture(currentState) { Dirty(it, currentState.type.merge(event.type)) }
|
||||
is Modify -> event.withFuture(currentState) { Dirty(it, currentState.type.merge(event.type)) }
|
||||
is Revert -> event.withFuture(currentState) { Dirty(it, currentState.type) }
|
||||
is Break -> event.withFuture(currentState) { Dirty(it, currentState.type) }
|
||||
}
|
||||
is Modified -> when (event) {
|
||||
is Synchronize -> event.ifFuture(currentState, ::Synchronized)
|
||||
is Invalidate -> event.withFuture(currentState) { Dirty(it, currentState.type.merge(event.type)) }
|
||||
is Modify -> event.withFuture(currentState) { Modified(it, currentState.type.merge(event.type)) }
|
||||
is Revert -> event.ifFuture(currentState, ::Reverted)
|
||||
is Break -> event.withFuture(currentState) { Dirty(it, currentState.type) }
|
||||
}
|
||||
is Reverted -> when (event) {
|
||||
is Synchronize -> event.ifFuture(currentState, ::Synchronized)
|
||||
is Invalidate -> event.withFuture(currentState) { Dirty(it, event.type) }
|
||||
is Modify -> event.ifFuture(currentState) { Modified(it, event.type) }
|
||||
is Revert -> event.withFuture(currentState, ::Reverted)
|
||||
is Break -> event.ifFuture(currentState, ::Broken)
|
||||
}
|
||||
is Broken -> when (event) {
|
||||
is Synchronize -> event.ifFuture(currentState, ::Synchronized)
|
||||
is Invalidate -> event.withFuture(currentState) { Dirty(it, event.type) }
|
||||
is Modify -> event.withFuture(currentState) { Dirty(it, event.type) }
|
||||
is Revert -> event.withFuture(currentState, ::Broken)
|
||||
is Break -> event.withFuture(currentState, ::Broken)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,6 +131,8 @@ class ProjectStatus(private val debugName: String? = null) {
|
||||
class Modify(stamp: Long, val type: ModificationType) : ProjectEvent(stamp)
|
||||
|
||||
class Revert(stamp: Long) : ProjectEvent(stamp)
|
||||
|
||||
class Break(stamp: Long) : ProjectEvent(stamp)
|
||||
}
|
||||
|
||||
sealed class ProjectState(val stamp: Long) {
|
||||
@@ -126,5 +143,7 @@ class ProjectStatus(private val debugName: String? = null) {
|
||||
class Modified(stamp: Long, val type: ModificationType) : ProjectState(stamp)
|
||||
|
||||
class Reverted(stamp: Long) : ProjectState(stamp)
|
||||
|
||||
class Broken(stamp: Long) : ProjectState(stamp)
|
||||
}
|
||||
}
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.openapi.externalSystem.autoimport
|
||||
|
||||
import com.intellij.openapi.components.PersistentStateComponent
|
||||
import com.intellij.openapi.components.State
|
||||
import com.intellij.openapi.components.Storage
|
||||
import com.intellij.openapi.components.StoragePathMacros.WORKSPACE_FILE
|
||||
import com.intellij.openapi.externalSystem.autoimport.ExternalSystemProjectTrackerSettings.AutoReloadType
|
||||
import com.intellij.openapi.observable.properties.AtomicLazyProperty
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
@State(name = "AutoImportSettings", storages = [Storage(WORKSPACE_FILE)])
|
||||
class ProjectTrackerSettings : ExternalSystemProjectTrackerSettings, PersistentStateComponent<ProjectTrackerSettings.State> {
|
||||
|
||||
internal val autoReloadTypeProperty = AtomicLazyProperty { AutoReloadType.SELECTIVE }
|
||||
|
||||
override var autoReloadType by autoReloadTypeProperty
|
||||
|
||||
override fun getState() = State(autoReloadType)
|
||||
|
||||
override fun loadState(state: State) {
|
||||
autoReloadType = state.autoReloadType
|
||||
}
|
||||
|
||||
data class State(var autoReloadType: AutoReloadType = AutoReloadType.SELECTIVE)
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@ApiStatus.Internal
|
||||
fun getInstance(project: Project): ProjectTrackerSettings {
|
||||
return ExternalSystemProjectTrackerSettings.getInstance(project) as ProjectTrackerSettings
|
||||
}
|
||||
}
|
||||
}
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.openapi.externalSystem.autoimport
|
||||
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.actionSystem.ToggleAction
|
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemBundle
|
||||
import com.intellij.openapi.project.DumbAware
|
||||
|
||||
class ToggleAutoReloadAction : ToggleAction(), DumbAware {
|
||||
override fun update(e: AnActionEvent) {
|
||||
super.update(e)
|
||||
e.presentation.description = when (isSelected(e)) {
|
||||
true -> ExternalSystemBundle.message("action.refresh.project.auto.description.disable")
|
||||
else -> ExternalSystemBundle.message("action.refresh.project.auto.description.enable")
|
||||
}
|
||||
}
|
||||
|
||||
override fun isSelected(e: AnActionEvent): Boolean {
|
||||
val project = e.project ?: return false
|
||||
val projectTracker = ExternalSystemProjectTracker.getInstance(project)
|
||||
return projectTracker.isAutoReloadExternalChanges
|
||||
}
|
||||
|
||||
override fun setSelected(e: AnActionEvent, state: Boolean) {
|
||||
val project = e.project ?: return
|
||||
val projectTracker = ExternalSystemProjectTracker.getInstance(project)
|
||||
projectTracker.isAutoReloadExternalChanges = state
|
||||
}
|
||||
|
||||
init {
|
||||
templatePresentation.icon = AllIcons.Actions.BuildAutoReloadChanges
|
||||
templatePresentation.text = ExternalSystemBundle.message("action.refresh.project.auto.text")
|
||||
templatePresentation.description = ExternalSystemBundle.message("action.refresh.project.auto.description.disable")
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.openapi.externalSystem.autoimport
|
||||
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.actionSystem.CheckedActionGroup
|
||||
import com.intellij.openapi.actionSystem.DefaultActionGroup
|
||||
import com.intellij.openapi.actionSystem.Toggleable
|
||||
import com.intellij.openapi.externalSystem.autoimport.ExternalSystemProjectTrackerSettings.AutoReloadType
|
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemBundle
|
||||
import com.intellij.openapi.project.DumbAware
|
||||
|
||||
class ToggleAutoReloadActionGroup : DefaultActionGroup(), Toggleable, CheckedActionGroup, DumbAware {
|
||||
override fun update(e: AnActionEvent) {
|
||||
val settings = e.projectTrackerSettings
|
||||
e.presentation.isEnabledAndVisible = settings != null
|
||||
val isDisabledAutoReload = settings?.autoReloadType == AutoReloadType.NONE
|
||||
Toggleable.setSelected(e.presentation, !isDisabledAutoReload)
|
||||
}
|
||||
|
||||
init {
|
||||
isPopup = true
|
||||
templatePresentation.icon = AllIcons.Actions.BuildAutoReloadChanges
|
||||
templatePresentation.text = ExternalSystemBundle.message("action.refresh.project.auto.text")
|
||||
}
|
||||
|
||||
companion object {
|
||||
internal val AnActionEvent.projectTrackerSettings: ExternalSystemProjectTrackerSettings?
|
||||
get() = project?.let { ExternalSystemProjectTrackerSettings.getInstance(it) }
|
||||
}
|
||||
}
|
||||
+125
-25
@@ -1,11 +1,14 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.openapi.externalSystem.autoimport
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.externalSystem.autoimport.ExternalSystemProjectTrackerSettings.AutoReloadType.*
|
||||
import com.intellij.openapi.externalSystem.autoimport.ExternalSystemRefreshStatus.FAILURE
|
||||
import com.intellij.openapi.externalSystem.autoimport.ExternalSystemRefreshStatus.SUCCESS
|
||||
import com.intellij.openapi.externalSystem.autoimport.MockProjectAware.RefreshCollisionPassType
|
||||
import com.intellij.openapi.externalSystem.autoimport.ProjectStatus.ModificationType.EXTERNAL
|
||||
import com.intellij.openapi.externalSystem.autoimport.ProjectStatus.ModificationType.INTERNAL
|
||||
import com.intellij.openapi.externalSystem.model.ProjectSystemId
|
||||
import com.intellij.openapi.externalSystem.util.Parallel.Companion.parallel
|
||||
import com.sun.media.jfxmedia.logging.Logger.setLevel
|
||||
import org.apache.log4j.Level
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
|
||||
@@ -453,23 +456,23 @@ class AutoImportTest : AutoImportTestCase() {
|
||||
|
||||
settingsFile.replaceContentInIoFile("println 'hello'")
|
||||
assertState(refresh = 2, notified = false, event = "external change")
|
||||
setRefreshStatus(ExternalSystemRefreshStatus.FAILURE)
|
||||
setRefreshStatus(FAILURE)
|
||||
settingsFile.replaceStringInIoFile("hello", "hi")
|
||||
assertState(refresh = 3, notified = true, event = "external change with failure refresh")
|
||||
refreshProject()
|
||||
assertState(refresh = 4, notified = true, event = "failure project refresh")
|
||||
setRefreshStatus(ExternalSystemRefreshStatus.SUCCESS)
|
||||
setRefreshStatus(SUCCESS)
|
||||
refreshProject()
|
||||
assertState(refresh = 5, notified = false, event = "project refresh")
|
||||
|
||||
settingsFile.replaceString("hi", "hello")
|
||||
assertState(refresh = 5, notified = true, event = "modify")
|
||||
setRefreshStatus(ExternalSystemRefreshStatus.FAILURE)
|
||||
setRefreshStatus(FAILURE)
|
||||
refreshProject()
|
||||
assertState(refresh = 6, notified = true, event = "failure project refresh")
|
||||
settingsFile.replaceString("hello", "hi")
|
||||
assertState(refresh = 6, notified = true, event = "try to revert changes after failure refresh")
|
||||
setRefreshStatus(ExternalSystemRefreshStatus.SUCCESS)
|
||||
setRefreshStatus(SUCCESS)
|
||||
refreshProject()
|
||||
assertState(refresh = 7, notified = false, event = "project refresh")
|
||||
}
|
||||
@@ -505,38 +508,38 @@ class AutoImportTest : AutoImportTestCase() {
|
||||
|
||||
fun `test disabling of auto-import`() {
|
||||
var state = simpleTest("settings.groovy") { settingsFile ->
|
||||
assertState(refresh = 1, enabled = true, notified = false, event = "register project without cache")
|
||||
disableAutoReloadExternalChanges()
|
||||
assertState(refresh = 1, enabled = false, notified = false, event = "disable project auto-import")
|
||||
assertState(refresh = 1, autoReloadType = SELECTIVE, notified = false, event = "register project without cache")
|
||||
setAutoReloadType(NONE)
|
||||
assertState(refresh = 1, autoReloadType = NONE, notified = false, event = "disable project auto-import")
|
||||
settingsFile.replaceContentInIoFile("println 'hello'")
|
||||
assertState(refresh = 1, enabled = false, notified = true, event = "modification with disabled auto-import")
|
||||
assertState(refresh = 1, autoReloadType = NONE, notified = true, event = "modification with disabled auto-import")
|
||||
}
|
||||
state = simpleTest("settings.groovy", state = state) { settingsFile ->
|
||||
// Open modified project with disabled auto-import for external changes
|
||||
assertState(refresh = 0, enabled = false, notified = true, event = "register modified project")
|
||||
assertState(refresh = 0, autoReloadType = NONE, notified = true, event = "register modified project")
|
||||
refreshProject()
|
||||
assertState(refresh = 1, enabled = false, notified = false, event = "refresh project")
|
||||
assertState(refresh = 1, autoReloadType = NONE, notified = false, event = "refresh project")
|
||||
|
||||
// Checkout git branch, that has additional linked project
|
||||
withLinkedProject("module/settings.groovy") { moduleSettingsFile ->
|
||||
assertState(refresh = 0, enabled = false, notified = true, event = "register project without cache with disabled auto-import")
|
||||
assertState(refresh = 0, autoReloadType = NONE, notified = true, event = "register project without cache with disabled auto-import")
|
||||
moduleSettingsFile.replaceContentInIoFile("println 'hello'")
|
||||
assertState(refresh = 0, enabled = false, notified = true, event = "modification with disabled auto-import")
|
||||
assertState(refresh = 0, autoReloadType = NONE, notified = true, event = "modification with disabled auto-import")
|
||||
}
|
||||
assertState(refresh = 1, enabled = false, notified = false, event = "remove modified linked project")
|
||||
assertState(refresh = 1, autoReloadType = NONE, notified = false, event = "remove modified linked project")
|
||||
|
||||
enableAutoReloadExternalChanges()
|
||||
assertState(refresh = 1, enabled = true, notified = false, event = "enable auto-import for project without modifications")
|
||||
disableAutoReloadExternalChanges()
|
||||
assertState(refresh = 1, enabled = false, notified = false, event = "disable project auto-import")
|
||||
setAutoReloadType(SELECTIVE)
|
||||
assertState(refresh = 1, autoReloadType = SELECTIVE, notified = false, event = "enable auto-import for project without modifications")
|
||||
setAutoReloadType(NONE)
|
||||
assertState(refresh = 1, autoReloadType = NONE, notified = false, event = "disable project auto-import")
|
||||
|
||||
settingsFile.replaceStringInIoFile("hello", "hi")
|
||||
assertState(refresh = 1, enabled = false, notified = true, event = "modification with disabled auto-import")
|
||||
enableAutoReloadExternalChanges()
|
||||
assertState(refresh = 2, enabled = true, notified = false, event = "enable auto-import for modified project")
|
||||
assertState(refresh = 1, autoReloadType = NONE, notified = true, event = "modification with disabled auto-import")
|
||||
setAutoReloadType(SELECTIVE)
|
||||
assertState(refresh = 2, autoReloadType = SELECTIVE, notified = false, event = "enable auto-import for modified project")
|
||||
}
|
||||
simpleTest("settings.groovy", state = state) {
|
||||
assertState(refresh = 0, enabled = true, notified = false, event = "register project with correct cache")
|
||||
assertState(refresh = 0, autoReloadType = SELECTIVE, notified = false, event = "register project with correct cache")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -594,7 +597,6 @@ class AutoImportTest : AutoImportTestCase() {
|
||||
|
||||
@Test
|
||||
fun `test merging of refreshes with different nature`() {
|
||||
Logger.getInstance("#com.intellij.openapi.externalSystem.autoimport").setLevel(Level.DEBUG)
|
||||
simpleTest("settings.groovy") { settingsFile ->
|
||||
assertState(1, notified = false, event = "register project without cache")
|
||||
|
||||
@@ -614,4 +616,102 @@ class AutoImportTest : AutoImportTestCase() {
|
||||
assertState(refresh = 2, notified = false, event = "modification")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test enabling-disabling internal-external changes importing`() {
|
||||
simpleModificationTest {
|
||||
modifySettingsFile(INTERNAL)
|
||||
assertState(refresh = 0, notified = true, autoReloadType = SELECTIVE, event = "internal modification")
|
||||
|
||||
refreshProject()
|
||||
assertState(refresh = 1, notified = false, autoReloadType = SELECTIVE, event = "refresh project")
|
||||
|
||||
modifySettingsFile(EXTERNAL)
|
||||
assertState(refresh = 2, notified = false, autoReloadType = SELECTIVE, event = "external modification")
|
||||
|
||||
setAutoReloadType(ALL)
|
||||
|
||||
modifySettingsFile(INTERNAL)
|
||||
assertState(refresh = 3, notified = false, autoReloadType = ALL, event = "internal modification with enabled auto-reload")
|
||||
|
||||
modifySettingsFile(EXTERNAL)
|
||||
assertState(refresh = 4, notified = false, autoReloadType = ALL, event = "external modification with enabled auto-reload")
|
||||
|
||||
setAutoReloadType(NONE)
|
||||
|
||||
modifySettingsFile(INTERNAL)
|
||||
assertState(refresh = 4, notified = true, autoReloadType = NONE, event = "internal modification with disabled auto-reload")
|
||||
|
||||
modifySettingsFile(EXTERNAL)
|
||||
assertState(refresh = 4, notified = true, autoReloadType = NONE, event = "external modification with disabled auto-reload")
|
||||
|
||||
setAutoReloadType(SELECTIVE)
|
||||
assertState(refresh = 4, notified = true, autoReloadType = SELECTIVE,
|
||||
event = "enable auto-reload external changes with internal and external modifications")
|
||||
|
||||
setAutoReloadType(ALL)
|
||||
assertState(refresh = 5, notified = false, autoReloadType = ALL, event = "enable auto-reload of any changes")
|
||||
|
||||
setAutoReloadType(NONE)
|
||||
|
||||
modifySettingsFile(INTERNAL)
|
||||
assertState(refresh = 5, notified = true, autoReloadType = NONE, event = "internal modification with disabled auto-reload")
|
||||
|
||||
modifySettingsFile(EXTERNAL)
|
||||
assertState(refresh = 5, notified = true, autoReloadType = NONE, event = "external modification with disabled auto-reload")
|
||||
|
||||
setAutoReloadType(ALL)
|
||||
assertState(refresh = 6, notified = false, autoReloadType = ALL, event = "enable auto-reload of any changes")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test failure auto-reload with enabled auto-reload of any changes`() {
|
||||
simpleModificationTest {
|
||||
setAutoReloadType(ALL)
|
||||
setRefreshStatus(FAILURE)
|
||||
modifySettingsFile(INTERNAL)
|
||||
assertState(refresh = 1, notified = true, autoReloadType = ALL, event = "failure modification with enabled auto-reload")
|
||||
|
||||
modifySettingsFile(INTERNAL)
|
||||
assertState(refresh = 2, notified = true, autoReloadType = ALL, event = "failure modification with enabled auto-reload")
|
||||
|
||||
setRefreshStatus(SUCCESS)
|
||||
refreshProject()
|
||||
assertState(refresh = 3, notified = false, autoReloadType = ALL, event = "refresh project")
|
||||
|
||||
setRefreshStatus(FAILURE)
|
||||
onceDuringRefresh {
|
||||
setRefreshStatus(SUCCESS)
|
||||
modifySettingsFile(INTERNAL)
|
||||
}
|
||||
modifySettingsFile(INTERNAL)
|
||||
assertState(refresh = 5, notified = false, autoReloadType = ALL, event = "success modification after failure")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test up-to-date promise after modifications with enabled auto-import`() {
|
||||
simpleModificationTest {
|
||||
for (collisionPassType in RefreshCollisionPassType.values()) {
|
||||
resetAssertionCounters()
|
||||
|
||||
setRefreshCollisionPassType(collisionPassType)
|
||||
|
||||
setAutoReloadType(SELECTIVE)
|
||||
onceDuringRefresh {
|
||||
modifySettingsFile(EXTERNAL)
|
||||
}
|
||||
modifySettingsFile(EXTERNAL)
|
||||
assertState(refresh = 2, notified = false, autoReloadType = SELECTIVE, event = "auto-reload inside reload ($collisionPassType)")
|
||||
|
||||
setAutoReloadType(ALL)
|
||||
onceDuringRefresh {
|
||||
modifySettingsFile(INTERNAL)
|
||||
}
|
||||
modifySettingsFile(INTERNAL)
|
||||
assertState(refresh = 4, notified = false, autoReloadType = ALL, event = "auto-reload inside reload ($collisionPassType)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+92
-38
@@ -9,6 +9,10 @@ import com.intellij.openapi.application.invokeAndWaitIfNeeded
|
||||
import com.intellij.openapi.command.WriteCommandAction
|
||||
import com.intellij.openapi.components.ComponentManager
|
||||
import com.intellij.openapi.editor.Document
|
||||
import com.intellij.openapi.externalSystem.autoimport.ExternalSystemProjectTrackerSettings.AutoReloadType
|
||||
import com.intellij.openapi.externalSystem.autoimport.ExternalSystemProjectTrackerSettings.AutoReloadType.*
|
||||
import com.intellij.openapi.externalSystem.autoimport.MockProjectAware.RefreshCollisionPassType
|
||||
import com.intellij.openapi.externalSystem.autoimport.ProjectStatus.ModificationType
|
||||
import com.intellij.openapi.externalSystem.model.ProjectSystemId
|
||||
import com.intellij.openapi.externalSystem.test.ExternalSystemTestCase
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||
@@ -35,6 +39,7 @@ abstract class AutoImportTestCase : ExternalSystemTestCase() {
|
||||
private lateinit var testDisposable: Disposable
|
||||
private val notificationAware get() = ProjectNotificationAware.getInstance(myProject)
|
||||
private val projectTracker get() = AutoImportProjectTracker.getInstance(myProject).also { it.enableAutoImportInTests() }
|
||||
private val projectTrackerSettings get() = ProjectTrackerSettings.getInstance(myProject)
|
||||
|
||||
private fun ensureExistsParentDirectory(relativePath: String): VirtualFile {
|
||||
return relativePath.split("/").dropLast(1)
|
||||
@@ -92,6 +97,12 @@ abstract class AutoImportTestCase : ExternalSystemTestCase() {
|
||||
localFileSystem.refreshIoFiles(paths.map { File(it) }, false, true, null)
|
||||
}
|
||||
|
||||
protected fun VirtualFile.appendLineInIoFile(line: String) =
|
||||
appendStringInIoFile(line + "\n")
|
||||
|
||||
protected fun VirtualFile.appendStringInIoFile(string: String) =
|
||||
updateIoFile { appendText(string) }
|
||||
|
||||
protected fun VirtualFile.replaceContentInIoFile(content: String) =
|
||||
updateIoFile { writeText(content) }
|
||||
|
||||
@@ -137,6 +148,9 @@ abstract class AutoImportTestCase : ExternalSystemTestCase() {
|
||||
VfsUtil.saveText(this, before + prefix + string + after)
|
||||
}
|
||||
|
||||
protected fun VirtualFile.appendLine(line: String) =
|
||||
appendString(line + "\n")
|
||||
|
||||
protected fun VirtualFile.appendString(string: String) =
|
||||
runWriteAction { VfsUtil.saveText(this, VfsUtil.loadText(this) + string) }
|
||||
|
||||
@@ -182,24 +196,22 @@ abstract class AutoImportTestCase : ExternalSystemTestCase() {
|
||||
projectTracker.scheduleProjectRefresh()
|
||||
}
|
||||
|
||||
private fun loadState(state: AutoImportProjectTracker.State) = projectTracker.loadState(state)
|
||||
|
||||
|
||||
protected fun enableAsyncExecution() {
|
||||
projectTracker.isAsyncChangesProcessing = true
|
||||
}
|
||||
|
||||
protected fun enableAutoReloadExternalChanges() {
|
||||
projectTracker.isAutoReloadExternalChanges = true
|
||||
}
|
||||
|
||||
protected fun disableAutoReloadExternalChanges() {
|
||||
projectTracker.isAutoReloadExternalChanges = false
|
||||
protected fun setAutoReloadType(type: AutoReloadType) {
|
||||
projectTrackerSettings.autoReloadType = type
|
||||
}
|
||||
|
||||
protected fun initialize() = projectTracker.initializeComponent()
|
||||
|
||||
protected fun getState() = projectTracker.state
|
||||
protected fun getState() = projectTracker.state to projectTrackerSettings.state
|
||||
|
||||
private fun loadState(state: Pair<AutoImportProjectTracker.State, ProjectTrackerSettings.State>) {
|
||||
projectTracker.loadState(state.first)
|
||||
projectTrackerSettings.loadState(state.second)
|
||||
}
|
||||
|
||||
protected fun assertProjectAware(projectAware: MockProjectAware,
|
||||
refresh: Int? = null,
|
||||
@@ -220,12 +232,13 @@ abstract class AutoImportTestCase : ExternalSystemTestCase() {
|
||||
assertEquals("$message on $event", expected, actual)
|
||||
}
|
||||
|
||||
protected fun assertProjectTracker(isAutoReload: Boolean, event: String) {
|
||||
val message = when (isAutoReload) {
|
||||
true -> "Auto reload must be enabled"
|
||||
false -> "Auto reload must be disabled"
|
||||
protected fun assertProjectTrackerSettings(autoReloadType: AutoReloadType, event: String) {
|
||||
val message = when (autoReloadType) {
|
||||
ALL -> "Auto reload must be enabled"
|
||||
SELECTIVE -> "Auto reload must be enabled"
|
||||
NONE -> "Auto reload must be disabled"
|
||||
}
|
||||
assertEquals("$message on $event", isAutoReload, projectTracker.isAutoReloadExternalChanges)
|
||||
assertEquals("$message on $event", autoReloadType, projectTrackerSettings.autoReloadType)
|
||||
}
|
||||
|
||||
protected fun assertActivationStatus(vararg projects: ExternalSystemProjectId, event: String) {
|
||||
@@ -260,6 +273,7 @@ abstract class AutoImportTestCase : ExternalSystemTestCase() {
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
testDisposable = Disposer.newDisposable()
|
||||
myProject.replaceService(ExternalSystemProjectTrackerSettings::class.java, ProjectTrackerSettings(), testDisposable)
|
||||
myProject.replaceService(ExternalSystemProjectTracker::class.java, AutoImportProjectTracker(myProject), testDisposable)
|
||||
}
|
||||
|
||||
@@ -268,28 +282,48 @@ abstract class AutoImportTestCase : ExternalSystemTestCase() {
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
protected fun simpleTest(fileRelativePath: String,
|
||||
content: String? = null,
|
||||
state: AutoImportProjectTracker.State = AutoImportProjectTracker.State(),
|
||||
test: SimpleTestBench.(VirtualFile) -> Unit): AutoImportProjectTracker.State {
|
||||
return myProject.replaceService(ExternalSystemProjectTracker::class.java, AutoImportProjectTracker(myProject)) {
|
||||
val systemId = ProjectSystemId("External System")
|
||||
val projectId = ExternalSystemProjectId(systemId, projectPath)
|
||||
val projectAware = MockProjectAware(projectId)
|
||||
loadState(state)
|
||||
initialize()
|
||||
val file = findOrCreateVirtualFile(fileRelativePath)
|
||||
content?.let { file.replaceContent(it) }
|
||||
projectAware.settingsFiles.add(file.path)
|
||||
register(projectAware)
|
||||
SimpleTestBench(projectAware).test(file)
|
||||
val newState = getState()
|
||||
remove(projectAware.projectId)
|
||||
newState
|
||||
protected fun simpleModificationTest(test: SimpleModificationTestBench.() -> Unit) {
|
||||
simpleTest("settings.groovy", "") {
|
||||
assertState(
|
||||
refresh = 1,
|
||||
notified = false,
|
||||
subscribe = 2,
|
||||
unsubscribe = 0,
|
||||
autoReloadType = SELECTIVE,
|
||||
event = "register project without cache"
|
||||
)
|
||||
resetAssertionCounters()
|
||||
SimpleModificationTestBench(projectAware, it).test()
|
||||
}
|
||||
}
|
||||
|
||||
protected inner class SimpleTestBench(private val projectAware: MockProjectAware) {
|
||||
protected fun simpleTest(
|
||||
fileRelativePath: String,
|
||||
content: String? = null,
|
||||
state: Pair<AutoImportProjectTracker.State, ProjectTrackerSettings.State> =
|
||||
AutoImportProjectTracker.State() to ProjectTrackerSettings.State(),
|
||||
test: SimpleTestBench.(VirtualFile) -> Unit
|
||||
): Pair<AutoImportProjectTracker.State, ProjectTrackerSettings.State> {
|
||||
return myProject.replaceService(ExternalSystemProjectTrackerSettings::class.java, ProjectTrackerSettings()) {
|
||||
myProject.replaceService(ExternalSystemProjectTracker::class.java, AutoImportProjectTracker(myProject)) {
|
||||
val systemId = ProjectSystemId("External System")
|
||||
val projectId = ExternalSystemProjectId(systemId, projectPath)
|
||||
val projectAware = MockProjectAware(projectId)
|
||||
loadState(state)
|
||||
initialize()
|
||||
val file = findOrCreateVirtualFile(fileRelativePath)
|
||||
content?.let { file.replaceContent(it) }
|
||||
projectAware.settingsFiles.add(file.path)
|
||||
register(projectAware)
|
||||
SimpleTestBench(projectAware).test(file)
|
||||
val newState = getState()
|
||||
remove(projectAware.projectId)
|
||||
newState
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected open inner class SimpleTestBench(val projectAware: MockProjectAware) {
|
||||
|
||||
fun forceRefreshProject() = forceRefreshProject(projectAware.projectId)
|
||||
|
||||
@@ -301,8 +335,16 @@ abstract class AutoImportTestCase : ExternalSystemTestCase() {
|
||||
|
||||
fun registerSettingsFile(relativePath: String) = projectAware.settingsFiles.add(getPath(relativePath))
|
||||
|
||||
fun setRefreshStatus(status: ExternalSystemRefreshStatus) {
|
||||
projectAware.refreshStatus = status
|
||||
fun onceDuringRefresh(action: () -> Unit) = projectAware.onceDuringRefresh(action)
|
||||
|
||||
fun setRefreshStatus(status: ExternalSystemRefreshStatus) = projectAware.refreshStatus.set(status)
|
||||
|
||||
fun setRefreshCollisionPassType(type: RefreshCollisionPassType) = projectAware.refreshCollisionPassType.set(type)
|
||||
|
||||
fun resetAssertionCounters() {
|
||||
projectAware.refreshCounter.set(0)
|
||||
projectAware.subscribeCounter.set(0)
|
||||
projectAware.unsubscribeCounter.set(0)
|
||||
}
|
||||
|
||||
fun withLinkedProject(fileRelativePath: String, test: SimpleTestBench.(VirtualFile) -> Unit) {
|
||||
@@ -318,11 +360,11 @@ abstract class AutoImportTestCase : ExternalSystemTestCase() {
|
||||
fun assertState(refresh: Int? = null,
|
||||
subscribe: Int? = null,
|
||||
unsubscribe: Int? = null,
|
||||
enabled: Boolean = true,
|
||||
autoReloadType: AutoReloadType = SELECTIVE,
|
||||
notified: Boolean,
|
||||
event: String) {
|
||||
assertProjectAware(projectAware, refresh, subscribe, unsubscribe, event)
|
||||
assertProjectTracker(enabled, event = event)
|
||||
assertProjectTrackerSettings(autoReloadType, event = event)
|
||||
when (notified) {
|
||||
true -> assertNotificationAware(projectAware.projectId, event = event)
|
||||
else -> assertNotificationAware(event = event)
|
||||
@@ -344,4 +386,16 @@ abstract class AutoImportTestCase : ExternalSystemTestCase() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected inner class SimpleModificationTestBench(
|
||||
projectAware: MockProjectAware,
|
||||
private val settingsFile: VirtualFile
|
||||
) : SimpleTestBench(projectAware) {
|
||||
fun modifySettingsFile(modificationType: ModificationType = ModificationType.INTERNAL) {
|
||||
when (modificationType) {
|
||||
ModificationType.INTERNAL -> settingsFile.appendLine("println 'hello'")
|
||||
ModificationType.EXTERNAL -> settingsFile.appendLineInIoFile("println 'hello'")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+70
-21
@@ -2,9 +2,17 @@
|
||||
package com.intellij.openapi.externalSystem.autoimport
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.externalSystem.autoimport.ExternalSystemRefreshStatus.SUCCESS
|
||||
import com.intellij.openapi.externalSystem.autoimport.MockProjectAware.RefreshCollisionPassType.*
|
||||
import com.intellij.openapi.observable.operations.AnonymousParallelOperationTrace
|
||||
import com.intellij.openapi.observable.operations.AnonymousParallelOperationTrace.Companion.task
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
import com.intellij.util.ConcurrencyUtil.once
|
||||
import com.intellij.util.EventDispatcher
|
||||
import java.util.*
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import kotlin.collections.LinkedHashSet
|
||||
|
||||
class MockProjectAware(override val projectId: ExternalSystemProjectId) : ExternalSystemProjectAware {
|
||||
|
||||
@@ -12,38 +20,79 @@ class MockProjectAware(override val projectId: ExternalSystemProjectId) : Extern
|
||||
val unsubscribeCounter = AtomicInteger(0)
|
||||
val refreshCounter = AtomicInteger(0)
|
||||
|
||||
var refreshStatus = ExternalSystemRefreshStatus.SUCCESS
|
||||
val refreshCollisionPassType = AtomicReference(DUPLICATE)
|
||||
val refreshStatus = AtomicReference(SUCCESS)
|
||||
|
||||
private val listeners = CopyOnWriteArrayList<ExternalSystemProjectRefreshListener>()
|
||||
private val inRefreshActions = CopyOnWriteArrayList<() -> Unit>()
|
||||
private val eventDispatcher = EventDispatcher.create(Listener::class.java)
|
||||
private val refresh = AnonymousParallelOperationTrace(debugName = "$projectId MockProjectAware.refreshProject")
|
||||
|
||||
override val settingsFiles = LinkedHashSet<String>()
|
||||
|
||||
override fun subscribe(listener: ExternalSystemProjectRefreshListener, parentDisposable: Disposable) {
|
||||
if (listeners.add(listener)) {
|
||||
subscribeCounter.incrementAndGet()
|
||||
}
|
||||
Disposer.register(parentDisposable, Disposable {
|
||||
if (listeners.remove(listener)) {
|
||||
unsubscribeCounter.incrementAndGet()
|
||||
}
|
||||
})
|
||||
eventDispatcher.addListener(listener.asListener(), parentDisposable)
|
||||
subscribeCounter.incrementAndGet()
|
||||
Disposer.register(parentDisposable, Disposable { unsubscribeCounter.incrementAndGet() })
|
||||
}
|
||||
|
||||
override fun refreshProject() {
|
||||
listeners.forEach { it.beforeProjectRefresh() }
|
||||
refreshCounter.incrementAndGet()
|
||||
inRefreshActions.forEach { it() }
|
||||
listeners.forEach { it.afterProjectRefresh(refreshStatus) }
|
||||
when (refreshCollisionPassType.get()!!) {
|
||||
DUPLICATE -> {
|
||||
doRefreshProject()
|
||||
}
|
||||
CANCEL -> {
|
||||
val task = once { doRefreshProject() }
|
||||
refresh.afterOperation { task.run() }
|
||||
if (refresh.isOperationCompleted()) task.run()
|
||||
}
|
||||
IGNORE -> {
|
||||
if (refresh.isOperationCompleted()) {
|
||||
doRefreshProject()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun doRefreshProject() {
|
||||
val refreshStatus = refreshStatus.get()
|
||||
eventDispatcher.multicaster.beforeProjectRefresh()
|
||||
refresh.task {
|
||||
refreshCounter.incrementAndGet()
|
||||
eventDispatcher.multicaster.insideProjectRefresh()
|
||||
}
|
||||
eventDispatcher.multicaster.afterProjectRefresh(refreshStatus)
|
||||
}
|
||||
|
||||
fun onceDuringRefresh(action: () -> Unit) {
|
||||
val inRefreshAction = object : (() -> Unit) {
|
||||
override fun invoke() {
|
||||
action()
|
||||
inRefreshActions.remove(this)
|
||||
val disposable = Disposer.newDisposable()
|
||||
duringRefresh(disposable) {
|
||||
Disposer.dispose(disposable)
|
||||
action()
|
||||
}
|
||||
}
|
||||
|
||||
fun duringRefresh(parentDisposable: Disposable, action: () -> Unit) {
|
||||
eventDispatcher.addListener(object : Listener {
|
||||
override fun insideProjectRefresh() = action()
|
||||
}, parentDisposable)
|
||||
}
|
||||
|
||||
private fun ExternalSystemProjectRefreshListener.asListener(): Listener {
|
||||
return object : Listener, ExternalSystemProjectRefreshListener {
|
||||
override fun beforeProjectRefresh() {
|
||||
this@asListener.beforeProjectRefresh()
|
||||
}
|
||||
|
||||
override fun afterProjectRefresh(status: ExternalSystemRefreshStatus) {
|
||||
this@asListener.afterProjectRefresh(status)
|
||||
}
|
||||
}
|
||||
inRefreshActions.add(inRefreshAction)
|
||||
}
|
||||
|
||||
interface Listener : ExternalSystemProjectRefreshListener, EventListener {
|
||||
@JvmDefault
|
||||
fun insideProjectRefresh() {
|
||||
}
|
||||
}
|
||||
|
||||
enum class RefreshCollisionPassType { DUPLICATE, CANCEL, IGNORE }
|
||||
}
|
||||
+28
@@ -130,4 +130,32 @@ class ProjectStatusTest {
|
||||
status.markModified(120, INTERNAL) as Dirty
|
||||
assertEquals(INTERNAL, status.getModificationType())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test tracking status after failed import`() {
|
||||
val status = ProjectStatus()
|
||||
|
||||
status.markModified(10, INTERNAL) as Modified
|
||||
status.markSynchronized(20) as Synchronized
|
||||
status.markBroken(30) as Broken
|
||||
|
||||
assertFalse(status.isUpToDate())
|
||||
assertFalse(status.isDirty())
|
||||
|
||||
status.markModified(40) as Dirty
|
||||
status.markReverted(50) as Dirty
|
||||
status.markSynchronized(60) as Synchronized
|
||||
status.markBroken(70) as Broken
|
||||
|
||||
assertFalse(status.isUpToDate())
|
||||
assertFalse(status.isDirty())
|
||||
|
||||
status.markReverted(80) as Broken
|
||||
status.markSynchronized(90) as Synchronized
|
||||
status.markModified(110) as Modified
|
||||
status.markBroken(100) as Dirty
|
||||
|
||||
assertFalse(status.isUpToDate())
|
||||
assertTrue(status.isDirty())
|
||||
}
|
||||
}
|
||||
@@ -614,8 +614,5 @@ public class ActionButton extends JComponent implements ActionButtonComponent, A
|
||||
}
|
||||
|
||||
// Contains actions IDs which descriptions are permitted for displaying in the ActionButton tooltip
|
||||
private static final Set<String> WHITE_LIST = ContainerUtil.immutableSet(
|
||||
"ExternalSystem.ProjectRefreshAction",
|
||||
"ExternalSystem.ToggleAutoReload"
|
||||
);
|
||||
private static final Set<String> WHITE_LIST = ContainerUtil.immutableSet("ExternalSystem.ProjectRefreshAction");
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
serviceImplementation="com.intellij.openapi.externalSystem.service.project.manage.ProjectDataManagerImpl"/>
|
||||
<projectService serviceInterface="com.intellij.openapi.externalSystem.autoimport.ExternalSystemProjectTracker"
|
||||
serviceImplementation="com.intellij.openapi.externalSystem.autoimport.AutoImportProjectTracker"/>
|
||||
<projectService serviceInterface="com.intellij.openapi.externalSystem.autoimport.ExternalSystemProjectTrackerSettings"
|
||||
serviceImplementation="com.intellij.openapi.externalSystem.autoimport.ProjectTrackerSettings"/>
|
||||
<projectService serviceImplementation="com.intellij.openapi.externalSystem.autoimport.ProjectNotificationAware"/>
|
||||
<editorFloatingToolbarProvider implementation="com.intellij.openapi.externalSystem.autoimport.ProjectRefreshFloatingProvider"/>
|
||||
<projectService serviceInterface="com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsManager"
|
||||
|
||||
@@ -19,7 +19,15 @@
|
||||
class="com.intellij.openapi.externalSystem.action.ShowExternalSystemSettingsAction"
|
||||
icon="AllIcons.General.Settings">
|
||||
</action>
|
||||
<action id="ExternalSystem.ToggleAutoReload" class="com.intellij.openapi.externalSystem.autoimport.ToggleAutoReloadAction"/>
|
||||
<group id="ExternalSystem.ToggleAutoReload"
|
||||
class="com.intellij.openapi.externalSystem.autoimport.ToggleAutoReloadActionGroup">
|
||||
<action id="ExternalSystem.ToggleAutoReload.All"
|
||||
class="com.intellij.openapi.externalSystem.autoimport.UseAllAutoReloadTypeAction"/>
|
||||
<action id="ExternalSystem.ToggleAutoReload.Selective"
|
||||
class="com.intellij.openapi.externalSystem.autoimport.UseSelectiveAutoReloadTypeAction"/>
|
||||
<action id="ExternalSystem.ToggleAutoReload.None"
|
||||
class="com.intellij.openapi.externalSystem.autoimport.UseNoneAutoReloadTypeAction"/>
|
||||
</group>
|
||||
<action id="ExternalSystem.OpenTasksActivationManager"
|
||||
class="com.intellij.openapi.externalSystem.action.OpenTasksActivationManagerAction">
|
||||
</action>
|
||||
|
||||
@@ -574,6 +574,7 @@
|
||||
<reference id="Maven.RunBuild"/>
|
||||
<reference id="Maven.ExecuteGoal"/>
|
||||
<reference id="Maven.ToggleOffline"/>
|
||||
<reference id="ExternalSystem.ToggleAutoReload"/>
|
||||
<reference id="Maven.ToggleSkipTests"/>
|
||||
<reference id="Maven.CollapseAll"/>
|
||||
<separator/>
|
||||
|
||||
Reference in New Issue
Block a user