Guest User

Untitled

a guest
Jan 20th, 2019
90
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.85 KB | None | 0 0
  1. import com.intellij.ide.DataManager
  2. import com.intellij.ide.IdeEventQueue
  3. import com.intellij.notification.NotificationDisplayType
  4. import com.intellij.notification.NotificationGroup
  5. import com.intellij.notification.NotificationType
  6. import com.intellij.notification.Notifications
  7. import com.intellij.openapi.Disposable
  8. import com.intellij.openapi.actionSystem.PlatformDataKeys
  9. import com.intellij.openapi.project.Project
  10. import com.intellij.openapi.util.Disposer
  11. import com.intellij.openapi.vcs.VcsDataKeys
  12. import com.intellij.openapi.vfs.VirtualFile
  13. import com.intellij.openapi.wm.IdeFocusManager
  14. import com.intellij.psi.PsiDirectory
  15. import com.intellij.psi.PsiElement
  16. import com.intellij.util.ui.update.MergingUpdateQueue
  17. import java.lang.IllegalStateException
  18. import java.util.concurrent.TimeUnit
  19. import kotlin.streams.toList
  20.  
  21.  
  22. val notificationGroup: NotificationGroup = NotificationGroup("LivePluginDebug", NotificationDisplayType.STICKY_BALLOON, true)
  23.  
  24. val IDE: com.intellij.ide.script.IDE = bindings.get("IDE") as com.intellij.ide.script.IDE
  25. val project = IDE.project!!
  26.  
  27. // Fix Kotlin scripting console output
  28. fun println(s: String) {
  29. kotlin.io.println(s)
  30. IDE.print(s)
  31. val notification = notificationGroup.createNotification("OnFileChange", s, NotificationType.INFORMATION, null)
  32. Notifications.Bus.notify(notification)
  33. }
  34.  
  35.  
  36. fun println(s: Any?) {
  37. println("" + s?.toString())
  38. }
  39.  
  40.  
  41. private object OnFileChangeId {}
  42.  
  43.  
  44.  
  45.  
  46.  
  47. fun deregisterQueue(name: String) {
  48. val mergingUpdateQueue = IDE.get("liveplugin-queue-$name") as MergingUpdateQueue?
  49. if (mergingUpdateQueue != null) {
  50.  
  51.  
  52. } else {
  53. println("No queue named $name")
  54. }
  55. }
  56.  
  57. fun registerQueue(name: String, queue: MergingUpdateQueue) {
  58. deregisterQueue("liveplugin-queue-$name")
  59. IDE.put("liveplugin-queue-$name", queue)
  60. }
  61.  
  62.  
  63. inline fun <reified T> registerObject(type: String, name: String, queue: T) {
  64. val objectKey = "liveplugin-$type-$name"
  65. if (IDE.get(objectKey) != null) {
  66. throw IllegalStateException("Object is already registered, type: $type name: $name, object: ${IDE.get(objectKey)}")
  67. }
  68. IDE.put("liveplugin-queue-$name", queue)
  69. }
  70.  
  71. inline fun <reified T> registerObject(registeredObject: RegisteredObject<T>, queue: T) {
  72. val objectKey = registeredObject.name
  73. if (IDE.get(objectKey) != null) {
  74. throw IllegalStateException("Object already registered, type: name: $objectKey, object: ${IDE.get(objectKey)}")
  75. }
  76. IDE.put(objectKey, queue)
  77. }
  78.  
  79.  
  80. inline fun <reified T> registerOrUpdateObject(registeredObject: RegisteredObject<T>, newObject: T): T? {
  81. val objectKey = registeredObject.name
  82. val oldObject = IDE.get(objectKey)
  83. IDE.put(objectKey, newObject)
  84. return oldObject as? T
  85. }
  86.  
  87.  
  88. inline fun <reified T> registeredObject(type: String, name: String, queue: T, makeT: () -> T): T {
  89. val objectKey = "liveplugin-$type-$name"
  90. if (IDE.get(objectKey) == null) {
  91. IDE.put(objectKey, makeT())
  92. }
  93. return IDE.get(objectKey) as T
  94. }
  95.  
  96. inline fun <reified T> getRegisteredObject(type: String, name: String): T? {
  97. deregisterQueue("liveplugin-$type-$name")
  98. return IDE.get("liveplugin-queue-$name") as T?
  99. }
  100.  
  101. inline fun <reified T> registeredObjectOnce(registeredObject: RegisteredObject<T>, makeT: () -> T): T {
  102. val key = registeredObject.name
  103. val oldObject = IDE.get(key)
  104. if (oldObject !is T) {
  105. if (oldObject != null) {
  106. println("Replacing ${oldObject} due to type mismatch")
  107. }
  108. IDE.put(key, makeT())
  109. }
  110.  
  111. return IDE.get(key) as T
  112. }
  113.  
  114.  
  115. inline fun <reified T> reloadableService(version: Int, type: String, name: String, onDispose: (T) -> Unit, makeT: () -> T) {
  116. val key = "liveplugin-$type-$name"
  117. (IDE.get(key) as T)?.let {
  118. IDE.put(key, null)
  119. onDispose(it)
  120. }
  121.  
  122. registerObject(type, name, makeT())
  123. }
  124.  
  125.  
  126. inline fun reloadableService(version: Int, reloadableService: ReloadableService, onDispose: () -> Unit, makeT: () -> Unit) {
  127. val registeredObject = objectKey<Unit>(reloadableService.type, reloadableService.name)
  128. val registeredVersion = objectKey<Int>(reloadableService.type, "${reloadableService.name}-version")
  129. val currentVersion = registeredVersion.fromIde()
  130. val key = registeredObject.name
  131. if (currentVersion == null || currentVersion != version) {
  132. if (currentVersion != null) {
  133. // Running a different version...
  134. (IDE.get(key) as Unit?)?.let {
  135. IDE.put(registeredVersion.name, null)
  136. IDE.put(key, null)
  137. onDispose()
  138. }
  139. }
  140.  
  141. registerObject(registeredObject, makeT())
  142. registerObject(registeredVersion, version)
  143. }
  144.  
  145.  
  146. }
  147.  
  148. data class RegisteredObject<T>(val name: String)
  149.  
  150. interface NameBasedService<T> {
  151. fun register(id: String, impl: T, parentDisposable: Disposable? = null)
  152. fun dispose(id: String)
  153. fun reset()
  154. }
  155.  
  156. typealias OnRegister<T> = (abc: String, T) -> Unit
  157. typealias OnDispose<T> = (abc: String) -> Unit
  158.  
  159. interface ServiceRegistryContext<T> {
  160. val previousService: T?
  161. val newService: T?
  162. }
  163.  
  164.  
  165. interface ServiceInitContext<T> {
  166. val allServices: MutableList<T>
  167. val delegatedDisposable: Disposable
  168. }
  169.  
  170. fun <T> makeNameBasedServiceManager(type: String, name: String, onCreate: ServiceInitContext<T>.() -> Unit): NameBasedService<T> {
  171. val listKey = objectKey<MutableList<T>>(type, "$name-list")
  172. val mapKey = objectKey<MutableMap<String, T>>(type, "$name-by-name")
  173. val serviceKey = ReloadableService(type, "$name-service")
  174. val onDispose = objectKey<OnDispose<T>>(type, "$name-on-dispose")
  175. val onRegister = objectKey<OnRegister<T>>(type, "$name-on-register")
  176.  
  177.  
  178. registeredObjectOnce(listKey, { mutableListOf()})
  179. registeredObjectOnce(mapKey, { mutableMapOf() })
  180.  
  181. reloadableService(9, serviceKey, {
  182. println("Discarding service $type $name")
  183. }, {
  184. println("Running ServiceInit")
  185.  
  186. println("Registering new onRegister")
  187. registerOrUpdateObject(onRegister, { id, newInstance ->
  188. mapKey.fromIde()?.apply {
  189. if (contains(id)) {
  190. println("Replacing $id")
  191. val oldInstance = get(id)!!
  192. listKey.applyFromIde {
  193. val position = indexOf(oldInstance)
  194. set(position, newInstance)
  195. put(id, newInstance)
  196. }
  197. } else if(containsValue(newInstance)) {
  198. throw IllegalStateException("Cannot register same instance mulitple times under different keys")
  199.  
  200. } else {
  201. listKey.applyFromIde {
  202. println("Adding $id")
  203. add(newInstance)
  204. put(id, newInstance)
  205. }
  206. }
  207. }
  208. })
  209.  
  210. println("Registering new dispose")
  211. registerOrUpdateObject(onDispose, { id ->
  212. mapKey.fromIde()?.apply {
  213. if (contains(id)) {
  214. println("Removing $id")
  215. val oldInstance = get(id)!!
  216. listKey.applyFromIde {
  217. val position = indexOf(oldInstance)
  218. removeAt(position)
  219. }
  220. } else {
  221. println("Failed to dispose")
  222. }
  223. }
  224. })
  225.  
  226. // Clean up
  227. mapKey.fromIde()?.let { byName ->
  228. listKey.fromIde()?.retainAll(byName.values)
  229. byName.values.retainAll(listKey.fromIde()!!)
  230. }
  231.  
  232.  
  233. onCreate(object: ServiceInitContext<T> {
  234. override val allServices: MutableList<T>
  235. get() = listKey.fromIde()!!
  236. override val delegatedDisposable: Disposable
  237. get() = Disposable {
  238. println("Does not yet work")
  239. }
  240. })
  241. })
  242.  
  243.  
  244.  
  245.  
  246. // val oldRegister = onRegister.fromIde()
  247. // val oldOnDispose = oldOnDispose.fromIde()
  248.  
  249. return object : NameBasedService<T> {
  250. override fun reset() {
  251. listKey.fromIde()?.apply { clear() }
  252. mapKey.fromIde()?.apply { clear() }
  253. }
  254.  
  255. override fun register(id: String, impl: T, parentDisposable: Disposable?) {
  256. if (parentDisposable != null) {
  257. Disposer.register(parentDisposable, Disposable { onDispose.fromIde()?.invoke(id) })
  258. }
  259. onRegister.fromIde()?.invoke(id, impl)
  260. }
  261.  
  262. override fun dispose(id: String) {
  263. onDispose.fromIde()?.invoke(id)
  264. }
  265. }
  266. }
  267.  
  268. val ActivityListenerService = makeNameBasedServiceManager<Runnable>("activitylistener", "service") {
  269. IdeEventQueue.getInstance().addActivityListener(Runnable {
  270. allServices.forEach {
  271. it.run()
  272. }
  273. }, delegatedDisposable)
  274. }
  275.  
  276. data class ReloadableService(val type: String, val name: String)
  277.  
  278. fun <T> objectKey(type: String, name: String): RegisteredObject<T> {
  279. return RegisteredObject("liveplugin-$type-$name")
  280. }
  281.  
  282. val ActivityListenersByName = objectKey<MutableMap<String, Runnable>>("activitylistener", "delegates-by-name")
  283. val ActivityListeners = objectKey<MutableList<Runnable>>("activitylistener", "delegate-list")
  284.  
  285.  
  286.  
  287.  
  288. inline fun <reified T> RegisteredObject<T>.with(block: (T) -> Unit) {
  289. (IDE.get(this.name) as T?)?.let(block)
  290. }
  291. inline fun <reified T> RegisteredObject<T>.applyFromIde(block: T.() -> Unit) {
  292. (IDE.get(this.name) as T?)?.apply(block)
  293. }
  294.  
  295. inline fun <reified T> RegisteredObject<T>.fromIde(): T? {
  296. return (IDE.get(this.name) as T)
  297. }
  298.  
  299. inline fun <reified T> RegisteredObject<T>.fromIdeAsAny(): Any? {
  300. return IDE.get(this.name)
  301. }
  302.  
  303. inline fun <reified T> RegisteredObject<T>.fromIdeIfTypeMatches(): T? {
  304. return (IDE.get(this.name) as? T)
  305. }
  306.  
  307. interface OnSelectedFilesChange {
  308. val selectedFiles: List<VirtualFile>
  309. }
  310.  
  311. fun onFileChange(delay: Int, timeUnit: TimeUnit, project: Project, onSelectedFilesChange: OnSelectedFilesChange.() -> Unit, parentDisposable: Disposable? = null) {
  312. var previousResult: Set<VirtualFile>? = null
  313.  
  314. val seenTypes = mutableSetOf<Class<*>>()
  315. class MyUpdate(identity: OnFileChangeId) : com.intellij.util.ui.update.Update(identity) {
  316. override fun run() {
  317. IdeFocusManager.getInstance(project).doWhenFocusSettlesDown {
  318. // println("Focus settled down")
  319. fun getListOfFiles(): List<VirtualFile>? {
  320.  
  321. val focusOwner = IdeFocusManager.getInstance(project).focusOwner
  322. if (focusOwner != null) {
  323. val dataContext = DataManager.getInstance().getDataContext(focusOwner)
  324. // println(dataContext);
  325. val selectedItems = PlatformDataKeys.SELECTED_ITEMS.getData(dataContext)
  326. if (selectedItems != null) {
  327. selectedItems.forEach {
  328. if (seenTypes.add(it::class.java)) {
  329. println(">>> NEW TYPE: ${it::class.java.canonicalName}")
  330. }
  331. }
  332. // println(Arrays.toString(selectedItems))
  333. val directories = selectedItems.filterIsInstance(PsiDirectory::class.java)
  334. val dirFiles = directories.map { it.virtualFile }
  335. val result = (selectedItems.filterIsInstance(PsiElement::class.java).mapNotNull { it.containingFile?.virtualFile } + dirFiles)
  336. return result
  337. }
  338. println("PsiElement")
  339. println(PlatformDataKeys.PSI_ELEMENT.getData(dataContext))
  340. println("PlatformDataKeys.SELECTED_ITEM")
  341. println(PlatformDataKeys.SELECTED_ITEM.getData(dataContext))
  342. println("VcsDataKeys.VIRTUAL_FILE_STREAM")
  343. println(VcsDataKeys.VIRTUAL_FILE_STREAM.getData(dataContext)?.toList())
  344. println("End data context")
  345. }
  346. return null
  347. }
  348.  
  349. val listOfFiles = getListOfFiles()
  350. val setOfFiles = listOfFiles?.toSet()
  351. if (previousResult != setOfFiles) {
  352. previousResult = setOfFiles
  353. if (setOfFiles != null) {
  354. onSelectedFilesChange(object: OnSelectedFilesChange {
  355. override val selectedFiles: List<VirtualFile>
  356. get() = listOfFiles
  357. })
  358. } else {
  359. println("No files detected")
  360. }
  361. }
  362. }
  363. }
  364. }
  365.  
  366. registeredObjectOnce(ActivityListenersByName) {
  367. mutableMapOf()
  368. }
  369.  
  370. registeredObjectOnce(ActivityListeners) {
  371. mutableListOf()
  372. }
  373.  
  374.  
  375. // TODO IDEA I could integrate the mergingTimeSpan into the key name.. then it would work seamlessly
  376. val mergingTimeSpan = timeUnit.toMillis(delay.toLong()).toInt()
  377. val queueKey = objectKey<MergingUpdateQueue>("queue", "onFileChange-$mergingTimeSpan")
  378. registeredObjectOnce(queueKey) {
  379. val queue = object : MergingUpdateQueue("livePlugin:onFileChange", mergingTimeSpan, true, null) {}
  380. queue
  381. }.let { queue ->
  382. // If queue exists already, we need to change it...
  383. // TODO This currently applies to all onFileChange listeners, it shouldn't otherwise the API shouldn't expose that parameter
  384. queue.setMergingTimeSpan(mergingTimeSpan)
  385. }
  386.  
  387. ActivityListenerService.register("onFileChange", Runnable {
  388. queueKey.fromIde()?.queue(MyUpdate(OnFileChangeId))
  389. })
  390. }
  391.  
  392.  
  393. fun main() {
  394. // ActivityListenerService.reset()
  395. println("Executing onFileChange")
  396. onFileChange(1, TimeUnit.SECONDS, project, {
  397. println("Set of selected files has changed;")
  398. selectedFiles.forEach {
  399. println(it)
  400. }
  401. })
  402. println("Executed onFIleChange")
  403. }
Add Comment
Please, Sign In to add comment