mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-08 15:09:39 +07:00
IDEA-CR-57413: [auto-import] added settings to disable auto reload of external changes
(cherry picked from commit c68f8ee33e515b6e7adf46708fbd8a6a65ea1ff6) GitOrigin-RevId: b3ce1d7b776b2af12a00f588c9375afb56ca68fe
This commit is contained in:
committed by
intellij-monorepo-bot
parent
d8a295131e
commit
502032aa69
@@ -62,8 +62,9 @@ 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-Import
|
||||
action.refresh.project.auto.description=Enable/disable automatic project importing on changes in build script files
|
||||
action.refresh.project.auto.text=Auto-reload external changes
|
||||
action.refresh.project.auto.description.enable=Enable automatic import on VCS updates and external changes
|
||||
action.refresh.project.auto.description.disable=Disable automatic import on VCS updates and external changes
|
||||
|
||||
action.open.settings.text={0} Settings
|
||||
action.open.settings.description=Edit {0} settings for the current project
|
||||
|
||||
@@ -8,6 +8,11 @@ import org.jetbrains.annotations.ApiStatus
|
||||
@ApiStatus.Experimental
|
||||
interface ExternalSystemProjectTracker {
|
||||
|
||||
/**
|
||||
* Enables/disables auto reload external changes for all projects
|
||||
*/
|
||||
var isAutoReloadExternalChanges: Boolean
|
||||
|
||||
/**
|
||||
* Starts tracking of project settings that will be defined by [projectAware]
|
||||
*/
|
||||
|
||||
@@ -13,7 +13,7 @@ import com.intellij.openapi.diagnostic.Logger
|
||||
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.model.ProjectSystemId
|
||||
import com.intellij.openapi.observable.operations.AnonymousParallelOperationTrace
|
||||
import com.intellij.openapi.observable.operations.CompoundParallelOperationTrace
|
||||
import com.intellij.openapi.observable.properties.AtomicBooleanProperty
|
||||
@@ -39,10 +39,13 @@ class AutoImportProjectTracker(private val project: Project) : ExternalSystemPro
|
||||
private val projectStates = ConcurrentHashMap<State.Id, State.Project>()
|
||||
private val projectDataMap = ConcurrentHashMap<ExternalSystemProjectId, ProjectData>()
|
||||
private val isDisabled = AtomicBooleanProperty(ApplicationManager.getApplication().isUnitTestMode)
|
||||
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("project tracker", AUTO_REPARSE_DELAY, false, null, project)
|
||||
|
||||
override var isAutoReloadExternalChanges by autoReloadExternalChangesProperty
|
||||
|
||||
private fun createProjectChangesListener() =
|
||||
object : ProjectBatchFileChangeListener(project) {
|
||||
override fun batchChangeStarted(activityName: String?) =
|
||||
@@ -89,10 +92,11 @@ class AutoImportProjectTracker(private val project: Project) : ExternalSystemPro
|
||||
LOG.debug("Schedule change processing")
|
||||
dispatcher.queue(object : Update("notify") {
|
||||
override fun run() {
|
||||
when (getModificationType()) {
|
||||
INTERNAL -> updateProjectNotification()
|
||||
EXTERNAL -> refreshProject()
|
||||
null -> updateProjectNotification()
|
||||
if (getModificationType() == EXTERNAL && isAutoReloadExternalChanges) {
|
||||
refreshProject()
|
||||
}
|
||||
else {
|
||||
updateProjectNotification()
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -102,9 +106,11 @@ class AutoImportProjectTracker(private val project: Project) : ExternalSystemPro
|
||||
LOG.debug("Incremental project refresh")
|
||||
if (isDisabled.get() || Registry.`is`("external.system.auto.import.disabled")) return
|
||||
if (!projectChangeOperation.isOperationCompleted()) return
|
||||
var isSkippedProjectRefresh = true
|
||||
for (projectData in projectDataMap.values) {
|
||||
val projectId = projectData.projectAware.projectId.readableName
|
||||
if (!projectData.isUpToDate()) {
|
||||
isSkippedProjectRefresh = false
|
||||
LOG.debug("$projectId: Project refresh")
|
||||
projectData.projectAware.refreshProject()
|
||||
}
|
||||
@@ -112,6 +118,9 @@ class AutoImportProjectTracker(private val project: Project) : ExternalSystemPro
|
||||
LOG.debug("$projectId: Skip project refresh")
|
||||
}
|
||||
}
|
||||
if (isSkippedProjectRefresh) {
|
||||
updateProjectNotification()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateProjectNotification() {
|
||||
@@ -175,13 +184,14 @@ class AutoImportProjectTracker(private val project: Project) : ExternalSystemPro
|
||||
}
|
||||
|
||||
override fun getState(): State {
|
||||
val projectSettingsTrackerStates = projectDataMap.values
|
||||
.map { it.projectAware.projectId.getState() to it.getState() }
|
||||
val projectSettingsTrackerStates = projectDataMap.asSequence()
|
||||
.map { (id, data) -> id.getState() to data.getState() }
|
||||
.toMap()
|
||||
return State(projectSettingsTrackerStates)
|
||||
return State(isAutoReloadExternalChanges, projectSettingsTrackerStates)
|
||||
}
|
||||
|
||||
override fun loadState(state: State) {
|
||||
isAutoReloadExternalChanges = state.isAutoReloadExternalChanges
|
||||
projectStates.putAll(state.projectSettingsTrackerStates)
|
||||
projectDataMap.forEach { (id, data) -> loadState(id, data) }
|
||||
}
|
||||
@@ -190,8 +200,8 @@ class AutoImportProjectTracker(private val project: Project) : ExternalSystemPro
|
||||
val projectState = projectStates.remove(projectId.getState())
|
||||
val settingsTrackerState = projectState?.settingsTracker
|
||||
if (settingsTrackerState == null || projectState.isDirty) {
|
||||
projectData.status.markDirty(currentTime())
|
||||
scheduleProjectRefresh()
|
||||
projectData.status.markDirty(currentTime(), EXTERNAL)
|
||||
scheduleChangeProcessing()
|
||||
return
|
||||
}
|
||||
projectData.settingsTracker.loadState(settingsTrackerState)
|
||||
@@ -225,12 +235,14 @@ class AutoImportProjectTracker(private val project: Project) : ExternalSystemPro
|
||||
projectChangeOperation.beforeOperation { notificationAware.notificationExpire() }
|
||||
projectChangeOperation.afterOperation { scheduleChangeProcessing() }
|
||||
projectChangeOperation.afterOperation { LOG.debug("Project change finished") }
|
||||
isDisabled.afterReset { scheduleProjectRefresh() }
|
||||
autoReloadExternalChangesProperty.afterSet { scheduleProjectRefresh() }
|
||||
}
|
||||
|
||||
private fun ProjectData.getState() = State.Project(status.isDirty(), settingsTracker.getState())
|
||||
|
||||
private fun ExternalSystemProjectId.getState() = State.Id(systemId.id, externalProjectPath)
|
||||
private fun ProjectSystemId.getState() = id
|
||||
|
||||
private fun ExternalSystemProjectId.getState() = State.Id(systemId.getState(), externalProjectPath)
|
||||
|
||||
private data class ProjectData(
|
||||
val status: ProjectStatus,
|
||||
@@ -240,10 +252,21 @@ class AutoImportProjectTracker(private val project: Project) : ExternalSystemPro
|
||||
) {
|
||||
fun isUpToDate() = status.isUpToDate() && settingsTracker.isUpToDate()
|
||||
|
||||
fun getModificationType() = settingsTracker.getModificationType()
|
||||
fun getModificationType(): ModificationType? {
|
||||
val trackerModificationType = status.getModificationType()
|
||||
val settingsTrackerModificationType = settingsTracker.getModificationType()
|
||||
return when {
|
||||
trackerModificationType == null -> settingsTrackerModificationType
|
||||
settingsTrackerModificationType == null -> trackerModificationType
|
||||
else -> settingsTrackerModificationType.merge(trackerModificationType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class State(var projectSettingsTrackerStates: Map<Id, Project> = emptyMap()) {
|
||||
data class State(
|
||||
var isAutoReloadExternalChanges: Boolean = true,
|
||||
var projectSettingsTrackerStates: Map<Id, Project> = emptyMap()
|
||||
) {
|
||||
data class Id(var systemId: String? = null, var externalProjectPath: String? = null)
|
||||
data class Project(
|
||||
var isDirty: Boolean = false,
|
||||
|
||||
@@ -107,10 +107,10 @@ class ProjectSettingsTracker(
|
||||
submitSettingsFilesRefresh {
|
||||
submitSettingsFilesCRCCalculation { newSettingsFilesCRC ->
|
||||
when (hasChanges(newSettingsFilesCRC)) {
|
||||
true -> status.markDirty(currentTime())
|
||||
true -> status.markDirty(currentTime(), EXTERNAL)
|
||||
else -> status.markReverted(currentTime())
|
||||
}
|
||||
projectTracker.scheduleProjectRefresh()
|
||||
projectTracker.scheduleChangeProcessing()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,7 +118,7 @@ class ProjectSettingsTracker(
|
||||
fun getState() = State(status.isDirty(), settingsFilesCRC.get().toMap())
|
||||
|
||||
fun loadState(state: State) {
|
||||
if (state.isDirty) status.markDirty(currentTime())
|
||||
if (state.isDirty) status.markDirty(currentTime(), EXTERNAL)
|
||||
settingsFilesCRC.set(state.settingsFiles.toMap())
|
||||
}
|
||||
|
||||
|
||||
@@ -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.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.actionSystem.ToggleAction
|
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemBundle
|
||||
import com.intellij.openapi.project.DumbAware
|
||||
|
||||
class ToggleProjectRefreshAction : 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 = null
|
||||
templatePresentation.text = ExternalSystemBundle.message("action.refresh.project.auto.text")
|
||||
templatePresentation.description = ExternalSystemBundle.message("action.refresh.project.auto.description.disable")
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
package com.intellij.openapi.externalSystem.service.project.autoimport
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.externalSystem.ExternalSystemAutoImportAware
|
||||
import com.intellij.openapi.externalSystem.autoimport.ExternalSystemProjectAware
|
||||
import com.intellij.openapi.externalSystem.autoimport.ExternalSystemProjectId
|
||||
@@ -26,8 +25,6 @@ class ProjectAware(
|
||||
private val autoImportAware: ExternalSystemAutoImportAware
|
||||
) : ExternalSystemProjectAware {
|
||||
|
||||
private val LOG = Logger.getInstance("#com.intellij.openapi.externalSystem.autoimport")
|
||||
|
||||
private val systemId = projectId.systemId
|
||||
private val projectPath = projectId.externalProjectPath
|
||||
|
||||
|
||||
@@ -498,4 +498,41 @@ class AutoImportTest : AutoImportTestCase() {
|
||||
assertProjectAware(projectAware, refresh = 3, event = "modification during project refresh")
|
||||
assertNotificationAware(projectId, event = "modification during project refresh")
|
||||
}
|
||||
|
||||
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")
|
||||
settingsFile.replaceContentInIoFile("println 'hello'")
|
||||
assertState(refresh = 1, enabled = false, 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")
|
||||
refreshProject()
|
||||
assertState(refresh = 1, enabled = false, 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")
|
||||
moduleSettingsFile.replaceContentInIoFile("println 'hello'")
|
||||
assertState(refresh = 0, enabled = false, notified = true, event = "modification with disabled auto-import")
|
||||
}
|
||||
assertState(refresh = 1, enabled = false, 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")
|
||||
|
||||
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")
|
||||
}
|
||||
simpleTest("settings.groovy", state = state) {
|
||||
assertState(refresh = 0, enabled = true, notified = false, event = "register project with correct cache")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -171,6 +171,14 @@ abstract class AutoImportTestCase : ExternalSystemTestCase() {
|
||||
|
||||
private fun loadState(state: AutoImportProjectTracker.State) = projectTracker.loadState(state)
|
||||
|
||||
protected fun enableAutoReloadExternalChanges() {
|
||||
projectTracker.isAutoReloadExternalChanges = true
|
||||
}
|
||||
|
||||
protected fun disableAutoReloadExternalChanges() {
|
||||
projectTracker.isAutoReloadExternalChanges = false
|
||||
}
|
||||
|
||||
protected fun initialize() = projectTracker.initializeComponent()
|
||||
|
||||
protected fun getState() = projectTracker.state
|
||||
@@ -194,11 +202,18 @@ 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"
|
||||
}
|
||||
assertEquals("$message on $event", isAutoReload, projectTracker.isAutoReloadExternalChanges)
|
||||
}
|
||||
|
||||
protected fun assertNotificationAware(vararg projects: ExternalSystemProjectId, event: String) {
|
||||
val message = when (projects.isEmpty()) {
|
||||
true -> "Notification must be expired"
|
||||
else -> "Notification must be notified for $projects"
|
||||
else -> "Notification must be notified"
|
||||
}
|
||||
assertEquals("$message on $event", projects.toSet(), notificationAware.getProjectsWithNotification())
|
||||
}
|
||||
@@ -266,12 +281,24 @@ abstract class AutoImportTestCase : ExternalSystemTestCase() {
|
||||
projectAware.refreshStatus = status
|
||||
}
|
||||
|
||||
fun withLinkedProject(fileRelativePath: String, test: SimpleTestBench.(VirtualFile) -> Unit) {
|
||||
val projectId = ExternalSystemProjectId(projectAware.projectId.systemId, "$projectPath/$name")
|
||||
val projectAware = MockProjectAware(projectId)
|
||||
register(projectAware)
|
||||
val file = findOrCreateVirtualFile("$name/$fileRelativePath")
|
||||
projectAware.settingsFiles.add(file.path)
|
||||
SimpleTestBench(projectAware).test(file)
|
||||
remove(projectId)
|
||||
}
|
||||
|
||||
fun assertState(refresh: Int? = null,
|
||||
subscribe: Int? = null,
|
||||
unsubscribe: Int? = null,
|
||||
enabled: Boolean = true,
|
||||
notified: Boolean,
|
||||
event: String) {
|
||||
assertProjectAware(projectAware, refresh, subscribe, unsubscribe, event)
|
||||
assertProjectTracker(enabled, event = event)
|
||||
when (notified) {
|
||||
true -> assertNotificationAware(projectAware.projectId, event = event)
|
||||
else -> assertNotificationAware(event = event)
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
icon="AllIcons.General.Settings">
|
||||
</action>
|
||||
|
||||
<action id="ExternalSystem.ToggleProjectRefresh"
|
||||
class="com.intellij.openapi.externalSystem.autoimport.ToggleProjectRefreshAction">
|
||||
</action>
|
||||
|
||||
<action id="ExternalSystem.OpenTasksActivationManager"
|
||||
class="com.intellij.openapi.externalSystem.action.OpenTasksActivationManagerAction">
|
||||
</action>
|
||||
|
||||
Reference in New Issue
Block a user