diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml
index 47aed21305a..d1975249664 100644
--- a/idea/src/META-INF/plugin.xml
+++ b/idea/src/META-INF/plugin.xml
@@ -84,9 +84,9 @@
-
+
diff --git a/idea/src/org/jetbrains/kotlin/idea/KotlinPluginUpdater.kt b/idea/src/org/jetbrains/kotlin/idea/KotlinPluginUpdater.kt
index e5c468ee3d6..b916941680b 100644
--- a/idea/src/org/jetbrains/kotlin/idea/KotlinPluginUpdater.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/KotlinPluginUpdater.kt
@@ -27,6 +27,7 @@ import com.intellij.notification.NotificationType
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.ApplicationInfo
import com.intellij.openapi.application.ApplicationManager
+import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.extensions.PluginId
@@ -45,6 +46,16 @@ import com.intellij.util.text.VersionComparatorUtil
import java.net.URLEncoder
import java.util.concurrent.TimeUnit
+sealed class PluginUpdateStatus {
+ object LatestVersionInstalled : PluginUpdateStatus()
+
+ class Update(val newVersion: String,
+ val descriptorToInstall: IdeaPluginDescriptor?,
+ val hostToInstallFrom: String?) : PluginUpdateStatus()
+
+ class CheckFailed(val message: String) : PluginUpdateStatus()
+}
+
class KotlinPluginUpdater(val propertiesComponent: PropertiesComponent) : Disposable {
private val INITIAL_UPDATE_DELAY = 5000L
private var updateDelay = INITIAL_UPDATE_DELAY
@@ -53,25 +64,30 @@ class KotlinPluginUpdater(val propertiesComponent: PropertiesComponent) : Dispos
private val notificationGroup = NotificationGroup("Kotlin plugin updates", NotificationDisplayType.STICKY_BALLOON, true)
fun kotlinFileEdited() {
- val lastUpdateTime = java.lang.Long.parseLong(propertiesComponent.getValue(PROPERTY_NAME, "0"))
- if (lastUpdateTime == 0L || System.currentTimeMillis() - lastUpdateTime > TimeUnit.DAYS.toMillis(1)) {
- queueUpdateCheck()
- }
- }
-
- fun queueUpdateCheck(reportNoUpdates: Boolean = false) {
if (ApplicationManager.getApplication().isUnitTestMode) return
if (!UpdateSettings.getInstance().isCheckNeeded) return
+ val lastUpdateTime = java.lang.Long.parseLong(propertiesComponent.getValue(PROPERTY_NAME, "0"))
+ if (lastUpdateTime == 0L || System.currentTimeMillis() - lastUpdateTime > TimeUnit.DAYS.toMillis(1)) {
+ queueUpdateCheck { updateStatus ->
+ if (updateStatus is PluginUpdateStatus.Update) {
+ notifyPluginUpdateAvailable(updateStatus)
+ }
+ true
+ }
+ }
+ }
+
+ fun queueUpdateCheck(callback: (PluginUpdateStatus) -> Boolean) {
ApplicationManager.getApplication().assertIsDispatchThread()
if (!checkQueued) {
checkQueued = true
- alarm.addRequest({ updateCheck(reportNoUpdates) }, updateDelay)
+ alarm.addRequest({ updateCheck(callback) }, updateDelay)
updateDelay *= 2 // exponential backoff
}
}
- private fun updateCheck(reportNoUpdates: Boolean) {
+ private fun updateCheck(callback: (PluginUpdateStatus) -> Boolean) {
try {
var (mainRepoUpdateSuccess, latestVersionInRepository) = getPluginVersionFromMainRepository()
var descriptorToInstall: IdeaPluginDescriptor? = null
@@ -99,28 +115,28 @@ class KotlinPluginUpdater(val propertiesComponent: PropertiesComponent) : Dispos
if (mainRepoUpdateSuccess || latestVersionInRepository != null) {
recordSuccessfulUpdateCheck()
if (latestVersionInRepository != null && VersionComparatorUtil.compare(latestVersionInRepository, KotlinPluginUtil.getPluginVersion()) > 0) {
- ApplicationManager.getApplication().invokeLater {
- notifyPluginUpdateAvailable(latestVersionInRepository!!, descriptorToInstall, hostToInstallFrom)
- }
+ ApplicationManager.getApplication().invokeLater({
+ callback(PluginUpdateStatus.Update(latestVersionInRepository!!, descriptorToInstall, hostToInstallFrom))
+ }, ModalityState.any())
}
- else if (reportNoUpdates) {
- ApplicationManager.getApplication().invokeLater {
- val notification = notificationGroup.createNotification(
- "Kotlin",
- "You have the latest version of the Kotlin plugin",
- NotificationType.INFORMATION, null)
- notification.notify(null)
- }
+ else {
+ ApplicationManager.getApplication().invokeLater({
+ callback(PluginUpdateStatus.LatestVersionInstalled)
+ }, ModalityState.any())
}
}
else {
- ApplicationManager.getApplication().invokeLater { queueUpdateCheck() }
+ ApplicationManager.getApplication().invokeLater {
+ if (callback(PluginUpdateStatus.CheckFailed("Plugin not found in repository"))) queueUpdateCheck(callback)
+ }
}
}
catch(e: Exception) {
LOG.info("Kotlin plugin update check failed", e)
checkQueued = false
- ApplicationManager.getApplication().invokeLater { queueUpdateCheck() }
+ ApplicationManager.getApplication().invokeLater {
+ if (callback(PluginUpdateStatus.CheckFailed(e.message ?: "Unknown error"))) queueUpdateCheck(callback)
+ }
}
}
@@ -157,43 +173,44 @@ class KotlinPluginUpdater(val propertiesComponent: PropertiesComponent) : Dispos
updateDelay = INITIAL_UPDATE_DELAY
}
- private fun notifyPluginUpdateAvailable(newVersion: String,
- descriptorToInstall: IdeaPluginDescriptor?,
- hostToInstallFrom: String?) {
+ private fun notifyPluginUpdateAvailable(update: PluginUpdateStatus.Update) {
val notification = notificationGroup.createNotification(
"Kotlin",
- "A new version $newVersion of the Kotlin plugin is available. Install",
+ "A new version ${update.newVersion} of the Kotlin plugin is available. Install",
NotificationType.INFORMATION) { notification, event ->
- val descriptor = descriptorToInstall ?: PluginManager.getPlugin(PluginId.getId("org.jetbrains.kotlin"))
- if (descriptor != null) {
- notification.expire()
-
- val pluginDownloader = PluginDownloader.createDownloader(descriptor, hostToInstallFrom, null)
- ProgressManager.getInstance().run(object : Task.Backgroundable(null, "Downloading plugins", true) {
- override fun run(indicator: ProgressIndicator) {
- if (pluginDownloader.prepareToInstall(indicator)) {
- val pluginDescriptor = pluginDownloader.getDescriptor()
- if (pluginDescriptor != null) {
- pluginDownloader.install()
-
- ApplicationManager.getApplication().invokeLater {
- notification.expire()
- PluginManagerMain.notifyPluginsUpdated(null)
- }
- }
- }
- }
-
- override fun onCancel() {
- notifyPluginUpdateAvailable(newVersion, descriptorToInstall, hostToInstallFrom)
- }
- })
+ notification.expire()
+ installPluginUpdate(update) {
+ notifyPluginUpdateAvailable(update)
}
}
notification.notify(null)
}
+ fun installPluginUpdate(update: PluginUpdateStatus.Update,
+ cancelCallback: () -> Unit = {}) {
+ val descriptor = update.descriptorToInstall ?: PluginManager.getPlugin(PluginId.getId("org.jetbrains.kotlin")) ?: return
+ val pluginDownloader = PluginDownloader.createDownloader(descriptor, update.hostToInstallFrom, null)
+ ProgressManager.getInstance().run(object : Task.Backgroundable(null, "Downloading plugins", true) {
+ override fun run(indicator: ProgressIndicator) {
+ if (pluginDownloader.prepareToInstall(indicator)) {
+ val pluginDescriptor = pluginDownloader.getDescriptor()
+ if (pluginDescriptor != null) {
+ pluginDownloader.install()
+
+ ApplicationManager.getApplication().invokeLater {
+ PluginManagerMain.notifyPluginsUpdated(null)
+ }
+ }
+ }
+ }
+
+ override fun onCancel() {
+ cancelCallback()
+ }
+ })
+ }
+
override fun dispose() {
}
diff --git a/idea/src/org/jetbrains/kotlin/idea/actions/ConfigurePluginUpdatesAction.kt b/idea/src/org/jetbrains/kotlin/idea/actions/ConfigurePluginUpdatesAction.kt
new file mode 100644
index 00000000000..5728b29f266
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/actions/ConfigurePluginUpdatesAction.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2010-2015 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.kotlin.idea.actions
+
+import com.intellij.openapi.actionSystem.AnAction
+import com.intellij.openapi.actionSystem.AnActionEvent
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.ui.DialogWrapper
+import com.intellij.openapi.updateSettings.impl.UpdateSettings
+import org.jetbrains.kotlin.idea.KotlinPluginUpdater
+import org.jetbrains.kotlin.idea.PluginUpdateStatus
+import javax.swing.JComponent
+
+class ConfigurePluginUpdatesAction : AnAction() {
+ override fun actionPerformed(e: AnActionEvent) {
+ val project = e.project ?: return
+ ConfigurePluginUpdatesDialog(project).show()
+ }
+}
+
+class ConfigurePluginUpdatesDialog(project: Project) : DialogWrapper(project, false) {
+ private val form = ConfigurePluginUpdatesForm()
+ private val initialSelectedChannel: Int
+ private var update: PluginUpdateStatus.Update? = null
+
+ init {
+ title = "Configure Kotlin Plugin Updates"
+ form.updateCheckProgressIcon.suspend()
+ form.updateCheckProgressIcon.setPaintPassiveIcon(false)
+
+ form.checkForUpdatesNowButton.addActionListener {
+ saveSettings()
+ form.updateCheckProgressIcon.resume()
+ resetUpdateStatus()
+ KotlinPluginUpdater.getInstance().queueUpdateCheck() { pluginUpdateStatus ->
+ form.updateCheckProgressIcon.suspend()
+ when (pluginUpdateStatus) {
+ PluginUpdateStatus.LatestVersionInstalled ->
+ form.updateStatusLabel.text = "You have the latest version of the plugin installed."
+
+ is PluginUpdateStatus.Update -> {
+ update = pluginUpdateStatus
+ form.installButton.isVisible = true
+ form.updateStatusLabel.text = "A new version ${pluginUpdateStatus.newVersion} is available"
+ }
+
+ is PluginUpdateStatus.CheckFailed ->
+ form.updateStatusLabel.text = "Update check failed: ${pluginUpdateStatus.message}"
+ }
+
+ false // do not auto-retry update check
+ }
+ }
+
+ form.installButton.isVisible = false
+ form.installButton.addActionListener {
+ update?.let {
+ close(OK_EXIT_CODE)
+ KotlinPluginUpdater.getInstance().installPluginUpdate(it)
+ }
+ }
+
+ form.channelCombo.addActionListener {
+ resetUpdateStatus()
+ }
+
+ initialSelectedChannel = if (hasEAPChannel()) 1 else 0
+ form.channelCombo.selectedIndex = initialSelectedChannel
+ init()
+ }
+
+ private fun resetUpdateStatus() {
+ form.updateStatusLabel.text = " "
+ form.installButton.isVisible = false
+ }
+
+ override fun createCenterPanel(): JComponent = form.mainPanel
+
+ private fun saveSettings() {
+ saveSelectedChannel(form.channelCombo.selectedIndex)
+ }
+
+ private fun saveSelectedChannel(channel: Int) {
+ val hosts = UpdateSettings.getInstance().storedPluginHosts
+ when (channel) {
+ 0 -> hosts.remove(EAP_UPDATE_HOST)
+ 1 -> if (EAP_UPDATE_HOST !in hosts) hosts.add(EAP_UPDATE_HOST)
+ }
+ }
+
+ override fun doOKAction() {
+ saveSettings()
+ super.doOKAction()
+ }
+
+ override fun doCancelAction() {
+ saveSelectedChannel(initialSelectedChannel)
+ super.doCancelAction()
+ }
+
+ private fun hasEAPChannel() = EAP_UPDATE_HOST in UpdateSettings.getInstance().pluginHosts
+
+ companion object {
+ val EAP_UPDATE_HOST = "https://plugins.jetbrains.com/plugins/eap/6954"
+ }
+}
diff --git a/idea/src/org/jetbrains/kotlin/idea/actions/ConfigurePluginUpdatesForm.form b/idea/src/org/jetbrains/kotlin/idea/actions/ConfigurePluginUpdatesForm.form
new file mode 100644
index 00000000000..6efab9ec99e
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/actions/ConfigurePluginUpdatesForm.form
@@ -0,0 +1,83 @@
+
+
diff --git a/idea/src/org/jetbrains/kotlin/idea/actions/CheckPluginUpdatesAction.kt b/idea/src/org/jetbrains/kotlin/idea/actions/ConfigurePluginUpdatesForm.java
similarity index 54%
rename from idea/src/org/jetbrains/kotlin/idea/actions/CheckPluginUpdatesAction.kt
rename to idea/src/org/jetbrains/kotlin/idea/actions/ConfigurePluginUpdatesForm.java
index 0591804a853..6c72a2143ac 100644
--- a/idea/src/org/jetbrains/kotlin/idea/actions/CheckPluginUpdatesAction.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/actions/ConfigurePluginUpdatesForm.java
@@ -14,14 +14,21 @@
* limitations under the License.
*/
-package org.jetbrains.kotlin.idea.actions
+package org.jetbrains.kotlin.idea.actions;
-import com.intellij.openapi.actionSystem.AnAction
-import com.intellij.openapi.actionSystem.AnActionEvent
-import org.jetbrains.kotlin.idea.KotlinPluginUpdater
+import com.intellij.util.ui.AsyncProcessIcon;
-class CheckPluginUpdatesAction : AnAction() {
- override fun actionPerformed(e: AnActionEvent) {
- KotlinPluginUpdater.getInstance().queueUpdateCheck(true)
+import javax.swing.*;
+
+public class ConfigurePluginUpdatesForm {
+ public JComboBox channelCombo;
+ public JButton checkForUpdatesNowButton;
+ public JPanel mainPanel;
+ public AsyncProcessIcon updateCheckProgressIcon;
+ public JLabel updateStatusLabel;
+ public JButton installButton;
+
+ private void createUIComponents() {
+ updateCheckProgressIcon = new AsyncProcessIcon("Plugin update check progress");
}
-}
\ No newline at end of file
+}