diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml
index 4d1ddf8c5a7..64ac9729de1 100644
--- a/idea/src/META-INF/plugin.xml
+++ b/idea/src/META-INF/plugin.xml
@@ -179,6 +179,9 @@
+
+
diff --git a/idea/src/org/jetbrains/kotlin/idea/KotlinPluginUpdater.kt b/idea/src/org/jetbrains/kotlin/idea/KotlinPluginUpdater.kt
new file mode 100644
index 00000000000..c64aad078d9
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/KotlinPluginUpdater.kt
@@ -0,0 +1,176 @@
+/*
+ * 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
+
+import com.intellij.ide.plugins.IdeaPluginDescriptor
+import com.intellij.ide.plugins.PluginManager
+import com.intellij.ide.plugins.PluginManagerMain
+import com.intellij.ide.plugins.RepositoryHelper
+import com.intellij.ide.util.PropertiesComponent
+import com.intellij.notification.NotificationDisplayType
+import com.intellij.notification.NotificationGroup
+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.components.ServiceManager
+import com.intellij.openapi.diagnostic.Logger
+import com.intellij.openapi.extensions.PluginId
+import com.intellij.openapi.progress.ProgressIndicator
+import com.intellij.openapi.progress.ProgressManager
+import com.intellij.openapi.progress.Task
+import com.intellij.openapi.updateSettings.impl.PluginDownloader
+import com.intellij.openapi.updateSettings.impl.UpdateChecker
+import com.intellij.openapi.util.JDOMUtil
+import com.intellij.openapi.util.SystemInfo
+import com.intellij.openapi.vfs.CharsetToolkit
+import com.intellij.util.Alarm
+import com.intellij.util.io.HttpRequests
+import com.intellij.util.text.VersionComparatorUtil
+import java.net.URLEncoder
+import java.util.concurrent.TimeUnit
+
+class KotlinPluginUpdater(val propertiesComponent: PropertiesComponent) : Disposable {
+ private val INITIAL_UPDATE_DELAY = 5000L
+ private var updateDelay = INITIAL_UPDATE_DELAY
+ private val alarm = Alarm(Alarm.ThreadToUse.POOLED_THREAD, this)
+ @Volatile private var checkQueued = false
+ 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()
+ }
+ }
+
+ private fun queueUpdateCheck() {
+ ApplicationManager.getApplication().assertIsDispatchThread()
+ if (!checkQueued) {
+ checkQueued = true
+ alarm.addRequest({ updateCheck() }, updateDelay)
+ updateDelay *= 2 // exponential backoff
+ }
+ }
+
+ private fun updateCheck() {
+ try {
+ checkQueued = false
+
+ var latestVersionInRepository = getPluginVersionFromMainRepository()
+ var descriptorToInstall: IdeaPluginDescriptor? = null
+ var hostToInstallFrom: String? = null
+
+ for (host in RepositoryHelper.getPluginHosts().filterNotNull()) {
+ val plugins = try {
+ RepositoryHelper.loadPlugins(host, null)
+ }
+ catch(e: Exception) {
+ LOG.info("Checking custom plugin reposityory $host failed", e)
+ continue
+ }
+
+ val kotlinPlugin = plugins.find { it.pluginId.toString() == "org.jetbrains.kotlin" }
+ if (kotlinPlugin != null && VersionComparatorUtil.compare(kotlinPlugin.version, latestVersionInRepository) > 0) {
+ latestVersionInRepository = kotlinPlugin.version
+ descriptorToInstall = kotlinPlugin
+ hostToInstallFrom = host
+ }
+ }
+
+ if (latestVersionInRepository != null) {
+ recordSuccessfulUpdateCheck()
+ if (VersionComparatorUtil.compare(latestVersionInRepository, JetPluginUtil.getPluginVersion()) > 0) {
+ ApplicationManager.getApplication().invokeLater {
+ notifyPluginUpdateAvailable(latestVersionInRepository!!, descriptorToInstall, hostToInstallFrom)
+ }
+ }
+ }
+ else {
+ queueUpdateCheck()
+ }
+ }
+ catch(e: Exception) {
+ LOG.info("Kotlin plugin update check failed", e)
+ checkQueued = false
+ queueUpdateCheck()
+ }
+ }
+
+ fun getPluginVersionFromMainRepository(): String? {
+ val buildNumber = ApplicationInfo.getInstance().build.asString()
+ val pluginVersion = JetPluginUtil.getPluginVersion()
+ val os = URLEncoder.encode(SystemInfo.OS_NAME, CharsetToolkit.UTF8)
+ val uid = UpdateChecker.getInstallationUID(propertiesComponent)
+ val url = "https://plugins.jetbrains.com/plugins/list?pluginId=6954&build=$buildNumber&pluginVersion=$pluginVersion&os=$os&uuid=a$uid"
+ val responseDoc = HttpRequests.request(url).connect {
+ JDOMUtil.load(it.inputStream)
+ }
+ val version = responseDoc.getChild("category")?.getChild("idea-plugin")?.getChild("version")?.text
+ if (version == null) {
+ LOG.info("Couldn't find plugin version in repository response: ${JDOMUtil.writeElement(responseDoc, "\n")}")
+ }
+ return version
+ }
+
+ private fun recordSuccessfulUpdateCheck() {
+ propertiesComponent.setValue(PROPERTY_NAME, System.currentTimeMillis().toString())
+ updateDelay = INITIAL_UPDATE_DELAY
+ }
+
+ private fun notifyPluginUpdateAvailable(newVersion: String,
+ descriptorToInstall: IdeaPluginDescriptor?,
+ hostToInstallFrom: String?) {
+ val notification = notificationGroup.createNotification(
+ "Kotlin",
+ "A new version $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) {
+ 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 descriptor = pluginDownloader.getDescriptor()
+ if (descriptor != null) {
+ pluginDownloader.install()
+
+ ApplicationManager.getApplication().invokeLater {
+ notification.expire()
+ PluginManagerMain.notifyPluginsUpdated(null)
+ }
+ }
+ }
+
+ }
+ })
+ }
+ }
+
+ notification.notify(null)
+ }
+
+ override fun dispose() {
+ }
+
+ companion object {
+ private val PROPERTY_NAME = "kotlin.lastUpdateCheck"
+ private val LOG = Logger.getInstance(KotlinPluginUpdater::class.java)
+
+ fun getInstance(): KotlinPluginUpdater = ServiceManager.getService(KotlinPluginUpdater::class.java)
+ }
+}
diff --git a/idea/src/org/jetbrains/kotlin/idea/PluginStartupComponent.java b/idea/src/org/jetbrains/kotlin/idea/PluginStartupComponent.java
index c350077a91f..2684a0b2b45 100644
--- a/idea/src/org/jetbrains/kotlin/idea/PluginStartupComponent.java
+++ b/idea/src/org/jetbrains/kotlin/idea/PluginStartupComponent.java
@@ -19,6 +19,12 @@ package org.jetbrains.kotlin.idea;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.PathMacros;
import com.intellij.openapi.components.ApplicationComponent;
+import com.intellij.openapi.editor.EditorFactory;
+import com.intellij.openapi.editor.event.DocumentAdapter;
+import com.intellij.openapi.editor.event.DocumentEvent;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.updateSettings.impl.UpdateChecker;
+import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.idea.caches.JarUserDataManager;
import org.jetbrains.kotlin.idea.debugger.filter.DebuggerFiltersUtilKt;
@@ -53,6 +59,17 @@ public class PluginStartupComponent implements ApplicationComponent {
BuiltInsReferenceResolver.Companion.refreshBuiltIns();
DebuggerFiltersUtilKt.addKotlinStdlibDebugFilterIfNeeded();
+
+ UpdateChecker.getDisabledToUpdatePlugins().add("org.jetbrains.kotlin");
+ EditorFactory.getInstance().getEventMulticaster().addDocumentListener(new DocumentAdapter() {
+ @Override
+ public void documentChanged(DocumentEvent e) {
+ VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(e.getDocument());
+ if (virtualFile != null && virtualFile.getFileType() == JetFileType.INSTANCE) {
+ KotlinPluginUpdater.Companion.getInstance().kotlinFileEdited();
+ }
+ }
+ });
}
private static void registerPathVariable() {