Kotlin plugin updater initial implementation
This commit is contained in:
@@ -179,6 +179,9 @@
|
|||||||
<applicationService serviceInterface="org.jetbrains.kotlin.idea.caches.FileAttributeService"
|
<applicationService serviceInterface="org.jetbrains.kotlin.idea.caches.FileAttributeService"
|
||||||
serviceImplementation="org.jetbrains.kotlin.idea.caches.FileAttributeServiceImpl"/>
|
serviceImplementation="org.jetbrains.kotlin.idea.caches.FileAttributeServiceImpl"/>
|
||||||
|
|
||||||
|
<applicationService serviceInterface="org.jetbrains.kotlin.idea.KotlinPluginUpdater"
|
||||||
|
serviceImplementation="org.jetbrains.kotlin.idea.KotlinPluginUpdater"/>
|
||||||
|
|
||||||
<projectService serviceInterface="org.jetbrains.kotlin.idea.util.ImportInsertHelper"
|
<projectService serviceInterface="org.jetbrains.kotlin.idea.util.ImportInsertHelper"
|
||||||
serviceImplementation="org.jetbrains.kotlin.util.ImportInsertHelperImpl"/>
|
serviceImplementation="org.jetbrains.kotlin.util.ImportInsertHelperImpl"/>
|
||||||
|
|
||||||
|
|||||||
@@ -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. <b><a href=\"#\">Install</a></b>",
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,6 +19,12 @@ package org.jetbrains.kotlin.idea;
|
|||||||
import com.intellij.openapi.application.ApplicationManager;
|
import com.intellij.openapi.application.ApplicationManager;
|
||||||
import com.intellij.openapi.application.PathMacros;
|
import com.intellij.openapi.application.PathMacros;
|
||||||
import com.intellij.openapi.components.ApplicationComponent;
|
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.annotations.NotNull;
|
||||||
import org.jetbrains.kotlin.idea.caches.JarUserDataManager;
|
import org.jetbrains.kotlin.idea.caches.JarUserDataManager;
|
||||||
import org.jetbrains.kotlin.idea.debugger.filter.DebuggerFiltersUtilKt;
|
import org.jetbrains.kotlin.idea.debugger.filter.DebuggerFiltersUtilKt;
|
||||||
@@ -53,6 +59,17 @@ public class PluginStartupComponent implements ApplicationComponent {
|
|||||||
BuiltInsReferenceResolver.Companion.refreshBuiltIns();
|
BuiltInsReferenceResolver.Companion.refreshBuiltIns();
|
||||||
|
|
||||||
DebuggerFiltersUtilKt.addKotlinStdlibDebugFilterIfNeeded();
|
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() {
|
private static void registerPathVariable() {
|
||||||
|
|||||||
Reference in New Issue
Block a user