mirror of
				https://github.com/chylex/IntelliJ-IdeaVim.git
				synced 2025-10-31 11:17:13 +01:00 
			
		
		
		
	Compare commits
	
		
			46 Commits
		
	
	
		
			e42b4d0ea3
			...
			85a1fbe89e
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 85a1fbe89e | ||
|   | 142550a1f8 | ||
|   | e3d3b73903 | ||
|   | 45f18ff91c | ||
|   | 2103163207 | ||
|   | 19dd49670c | ||
|   | e738a1a821 | ||
|   | 6e0f301fb8 | ||
|   | c76b8db293 | ||
|   | 9fa4ca8fb3 | ||
|   | 871b60fe8d | ||
|   | 6715a5b61f | ||
|   | d7d91f1cc5 | ||
|   | 9f00dbd6f4 | ||
|   | f95cf3d671 | ||
|   | 7fbc17624f | ||
|   | b9c2ea37cb | ||
|   | ca0db15e01 | ||
|   | c32c62eacc | ||
|   | 43a79dbad4 | ||
|   | 2829a13187 | ||
|   | efc8c9207d | ||
|   | 183ed10592 | ||
|   | 926b47a31e | ||
|   | d272c919ea | ||
|   | f6e7d04fd5 | ||
|   | ccdff4f087 | ||
|   | ff14303e88 | ||
|   | 48a592340b | ||
|   | da8f5f3231 | ||
|   | f8fa8b73fa | ||
|   | aee126b625 | ||
|   | 396ac86939 | ||
|   | 81816f903f | ||
|   | 06a85b784b | ||
|   | 7f1e3bb155 | ||
|   | 241f554133 | ||
|   | 9498d0779c | ||
|   | b12fd5100f | ||
|   | 92f622430d | ||
|   | ef518f5b23 | ||
|   | 7acb17ebdb | ||
|   | 479a7dbbaf | ||
|   | 4fd1a25557 | ||
|   | f32d42e625 | ||
|   | 362b9a5c3a | 
							
								
								
									
										2
									
								
								.teamcity/_Self/Project.kt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.teamcity/_Self/Project.kt
									
									
									
									
										vendored
									
									
								
							| @@ -6,6 +6,7 @@ import _Self.buildTypes.Nvim | |||||||
| import _Self.buildTypes.PluginVerifier | import _Self.buildTypes.PluginVerifier | ||||||
| import _Self.buildTypes.PropertyBased | import _Self.buildTypes.PropertyBased | ||||||
| import _Self.buildTypes.Qodana | import _Self.buildTypes.Qodana | ||||||
|  | import _Self.buildTypes.TestsForIntelliJ20222 | ||||||
| import _Self.buildTypes.TestsForIntelliJEAP | import _Self.buildTypes.TestsForIntelliJEAP | ||||||
| import _Self.subprojects.GitHub | import _Self.subprojects.GitHub | ||||||
| import _Self.subprojects.OldTests | import _Self.subprojects.OldTests | ||||||
| @@ -39,6 +40,7 @@ object Project : Project({ | |||||||
|  |  | ||||||
|   // Builds |   // Builds | ||||||
|   buildType(TestsForIntelliJEAP) |   buildType(TestsForIntelliJEAP) | ||||||
|  |   buildType(TestsForIntelliJ20222) | ||||||
|  |  | ||||||
|   buildType(PropertyBased) |   buildType(PropertyBased) | ||||||
|   buildType(LongRunning) |   buildType(LongRunning) | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								.teamcity/_Self/buildTypes/ActiveTests.kt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.teamcity/_Self/buildTypes/ActiveTests.kt
									
									
									
									
										vendored
									
									
								
							| @@ -55,4 +55,4 @@ sealed class ActiveTests(buildName: String, ijVersion: String) : BuildType({ | |||||||
| }) | }) | ||||||
|  |  | ||||||
| object TestsForIntelliJEAP : ActiveTests("Tests for IntelliJ Latest EAP", "LATEST-EAP-SNAPSHOT") | object TestsForIntelliJEAP : ActiveTests("Tests for IntelliJ Latest EAP", "LATEST-EAP-SNAPSHOT") | ||||||
| //object TestsForIntelliJ20213 : ActiveTests("Tests for IntelliJ 2021.3", "2021.3.2") | object TestsForIntelliJ20222 : ActiveTests("Tests for IntelliJ 2022.2", "2022.2.1") | ||||||
|   | |||||||
							
								
								
									
										69
									
								
								AUTHORS.md
									
									
									
									
									
								
							
							
						
						
									
										69
									
								
								AUTHORS.md
									
									
									
									
									
								
							| @@ -32,16 +32,48 @@ Contributors: | |||||||
|   [![icon][github]](https://github.com/yole) |   [![icon][github]](https://github.com/yole) | ||||||
|     |     | ||||||
|   Dmitry Jemerov |   Dmitry Jemerov | ||||||
|  | * [![icon][mail]](mailto:tony.kay@gmail.com) | ||||||
|  |   [![icon][github]](https://github.com/awkay) | ||||||
|  |     | ||||||
|  |   Tony Kay | ||||||
|  | * [![icon][mail]](mailto:jamescmartinez@gmail.com) | ||||||
|  |   [![icon][github]](https://github.com/jamescmartinez) | ||||||
|  |     | ||||||
|  |   James Martinez | ||||||
|  | * [![icon][mail]](mailto:almas337519@gmail.com) | ||||||
|  |   [![icon][github]](https://github.com/strogiyotec) | ||||||
|  |     | ||||||
|  |   strogiyotec | ||||||
|  | * [![icon][mail]](mailto:raimon49@hotmail.com) | ||||||
|  |   [![icon][github]](https://github.com/raimon49) | ||||||
|  |     | ||||||
|  |   raimon | ||||||
|  | * [![icon][mail]](mailto:agrsbm@gmail.com) | ||||||
|  |   [![icon][github-off]](#) | ||||||
|  |     | ||||||
|  |   Alexander Griesbaum | ||||||
|  | * [![icon][mail]](mailto:manwe64@gmail.com) | ||||||
|  |   [![icon][github]](https://github.com/baldrs) | ||||||
|  |     | ||||||
|  |   Baldrs | ||||||
|  | * [![icon][mail]](mailto:yury@shurup.com) | ||||||
|  |   [![icon][github]](https://github.com/zyv) | ||||||
|  |     | ||||||
|  |   Yury V. Zaytsev | ||||||
|  | * [![icon][mail]](mailto:jflorian@doubledog.org) | ||||||
|  |   [![icon][github]](https://github.com/jflorian) | ||||||
|  |     | ||||||
|  |   John Florian | ||||||
| * [![icon][mail]](mailto:marquis@marquiswang.com) | * [![icon][mail]](mailto:marquis@marquiswang.com) | ||||||
|   [![icon][github]](https://github.com/marquiswang) |   [![icon][github]](https://github.com/marquiswang) | ||||||
|     |     | ||||||
|   Marquis Wang |   Marquis Wang | ||||||
| * [![icon][mail]](mailto:madgnome@gmail.com) | * [![icon][mail]](mailto:madgnome@gmail.com) | ||||||
|   [![icon][github-off]](#) |   [![icon][github]](https://github.com/madgnome) | ||||||
|     |     | ||||||
|   Julien Hoarau   |   Julien Hoarau   | ||||||
| * [![icon][mail]](mailto:masanobu.imai@gmail.com) | * [![icon][mail]](mailto:masanobu.imai@gmail.com) | ||||||
|   [![icon][github-off]](#) |   [![icon][github]](https://github.com/masanobuimai) | ||||||
|     |     | ||||||
|   Masanobu Imai |   Masanobu Imai | ||||||
| * [![icon][mail]](mailto:poxvuibr@gmail.com) | * [![icon][mail]](mailto:poxvuibr@gmail.com) | ||||||
| @@ -57,7 +89,7 @@ Contributors: | |||||||
|     |     | ||||||
|   John Lindquist |   John Lindquist | ||||||
| * [![icon][mail]](mailto:iklotzko@ltech.com) | * [![icon][mail]](mailto:iklotzko@ltech.com) | ||||||
|   [![icon][github-off]](#) |   [![icon][github]](https://github.com/iklotzko) | ||||||
|     |     | ||||||
|   Ira Klotzko |   Ira Klotzko | ||||||
| * [![icon][mail]](mailto:alex@selesse.com) | * [![icon][mail]](mailto:alex@selesse.com) | ||||||
| @@ -65,7 +97,7 @@ Contributors: | |||||||
|     |     | ||||||
|   Alex Selesse |   Alex Selesse | ||||||
| * [![icon][mail]](mailto:dbennett@palantir.com) | * [![icon][mail]](mailto:dbennett@palantir.com) | ||||||
|   [![icon][github-off]](#) |   [![icon][github]](https://github.com/dathanb) | ||||||
|     |     | ||||||
|   Dathan Bennett |   Dathan Bennett | ||||||
| * [![icon][mail]](mailto:kphayen@gmail.com) | * [![icon][mail]](mailto:kphayen@gmail.com) | ||||||
| @@ -77,11 +109,11 @@ Contributors: | |||||||
|     |     | ||||||
|   Alexey Shmalko |   Alexey Shmalko | ||||||
| * [![icon][mail]](mailto:a.m.brookins@gmail.com) | * [![icon][mail]](mailto:a.m.brookins@gmail.com) | ||||||
|   [![icon][github-off]](#) |   [![icon][github]](https://github.com/abrookins) | ||||||
|     |     | ||||||
|   Andrew Brookins |   Andrew Brookins | ||||||
| * [![icon][mail]](mailto:changwang83@gmail.com) | * [![icon][mail]](mailto:changwang83@gmail.com) | ||||||
|   [![icon][github-off]](#) |   [![icon][github]](https://github.com/changwang) | ||||||
|     |     | ||||||
|   Chang Wang |   Chang Wang | ||||||
| * [![icon][mail]](mailto:josejaime.sanchez@gmail.com) | * [![icon][mail]](mailto:josejaime.sanchez@gmail.com) | ||||||
| @@ -89,19 +121,19 @@ Contributors: | |||||||
|     |     | ||||||
|   Jaime Sanchez |   Jaime Sanchez | ||||||
| * [![icon][mail]](mailto:thomas@homburg.dk) | * [![icon][mail]](mailto:thomas@homburg.dk) | ||||||
|   [![icon][github-off]](#) |   [![icon][github]](https://github.com/homburg) | ||||||
|     |     | ||||||
|   Thomas B Homburg |   Thomas B Homburg | ||||||
| * [![icon][mail]](mailto:smartbomb@server.fake) | * [![icon][mail]](mailto:smartbomb@server.fake) | ||||||
|   [![icon][github-off]](#) |   [![icon][github]](https://github.com/smartbomb) | ||||||
|     |     | ||||||
|   smartbomb |   smartbomb | ||||||
| * [![icon][mail]](mailto:tuomas.tynkkynen@iki.fi) | * [![icon][mail]](mailto:tuomas.tynkkynen@iki.fi) | ||||||
|   [![icon][github-off]](#) |   [![icon][github]](https://github.com/dezgeg) | ||||||
|     |     | ||||||
|   Tuomas Tynkkynen |   Tuomas Tynkkynen | ||||||
| * [![icon][mail]](mailto:jackson@donorschoose.org) | * [![icon][mail]](mailto:jackson@donorschoose.org) | ||||||
|   [![icon][github-off]](#) |   [![icon][github]](https://github.com/jdpopkin) | ||||||
|     |     | ||||||
|   Jackson Popkin |   Jackson Popkin | ||||||
| * [![icon][mail]](mailto:yuyuyu1999@gmail.com) | * [![icon][mail]](mailto:yuyuyu1999@gmail.com) | ||||||
| @@ -109,7 +141,7 @@ Contributors: | |||||||
|     |     | ||||||
|   Teruo Kunihiro |   Teruo Kunihiro | ||||||
| * [![icon][mail]](mailto:lubashka.994@mail.ru) | * [![icon][mail]](mailto:lubashka.994@mail.ru) | ||||||
|   [![icon][github-off]](#) |   [![icon][github]](https://github.com/lubba) | ||||||
|     |     | ||||||
|   Liubov Paina |   Liubov Paina | ||||||
| * [![icon][mail]](mailto:me@dhleong.net) | * [![icon][mail]](mailto:me@dhleong.net) | ||||||
| @@ -137,7 +169,7 @@ Contributors: | |||||||
|     |     | ||||||
|   tieTYT |   tieTYT | ||||||
| * [![icon][mail]](mailto:nickgieschen@gmail.com) | * [![icon][mail]](mailto:nickgieschen@gmail.com) | ||||||
|   [![icon][github-off]](#) |   [![icon][github]](https://github.com/nickgieschen) | ||||||
|     |     | ||||||
|   Nick Gieschen |   Nick Gieschen | ||||||
| * [![icon][mail]](mailto:ikenox@gmail.com) | * [![icon][mail]](mailto:ikenox@gmail.com) | ||||||
| @@ -149,7 +181,7 @@ Contributors: | |||||||
|     |     | ||||||
|   Maximilian Luz |   Maximilian Luz | ||||||
| * [![icon][mail]](mailto:vparfinenko@excelsior-usa.com) | * [![icon][mail]](mailto:vparfinenko@excelsior-usa.com) | ||||||
|   [![icon][github-off]](#) |   [![icon][github]](https://github.com/cypok) | ||||||
|     |     | ||||||
|   Vladimir Parfinenko |   Vladimir Parfinenko | ||||||
| * [![icon][mail]](mailto:hassmann@hwdev.de) | * [![icon][mail]](mailto:hassmann@hwdev.de) | ||||||
| @@ -193,7 +225,7 @@ Contributors: | |||||||
|     |     | ||||||
|   Marcel Hild |   Marcel Hild | ||||||
| * [![icon][mail]](mailto:vedranb@gmail.com) | * [![icon][mail]](mailto:vedranb@gmail.com) | ||||||
|   [![icon][github-off]](#) |   [![icon][github]](https://github.com/vedran) | ||||||
|     |     | ||||||
|   Vedran Budimcic |   Vedran Budimcic | ||||||
| * [![icon][mail]](mailto:andreigasparovici1@gmail.com) | * [![icon][mail]](mailto:andreigasparovici1@gmail.com) | ||||||
| @@ -208,10 +240,13 @@ Contributors: | |||||||
|   [![icon][github]](https://github.com/TonyArra) |   [![icon][github]](https://github.com/TonyArra) | ||||||
|     |     | ||||||
|   Tony Arra |   Tony Arra | ||||||
| * [![icon][mail]](mailto:bradziolko@gmail.com) | * [![icon][mail]](mailto:mj@ziolko.dev) | ||||||
|   [![icon][github]](https://github.com/bradziolko) |   [![icon][github]](https://github.com/mjziolko) | ||||||
|     |     | ||||||
|   Brad Ziolko |   Madeline Ziolko | ||||||
|  |   [Original contribution from: | ||||||
|  |   [![icon][mail]](mailto:bradziolko@gmail.com) | ||||||
|  |   [![icon][github]](https://github.com/bradziolko)] | ||||||
| * [![icon][mail]](mailto:sumoooru2@gmail.com) | * [![icon][mail]](mailto:sumoooru2@gmail.com) | ||||||
|   [![icon][github]](https://github.com/sumoooru2) |   [![icon][github]](https://github.com/sumoooru2) | ||||||
|     |     | ||||||
|   | |||||||
| @@ -23,6 +23,15 @@ It is important to distinguish EAP from traditional pre-release software. | |||||||
| Please note that the quality of EAP versions may at times be way below even | Please note that the quality of EAP versions may at times be way below even | ||||||
| usual beta standards. | usual beta standards. | ||||||
|  |  | ||||||
|  | ## To Be Released | ||||||
|  |  | ||||||
|  | ### Fixes: | ||||||
|  | * [VIM-1758](https://youtrack.jetbrains.com/issue/VIM-1758) Commentary plugin in rider | ||||||
|  | * [VIM-1903](https://youtrack.jetbrains.com/issue/VIM-1903) Autoindent now works in rider | ||||||
|  | * [VIM-2744](https://youtrack.jetbrains.com/issue/VIM-2744) Fix undo from ex line | ||||||
|  | * [VIM-2749](https://youtrack.jetbrains.com/issue/VIM-2749) Fix :tabn and :tabN commands | ||||||
|  | * [VIM-2718](https://youtrack.jetbrains.com/issue/VIM-2718) Fixed case where the primary caret was changed | ||||||
|  |  | ||||||
| ## 1.11.0, 2022-08-09 | ## 1.11.0, 2022-08-09 | ||||||
|  |  | ||||||
| ### Features: | ### Features: | ||||||
|   | |||||||
| @@ -26,7 +26,7 @@ plugins { | |||||||
|     java |     java | ||||||
|     kotlin("jvm") version "1.6.21" |     kotlin("jvm") version "1.6.21" | ||||||
|  |  | ||||||
|     id("org.jetbrains.intellij") version "1.8.0" |     id("org.jetbrains.intellij") version "1.9.0" | ||||||
|     id("org.jetbrains.changelog") version "1.3.1" |     id("org.jetbrains.changelog") version "1.3.1" | ||||||
|  |  | ||||||
|     // ktlint linter - read more: https://github.com/JLLeitschuh/ktlint-gradle |     // ktlint linter - read more: https://github.com/JLLeitschuh/ktlint-gradle | ||||||
| @@ -335,8 +335,15 @@ tasks.register("slackNotification") { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| // --- Update authors | // Uncomment to enable FUS testing mode | ||||||
|  | // tasks { | ||||||
|  | //    withType<org.jetbrains.intellij.tasks.RunIdeTask> { | ||||||
|  | //        jvmArgs("-Didea.is.internal=true") | ||||||
|  | //        jvmArgs("-Dfus.internal.test.mode=true") | ||||||
|  | //    } | ||||||
|  | // } | ||||||
|  |  | ||||||
|  | // --- Update authors | ||||||
| tasks.register("updateAuthors") { | tasks.register("updateAuthors") { | ||||||
|     doLast { |     doLast { | ||||||
|         val uncheckedEmails = setOf( |         val uncheckedEmails = setOf( | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| # suppress inspection "UnusedProperty" for whole file | # suppress inspection "UnusedProperty" for whole file | ||||||
|  |  | ||||||
| ideaVersion=LATEST-EAP-SNAPSHOT | ideaVersion=2022.2.2 | ||||||
| downloadIdeaSources=true | downloadIdeaSources=true | ||||||
| instrumentPluginCode=true | instrumentPluginCode=true | ||||||
| version=SNAPSHOT | version=SNAPSHOT | ||||||
|   | |||||||
| @@ -22,6 +22,7 @@ import com.maddyhome.idea.vim.VimPlugin | |||||||
| import com.maddyhome.idea.vim.api.ExecutionContext | import com.maddyhome.idea.vim.api.ExecutionContext | ||||||
| import com.maddyhome.idea.vim.api.VimCaret | import com.maddyhome.idea.vim.api.VimCaret | ||||||
| import com.maddyhome.idea.vim.api.VimEditor | import com.maddyhome.idea.vim.api.VimEditor | ||||||
|  | import com.maddyhome.idea.vim.api.injector | ||||||
| import com.maddyhome.idea.vim.command.Argument | import com.maddyhome.idea.vim.command.Argument | ||||||
| import com.maddyhome.idea.vim.command.Command | import com.maddyhome.idea.vim.command.Command | ||||||
| import com.maddyhome.idea.vim.command.CommandFlags | import com.maddyhome.idea.vim.command.CommandFlags | ||||||
| @@ -40,7 +41,7 @@ import com.maddyhome.idea.vim.newapi.ij | |||||||
| import java.util.* | import java.util.* | ||||||
|  |  | ||||||
| private fun doOperatorAction(editor: VimEditor, context: ExecutionContext, textRange: TextRange, selectionType: SelectionType): Boolean { | private fun doOperatorAction(editor: VimEditor, context: ExecutionContext, textRange: TextRange, selectionType: SelectionType): Boolean { | ||||||
|   val operatorFunction = VimPlugin.getKey().operatorFunction |   val operatorFunction = injector.keyGroup.operatorFunction | ||||||
|   if (operatorFunction == null) { |   if (operatorFunction == null) { | ||||||
|     VimPlugin.showMessage(MessageHelper.message("E774")) |     VimPlugin.showMessage(MessageHelper.message("E774")) | ||||||
|     return false |     return false | ||||||
| @@ -49,7 +50,7 @@ private fun doOperatorAction(editor: VimEditor, context: ExecutionContext, textR | |||||||
|   val saveRepeatHandler = VimRepeater.repeatHandler |   val saveRepeatHandler = VimRepeater.repeatHandler | ||||||
|   VimPlugin.getMark().setChangeMarks(editor, textRange) |   VimPlugin.getMark().setChangeMarks(editor, textRange) | ||||||
|   KeyHandler.getInstance().reset(editor) |   KeyHandler.getInstance().reset(editor) | ||||||
|   val result = operatorFunction.apply(editor.ij, context.ij, selectionType) |   val result = operatorFunction.apply(editor, context, selectionType) | ||||||
|   VimRepeater.repeatHandler = saveRepeatHandler |   VimRepeater.repeatHandler = saveRepeatHandler | ||||||
|   return result |   return result | ||||||
| } | } | ||||||
|   | |||||||
| @@ -127,7 +127,7 @@ object VimExtensionFacade { | |||||||
|   /** Sets the value of 'operatorfunc' to be used as the operator function in 'g@'. */ |   /** Sets the value of 'operatorfunc' to be used as the operator function in 'g@'. */ | ||||||
|   @JvmStatic |   @JvmStatic | ||||||
|   fun setOperatorFunction(function: OperatorFunction) { |   fun setOperatorFunction(function: OperatorFunction) { | ||||||
|     VimPlugin.getKey().setOperatorFunction(function) |     VimPlugin.getKey().operatorFunction = function | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
| @@ -186,7 +186,7 @@ object VimExtensionFacade { | |||||||
|  |  | ||||||
|   @JvmStatic |   @JvmStatic | ||||||
|   fun getRegisterForCaret(register: Char, caret: VimCaret): List<KeyStroke>? { |   fun getRegisterForCaret(register: Char, caret: VimCaret): List<KeyStroke>? { | ||||||
|     val reg = caret.registerStorage.getRegister(register) ?: return null |     val reg = caret.registerStorage.getRegister(caret, register) ?: return null | ||||||
|     return reg.keys |     return reg.keys | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -199,7 +199,7 @@ object VimExtensionFacade { | |||||||
|   /** Set the current contents of the given register */ |   /** Set the current contents of the given register */ | ||||||
|   @JvmStatic |   @JvmStatic | ||||||
|   fun setRegisterForCaret(register: Char, caret: VimCaret, keys: List<KeyStroke?>?) { |   fun setRegisterForCaret(register: Char, caret: VimCaret, keys: List<KeyStroke?>?) { | ||||||
|     caret.registerStorage.setKeys(register, keys?.filterNotNull() ?: emptyList()) |     caret.registerStorage.setKeys(caret, register, keys?.filterNotNull() ?: emptyList()) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   /** Set the current contents of the given register */ |   /** Set the current contents of the given register */ | ||||||
|   | |||||||
| @@ -17,10 +17,11 @@ | |||||||
|  */ |  */ | ||||||
| package com.maddyhome.idea.vim.extension.commentary | package com.maddyhome.idea.vim.extension.commentary | ||||||
|  |  | ||||||
| import com.intellij.openapi.actionSystem.DataContext | import com.intellij.codeInsight.actions.AsyncActionExecutionService | ||||||
| import com.intellij.openapi.actionSystem.IdeActions | import com.intellij.openapi.actionSystem.IdeActions | ||||||
| import com.intellij.openapi.application.runWriteAction | import com.intellij.openapi.application.runWriteAction | ||||||
| import com.intellij.openapi.editor.Editor | import com.intellij.openapi.editor.Editor | ||||||
|  | import com.intellij.openapi.util.Ref | ||||||
| import com.intellij.psi.PsiComment | import com.intellij.psi.PsiComment | ||||||
| import com.intellij.psi.PsiElement | import com.intellij.psi.PsiElement | ||||||
| import com.intellij.psi.PsiFile | import com.intellij.psi.PsiFile | ||||||
| @@ -56,47 +57,65 @@ import com.maddyhome.idea.vim.helper.vimStateMachine | |||||||
| import com.maddyhome.idea.vim.key.OperatorFunction | import com.maddyhome.idea.vim.key.OperatorFunction | ||||||
| import com.maddyhome.idea.vim.newapi.IjVimEditor | import com.maddyhome.idea.vim.newapi.IjVimEditor | ||||||
| import com.maddyhome.idea.vim.newapi.ij | import com.maddyhome.idea.vim.newapi.ij | ||||||
| import com.maddyhome.idea.vim.newapi.vim |  | ||||||
| import java.util.* | import java.util.* | ||||||
|  |  | ||||||
| class CommentaryExtension : VimExtension { | class CommentaryExtension : VimExtension { | ||||||
|  |  | ||||||
|   companion object { |   companion object { | ||||||
|     fun doCommentary(editor: VimEditor, context: ExecutionContext, range: TextRange, selectionType: SelectionType, resetCaret: Boolean): Boolean { |     fun doCommentary( | ||||||
|  |       editor: VimEditor, | ||||||
|  |       context: ExecutionContext, | ||||||
|  |       range: TextRange, | ||||||
|  |       selectionType: SelectionType, | ||||||
|  |       resetCaret: Boolean, | ||||||
|  |     ): Boolean { | ||||||
|       val mode = editor.vimStateMachine.mode |       val mode = editor.vimStateMachine.mode | ||||||
|       if (mode !== VimStateMachine.Mode.VISUAL) { |       if (mode !== VimStateMachine.Mode.VISUAL) { | ||||||
|         editor.ij.selectionModel.setSelection(range.startOffset, range.endOffset) |         editor.ij.selectionModel.setSelection(range.startOffset, range.endOffset) | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       return runWriteAction { |       return runWriteAction { | ||||||
|         try { |         // Treat block- and character-wise selections as block comments. Be ready to fall back to if the first action | ||||||
|           // Treat block- and character-wise selections as block comments. Be ready to fall back to if the first action |         // isn't available | ||||||
|           // isn't available |         val actions = if (selectionType === SelectionType.LINE_WISE) { | ||||||
|           val actions = if (selectionType === SelectionType.LINE_WISE) { |           listOf(IdeActions.ACTION_COMMENT_LINE, IdeActions.ACTION_COMMENT_BLOCK) | ||||||
|             listOf(IdeActions.ACTION_COMMENT_LINE, IdeActions.ACTION_COMMENT_BLOCK) |         } else { | ||||||
|           } else { |           listOf(IdeActions.ACTION_COMMENT_BLOCK, IdeActions.ACTION_COMMENT_LINE) | ||||||
|             listOf(IdeActions.ACTION_COMMENT_BLOCK, IdeActions.ACTION_COMMENT_LINE) |  | ||||||
|           } |  | ||||||
|  |  | ||||||
|           injector.actionExecutor.executeAction(actions[0], context) || |  | ||||||
|             injector.actionExecutor.executeAction(actions[1], context) |  | ||||||
|         } finally { |  | ||||||
|           // Remove the selection, if we added it |  | ||||||
|           if (mode !== VimStateMachine.Mode.VISUAL) { |  | ||||||
|             editor.removeSelection() |  | ||||||
|           } |  | ||||||
|  |  | ||||||
|           // Put the caret back at the start of the range, as though it was moved by the operator's motion argument. |  | ||||||
|           // This is what Vim does. If IntelliJ is configured to add comments at the start of the line, this might put |  | ||||||
|           // the caret in the "wrong" place. E.g. gc_ should put the caret on the first non-whitespace character. This |  | ||||||
|           // is calculated by the motion, saved in the marks, and then we insert the comment. If it's inserted at the |  | ||||||
|           // first non-whitespace character, then the caret is in the right place. If it's inserted at the first column, |  | ||||||
|           // then the caret is now in a bit of a weird place. We can't detect this scenario, so we just have to accept |  | ||||||
|           // the difference |  | ||||||
|           if (resetCaret) { |  | ||||||
|             editor.primaryCaret().moveToOffset(range.startOffset) |  | ||||||
|           } |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         val res = Ref.create<Boolean>(true) | ||||||
|  |         AsyncActionExecutionService.getInstance(editor.ij.project!!).withExecutionAfterAction(actions[0], { | ||||||
|  |           res.set(injector.actionExecutor.executeAction(actions[0], context)) | ||||||
|  |         }, { afterCommenting(mode, editor, resetCaret, range) }) | ||||||
|  |         if (!res.get()) { | ||||||
|  |           AsyncActionExecutionService.getInstance(editor.ij.project!!).withExecutionAfterAction(actions[1], { | ||||||
|  |             res.set(injector.actionExecutor.executeAction(actions[1], context)) | ||||||
|  |           }, { afterCommenting(mode, editor, resetCaret, range) }) | ||||||
|  |         } | ||||||
|  |         res.get() | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private fun afterCommenting( | ||||||
|  |       mode: VimStateMachine.Mode, | ||||||
|  |       editor: VimEditor, | ||||||
|  |       resetCaret: Boolean, | ||||||
|  |       range: TextRange, | ||||||
|  |     ) { | ||||||
|  |       // Remove the selection, if we added it | ||||||
|  |       if (mode !== VimStateMachine.Mode.VISUAL) { | ||||||
|  |         editor.removeSelection() | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       // Put the caret back at the start of the range, as though it was moved by the operator's motion argument. | ||||||
|  |       // This is what Vim does. If IntelliJ is configured to add comments at the start of the line, this might put | ||||||
|  |       // the caret in the "wrong" place. E.g. gc_ should put the caret on the first non-whitespace character. This | ||||||
|  |       // is calculated by the motion, saved in the marks, and then we insert the comment. If it's inserted at the | ||||||
|  |       // first non-whitespace character, then the caret is in the right place. If it's inserted at the first column, | ||||||
|  |       // then the caret is now in a bit of a weird place. We can't detect this scenario, so we just have to accept | ||||||
|  |       // the difference | ||||||
|  |       if (resetCaret) { | ||||||
|  |         editor.primaryCaret().moveToOffset(range.startOffset) | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| @@ -112,7 +131,13 @@ class CommentaryExtension : VimExtension { | |||||||
|  |  | ||||||
|     putKeyMappingIfMissing(MappingMode.NXO, injector.parser.parseKeys("gc"), owner, plugCommentaryKeys, true) |     putKeyMappingIfMissing(MappingMode.NXO, injector.parser.parseKeys("gc"), owner, plugCommentaryKeys, true) | ||||||
|     putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("gcc"), owner, plugCommentaryLineKeys, true) |     putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("gcc"), owner, plugCommentaryLineKeys, true) | ||||||
|     putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("gcu"), owner, injector.parser.parseKeys("<Plug>Commentary<Plug>Commentary"), true) |     putKeyMappingIfMissing( | ||||||
|  |       MappingMode.N, | ||||||
|  |       injector.parser.parseKeys("gcu"), | ||||||
|  |       owner, | ||||||
|  |       injector.parser.parseKeys("<Plug>Commentary<Plug>Commentary"), | ||||||
|  |       true | ||||||
|  |     ) | ||||||
|  |  | ||||||
|     // Previous versions of IdeaVim used different mappings to Vim's Commentary. Make sure everything works if someone |     // Previous versions of IdeaVim used different mappings to Vim's Commentary. Make sure everything works if someone | ||||||
|     // is still using the old mapping |     // is still using the old mapping | ||||||
| @@ -132,14 +157,19 @@ class CommentaryExtension : VimExtension { | |||||||
|   private class CommentaryOperatorHandler : OperatorFunction, ExtensionHandler { |   private class CommentaryOperatorHandler : OperatorFunction, ExtensionHandler { | ||||||
|     override val isRepeatable = true |     override val isRepeatable = true | ||||||
|  |  | ||||||
|  |     // In this operator we process selection by ourselves. This is necessary for rider, VIM-1758 | ||||||
|  |     override fun postProcessSelection(): Boolean { | ||||||
|  |       return false | ||||||
|  |     } | ||||||
|  |  | ||||||
|     override fun execute(editor: VimEditor, context: ExecutionContext) { |     override fun execute(editor: VimEditor, context: ExecutionContext) { | ||||||
|       setOperatorFunction(this) |       setOperatorFunction(this) | ||||||
|       executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij) |       executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     override fun apply(editor: Editor, context: DataContext, selectionType: SelectionType): Boolean { |     override fun apply(editor: VimEditor, context: ExecutionContext, selectionType: SelectionType): Boolean { | ||||||
|       val range = VimPlugin.getMark().getChangeMarks(editor.vim) ?: return false |       val range = VimPlugin.getMark().getChangeMarks(editor) ?: return false | ||||||
|       return doCommentary(editor.vim, context.vim, range, selectionType, true) |       return doCommentary(editor, context, range, selectionType, true) | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -174,7 +204,7 @@ class CommentaryExtension : VimExtension { | |||||||
|       context: ExecutionContext, |       context: ExecutionContext, | ||||||
|       count: Int, |       count: Int, | ||||||
|       rawCount: Int, |       rawCount: Int, | ||||||
|       argument: Argument? |       argument: Argument?, | ||||||
|     ): TextRange? { |     ): TextRange? { | ||||||
|  |  | ||||||
|       val nativeEditor = (editor as IjVimEditor).editor |       val nativeEditor = (editor as IjVimEditor).editor | ||||||
|   | |||||||
| @@ -18,7 +18,6 @@ | |||||||
|  |  | ||||||
| package com.maddyhome.idea.vim.extension.exchange | package com.maddyhome.idea.vim.extension.exchange | ||||||
|  |  | ||||||
| import com.intellij.openapi.actionSystem.DataContext |  | ||||||
| import com.intellij.openapi.application.runWriteAction | import com.intellij.openapi.application.runWriteAction | ||||||
| import com.intellij.openapi.editor.Editor | import com.intellij.openapi.editor.Editor | ||||||
| import com.intellij.openapi.editor.LogicalPosition | import com.intellij.openapi.editor.LogicalPosition | ||||||
| @@ -133,7 +132,7 @@ class VimExchangeExtension : VimExtension { | |||||||
|         val subMode = editor.subMode |         val subMode = editor.subMode | ||||||
|         // Leave visual mode to create selection marks |         // Leave visual mode to create selection marks | ||||||
|         executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij) |         executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij) | ||||||
|         Operator(true).apply(editor.ij, context.ij, SelectionType.fromSubMode(subMode)) |         Operator(true).apply(editor, context, SelectionType.fromSubMode(subMode)) | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| @@ -147,7 +146,8 @@ class VimExchangeExtension : VimExtension { | |||||||
|       else -> error("Invalid SubMode: $this") |       else -> error("Invalid SubMode: $this") | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     override fun apply(editor: Editor, context: DataContext, selectionType: SelectionType): Boolean { |     override fun apply(vimEditor: VimEditor, context: ExecutionContext, selectionType: SelectionType): Boolean { | ||||||
|  |       val editor = vimEditor.ij | ||||||
|       fun highlightExchange(ex: Exchange): RangeHighlighter { |       fun highlightExchange(ex: Exchange): RangeHighlighter { | ||||||
|         val attributes = editor.colorsScheme.getAttributes(EditorColors.TEXT_SEARCH_RESULT_ATTRIBUTES) |         val attributes = editor.colorsScheme.getAttributes(EditorColors.TEXT_SEARCH_RESULT_ATTRIBUTES) | ||||||
|         val hlArea = when (ex.type) { |         val hlArea = when (ex.type) { | ||||||
|   | |||||||
| @@ -345,7 +345,7 @@ class NerdTree : VimExtension { | |||||||
|         if (file.isDirectory) return@Code |         if (file.isDirectory) return@Code | ||||||
|         val splitters = FileEditorManagerEx.getInstanceEx(project).splitters |         val splitters = FileEditorManagerEx.getInstanceEx(project).splitters | ||||||
|         val currentWindow = splitters.currentWindow |         val currentWindow = splitters.currentWindow | ||||||
|         currentWindow.split(SwingConstants.HORIZONTAL, true, file, true) |         currentWindow?.split(SwingConstants.HORIZONTAL, true, file, true) | ||||||
|       } |       } | ||||||
|     ) |     ) | ||||||
|     registerCommand( |     registerCommand( | ||||||
| @@ -354,7 +354,7 @@ class NerdTree : VimExtension { | |||||||
|         val file = event.getData(CommonDataKeys.VIRTUAL_FILE) ?: return@Code |         val file = event.getData(CommonDataKeys.VIRTUAL_FILE) ?: return@Code | ||||||
|         val splitters = FileEditorManagerEx.getInstanceEx(project).splitters |         val splitters = FileEditorManagerEx.getInstanceEx(project).splitters | ||||||
|         val currentWindow = splitters.currentWindow |         val currentWindow = splitters.currentWindow | ||||||
|         currentWindow.split(SwingConstants.VERTICAL, true, file, true) |         currentWindow?.split(SwingConstants.VERTICAL, true, file, true) | ||||||
|  |  | ||||||
|         // FIXME: 22.01.2021 This solution bouncing a bit |         // FIXME: 22.01.2021 This solution bouncing a bit | ||||||
|         callAction("ActivateProjectToolWindow", context.vim) |         callAction("ActivateProjectToolWindow", context.vim) | ||||||
| @@ -366,7 +366,7 @@ class NerdTree : VimExtension { | |||||||
|         val file = event.getData(CommonDataKeys.VIRTUAL_FILE) ?: return@Code |         val file = event.getData(CommonDataKeys.VIRTUAL_FILE) ?: return@Code | ||||||
|         val splitters = FileEditorManagerEx.getInstanceEx(project).splitters |         val splitters = FileEditorManagerEx.getInstanceEx(project).splitters | ||||||
|         val currentWindow = splitters.currentWindow |         val currentWindow = splitters.currentWindow | ||||||
|         currentWindow.split(SwingConstants.HORIZONTAL, true, file, true) |         currentWindow?.split(SwingConstants.HORIZONTAL, true, file, true) | ||||||
|  |  | ||||||
|         callAction("ActivateProjectToolWindow", context.vim) |         callAction("ActivateProjectToolWindow", context.vim) | ||||||
|       } |       } | ||||||
|   | |||||||
| @@ -18,7 +18,6 @@ | |||||||
|  |  | ||||||
| package com.maddyhome.idea.vim.extension.replacewithregister | package com.maddyhome.idea.vim.extension.replacewithregister | ||||||
|  |  | ||||||
| import com.intellij.openapi.actionSystem.DataContext |  | ||||||
| import com.intellij.openapi.editor.Editor | import com.intellij.openapi.editor.Editor | ||||||
| import com.maddyhome.idea.vim.VimPlugin | import com.maddyhome.idea.vim.VimPlugin | ||||||
| import com.maddyhome.idea.vim.api.ExecutionContext | import com.maddyhome.idea.vim.api.ExecutionContext | ||||||
| @@ -115,21 +114,22 @@ class ReplaceWithRegister : VimExtension { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   private class Operator : OperatorFunction { |   private class Operator : OperatorFunction { | ||||||
|     override fun apply(editor: Editor, context: DataContext, selectionType: SelectionType): Boolean { |     override fun apply(vimEditor: VimEditor, context: ExecutionContext, selectionType: SelectionType): Boolean { | ||||||
|  |       val editor = (vimEditor as IjVimEditor).editor | ||||||
|       val range = getRange(editor) ?: return false |       val range = getRange(editor) ?: return false | ||||||
|       val visualSelection = PutData.VisualSelection( |       val visualSelection = PutData.VisualSelection( | ||||||
|         mapOf( |         mapOf( | ||||||
|           editor.caretModel.primaryCaret.vim to VimSelection.create( |           vimEditor.primaryCaret() to VimSelection.create( | ||||||
|             range.startOffset, |             range.startOffset, | ||||||
|             range.endOffset - 1, |             range.endOffset - 1, | ||||||
|             selectionType, |             selectionType, | ||||||
|             IjVimEditor(editor) |             vimEditor | ||||||
|           ) |           ) | ||||||
|         ), |         ), | ||||||
|         selectionType |         selectionType | ||||||
|       ) |       ) | ||||||
|       // todo multicaret |       // todo multicaret | ||||||
|       doReplace(editor, editor.vim.primaryCaret(), visualSelection) |       doReplace(editor, vimEditor.primaryCaret(), visualSelection) | ||||||
|       return true |       return true | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -152,7 +152,7 @@ class ReplaceWithRegister : VimExtension { | |||||||
|  |  | ||||||
|     private fun doReplace(editor: Editor, caret: VimCaret, visualSelection: PutData.VisualSelection) { |     private fun doReplace(editor: Editor, caret: VimCaret, visualSelection: PutData.VisualSelection) { | ||||||
|       val lastRegisterChar = injector.registerGroup.lastRegisterChar |       val lastRegisterChar = injector.registerGroup.lastRegisterChar | ||||||
|       val savedRegister = caret.registerStorage.getRegister(lastRegisterChar) ?: return |       val savedRegister = caret.registerStorage.getRegister(caret, lastRegisterChar) ?: return | ||||||
|  |  | ||||||
|       var usedType = savedRegister.type |       var usedType = savedRegister.type | ||||||
|       var usedText = savedRegister.text |       var usedText = savedRegister.text | ||||||
| @@ -185,8 +185,8 @@ class ReplaceWithRegister : VimExtension { | |||||||
|         ) |         ) | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       caret.registerStorage.saveRegister(savedRegister.name, savedRegister) |       caret.registerStorage.saveRegister(caret, savedRegister.name, savedRegister) | ||||||
|       caret.registerStorage.saveRegister(VimPlugin.getRegister().defaultRegister, savedRegister) |       caret.registerStorage.saveRegister(caret, VimPlugin.getRegister().defaultRegister, savedRegister) | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -17,7 +17,6 @@ | |||||||
|  */ |  */ | ||||||
| package com.maddyhome.idea.vim.extension.surround | package com.maddyhome.idea.vim.extension.surround | ||||||
|  |  | ||||||
| import com.intellij.openapi.actionSystem.DataContext |  | ||||||
| import com.intellij.openapi.application.runWriteAction | import com.intellij.openapi.application.runWriteAction | ||||||
| import com.intellij.openapi.editor.Editor | import com.intellij.openapi.editor.Editor | ||||||
| import com.maddyhome.idea.vim.VimPlugin | import com.maddyhome.idea.vim.VimPlugin | ||||||
| @@ -94,7 +93,7 @@ class VimSurroundExtension : VimExtension { | |||||||
|     override fun execute(editor: VimEditor, context: ExecutionContext) { |     override fun execute(editor: VimEditor, context: ExecutionContext) { | ||||||
|       val selectionStart = editor.ij.caretModel.primaryCaret.selectionStart |       val selectionStart = editor.ij.caretModel.primaryCaret.selectionStart | ||||||
|       // NB: Operator ignores SelectionType anyway |       // NB: Operator ignores SelectionType anyway | ||||||
|       if (!Operator().apply(editor.ij, context.ij, SelectionType.CHARACTER_WISE)) { |       if (!Operator().apply(editor, context, SelectionType.CHARACTER_WISE)) { | ||||||
|         return |         return | ||||||
|       } |       } | ||||||
|       runWriteAction { |       runWriteAction { | ||||||
| @@ -220,7 +219,8 @@ class VimSurroundExtension : VimExtension { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   private class Operator : OperatorFunction { |   private class Operator : OperatorFunction { | ||||||
|     override fun apply(editor: Editor, context: DataContext, selectionType: SelectionType): Boolean { |     override fun apply(vimEditor: VimEditor, context: ExecutionContext, selectionType: SelectionType): Boolean { | ||||||
|  |       val editor = vimEditor.ij | ||||||
|       val c = getChar(editor) |       val c = getChar(editor) | ||||||
|       if (c.code == 0) return true |       if (c.code == 0) return true | ||||||
|  |  | ||||||
|   | |||||||
| @@ -20,7 +20,9 @@ package com.maddyhome.idea.vim.group; | |||||||
| import com.google.common.base.Splitter; | import com.google.common.base.Splitter; | ||||||
| import com.google.common.collect.ImmutableSet; | import com.google.common.collect.ImmutableSet; | ||||||
| import com.google.common.collect.Lists; | import com.google.common.collect.Lists; | ||||||
|  | import com.intellij.codeInsight.actions.AsyncActionExecutionService; | ||||||
| import com.intellij.openapi.actionSystem.DataContext; | import com.intellij.openapi.actionSystem.DataContext; | ||||||
|  | import com.intellij.openapi.actionSystem.IdeActions; | ||||||
| import com.intellij.openapi.application.ApplicationManager; | import com.intellij.openapi.application.ApplicationManager; | ||||||
| import com.intellij.openapi.command.CommandProcessor; | import com.intellij.openapi.command.CommandProcessor; | ||||||
| import com.intellij.openapi.command.UndoConfirmationPolicy; | import com.intellij.openapi.command.UndoConfirmationPolicy; | ||||||
| @@ -33,10 +35,13 @@ import com.intellij.openapi.editor.event.EditorMouseEvent; | |||||||
| import com.intellij.openapi.editor.event.EditorMouseListener; | import com.intellij.openapi.editor.event.EditorMouseListener; | ||||||
| import com.intellij.openapi.editor.impl.TextRangeInterval; | import com.intellij.openapi.editor.impl.TextRangeInterval; | ||||||
| import com.intellij.openapi.project.Project; | import com.intellij.openapi.project.Project; | ||||||
|  | import com.intellij.openapi.ui.popup.Balloon; | ||||||
|  | import com.intellij.openapi.ui.popup.JBPopupFactory; | ||||||
| import com.intellij.openapi.util.text.StringUtil; | import com.intellij.openapi.util.text.StringUtil; | ||||||
| import com.intellij.psi.PsiFile; | import com.intellij.psi.PsiFile; | ||||||
| import com.intellij.psi.codeStyle.CodeStyleManager; | import com.intellij.psi.codeStyle.CodeStyleManager; | ||||||
| import com.intellij.psi.util.PsiUtilBase; | import com.intellij.psi.util.PsiUtilBase; | ||||||
|  | import com.intellij.ui.JBColor; | ||||||
| import com.intellij.util.containers.ContainerUtil; | import com.intellij.util.containers.ContainerUtil; | ||||||
| import com.maddyhome.idea.vim.EventFacade; | import com.maddyhome.idea.vim.EventFacade; | ||||||
| import com.maddyhome.idea.vim.VimPlugin; | import com.maddyhome.idea.vim.VimPlugin; | ||||||
| @@ -48,13 +53,19 @@ import com.maddyhome.idea.vim.ex.ranges.LineRange; | |||||||
| import com.maddyhome.idea.vim.group.visual.VimSelection; | import com.maddyhome.idea.vim.group.visual.VimSelection; | ||||||
| import com.maddyhome.idea.vim.group.visual.VisualModeHelperKt; | import com.maddyhome.idea.vim.group.visual.VisualModeHelperKt; | ||||||
| import com.maddyhome.idea.vim.helper.*; | import com.maddyhome.idea.vim.helper.*; | ||||||
|  | import com.maddyhome.idea.vim.icons.VimIcons; | ||||||
| import com.maddyhome.idea.vim.key.KeyHandlerKeeper; | import com.maddyhome.idea.vim.key.KeyHandlerKeeper; | ||||||
| import com.maddyhome.idea.vim.listener.VimInsertListener; | import com.maddyhome.idea.vim.listener.VimInsertListener; | ||||||
| import com.maddyhome.idea.vim.newapi.*; | import com.maddyhome.idea.vim.newapi.IjExecutionContext; | ||||||
|  | import com.maddyhome.idea.vim.newapi.IjExecutionContextKt; | ||||||
|  | import com.maddyhome.idea.vim.newapi.IjVimCaret; | ||||||
|  | import com.maddyhome.idea.vim.newapi.IjVimEditor; | ||||||
| import com.maddyhome.idea.vim.options.OptionConstants; | import com.maddyhome.idea.vim.options.OptionConstants; | ||||||
| import com.maddyhome.idea.vim.options.OptionScope; | import com.maddyhome.idea.vim.options.OptionScope; | ||||||
| import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString; | import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString; | ||||||
| import kotlin.Pair; | import kotlin.Pair; | ||||||
|  | import kotlin.Unit; | ||||||
|  | import kotlin.jvm.functions.Function0; | ||||||
| import kotlin.text.StringsKt; | import kotlin.text.StringsKt; | ||||||
| import org.jetbrains.annotations.NonNls; | import org.jetbrains.annotations.NonNls; | ||||||
| import org.jetbrains.annotations.NotNull; | import org.jetbrains.annotations.NotNull; | ||||||
| @@ -85,6 +96,8 @@ public class ChangeGroup extends VimChangeGroupBase { | |||||||
|  |  | ||||||
|   private final List<VimInsertListener> insertListeners = ContainerUtil.createLockFreeCopyOnWriteList(); |   private final List<VimInsertListener> insertListeners = ContainerUtil.createLockFreeCopyOnWriteList(); | ||||||
|  |  | ||||||
|  |   private long lastShownTime = 0L; | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    * Inserts a new line above the caret position |    * Inserts a new line above the caret position | ||||||
|    * |    * | ||||||
| @@ -571,17 +584,31 @@ public class ChangeGroup extends VimChangeGroupBase { | |||||||
|     final int startOffset = injector.getEngineEditorHelper().getLineStartForOffset(editor, range.getStartOffset()); |     final int startOffset = injector.getEngineEditorHelper().getLineStartForOffset(editor, range.getStartOffset()); | ||||||
|     final int endOffset = injector.getEngineEditorHelper().getLineEndForOffset(editor, range.getEndOffset()); |     final int endOffset = injector.getEngineEditorHelper().getLineEndForOffset(editor, range.getEndOffset()); | ||||||
|  |  | ||||||
|     VisualModeHelperKt.vimSetSystemSelectionSilently(((IjVimEditor) editor).getEditor().getSelectionModel(), startOffset, endOffset); |     Editor ijEditor = ((IjVimEditor)editor).getEditor(); | ||||||
|  |     VisualModeHelperKt.vimSetSystemSelectionSilently(ijEditor.getSelectionModel(), startOffset, endOffset); | ||||||
|  |  | ||||||
|     NativeAction joinLinesAction = VimInjectorKt.getInjector().getNativeActionManager().getIndentLines(); |     Project project = ijEditor.getProject(); | ||||||
|     if (joinLinesAction != null) { |     Function0<Unit> actionExecution = () -> { | ||||||
|       VimInjectorKt.getInjector().getActionExecutor().executeAction(joinLinesAction, context); |       NativeAction joinLinesAction = VimInjectorKt.getInjector().getNativeActionManager().getIndentLines(); | ||||||
|  |       if (joinLinesAction != null) { | ||||||
|  |         VimInjectorKt.getInjector().getActionExecutor().executeAction(joinLinesAction, context); | ||||||
|  |       } | ||||||
|  |       return null; | ||||||
|  |     }; | ||||||
|  |     Function0<Unit> afterAction = () -> { | ||||||
|  |       final int firstLine = editor.offsetToLogicalPosition(Math.min(startOffset, endOffset)).getLine(); | ||||||
|  |       final int newOffset = VimPlugin.getMotion().moveCaretToLineStartSkipLeading(editor, firstLine); | ||||||
|  |       injector.getMotion().moveCaret(editor, caret, newOffset); | ||||||
|  |       restoreCursor(editor, caret, ((IjVimCaret)caret).getCaret().getLogicalPosition().line); | ||||||
|  |       return null; | ||||||
|  |     }; | ||||||
|  |     if (project != null) { | ||||||
|  |       AsyncActionExecutionService.Companion.getInstance(project) | ||||||
|  |         .withExecutionAfterAction(IdeActions.ACTION_EDITOR_AUTO_INDENT_LINES, actionExecution, afterAction); | ||||||
|  |     } else { | ||||||
|  |       actionExecution.invoke(); | ||||||
|  |       afterAction.invoke(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     final int firstLine = editor.offsetToLogicalPosition(Math.min(startOffset, endOffset)).getLine(); |  | ||||||
|     final int newOffset = VimPlugin.getMotion().moveCaretToLineStartSkipLeading(editor, firstLine); |  | ||||||
|     injector.getMotion().moveCaret(editor, caret, newOffset); |  | ||||||
|     restoreCursor(editor, caret, ((IjVimCaret) caret).getCaret().getLogicalPosition().line); |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @Override |   @Override | ||||||
| @@ -773,6 +800,22 @@ public class ChangeGroup extends VimChangeGroupBase { | |||||||
|                                         @NotNull TextRange selectedRange, |                                         @NotNull TextRange selectedRange, | ||||||
|                                         final int count, |                                         final int count, | ||||||
|                                         boolean avalanche) { |                                         boolean avalanche) { | ||||||
|  |  | ||||||
|  |     // Just an easter egg | ||||||
|  |     if (avalanche) { | ||||||
|  |       long currentTime = System.currentTimeMillis(); | ||||||
|  |       if (currentTime - lastShownTime > 60_000) { | ||||||
|  |         lastShownTime = currentTime; | ||||||
|  |         ApplicationManager.getApplication().invokeLater(() -> { | ||||||
|  |           final Balloon balloon = JBPopupFactory.getInstance() | ||||||
|  |             .createHtmlTextBalloonBuilder("Wow, nice vim skills!", VimIcons.IDEAVIM, JBColor.background(), null) | ||||||
|  |             .createBalloon(); | ||||||
|  |           balloon.show(JBPopupFactory.getInstance().guessBestPopupLocation(((IjVimEditor)editor).getEditor()), | ||||||
|  |                        Balloon.Position.below); | ||||||
|  |         }); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     String nf = ((VimString) VimPlugin.getOptionService().getOptionValue(new OptionScope.LOCAL(editor), OptionConstants.nrformatsName, OptionConstants.nrformatsName)).getValue(); |     String nf = ((VimString) VimPlugin.getOptionService().getOptionValue(new OptionScope.LOCAL(editor), OptionConstants.nrformatsName, OptionConstants.nrformatsName)).getValue(); | ||||||
|     boolean alpha = nf.contains("alpha"); |     boolean alpha = nf.contains("alpha"); | ||||||
|     boolean hex = nf.contains("hex"); |     boolean hex = nf.contains("hex"); | ||||||
|   | |||||||
| @@ -73,8 +73,6 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen | |||||||
|  |  | ||||||
|   private static final Logger logger = Logger.getInstance(KeyGroup.class); |   private static final Logger logger = Logger.getInstance(KeyGroup.class); | ||||||
|  |  | ||||||
|   private @Nullable OperatorFunction operatorFunction = null; |  | ||||||
|  |  | ||||||
|   public void registerRequiredShortcutKeys(@NotNull VimEditor editor) { |   public void registerRequiredShortcutKeys(@NotNull VimEditor editor) { | ||||||
|     EventFacade.getInstance() |     EventFacade.getInstance() | ||||||
|       .registerCustomShortcutSet(VimShortcutKeyAction.getInstance(), toShortcutSet(getRequiredShortcutKeys()), |       .registerCustomShortcutSet(VimShortcutKeyAction.getInstance(), toShortcutSet(getRequiredShortcutKeys()), | ||||||
| @@ -119,14 +117,6 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public @Nullable OperatorFunction getOperatorFunction() { |  | ||||||
|     return operatorFunction; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   public void setOperatorFunction(@NotNull OperatorFunction function) { |  | ||||||
|     operatorFunction = function; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   public void saveData(@NotNull Element element) { |   public void saveData(@NotNull Element element) { | ||||||
|     final Element conflictsElement = new Element(SHORTCUT_CONFLICTS_ELEMENT); |     final Element conflictsElement = new Element(SHORTCUT_CONFLICTS_ELEMENT); | ||||||
|     for (Map.Entry<KeyStroke, ShortcutOwnerInfo> entry : myShortcutConflicts.entrySet()) { |     for (Map.Entry<KeyStroke, ShortcutOwnerInfo> entry : myShortcutConflicts.entrySet()) { | ||||||
|   | |||||||
| @@ -23,8 +23,10 @@ import com.intellij.openapi.editor.ex.util.EditorUtil; | |||||||
| import com.intellij.openapi.fileEditor.FileEditor; | import com.intellij.openapi.fileEditor.FileEditor; | ||||||
| import com.intellij.openapi.fileEditor.FileEditorManagerEvent; | import com.intellij.openapi.fileEditor.FileEditorManagerEvent; | ||||||
| import com.intellij.openapi.fileEditor.TextEditor; | import com.intellij.openapi.fileEditor.TextEditor; | ||||||
|  | import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx; | ||||||
| import com.intellij.openapi.fileEditor.impl.EditorTabbedContainer; | import com.intellij.openapi.fileEditor.impl.EditorTabbedContainer; | ||||||
| import com.intellij.openapi.fileEditor.impl.EditorWindow; | import com.intellij.openapi.fileEditor.impl.EditorWindow; | ||||||
|  | import com.intellij.openapi.project.Project; | ||||||
| import com.intellij.openapi.vfs.LocalFileSystem; | import com.intellij.openapi.vfs.LocalFileSystem; | ||||||
| import com.intellij.openapi.vfs.VirtualFile; | import com.intellij.openapi.vfs.VirtualFile; | ||||||
| import com.intellij.openapi.vfs.VirtualFileManager; | import com.intellij.openapi.vfs.VirtualFileManager; | ||||||
| @@ -1168,15 +1170,25 @@ public class MotionGroup extends VimMotionGroupBase { | |||||||
|  |  | ||||||
|   @Override |   @Override | ||||||
|   public int moveCaretGotoPreviousTab(@NotNull VimEditor editor, @NotNull ExecutionContext context, int rawCount) { |   public int moveCaretGotoPreviousTab(@NotNull VimEditor editor, @NotNull ExecutionContext context, int rawCount) { | ||||||
|     switchEditorTab(EditorWindow.DATA_KEY.getData((DataContext)context.getContext()), rawCount >= 1 ? -rawCount : -1, false); |     Project project = ((IjVimEditor)editor).getEditor().getProject(); | ||||||
|  |     if (project == null) { | ||||||
|  |       return editor.currentCaret().getOffset().getPoint(); | ||||||
|  |     } | ||||||
|  |     EditorWindow currentWindow = FileEditorManagerEx.getInstanceEx(project).getSplitters().getCurrentWindow(); | ||||||
|  |     switchEditorTab(currentWindow, rawCount >= 1 ? -rawCount : -1, false); | ||||||
|     return editor.currentCaret().getOffset().getPoint(); |     return editor.currentCaret().getOffset().getPoint(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @Override |   @Override | ||||||
|   public int moveCaretGotoNextTab(@NotNull VimEditor editor, @NotNull ExecutionContext context, int rawCount) { |   public int moveCaretGotoNextTab(@NotNull VimEditor editor, @NotNull ExecutionContext context, int rawCount) { | ||||||
|     final boolean absolute = rawCount >= 1; |     final boolean absolute = rawCount >= 1; | ||||||
|     switchEditorTab(EditorWindow.DATA_KEY.getData((DataContext)context.getContext()), absolute ? rawCount - 1 : 1, |  | ||||||
|                     absolute); |     Project project = ((IjVimEditor)editor).getEditor().getProject(); | ||||||
|  |     if (project == null) { | ||||||
|  |       return editor.currentCaret().getOffset().getPoint(); | ||||||
|  |     } | ||||||
|  |     EditorWindow currentWindow = FileEditorManagerEx.getInstanceEx(project).getSplitters().getCurrentWindow(); | ||||||
|  |     switchEditorTab(currentWindow, absolute ? rawCount - 1 : 1, absolute); | ||||||
|     return editor.currentCaret().getOffset().getPoint(); |     return editor.currentCaret().getOffset().getPoint(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -61,7 +61,10 @@ internal fun Project.createLineBookmark(editor: Editor, line: Int, mnemonic: Cha | |||||||
|   val type = BookmarkType.get(mnemonic) |   val type = BookmarkType.get(mnemonic) | ||||||
|   if (type == BookmarkType.DEFAULT) return null |   if (type == BookmarkType.DEFAULT) return null | ||||||
|  |  | ||||||
|   val group = bookmarksManager.defaultGroup ?: bookmarksManager.addGroup("IdeaVim", true) ?: return null |   val group = bookmarksManager.defaultGroup | ||||||
|  |     ?: bookmarksManager.getGroup("IdeaVim") | ||||||
|  |     ?: bookmarksManager.addGroup("IdeaVim", true) | ||||||
|  |     ?: return null | ||||||
|   if (group.canAdd(bookmark)) { |   if (group.canAdd(bookmark)) { | ||||||
|     group.add(bookmark, type) |     group.add(bookmark, type) | ||||||
|     return bookmark |     return bookmark | ||||||
|   | |||||||
| @@ -190,7 +190,12 @@ class PutGroup : VimPutBase() { | |||||||
|           } |           } | ||||||
|         } |         } | ||||||
|         visualSelection.typeInEditor.isLine -> { |         visualSelection.typeInEditor.isLine -> { | ||||||
|           if (caret.offset == editor.fileSize && editor.fileSize != 0) { |           val lastChar = if (editor.fileSize > 0) { | ||||||
|  |             editor.document.getText(com.intellij.openapi.util.TextRange(editor.fileSize - 1, editor.fileSize))[0] | ||||||
|  |           } else { | ||||||
|  |             null | ||||||
|  |           } | ||||||
|  |           if (caret.offset == editor.fileSize && editor.fileSize != 0 && lastChar != '\n') { | ||||||
|             application.runWriteAction { editor.document.insertString(caret.offset, "\n") } |             application.runWriteAction { editor.document.insertString(caret.offset, "\n") } | ||||||
|             listOf(caret.offset + 1) |             listOf(caret.offset + 1) | ||||||
|           } else listOf(caret.offset) |           } else listOf(caret.offset) | ||||||
|   | |||||||
| @@ -211,7 +211,7 @@ class YankGroup : YankGroupBase() { | |||||||
|  |  | ||||||
|     var result = true |     var result = true | ||||||
|     for ((caret, myRange) in caretToRange) { |     for ((caret, myRange) in caretToRange) { | ||||||
|       result = caret.registerStorage.storeText(editor, myRange, type, false) && result |       result = caret.registerStorage.storeText(caret, editor, myRange, type, false) && result | ||||||
|     } |     } | ||||||
|     return result |     return result | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -31,6 +31,7 @@ import com.intellij.psi.PsiElement; | |||||||
| import com.intellij.psi.PsiFile; | import com.intellij.psi.PsiFile; | ||||||
| import com.intellij.psi.util.PsiTreeUtil; | import com.intellij.psi.util.PsiTreeUtil; | ||||||
| import com.maddyhome.idea.vim.VimPlugin; | import com.maddyhome.idea.vim.VimPlugin; | ||||||
|  | import com.maddyhome.idea.vim.api.VimSearchHelperBase; | ||||||
| import com.maddyhome.idea.vim.command.VimStateMachine; | import com.maddyhome.idea.vim.command.VimStateMachine; | ||||||
| import com.maddyhome.idea.vim.common.CharacterPosition; | import com.maddyhome.idea.vim.common.CharacterPosition; | ||||||
| import com.maddyhome.idea.vim.common.Direction; | import com.maddyhome.idea.vim.common.Direction; | ||||||
| @@ -1201,7 +1202,7 @@ public class SearchHelper { | |||||||
|     int last = -1; |     int last = -1; | ||||||
|     int res = start; |     int res = start; | ||||||
|     while (true) { |     while (true) { | ||||||
|       res = findNextWordOne(chars, res, end, 1, true, false); |       res = (int)VimSearchHelperBase.Companion.findNextWordOne(chars, res, end, 1, true, false); | ||||||
|       if (res == start || res == 0 || res > end || res == last) { |       if (res == start || res == 0 || res > end || res == last) { | ||||||
|         break; |         break; | ||||||
|       } |       } | ||||||
| @@ -1230,105 +1231,6 @@ public class SearchHelper { | |||||||
|     return new CountPosition(count, position); |     return new CountPosition(count, position); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public static int findNextWord(@NotNull Editor editor, int searchFrom, int count, boolean bigWord) { |  | ||||||
|     CharSequence chars = editor.getDocument().getCharsSequence(); |  | ||||||
|     final int size = EditorHelperRt.getFileSize(editor); |  | ||||||
|  |  | ||||||
|     return findNextWord(chars, searchFrom, size, count, bigWord, false); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   public static int findNextWord(@NotNull CharSequence chars, |  | ||||||
|                                  int pos, |  | ||||||
|                                  int size, |  | ||||||
|                                  int count, |  | ||||||
|                                  boolean bigWord, |  | ||||||
|                                  boolean spaceWords) { |  | ||||||
|     int step = count >= 0 ? 1 : -1; |  | ||||||
|     count = Math.abs(count); |  | ||||||
|  |  | ||||||
|     int res = pos; |  | ||||||
|     for (int i = 0; i < count; i++) { |  | ||||||
|       res = findNextWordOne(chars, res, size, step, bigWord, spaceWords); |  | ||||||
|       if (res == pos || res == 0 || res == size - 1) { |  | ||||||
|         break; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return res; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   private static int findNextWordOne(@NotNull CharSequence chars, |  | ||||||
|                                      int pos, |  | ||||||
|                                      int size, |  | ||||||
|                                      int step, |  | ||||||
|                                      boolean bigWord, |  | ||||||
|                                      boolean spaceWords) { |  | ||||||
|     boolean found = false; |  | ||||||
|     pos = pos < size ? pos : Math.min(size, chars.length() - 1); |  | ||||||
|     // For back searches, skip any current whitespace so we start at the end of a word |  | ||||||
|     if (step < 0 && pos > 0) { |  | ||||||
|       if (CharacterHelper.charType(chars.charAt(pos - 1), bigWord) == CharacterHelper.CharacterType.WHITESPACE && |  | ||||||
|           !spaceWords) { |  | ||||||
|         pos = skipSpace(chars, pos - 1, step, size) + 1; |  | ||||||
|       } |  | ||||||
|       if (pos > 0 && |  | ||||||
|           CharacterHelper.charType(chars.charAt(pos), bigWord) != |  | ||||||
|           CharacterHelper.charType(chars.charAt(pos - 1), bigWord)) { |  | ||||||
|         pos += step; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|     int res = pos; |  | ||||||
|     if (pos < 0 || pos >= size) { |  | ||||||
|       return pos; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     CharacterHelper.CharacterType type = CharacterHelper.charType(chars.charAt(pos), bigWord); |  | ||||||
|     if (type == CharacterHelper.CharacterType.WHITESPACE && step < 0 && pos > 0 && !spaceWords) { |  | ||||||
|       type = CharacterHelper.charType(chars.charAt(pos - 1), bigWord); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     pos += step; |  | ||||||
|     while (pos >= 0 && pos < size && !found) { |  | ||||||
|       CharacterHelper.CharacterType newType = CharacterHelper.charType(chars.charAt(pos), bigWord); |  | ||||||
|       if (newType != type) { |  | ||||||
|         if (newType == CharacterHelper.CharacterType.WHITESPACE && step >= 0 && !spaceWords) { |  | ||||||
|           pos = skipSpace(chars, pos, step, size); |  | ||||||
|           res = pos; |  | ||||||
|         } |  | ||||||
|         else if (step < 0) { |  | ||||||
|           res = pos + 1; |  | ||||||
|         } |  | ||||||
|         else { |  | ||||||
|           res = pos; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         type = CharacterHelper.charType(chars.charAt(res), bigWord); |  | ||||||
|         found = true; |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       pos += step; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (found) { |  | ||||||
|       if (res < 0) //(pos <= 0) |  | ||||||
|       { |  | ||||||
|         res = 0; |  | ||||||
|       } |  | ||||||
|       else if (res >= size) //(pos >= size) |  | ||||||
|       { |  | ||||||
|         res = size - 1; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|     else if (pos <= 0) { |  | ||||||
|       res = 0; |  | ||||||
|     } |  | ||||||
|     else if (pos >= size) { |  | ||||||
|       res = size; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return res; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   public static @NotNull List<Pair<TextRange, NumberType>> findNumbersInRange(final @NotNull Editor editor, |   public static @NotNull List<Pair<TextRange, NumberType>> findNumbersInRange(final @NotNull Editor editor, | ||||||
|                                                                               @NotNull TextRange textRange, |                                                                               @NotNull TextRange textRange, | ||||||
|                                                                               final boolean alpha, |                                                                               final boolean alpha, | ||||||
| @@ -1654,10 +1556,10 @@ public class SearchHelper { | |||||||
|  |  | ||||||
|     if ((!onWordStart && !(startSpace && isOuter)) || hasSelection || (count > 1 && dir == -1)) { |     if ((!onWordStart && !(startSpace && isOuter)) || hasSelection || (count > 1 && dir == -1)) { | ||||||
|       if (dir == 1) { |       if (dir == 1) { | ||||||
|         start = findNextWord(chars, pos, max, -1, isBig, !isOuter); |         start = (int)VimSearchHelperBase.Companion.findNextWord(chars, pos, max, -1, isBig, !isOuter); | ||||||
|       } |       } | ||||||
|       else { |       else { | ||||||
|         start = findNextWord(chars, pos, max, -(count - (onWordStart && !hasSelection ? 1 : 0)), isBig, !isOuter); |         start = (int)VimSearchHelperBase.Companion.findNextWord(chars, pos, max, -(count - (onWordStart && !hasSelection ? 1 : 0)), isBig, !isOuter); | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       start = EditorHelper.normalizeOffset(editor, start, false); |       start = EditorHelper.normalizeOffset(editor, start, false); | ||||||
| @@ -1805,7 +1707,7 @@ public class SearchHelper { | |||||||
|     if (step > 0 && pos < size - 1) { |     if (step > 0 && pos < size - 1) { | ||||||
|       if (CharacterHelper.charType(chars.charAt(pos + 1), bigWord) == CharacterHelper.CharacterType.WHITESPACE && |       if (CharacterHelper.charType(chars.charAt(pos + 1), bigWord) == CharacterHelper.CharacterType.WHITESPACE && | ||||||
|           !spaceWords) { |           !spaceWords) { | ||||||
|         pos = skipSpace(chars, pos + 1, step, size) - 1; |         pos = (int)(VimSearchHelperBase.Companion.skipSpace(chars, pos + 1, step, size) - 1); | ||||||
|       } |       } | ||||||
|       if (pos < size - 1 && |       if (pos < size - 1 && | ||||||
|           CharacterHelper.charType(chars.charAt(pos), bigWord) != |           CharacterHelper.charType(chars.charAt(pos), bigWord) != | ||||||
| @@ -1830,7 +1732,7 @@ public class SearchHelper { | |||||||
|           res = pos - 1; |           res = pos - 1; | ||||||
|         } |         } | ||||||
|         else if (newType == CharacterHelper.CharacterType.WHITESPACE && step < 0 && !spaceWords) { |         else if (newType == CharacterHelper.CharacterType.WHITESPACE && step < 0 && !spaceWords) { | ||||||
|           pos = skipSpace(chars, pos, step, size); |           pos = (int)VimSearchHelperBase.Companion.skipSpace(chars, pos, step, size); | ||||||
|           res = pos; |           res = pos; | ||||||
|         } |         } | ||||||
|         else { |         else { | ||||||
| @@ -1858,34 +1760,6 @@ public class SearchHelper { | |||||||
|     return res; |     return res; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   /** |  | ||||||
|    * Skip whitespace starting with the supplied position. |  | ||||||
|    * <p> |  | ||||||
|    * An empty line is considered a whitespace break. |  | ||||||
|    * |  | ||||||
|    * @param chars  The text as a character array |  | ||||||
|    * @param offset The starting position |  | ||||||
|    * @param step   The direction to move |  | ||||||
|    * @param size   The size of the document |  | ||||||
|    * @return The new position. This will be the first non-whitespace character found or an empty line |  | ||||||
|    */ |  | ||||||
|   private static int skipSpace(@NotNull CharSequence chars, int offset, int step, int size) { |  | ||||||
|     char prev = 0; |  | ||||||
|     while (offset >= 0 && offset < size) { |  | ||||||
|       final char c = chars.charAt(offset); |  | ||||||
|       if (c == '\n' && c == prev) { |  | ||||||
|         break; |  | ||||||
|       } |  | ||||||
|       if (CharacterHelper.charType(c, false) != CharacterHelper.CharacterType.WHITESPACE) { |  | ||||||
|         break; |  | ||||||
|       } |  | ||||||
|       prev = c; |  | ||||||
|       offset += step; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return offset < size ? offset : size - 1; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    * This locates the position with the document of the count-th occurrence of ch on the current line |    * This locates the position with the document of the count-th occurrence of ch on the current line | ||||||
|    * |    * | ||||||
|   | |||||||
| @@ -25,6 +25,7 @@ import com.intellij.openapi.command.CommandProcessor | |||||||
| import com.intellij.openapi.command.impl.UndoManagerImpl | import com.intellij.openapi.command.impl.UndoManagerImpl | ||||||
| import com.intellij.openapi.command.undo.UndoManager | import com.intellij.openapi.command.undo.UndoManager | ||||||
| import com.intellij.openapi.components.Service | import com.intellij.openapi.components.Service | ||||||
|  | import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider | ||||||
| import com.maddyhome.idea.vim.api.ExecutionContext | import com.maddyhome.idea.vim.api.ExecutionContext | ||||||
| import com.maddyhome.idea.vim.api.injector | import com.maddyhome.idea.vim.api.injector | ||||||
| import com.maddyhome.idea.vim.common.ChangesListener | import com.maddyhome.idea.vim.common.ChangesListener | ||||||
| @@ -48,16 +49,17 @@ class UndoRedoHelper : UndoRedoBase() { | |||||||
|   override fun undo(context: ExecutionContext): Boolean { |   override fun undo(context: ExecutionContext): Boolean { | ||||||
|     val ijContext = context.context as DataContext |     val ijContext = context.context as DataContext | ||||||
|     val project = PlatformDataKeys.PROJECT.getData(ijContext) ?: return false |     val project = PlatformDataKeys.PROJECT.getData(ijContext) ?: return false | ||||||
|     val fileEditor = PlatformDataKeys.FILE_EDITOR.getData(ijContext) |     val editor = CommonDataKeys.EDITOR.getData(context.ij) ?: return false | ||||||
|  |     val vimEditor = editor.vim | ||||||
|  |     val fileEditor = TextEditorProvider.getInstance().getTextEditor(editor) | ||||||
|     val undoManager = UndoManager.getInstance(project) |     val undoManager = UndoManager.getInstance(project) | ||||||
|     if (fileEditor != null && undoManager.isUndoAvailable(fileEditor)) { |     if (undoManager.isUndoAvailable(fileEditor)) { | ||||||
|       val editor = CommonDataKeys.EDITOR.getData(context.ij)?.vim |  | ||||||
|       if (injector.optionService.isSet(OptionScope.GLOBAL, IjVimOptionService.oldUndo)) { |       if (injector.optionService.isSet(OptionScope.GLOBAL, IjVimOptionService.oldUndo)) { | ||||||
|         SelectionVimListenerSuppressor.lock().use { undoManager.undo(fileEditor) } |         SelectionVimListenerSuppressor.lock().use { undoManager.undo(fileEditor) } | ||||||
|       } else { |       } else { | ||||||
|         performUntilFileChanges(editor, { undoManager.isUndoAvailable(fileEditor) }, { undoManager.undo(fileEditor) }) |         performUntilFileChanges(vimEditor, { undoManager.isUndoAvailable(fileEditor) }, { undoManager.undo(fileEditor) }) | ||||||
|  |  | ||||||
|         editor?.carets()?.forEach { |         vimEditor.carets().forEach { | ||||||
|           val ijCaret = it.ij |           val ijCaret = it.ij | ||||||
|           val hasSelection = ijCaret.hasSelection() |           val hasSelection = ijCaret.hasSelection() | ||||||
|           if (hasSelection) { |           if (hasSelection) { | ||||||
| @@ -78,16 +80,17 @@ class UndoRedoHelper : UndoRedoBase() { | |||||||
|   override fun redo(context: ExecutionContext): Boolean { |   override fun redo(context: ExecutionContext): Boolean { | ||||||
|     val ijContext = context.context as DataContext |     val ijContext = context.context as DataContext | ||||||
|     val project = PlatformDataKeys.PROJECT.getData(ijContext) ?: return false |     val project = PlatformDataKeys.PROJECT.getData(ijContext) ?: return false | ||||||
|     val fileEditor = PlatformDataKeys.FILE_EDITOR.getData(ijContext) |     val editor = CommonDataKeys.EDITOR.getData(context.ij) ?: return false | ||||||
|  |     val vimEditor = editor.vim | ||||||
|  |     val fileEditor = TextEditorProvider.getInstance().getTextEditor(editor) | ||||||
|     val undoManager = UndoManager.getInstance(project) |     val undoManager = UndoManager.getInstance(project) | ||||||
|     val editor = CommonDataKeys.EDITOR.getData(context.ij)?.vim |     if (undoManager.isRedoAvailable(fileEditor)) { | ||||||
|     if (fileEditor != null && undoManager.isRedoAvailable(fileEditor)) { |  | ||||||
|       if (injector.optionService.isSet(OptionScope.GLOBAL, IjVimOptionService.oldUndo)) { |       if (injector.optionService.isSet(OptionScope.GLOBAL, IjVimOptionService.oldUndo)) { | ||||||
|         SelectionVimListenerSuppressor.lock().use { undoManager.redo(fileEditor) } |         SelectionVimListenerSuppressor.lock().use { undoManager.redo(fileEditor) } | ||||||
|       } else { |       } else { | ||||||
|         performUntilFileChanges(editor, { undoManager.isRedoAvailable(fileEditor) }, { undoManager.redo(fileEditor) }) |         performUntilFileChanges(vimEditor, { undoManager.isRedoAvailable(fileEditor) }, { undoManager.redo(fileEditor) }) | ||||||
|         CommandProcessor.getInstance().runUndoTransparentAction { |         CommandProcessor.getInstance().runUndoTransparentAction { | ||||||
|           editor?.carets()?.forEach { it.ij.removeSelection() } |           vimEditor.carets().forEach { it.ij.removeSelection() } | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|       return true |       return true | ||||||
|   | |||||||
| @@ -53,7 +53,7 @@ class IjVimCaret(val caret: Caret) : VimCaretBase() { | |||||||
|     get() { |     get() { | ||||||
|       var storage = this.caret.registerStorage |       var storage = this.caret.registerStorage | ||||||
|       if (storage == null) { |       if (storage == null) { | ||||||
|         storage = CaretRegisterStorageBase(editor.primaryCaret().ij == caret) |         storage = CaretRegisterStorageBase() | ||||||
|         this.caret.registerStorage = storage |         this.caret.registerStorage = storage | ||||||
|       } |       } | ||||||
|       return storage |       return storage | ||||||
| @@ -87,6 +87,8 @@ class IjVimCaret(val caret: Caret) : VimCaretBase() { | |||||||
|     } |     } | ||||||
|   override val vimLine: Int |   override val vimLine: Int | ||||||
|     get() = this.caret.vimLine |     get() = this.caret.vimLine | ||||||
|  |   override val isPrimary: Boolean | ||||||
|  |     get() = editor.primaryCaret().ij == this.caret | ||||||
|  |  | ||||||
|   override fun moveToOffset(offset: Int) { |   override fun moveToOffset(offset: Int) { | ||||||
|     // TODO: 17.12.2021 Unpack internal actions |     // TODO: 17.12.2021 Unpack internal actions | ||||||
|   | |||||||
| @@ -3,14 +3,14 @@ package com.maddyhome.idea.vim.newapi | |||||||
| import com.intellij.openapi.components.Service | import com.intellij.openapi.components.Service | ||||||
| import com.maddyhome.idea.vim.api.VimCaret | import com.maddyhome.idea.vim.api.VimCaret | ||||||
| import com.maddyhome.idea.vim.api.VimEditor | import com.maddyhome.idea.vim.api.VimEditor | ||||||
| import com.maddyhome.idea.vim.api.VimSearchHelper | import com.maddyhome.idea.vim.api.VimSearchHelperBase | ||||||
| import com.maddyhome.idea.vim.common.TextRange | import com.maddyhome.idea.vim.common.TextRange | ||||||
| import com.maddyhome.idea.vim.helper.SearchHelper | import com.maddyhome.idea.vim.helper.SearchHelper | ||||||
| import com.maddyhome.idea.vim.helper.SearchOptions | import com.maddyhome.idea.vim.helper.SearchOptions | ||||||
| import java.util.* | import java.util.* | ||||||
|  |  | ||||||
| @Service | @Service | ||||||
| class IjVimSearchHelper : VimSearchHelper { | class IjVimSearchHelper : VimSearchHelperBase() { | ||||||
|   override fun findNextParagraph(editor: VimEditor, caret: VimCaret, count: Int, allowBlanks: Boolean): Int { |   override fun findNextParagraph(editor: VimEditor, caret: VimCaret, count: Int, allowBlanks: Boolean): Int { | ||||||
|     return SearchHelper.findNextParagraph( |     return SearchHelper.findNextParagraph( | ||||||
|       (editor as IjVimEditor).editor, |       (editor as IjVimEditor).editor, | ||||||
| @@ -121,15 +121,6 @@ class IjVimSearchHelper : VimSearchHelper { | |||||||
|     return SearchHelper.findNextWordEnd(chars, pos, size, count, bigWord, spaceWords) |     return SearchHelper.findNextWordEnd(chars, pos, size, count, bigWord, spaceWords) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   override fun findNextWord(editor: VimEditor, searchFrom: Int, count: Int, bigWord: Boolean): Int { |  | ||||||
|     return SearchHelper.findNextWord( |  | ||||||
|       (editor as IjVimEditor).editor, |  | ||||||
|       searchFrom, |  | ||||||
|       count, |  | ||||||
|       bigWord |  | ||||||
|     ) |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   override fun findPattern( |   override fun findPattern( | ||||||
|     editor: VimEditor, |     editor: VimEditor, | ||||||
|     pattern: String?, |     pattern: String?, | ||||||
|   | |||||||
| @@ -23,9 +23,9 @@ import com.intellij.internal.statistic.eventLog.EventLogGroup | |||||||
| import com.intellij.internal.statistic.eventLog.events.EventFields | import com.intellij.internal.statistic.eventLog.events.EventFields | ||||||
| import com.intellij.internal.statistic.eventLog.events.EventPair | import com.intellij.internal.statistic.eventLog.events.EventPair | ||||||
| import com.intellij.internal.statistic.eventLog.events.StringListEventField | import com.intellij.internal.statistic.eventLog.events.StringListEventField | ||||||
| import com.intellij.internal.statistic.eventLog.events.VarargEventId |  | ||||||
| import com.intellij.internal.statistic.service.fus.collectors.ApplicationUsagesCollector | import com.intellij.internal.statistic.service.fus.collectors.ApplicationUsagesCollector | ||||||
| import com.maddyhome.idea.vim.VimPlugin | import com.maddyhome.idea.vim.VimPlugin | ||||||
|  | import com.maddyhome.idea.vim.api.injector | ||||||
| import com.maddyhome.idea.vim.key.ShortcutOwner | import com.maddyhome.idea.vim.key.ShortcutOwner | ||||||
| import com.maddyhome.idea.vim.key.ShortcutOwnerInfo | import com.maddyhome.idea.vim.key.ShortcutOwnerInfo | ||||||
| import java.awt.event.InputEvent.CTRL_DOWN_MASK | import java.awt.event.InputEvent.CTRL_DOWN_MASK | ||||||
| @@ -37,92 +37,16 @@ internal class ShortcutConflictState : ApplicationUsagesCollector() { | |||||||
|   override fun getGroup(): EventLogGroup = GROUP |   override fun getGroup(): EventLogGroup = GROUP | ||||||
|  |  | ||||||
|   override fun getMetrics(): Set<MetricEvent> { |   override fun getMetrics(): Set<MetricEvent> { | ||||||
|     return setOf( |     val metrics = mutableSetOf<MetricEvent>() | ||||||
|       HANDLERS.metric( |     keyStrokes.forEach { keystroke -> | ||||||
|         CTRL_1 withKeyStroke KeyStroke.getKeyStroke('1'.code, CTRL_DOWN_MASK), |       getHandlersForShortcut(keystroke).forEach { mode -> | ||||||
|         CTRL_2 withKeyStroke KeyStroke.getKeyStroke('2'.code, CTRL_DOWN_MASK), |         metrics += HANDLER.metric(injector.parser.toKeyNotation(keystroke), mode) | ||||||
|         CTRL_3 withKeyStroke KeyStroke.getKeyStroke('3'.code, CTRL_DOWN_MASK), |       } | ||||||
|         CTRL_4 withKeyStroke KeyStroke.getKeyStroke('4'.code, CTRL_DOWN_MASK), |     } | ||||||
|         CTRL_5 withKeyStroke KeyStroke.getKeyStroke('5'.code, CTRL_DOWN_MASK), |     return metrics | ||||||
|         CTRL_6 withKeyStroke KeyStroke.getKeyStroke('6'.code, CTRL_DOWN_MASK), |  | ||||||
|         CTRL_7 withKeyStroke KeyStroke.getKeyStroke('7'.code, CTRL_DOWN_MASK), |  | ||||||
|         CTRL_8 withKeyStroke KeyStroke.getKeyStroke('8'.code, CTRL_DOWN_MASK), |  | ||||||
|         CTRL_9 withKeyStroke KeyStroke.getKeyStroke('9'.code, CTRL_DOWN_MASK), |  | ||||||
|         CTRL_0 withKeyStroke KeyStroke.getKeyStroke('0'.code, CTRL_DOWN_MASK), |  | ||||||
|  |  | ||||||
|         CTRL_SHIFT_1 withKeyStroke KeyStroke.getKeyStroke('1'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_2 withKeyStroke KeyStroke.getKeyStroke('2'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_3 withKeyStroke KeyStroke.getKeyStroke('3'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_4 withKeyStroke KeyStroke.getKeyStroke('4'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_5 withKeyStroke KeyStroke.getKeyStroke('5'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_6 withKeyStroke KeyStroke.getKeyStroke('6'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_7 withKeyStroke KeyStroke.getKeyStroke('7'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_8 withKeyStroke KeyStroke.getKeyStroke('8'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_9 withKeyStroke KeyStroke.getKeyStroke('9'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_0 withKeyStroke KeyStroke.getKeyStroke('0'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|  |  | ||||||
|         CTRL_A withKeyStroke KeyStroke.getKeyStroke('A'.code, CTRL_DOWN_MASK), |  | ||||||
|         CTRL_B withKeyStroke KeyStroke.getKeyStroke('B'.code, CTRL_DOWN_MASK), |  | ||||||
|         CTRL_C withKeyStroke KeyStroke.getKeyStroke('C'.code, CTRL_DOWN_MASK), |  | ||||||
|         CTRL_D withKeyStroke KeyStroke.getKeyStroke('D'.code, CTRL_DOWN_MASK), |  | ||||||
|         CTRL_E withKeyStroke KeyStroke.getKeyStroke('E'.code, CTRL_DOWN_MASK), |  | ||||||
|         CTRL_F withKeyStroke KeyStroke.getKeyStroke('F'.code, CTRL_DOWN_MASK), |  | ||||||
|         CTRL_G withKeyStroke KeyStroke.getKeyStroke('G'.code, CTRL_DOWN_MASK), |  | ||||||
|         CTRL_H withKeyStroke KeyStroke.getKeyStroke('H'.code, CTRL_DOWN_MASK), |  | ||||||
|         CTRL_I withKeyStroke KeyStroke.getKeyStroke('I'.code, CTRL_DOWN_MASK), |  | ||||||
|         CTRL_J withKeyStroke KeyStroke.getKeyStroke('J'.code, CTRL_DOWN_MASK), |  | ||||||
|         CTRL_K withKeyStroke KeyStroke.getKeyStroke('K'.code, CTRL_DOWN_MASK), |  | ||||||
|         CTRL_L withKeyStroke KeyStroke.getKeyStroke('L'.code, CTRL_DOWN_MASK), |  | ||||||
|         CTRL_M withKeyStroke KeyStroke.getKeyStroke('M'.code, CTRL_DOWN_MASK), |  | ||||||
|         CTRL_N withKeyStroke KeyStroke.getKeyStroke('N'.code, CTRL_DOWN_MASK), |  | ||||||
|         CTRL_O withKeyStroke KeyStroke.getKeyStroke('O'.code, CTRL_DOWN_MASK), |  | ||||||
|         CTRL_P withKeyStroke KeyStroke.getKeyStroke('P'.code, CTRL_DOWN_MASK), |  | ||||||
|         CTRL_Q withKeyStroke KeyStroke.getKeyStroke('Q'.code, CTRL_DOWN_MASK), |  | ||||||
|         CTRL_R withKeyStroke KeyStroke.getKeyStroke('R'.code, CTRL_DOWN_MASK), |  | ||||||
|         CTRL_S withKeyStroke KeyStroke.getKeyStroke('S'.code, CTRL_DOWN_MASK), |  | ||||||
|         CTRL_T withKeyStroke KeyStroke.getKeyStroke('T'.code, CTRL_DOWN_MASK), |  | ||||||
|         CTRL_U withKeyStroke KeyStroke.getKeyStroke('U'.code, CTRL_DOWN_MASK), |  | ||||||
|         CTRL_V withKeyStroke KeyStroke.getKeyStroke('V'.code, CTRL_DOWN_MASK), |  | ||||||
|         CTRL_W withKeyStroke KeyStroke.getKeyStroke('W'.code, CTRL_DOWN_MASK), |  | ||||||
|         CTRL_X withKeyStroke KeyStroke.getKeyStroke('X'.code, CTRL_DOWN_MASK), |  | ||||||
|         CTRL_Y withKeyStroke KeyStroke.getKeyStroke('Y'.code, CTRL_DOWN_MASK), |  | ||||||
|         CTRL_Z withKeyStroke KeyStroke.getKeyStroke('Z'.code, CTRL_DOWN_MASK), |  | ||||||
|         CTRL_BR1 withKeyStroke KeyStroke.getKeyStroke('['.code, CTRL_DOWN_MASK), |  | ||||||
|         CTRL_BR2 withKeyStroke KeyStroke.getKeyStroke(']'.code, CTRL_DOWN_MASK), |  | ||||||
|  |  | ||||||
|         CTRL_SHIFT_A withKeyStroke KeyStroke.getKeyStroke('A'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_B withKeyStroke KeyStroke.getKeyStroke('B'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_C withKeyStroke KeyStroke.getKeyStroke('C'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_D withKeyStroke KeyStroke.getKeyStroke('D'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_E withKeyStroke KeyStroke.getKeyStroke('E'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_F withKeyStroke KeyStroke.getKeyStroke('F'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_G withKeyStroke KeyStroke.getKeyStroke('G'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_H withKeyStroke KeyStroke.getKeyStroke('H'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_I withKeyStroke KeyStroke.getKeyStroke('I'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_J withKeyStroke KeyStroke.getKeyStroke('J'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_K withKeyStroke KeyStroke.getKeyStroke('K'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_L withKeyStroke KeyStroke.getKeyStroke('L'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_M withKeyStroke KeyStroke.getKeyStroke('M'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_N withKeyStroke KeyStroke.getKeyStroke('N'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_O withKeyStroke KeyStroke.getKeyStroke('O'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_P withKeyStroke KeyStroke.getKeyStroke('P'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_Q withKeyStroke KeyStroke.getKeyStroke('Q'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_R withKeyStroke KeyStroke.getKeyStroke('R'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_S withKeyStroke KeyStroke.getKeyStroke('S'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_T withKeyStroke KeyStroke.getKeyStroke('T'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_U withKeyStroke KeyStroke.getKeyStroke('U'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_V withKeyStroke KeyStroke.getKeyStroke('V'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_W withKeyStroke KeyStroke.getKeyStroke('W'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_X withKeyStroke KeyStroke.getKeyStroke('X'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_Y withKeyStroke KeyStroke.getKeyStroke('Y'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_Z withKeyStroke KeyStroke.getKeyStroke('Z'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_BR1 withKeyStroke KeyStroke.getKeyStroke('['.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|         CTRL_SHIFT_BR2 withKeyStroke KeyStroke.getKeyStroke(']'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), |  | ||||||
|       ) |  | ||||||
|     ) |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private infix fun StringListEventField.withKeyStroke(ks: KeyStroke): EventPair<List<String>> { |   fun StringListEventField.withKeyStroke(ks: KeyStroke): EventPair<List<String>> { | ||||||
|     return this.with(getHandlersForShortcut(ks).map { it.name }) |     return this.with(getHandlersForShortcut(ks).map { it.name }) | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -162,170 +86,91 @@ internal class ShortcutConflictState : ApplicationUsagesCollector() { | |||||||
|  |  | ||||||
|   companion object { |   companion object { | ||||||
|     private val GROUP = EventLogGroup("vim.handlers", 1) |     private val GROUP = EventLogGroup("vim.handlers", 1) | ||||||
|     private val values = HandledModes.values().map { it.name } |  | ||||||
|  |  | ||||||
|     private val CTRL_1 = EventFields.StringList("Ctrl-1", values) |     private val keyStrokes = listOf( | ||||||
|     private val CTRL_2 = EventFields.StringList("Ctrl-2", values) |       KeyStroke.getKeyStroke('1'.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_3 = EventFields.StringList("Ctrl-3", values) |       KeyStroke.getKeyStroke('2'.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_4 = EventFields.StringList("Ctrl-4", values) |       KeyStroke.getKeyStroke('3'.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_5 = EventFields.StringList("Ctrl-5", values) |       KeyStroke.getKeyStroke('4'.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_6 = EventFields.StringList("Ctrl-6", values) |       KeyStroke.getKeyStroke('5'.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_7 = EventFields.StringList("Ctrl-7", values) |       KeyStroke.getKeyStroke('6'.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_8 = EventFields.StringList("Ctrl-8", values) |       KeyStroke.getKeyStroke('7'.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_9 = EventFields.StringList("Ctrl-9", values) |       KeyStroke.getKeyStroke('8'.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_0 = EventFields.StringList("Ctrl-0", values) |       KeyStroke.getKeyStroke('9'.code, CTRL_DOWN_MASK), | ||||||
|  |       KeyStroke.getKeyStroke('0'.code, CTRL_DOWN_MASK), | ||||||
|  |  | ||||||
|     private val CTRL_SHIFT_1 = EventFields.StringList("Ctrl-Shift-1", values) |       KeyStroke.getKeyStroke('1'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_2 = EventFields.StringList("Ctrl-Shift-2", values) |       KeyStroke.getKeyStroke('2'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_3 = EventFields.StringList("Ctrl-Shift-3", values) |       KeyStroke.getKeyStroke('3'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_4 = EventFields.StringList("Ctrl-Shift-4", values) |       KeyStroke.getKeyStroke('4'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_5 = EventFields.StringList("Ctrl-Shift-5", values) |       KeyStroke.getKeyStroke('5'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_6 = EventFields.StringList("Ctrl-Shift-6", values) |       KeyStroke.getKeyStroke('6'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_7 = EventFields.StringList("Ctrl-Shift-7", values) |       KeyStroke.getKeyStroke('7'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_8 = EventFields.StringList("Ctrl-Shift-8", values) |       KeyStroke.getKeyStroke('8'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_9 = EventFields.StringList("Ctrl-Shift-9", values) |       KeyStroke.getKeyStroke('9'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_0 = EventFields.StringList("Ctrl-Shift-0", values) |       KeyStroke.getKeyStroke('0'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|  |  | ||||||
|     private val CTRL_A = EventFields.StringList("Ctrl-A", values) |       KeyStroke.getKeyStroke('A'.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_B = EventFields.StringList("Ctrl-B", values) |       KeyStroke.getKeyStroke('B'.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_C = EventFields.StringList("Ctrl-C", values) |       KeyStroke.getKeyStroke('C'.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_D = EventFields.StringList("Ctrl-D", values) |       KeyStroke.getKeyStroke('D'.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_E = EventFields.StringList("Ctrl-E", values) |       KeyStroke.getKeyStroke('E'.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_F = EventFields.StringList("Ctrl-F", values) |       KeyStroke.getKeyStroke('F'.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_G = EventFields.StringList("Ctrl-G", values) |       KeyStroke.getKeyStroke('G'.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_H = EventFields.StringList("Ctrl-H", values) |       KeyStroke.getKeyStroke('H'.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_I = EventFields.StringList("Ctrl-I", values) |       KeyStroke.getKeyStroke('I'.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_J = EventFields.StringList("Ctrl-J", values) |       KeyStroke.getKeyStroke('J'.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_K = EventFields.StringList("Ctrl-K", values) |       KeyStroke.getKeyStroke('K'.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_L = EventFields.StringList("Ctrl-L", values) |       KeyStroke.getKeyStroke('L'.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_M = EventFields.StringList("Ctrl-M", values) |       KeyStroke.getKeyStroke('M'.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_N = EventFields.StringList("Ctrl-N", values) |       KeyStroke.getKeyStroke('N'.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_O = EventFields.StringList("Ctrl-O", values) |       KeyStroke.getKeyStroke('O'.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_P = EventFields.StringList("Ctrl-P", values) |       KeyStroke.getKeyStroke('P'.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_Q = EventFields.StringList("Ctrl-Q", values) |       KeyStroke.getKeyStroke('Q'.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_R = EventFields.StringList("Ctrl-R", values) |       KeyStroke.getKeyStroke('R'.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_S = EventFields.StringList("Ctrl-S", values) |       KeyStroke.getKeyStroke('S'.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_T = EventFields.StringList("Ctrl-T", values) |       KeyStroke.getKeyStroke('T'.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_U = EventFields.StringList("Ctrl-U", values) |       KeyStroke.getKeyStroke('U'.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_V = EventFields.StringList("Ctrl-V", values) |       KeyStroke.getKeyStroke('V'.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_W = EventFields.StringList("Ctrl-W", values) |       KeyStroke.getKeyStroke('W'.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_X = EventFields.StringList("Ctrl-X", values) |       KeyStroke.getKeyStroke('X'.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_Y = EventFields.StringList("Ctrl-Y", values) |       KeyStroke.getKeyStroke('Y'.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_Z = EventFields.StringList("Ctrl-Z", values) |       KeyStroke.getKeyStroke('Z'.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_BR1 = EventFields.StringList("Ctrl-[", values) |       KeyStroke.getKeyStroke('['.code, CTRL_DOWN_MASK), | ||||||
|     private val CTRL_BR2 = EventFields.StringList("Ctrl-]", values) |       KeyStroke.getKeyStroke(']'.code, CTRL_DOWN_MASK), | ||||||
|  |  | ||||||
|     private val CTRL_SHIFT_A = EventFields.StringList("Ctrl-Shift-A", values) |       KeyStroke.getKeyStroke('A'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_B = EventFields.StringList("Ctrl-Shift-B", values) |       KeyStroke.getKeyStroke('B'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_C = EventFields.StringList("Ctrl-Shift-C", values) |       KeyStroke.getKeyStroke('C'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_D = EventFields.StringList("Ctrl-Shift-D", values) |       KeyStroke.getKeyStroke('D'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_E = EventFields.StringList("Ctrl-Shift-E", values) |       KeyStroke.getKeyStroke('E'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_F = EventFields.StringList("Ctrl-Shift-F", values) |       KeyStroke.getKeyStroke('F'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_G = EventFields.StringList("Ctrl-Shift-G", values) |       KeyStroke.getKeyStroke('G'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_H = EventFields.StringList("Ctrl-Shift-H", values) |       KeyStroke.getKeyStroke('H'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_I = EventFields.StringList("Ctrl-Shift-I", values) |       KeyStroke.getKeyStroke('I'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_J = EventFields.StringList("Ctrl-Shift-J", values) |       KeyStroke.getKeyStroke('J'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_K = EventFields.StringList("Ctrl-Shift-K", values) |       KeyStroke.getKeyStroke('K'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_L = EventFields.StringList("Ctrl-Shift-L", values) |       KeyStroke.getKeyStroke('L'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_M = EventFields.StringList("Ctrl-Shift-M", values) |       KeyStroke.getKeyStroke('M'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_N = EventFields.StringList("Ctrl-Shift-N", values) |       KeyStroke.getKeyStroke('N'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_O = EventFields.StringList("Ctrl-Shift-O", values) |       KeyStroke.getKeyStroke('O'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_P = EventFields.StringList("Ctrl-Shift-P", values) |       KeyStroke.getKeyStroke('P'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_Q = EventFields.StringList("Ctrl-Shift-Q", values) |       KeyStroke.getKeyStroke('Q'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_R = EventFields.StringList("Ctrl-Shift-R", values) |       KeyStroke.getKeyStroke('R'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_S = EventFields.StringList("Ctrl-Shift-S", values) |       KeyStroke.getKeyStroke('S'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_T = EventFields.StringList("Ctrl-Shift-T", values) |       KeyStroke.getKeyStroke('T'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_U = EventFields.StringList("Ctrl-Shift-U", values) |       KeyStroke.getKeyStroke('U'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_V = EventFields.StringList("Ctrl-Shift-V", values) |       KeyStroke.getKeyStroke('V'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_W = EventFields.StringList("Ctrl-Shift-W", values) |       KeyStroke.getKeyStroke('W'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_X = EventFields.StringList("Ctrl-Shift-X", values) |       KeyStroke.getKeyStroke('X'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_Y = EventFields.StringList("Ctrl-Shift-Y", values) |       KeyStroke.getKeyStroke('Y'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_Z = EventFields.StringList("Ctrl-Shift-Z", values) |       KeyStroke.getKeyStroke('Z'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_BR1 = EventFields.StringList("Ctrl-Shift-[", values) |       KeyStroke.getKeyStroke('['.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|     private val CTRL_SHIFT_BR2 = EventFields.StringList("Ctrl-Shift-]", values) |       KeyStroke.getKeyStroke(']'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK), | ||||||
|  |  | ||||||
|     private val HANDLERS: VarargEventId = GROUP.registerVarargEvent( |  | ||||||
|       "vim.handlers", |  | ||||||
|       CTRL_1, |  | ||||||
|       CTRL_2, |  | ||||||
|       CTRL_3, |  | ||||||
|       CTRL_4, |  | ||||||
|       CTRL_5, |  | ||||||
|       CTRL_6, |  | ||||||
|       CTRL_7, |  | ||||||
|       CTRL_8, |  | ||||||
|       CTRL_9, |  | ||||||
|       CTRL_0, |  | ||||||
|  |  | ||||||
|       CTRL_SHIFT_1, |  | ||||||
|       CTRL_SHIFT_2, |  | ||||||
|       CTRL_SHIFT_3, |  | ||||||
|       CTRL_SHIFT_4, |  | ||||||
|       CTRL_SHIFT_5, |  | ||||||
|       CTRL_SHIFT_6, |  | ||||||
|       CTRL_SHIFT_7, |  | ||||||
|       CTRL_SHIFT_8, |  | ||||||
|       CTRL_SHIFT_9, |  | ||||||
|       CTRL_SHIFT_0, |  | ||||||
|  |  | ||||||
|       CTRL_A, |  | ||||||
|       CTRL_B, |  | ||||||
|       CTRL_C, |  | ||||||
|       CTRL_D, |  | ||||||
|       CTRL_E, |  | ||||||
|       CTRL_F, |  | ||||||
|       CTRL_G, |  | ||||||
|       CTRL_H, |  | ||||||
|       CTRL_I, |  | ||||||
|       CTRL_J, |  | ||||||
|       CTRL_K, |  | ||||||
|       CTRL_L, |  | ||||||
|       CTRL_M, |  | ||||||
|       CTRL_N, |  | ||||||
|       CTRL_O, |  | ||||||
|       CTRL_P, |  | ||||||
|       CTRL_Q, |  | ||||||
|       CTRL_R, |  | ||||||
|       CTRL_S, |  | ||||||
|       CTRL_T, |  | ||||||
|       CTRL_U, |  | ||||||
|       CTRL_V, |  | ||||||
|       CTRL_W, |  | ||||||
|       CTRL_X, |  | ||||||
|       CTRL_Y, |  | ||||||
|       CTRL_Z, |  | ||||||
|       CTRL_BR1, |  | ||||||
|       CTRL_BR2, |  | ||||||
|  |  | ||||||
|       CTRL_SHIFT_A, |  | ||||||
|       CTRL_SHIFT_B, |  | ||||||
|       CTRL_SHIFT_C, |  | ||||||
|       CTRL_SHIFT_D, |  | ||||||
|       CTRL_SHIFT_E, |  | ||||||
|       CTRL_SHIFT_F, |  | ||||||
|       CTRL_SHIFT_G, |  | ||||||
|       CTRL_SHIFT_H, |  | ||||||
|       CTRL_SHIFT_I, |  | ||||||
|       CTRL_SHIFT_J, |  | ||||||
|       CTRL_SHIFT_K, |  | ||||||
|       CTRL_SHIFT_L, |  | ||||||
|       CTRL_SHIFT_M, |  | ||||||
|       CTRL_SHIFT_N, |  | ||||||
|       CTRL_SHIFT_O, |  | ||||||
|       CTRL_SHIFT_P, |  | ||||||
|       CTRL_SHIFT_Q, |  | ||||||
|       CTRL_SHIFT_R, |  | ||||||
|       CTRL_SHIFT_S, |  | ||||||
|       CTRL_SHIFT_T, |  | ||||||
|       CTRL_SHIFT_U, |  | ||||||
|       CTRL_SHIFT_V, |  | ||||||
|       CTRL_SHIFT_W, |  | ||||||
|       CTRL_SHIFT_X, |  | ||||||
|       CTRL_SHIFT_Y, |  | ||||||
|       CTRL_SHIFT_Z, |  | ||||||
|       CTRL_SHIFT_BR1, |  | ||||||
|       CTRL_SHIFT_BR2, |  | ||||||
|     ) |     ) | ||||||
|  |     private val KEY_STROKE = EventFields.String("key_stroke", keyStrokes.map { injector.parser.toKeyNotation(it) }) | ||||||
|  |     private val HANDLER_MODE = EventFields.Enum<HandledModes>("handler") | ||||||
|  |     private val HANDLER = GROUP.registerEvent("vim.handler", KEY_STROKE, HANDLER_MODE) | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -59,8 +59,8 @@ internal class VimscriptState : ApplicationUsagesCollector() { | |||||||
|     var isFunctionDeclarationUsed = false |     var isFunctionDeclarationUsed = false | ||||||
|     var isFunctionCallUsed = false |     var isFunctionCallUsed = false | ||||||
|  |  | ||||||
|     private val SOURCED_FILES = EventFields.Int("number_of_sourced_files") |     private val SOURCED_FILES = EventFields.RoundedInt("number_of_sourced_files") | ||||||
|     private val IDEAVIMRC_SIZE = EventFields.Int("ideavimrc_size") |     private val IDEAVIMRC_SIZE = EventFields.RoundedInt("ideavimrc_size") | ||||||
|     private val EXTENSIONS_ENABLED_BY_SET = EventFields.StringList("extensions_enabled_by_set", PluginState.extensionNames) |     private val EXTENSIONS_ENABLED_BY_SET = EventFields.StringList("extensions_enabled_by_set", PluginState.extensionNames) | ||||||
|     private val EXTENSIONS_ENABLED_BY_PLUG = EventFields.StringList("extensions_enabled_by_plug", PluginState.extensionNames) |     private val EXTENSIONS_ENABLED_BY_PLUG = EventFields.StringList("extensions_enabled_by_plug", PluginState.extensionNames) | ||||||
|     private val IS_IDE_SPECIFIC_CONFIGURATION_USED = EventFields.Boolean("is_IDE-specific_configuration_used") |     private val IS_IDE_SPECIFIC_CONFIGURATION_USED = EventFields.Boolean("is_IDE-specific_configuration_used") | ||||||
|   | |||||||
| @@ -24,7 +24,6 @@ import com.intellij.openapi.actionSystem.ActionUpdateThread | |||||||
| import com.intellij.openapi.actionSystem.AnActionEvent | import com.intellij.openapi.actionSystem.AnActionEvent | ||||||
| import com.intellij.openapi.actionSystem.DefaultActionGroup | import com.intellij.openapi.actionSystem.DefaultActionGroup | ||||||
| import com.intellij.openapi.actionSystem.PlatformDataKeys | import com.intellij.openapi.actionSystem.PlatformDataKeys | ||||||
| import com.intellij.openapi.components.service |  | ||||||
| import com.intellij.openapi.editor.Document | import com.intellij.openapi.editor.Document | ||||||
| import com.intellij.openapi.editor.toolbar.floating.AbstractFloatingToolbarProvider | import com.intellij.openapi.editor.toolbar.floating.AbstractFloatingToolbarProvider | ||||||
| import com.intellij.openapi.editor.toolbar.floating.FloatingToolbarComponent | import com.intellij.openapi.editor.toolbar.floating.FloatingToolbarComponent | ||||||
| @@ -151,7 +150,7 @@ class ReloadVimRc : DumbAwareAction() { | |||||||
|     val editor = e.getData(PlatformDataKeys.EDITOR) ?: return |     val editor = e.getData(PlatformDataKeys.EDITOR) ?: return | ||||||
|     FileDocumentManager.getInstance().saveDocumentAsIs(editor.document) |     FileDocumentManager.getInstance().saveDocumentAsIs(editor.document) | ||||||
|     injector.keyGroup.removeKeyMapping(MappingOwner.IdeaVim.InitScript) |     injector.keyGroup.removeKeyMapping(MappingOwner.IdeaVim.InitScript) | ||||||
|     service<Troubleshooter>().removeByType("old-action-notation-in-mappings") |     Troubleshooter.instance.removeByType("old-action-notation-in-mappings") | ||||||
|     executeIdeaVimRc() |     executeIdeaVimRc() | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -21,7 +21,7 @@ package com.maddyhome.idea.vim.ui.ex | |||||||
| import com.intellij.openapi.diagnostic.logger | import com.intellij.openapi.diagnostic.logger | ||||||
| import com.maddyhome.idea.vim.KeyHandler | import com.maddyhome.idea.vim.KeyHandler | ||||||
| import com.maddyhome.idea.vim.VimPlugin | import com.maddyhome.idea.vim.VimPlugin | ||||||
| import com.maddyhome.idea.vim.helper.SearchHelper | import com.maddyhome.idea.vim.api.VimSearchHelperBase | ||||||
| import com.maddyhome.idea.vim.newapi.vim | import com.maddyhome.idea.vim.newapi.vim | ||||||
| import java.awt.event.ActionEvent | import java.awt.event.ActionEvent | ||||||
| import java.awt.event.KeyEvent | import java.awt.event.KeyEvent | ||||||
| @@ -238,14 +238,14 @@ class DeletePreviousWordAction : TextAction(DefaultEditorKit.deletePrevWordActio | |||||||
|     target.saveLastEntry() |     target.saveLastEntry() | ||||||
|     val doc = target.document |     val doc = target.document | ||||||
|     val caret = target.caret |     val caret = target.caret | ||||||
|     val offset = SearchHelper.findNextWord( |     val offset = VimSearchHelperBase.Companion.findNextWord( | ||||||
|       target.actualText, caret.dot, target.actualText.length, |       target.actualText, caret.dot.toLong(), target.actualText.length.toLong(), | ||||||
|       -1, false, false |       -1, false, false | ||||||
|     ) |     ) | ||||||
|     if (logger.isDebugEnabled) logger.debug("offset=$offset") |     if (logger.isDebugEnabled) logger.debug("offset=$offset") | ||||||
|     try { |     try { | ||||||
|       val pos = caret.dot |       val pos = caret.dot | ||||||
|       doc.remove(offset, pos - offset) |       doc.remove(offset.toInt(), (pos - offset).toInt()) | ||||||
|     } catch (ex: BadLocationException) { |     } catch (ex: BadLocationException) { | ||||||
|       // ignore |       // ignore | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -94,7 +94,7 @@ abstract class VimTestCase : UsefulTestCase() { | |||||||
|     super.setUp() |     super.setUp() | ||||||
|     val factory = IdeaTestFixtureFactory.getFixtureFactory() |     val factory = IdeaTestFixtureFactory.getFixtureFactory() | ||||||
|     val projectDescriptor = LightProjectDescriptor.EMPTY_PROJECT_DESCRIPTOR |     val projectDescriptor = LightProjectDescriptor.EMPTY_PROJECT_DESCRIPTOR | ||||||
|     val fixtureBuilder = factory.createLightFixtureBuilder(projectDescriptor) |     val fixtureBuilder = factory.createLightFixtureBuilder(projectDescriptor, "IdeaVim") | ||||||
|     val fixture = fixtureBuilder.fixture |     val fixture = fixtureBuilder.fixture | ||||||
|     myFixture = IdeaTestFixtureFactory.getFixtureFactory().createCodeInsightFixture( |     myFixture = IdeaTestFixtureFactory.getFixtureFactory().createCodeInsightFixture( | ||||||
|       fixture, |       fixture, | ||||||
|   | |||||||
| @@ -20,6 +20,7 @@ package org.jetbrains.plugins.ideavim.action | |||||||
| import com.maddyhome.idea.vim.VimPlugin | import com.maddyhome.idea.vim.VimPlugin | ||||||
| import com.maddyhome.idea.vim.api.injector | import com.maddyhome.idea.vim.api.injector | ||||||
| import com.maddyhome.idea.vim.command.VimStateMachine | import com.maddyhome.idea.vim.command.VimStateMachine | ||||||
|  | import com.maddyhome.idea.vim.helper.StringHelper.parseKeys | ||||||
| import com.maddyhome.idea.vim.helper.vimStateMachine | import com.maddyhome.idea.vim.helper.vimStateMachine | ||||||
| import org.jetbrains.plugins.ideavim.SkipNeovimReason | import org.jetbrains.plugins.ideavim.SkipNeovimReason | ||||||
| import org.jetbrains.plugins.ideavim.TestWithoutNeovim | import org.jetbrains.plugins.ideavim.TestWithoutNeovim | ||||||
| @@ -1039,4 +1040,16 @@ two | |||||||
|     ) |     ) | ||||||
|     assertOffset(4) |     assertOffset(4) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   fun `test gv after backwards selection`() { | ||||||
|  |     configureByText("${c}Oh, hi Mark\n") | ||||||
|  |     typeText(parseKeys("yw" + "$" + "vb" + "p" + "gv")) | ||||||
|  |     assertSelection("Oh") | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   fun `test gv after linewise selection`() { | ||||||
|  |     configureByText("${c}Oh, hi Mark\nOh, hi Markus\n") | ||||||
|  |     typeText(parseKeys("V" + "y" + "j" + "V" + "p" + "gv")) | ||||||
|  |     assertSelection("Oh, hi Mark") | ||||||
|  |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -18,8 +18,8 @@ | |||||||
|  |  | ||||||
| package org.jetbrains.plugins.ideavim.action.change.change | package org.jetbrains.plugins.ideavim.action.change.change | ||||||
|  |  | ||||||
| //class InsertRegisterTest : VimTestCase() { | // class InsertRegisterTest : VimTestCase() { | ||||||
|   // todo test cursor position VIM-2732 | // todo test cursor position VIM-2732 | ||||||
| //  fun `test multiline insert from expression register`() { | //  fun `test multiline insert from expression register`() { | ||||||
| //    val keys = "VjyGo<C-r>=@\"<CR>" | //    val keys = "VjyGo<C-r>=@\"<CR>" | ||||||
| //    val before = """ | //    val before = """ | ||||||
| @@ -43,4 +43,4 @@ package org.jetbrains.plugins.ideavim.action.change.change | |||||||
| //    """.trimIndent() | //    """.trimIndent() | ||||||
| //    doTest(keys, before, after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE) | //    doTest(keys, before, after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE) | ||||||
| //  } | //  } | ||||||
| //} | // } | ||||||
|   | |||||||
| @@ -49,6 +49,23 @@ class DeleteMotionActionTest : VimTestCase() { | |||||||
|     ) |     ) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   fun `test on line in middle`() { | ||||||
|  |     typeTextInFile( | ||||||
|  |       injector.parser.parseKeys("dd"), | ||||||
|  |       """ | ||||||
|  |         def xxx(): | ||||||
|  |           expression${c} one | ||||||
|  |           expression two | ||||||
|  |       """.trimIndent() | ||||||
|  |     ) | ||||||
|  |     assertState( | ||||||
|  |       """ | ||||||
|  |         def xxx(): | ||||||
|  |           ${c}expression two | ||||||
|  |       """.trimIndent() | ||||||
|  |     ) | ||||||
|  |   } | ||||||
|  |  | ||||||
|   fun `test delete single line`() { |   fun `test delete single line`() { | ||||||
|     typeTextInFile( |     typeTextInFile( | ||||||
|       injector.parser.parseKeys("dd"), |       injector.parser.parseKeys("dd"), | ||||||
|   | |||||||
| @@ -18,6 +18,13 @@ | |||||||
|  |  | ||||||
| package org.jetbrains.plugins.ideavim.action.copy | package org.jetbrains.plugins.ideavim.action.copy | ||||||
|  |  | ||||||
|  | import com.intellij.codeInsight.editorActions.CopyPastePostProcessor | ||||||
|  | import com.intellij.codeInsight.editorActions.CopyPastePreProcessor | ||||||
|  | import com.intellij.codeInsight.editorActions.TextBlockTransferableData | ||||||
|  | import com.intellij.openapi.editor.CaretStateTransferableData | ||||||
|  | import com.intellij.openapi.editor.Editor | ||||||
|  | import com.intellij.psi.PsiFile | ||||||
|  | import com.intellij.testFramework.ExtensionTestUtil | ||||||
| import com.maddyhome.idea.vim.VimPlugin | import com.maddyhome.idea.vim.VimPlugin | ||||||
| import com.maddyhome.idea.vim.api.injector | import com.maddyhome.idea.vim.api.injector | ||||||
| import com.maddyhome.idea.vim.command.SelectionType | import com.maddyhome.idea.vim.command.SelectionType | ||||||
| @@ -27,8 +34,32 @@ import com.maddyhome.idea.vim.newapi.vim | |||||||
| import org.jetbrains.plugins.ideavim.VimTestCase | import org.jetbrains.plugins.ideavim.VimTestCase | ||||||
| import org.jetbrains.plugins.ideavim.rangeOf | import org.jetbrains.plugins.ideavim.rangeOf | ||||||
| import org.junit.Test | import org.junit.Test | ||||||
|  | import java.awt.datatransfer.Transferable | ||||||
|  |  | ||||||
| class PutTestAfterCursorActionTest : VimTestCase() { | class PutTestAfterCursorActionTest : VimTestCase() { | ||||||
|  |   fun `test platform handlers are called`() { | ||||||
|  |     val extension = TestExtension() | ||||||
|  |     ExtensionTestUtil.maskExtensions( | ||||||
|  |       CopyPastePostProcessor.EP_NAME, | ||||||
|  |       listOf(extension), | ||||||
|  |       myFixture.testRootDisposable | ||||||
|  |     ) | ||||||
|  |     ExtensionTestUtil.maskExtensions( | ||||||
|  |       CopyPastePreProcessor.EP_NAME, | ||||||
|  |       listOf(), | ||||||
|  |       myFixture.testRootDisposable | ||||||
|  |     ) | ||||||
|  |     setRegister('4', "XXX ") | ||||||
|  |     doTest( | ||||||
|  |       "\"4p", | ||||||
|  |       "This is my$c text", | ||||||
|  |       "This is my XXX$c text", | ||||||
|  |       VimStateMachine.Mode.COMMAND, | ||||||
|  |       VimStateMachine.SubMode.NONE | ||||||
|  |     ) | ||||||
|  |     assertEquals(1, extension.calledExtractTransferableData) | ||||||
|  |   } | ||||||
|  |  | ||||||
|   fun `test put from number register`() { |   fun `test put from number register`() { | ||||||
|     setRegister('4', "XXX ") |     setRegister('4', "XXX ") | ||||||
|     doTest( |     doTest( | ||||||
| @@ -99,4 +130,24 @@ class PutTestAfterCursorActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     assertState(after) |     assertState(after) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   private class TestExtension : CopyPastePostProcessor<TextBlockTransferableData>() { | ||||||
|  |     var calledExtractTransferableData = 0 | ||||||
|  |     override fun collectTransferableData( | ||||||
|  |       file: PsiFile, | ||||||
|  |       editor: Editor, | ||||||
|  |       startOffsets: IntArray?, | ||||||
|  |       endOffsets: IntArray?, | ||||||
|  |     ): List<TextBlockTransferableData> { | ||||||
|  |       return emptyList() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override fun extractTransferableData(content: Transferable): List<TextBlockTransferableData> { | ||||||
|  |       calledExtractTransferableData += 1 | ||||||
|  |       return listOf( | ||||||
|  |         // Just some random data | ||||||
|  |         CaretStateTransferableData(intArrayOf(), intArrayOf()) | ||||||
|  |       ) | ||||||
|  |     } | ||||||
|  |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -91,7 +91,7 @@ class YankVisualActionTest : VimTestCase() { | |||||||
|     typeText(injector.parser.parseKeys("viw" + "y")) |     typeText(injector.parser.parseKeys("viw" + "y")) | ||||||
|     val editor = myFixture.editor.vim |     val editor = myFixture.editor.vim | ||||||
|     val lastRegister = injector.registerGroup.lastRegisterChar |     val lastRegister = injector.registerGroup.lastRegisterChar | ||||||
|     val registers = editor.carets().map { it.registerStorage.getRegister(lastRegister)?.rawText } |     val registers = editor.carets().map { it.registerStorage.getRegister(it, lastRegister)?.rawText } | ||||||
|     assertEquals(listOf("found", "was"), registers) |     assertEquals(listOf("found", "was"), registers) | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -165,7 +165,7 @@ class YankVisualActionTest : VimTestCase() { | |||||||
|     typeText(injector.parser.parseKeys("V" + "y")) |     typeText(injector.parser.parseKeys("V" + "y")) | ||||||
|     val editor = myFixture.editor.vim |     val editor = myFixture.editor.vim | ||||||
|     val lastRegister = injector.registerGroup.lastRegisterChar |     val lastRegister = injector.registerGroup.lastRegisterChar | ||||||
|     val registers = editor.carets().map { it.registerStorage.getRegister(lastRegister)?.rawText } |     val registers = editor.carets().map { it.registerStorage.getRegister(it, lastRegister)?.rawText } | ||||||
|     assertEquals( |     assertEquals( | ||||||
|       listOf("all rocks and lavender and tufted grass,\n", "hard by the torrent of a mountain pass.\n"), |       listOf("all rocks and lavender and tufted grass,\n", "hard by the torrent of a mountain pass.\n"), | ||||||
|       registers |       registers | ||||||
|   | |||||||
| @@ -121,6 +121,22 @@ n  ,f            <Plug>Foo | |||||||
|     assertState("quux\n") |     assertState("quux\n") | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   fun testddWithMapping() { | ||||||
|  |     configureByText( | ||||||
|  |       """ | ||||||
|  |       Hello$c 1 | ||||||
|  |       Hello 2 | ||||||
|  |       """.trimIndent() | ||||||
|  |     ) | ||||||
|  |     typeText(commandToKeys("nmap dc k")) | ||||||
|  |     typeText(injector.parser.parseKeys("dd")) | ||||||
|  |     assertState( | ||||||
|  |       """ | ||||||
|  |       Hello 2 | ||||||
|  |       """.trimIndent() | ||||||
|  |     ) | ||||||
|  |   } | ||||||
|  |  | ||||||
|   fun testNonRecursiveMapping() { |   fun testNonRecursiveMapping() { | ||||||
|     configureByText("\n") |     configureByText("\n") | ||||||
|     typeText(commandToKeys("inoremap a b")) |     typeText(commandToKeys("inoremap a b")) | ||||||
|   | |||||||
| @@ -38,8 +38,8 @@ class MarksCommandTest : VimTestCase() { | |||||||
|     assertState( |     assertState( | ||||||
|       """I found it in a legendary land |       """I found it in a legendary land | ||||||
|                       |all rocks and lavender and tufted grass, |                       |all rocks and lavender and tufted grass, | ||||||
|                       |${s}all rocks and lavender and tufted grass, |                       |${s}all rocks and lavender and tufted grass,$se | ||||||
|                       |${se}hard by the torrent of a mountain pass. |                       |hard by the torrent of a mountain pass. | ||||||
|                     """.trimMargin() |                     """.trimMargin() | ||||||
|     ) |     ) | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -23,7 +23,7 @@ import com.maddyhome.idea.vim.vimscript.model.expressions.Register | |||||||
| import org.jetbrains.plugins.ideavim.VimTestCase | import org.jetbrains.plugins.ideavim.VimTestCase | ||||||
| import org.jetbrains.plugins.ideavim.ex.evaluate | import org.jetbrains.plugins.ideavim.ex.evaluate | ||||||
|  |  | ||||||
| class ExpressionTest: VimTestCase() { | class ExpressionTest : VimTestCase() { | ||||||
|  |  | ||||||
|   fun `test multiline register content`() { |   fun `test multiline register content`() { | ||||||
|     configureByText("${c}Oh\nHi\nMark\n") |     configureByText("${c}Oh\nHi\nMark\n") | ||||||
|   | |||||||
| @@ -18,8 +18,8 @@ | |||||||
|  |  | ||||||
| package org.jetbrains.plugins.ideavim.helper; | package org.jetbrains.plugins.ideavim.helper; | ||||||
|  |  | ||||||
|  | import com.maddyhome.idea.vim.api.VimSearchHelperBase; | ||||||
| import com.maddyhome.idea.vim.command.VimStateMachine; | import com.maddyhome.idea.vim.command.VimStateMachine; | ||||||
| import com.maddyhome.idea.vim.helper.SearchHelper; |  | ||||||
| import com.maddyhome.idea.vim.helper.SearchHelperKtKt; | import com.maddyhome.idea.vim.helper.SearchHelperKtKt; | ||||||
| import org.jetbrains.plugins.ideavim.SkipNeovimReason; | import org.jetbrains.plugins.ideavim.SkipNeovimReason; | ||||||
| import org.jetbrains.plugins.ideavim.TestWithoutNeovim; | import org.jetbrains.plugins.ideavim.TestWithoutNeovim; | ||||||
| @@ -29,7 +29,7 @@ public class SearchHelperTest extends VimTestCase { | |||||||
|   @TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING) |   @TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING) | ||||||
|   public void testFindNextWord() { |   public void testFindNextWord() { | ||||||
|     String text = "first second"; |     String text = "first second"; | ||||||
|     int nextWordPosition = SearchHelper.findNextWord(text, 0, text.length(), 1, true, false); |     int nextWordPosition = (int)VimSearchHelperBase.Companion.findNextWord(text, 0, text.length(), 1, true, false); | ||||||
|  |  | ||||||
|     assertEquals(nextWordPosition, text.indexOf("second")); |     assertEquals(nextWordPosition, text.indexOf("second")); | ||||||
|   } |   } | ||||||
| @@ -37,7 +37,7 @@ public class SearchHelperTest extends VimTestCase { | |||||||
|   @TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING) |   @TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING) | ||||||
|   public void testFindSecondNextWord() { |   public void testFindSecondNextWord() { | ||||||
|     String text = "first second third"; |     String text = "first second third"; | ||||||
|     int nextWordPosition = SearchHelper.findNextWord(text, 0, text.length(), 2, true, false); |     int nextWordPosition = (int)VimSearchHelperBase.Companion.findNextWord(text, 0, text.length(), 2, true, false); | ||||||
|  |  | ||||||
|     assertEquals(nextWordPosition, text.indexOf("third")); |     assertEquals(nextWordPosition, text.indexOf("third")); | ||||||
|   } |   } | ||||||
| @@ -45,7 +45,7 @@ public class SearchHelperTest extends VimTestCase { | |||||||
|   @TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING) |   @TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING) | ||||||
|   public void testFindAfterLastWord() { |   public void testFindAfterLastWord() { | ||||||
|     String text = "first second"; |     String text = "first second"; | ||||||
|     int nextWordPosition = SearchHelper.findNextWord(text, 0, text.length(), 3, true, false); |     int nextWordPosition = (int)VimSearchHelperBase.Companion.findNextWord(text, 0, text.length(), 3, true, false); | ||||||
|  |  | ||||||
|     assertEquals(nextWordPosition, text.length()); |     assertEquals(nextWordPosition, text.length()); | ||||||
|   } |   } | ||||||
| @@ -53,7 +53,8 @@ public class SearchHelperTest extends VimTestCase { | |||||||
|   @TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING) |   @TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING) | ||||||
|   public void testFindPreviousWord() { |   public void testFindPreviousWord() { | ||||||
|     String text = "first second"; |     String text = "first second"; | ||||||
|     int previousWordPosition = SearchHelper.findNextWord(text, text.indexOf("second"), text.length(), -1, true, false); |     int previousWordPosition = | ||||||
|  |       (int)VimSearchHelperBase.Companion.findNextWord(text, text.indexOf("second"), text.length(), -1, true, false); | ||||||
|  |  | ||||||
|     //noinspection ConstantConditions |     //noinspection ConstantConditions | ||||||
|     assertEquals(previousWordPosition, text.indexOf("first")); |     assertEquals(previousWordPosition, text.indexOf("first")); | ||||||
| @@ -62,7 +63,8 @@ public class SearchHelperTest extends VimTestCase { | |||||||
|   @TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING) |   @TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING) | ||||||
|   public void testFindSecondPreviousWord() { |   public void testFindSecondPreviousWord() { | ||||||
|     String text = "first second third"; |     String text = "first second third"; | ||||||
|     int previousWordPosition = SearchHelper.findNextWord(text, text.indexOf("third"), text.length(), -2, true, false); |     int previousWordPosition = | ||||||
|  |       (int)VimSearchHelperBase.Companion.findNextWord(text, text.indexOf("third"), text.length(), -2, true, false); | ||||||
|  |  | ||||||
|     //noinspection ConstantConditions |     //noinspection ConstantConditions | ||||||
|     assertEquals(previousWordPosition, text.indexOf("first")); |     assertEquals(previousWordPosition, text.indexOf("first")); | ||||||
| @@ -71,7 +73,8 @@ public class SearchHelperTest extends VimTestCase { | |||||||
|   @TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING) |   @TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING) | ||||||
|   public void testFindBeforeFirstWord() { |   public void testFindBeforeFirstWord() { | ||||||
|     String text = "first second"; |     String text = "first second"; | ||||||
|     int previousWordPosition = SearchHelper.findNextWord(text, text.indexOf("second"), text.length(), -3, true, false); |     int previousWordPosition = | ||||||
|  |       (int)VimSearchHelperBase.Companion.findNextWord(text, text.indexOf("second"), text.length(), -3, true, false); | ||||||
|  |  | ||||||
|     //noinspection ConstantConditions |     //noinspection ConstantConditions | ||||||
|     assertEquals(previousWordPosition, text.indexOf("first")); |     assertEquals(previousWordPosition, text.indexOf("first")); | ||||||
| @@ -80,7 +83,8 @@ public class SearchHelperTest extends VimTestCase { | |||||||
|   @TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING) |   @TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING) | ||||||
|   public void testFindPreviousWordWhenCursorOutOfBound() { |   public void testFindPreviousWordWhenCursorOutOfBound() { | ||||||
|     String text = "first second"; |     String text = "first second"; | ||||||
|     int previousWordPosition = SearchHelper.findNextWord(text, text.length(), text.length(), -1, true, false); |     int previousWordPosition = | ||||||
|  |       (int)VimSearchHelperBase.Companion.findNextWord(text, text.length(), text.length(), -1, true, false); | ||||||
|  |  | ||||||
|     assertEquals(previousWordPosition, text.indexOf("second")); |     assertEquals(previousWordPosition, text.indexOf("second")); | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -42,19 +42,25 @@ sealed class PutTextBaseAction( | |||||||
|     operatorArguments: OperatorArguments |     operatorArguments: OperatorArguments | ||||||
|   ): Boolean { |   ): Boolean { | ||||||
|     val count = operatorArguments.count1 |     val count = operatorArguments.count1 | ||||||
|     val caretToPutData = editor.sortedCarets().associateWith { getPutDataForCaret(it, count) } |     val sortedCarets = editor.sortedCarets() | ||||||
|     var result = true |     return if (sortedCarets.size > 1) { | ||||||
|     injector.application.runWriteAction { |       val caretToPutData = sortedCarets.associateWith { getPutDataForCaret(it, count) } | ||||||
|       caretToPutData.forEach { |       var result = true | ||||||
|         result = injector.put.putTextForCaret(editor, it.key, context, it.value) && result |       injector.application.runWriteAction { | ||||||
|  |         caretToPutData.forEach { | ||||||
|  |           result = injector.put.putTextForCaret(editor, it.key, context, it.value) && result | ||||||
|  |         } | ||||||
|       } |       } | ||||||
|  |       result | ||||||
|  |     } else { | ||||||
|  |       val putData = getPutDataForCaret(sortedCarets.single(), count) | ||||||
|  |       injector.put.putText(editor, context, putData, operatorArguments) | ||||||
|     } |     } | ||||||
|     return result |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private fun getPutDataForCaret(caret: VimCaret, count: Int): PutData { |   private fun getPutDataForCaret(caret: VimCaret, count: Int): PutData { | ||||||
|     val lastRegisterChar = injector.registerGroup.lastRegisterChar |     val lastRegisterChar = injector.registerGroup.lastRegisterChar | ||||||
|     val register = caret.registerStorage.getRegister(lastRegisterChar) |     val register = caret.registerStorage.getRegister(caret, lastRegisterChar) | ||||||
|     val textData = register?.let { |     val textData = register?.let { | ||||||
|       TextData( |       TextData( | ||||||
|         register.text ?: injector.parser.toPrintableString(register.keys), |         register.text ?: injector.parser.toPrintableString(register.keys), | ||||||
|   | |||||||
| @@ -66,7 +66,7 @@ sealed class PutVisualTextBaseAction( | |||||||
|  |  | ||||||
|   private fun getPutDataForCaret(caret: VimCaret, selection: VimSelection?, count: Int): PutData { |   private fun getPutDataForCaret(caret: VimCaret, selection: VimSelection?, count: Int): PutData { | ||||||
|     val lastRegisterChar = injector.registerGroup.lastRegisterChar |     val lastRegisterChar = injector.registerGroup.lastRegisterChar | ||||||
|     val register = caret.registerStorage.getRegister(lastRegisterChar) |     val register = caret.registerStorage.getRegister(caret, lastRegisterChar) | ||||||
|     val textData = register?.let { |     val textData = register?.let { | ||||||
|       PutData.TextData( |       PutData.TextData( | ||||||
|         register.text ?: injector.parser.toPrintableString(register.keys), |         register.text ?: injector.parser.toPrintableString(register.keys), | ||||||
|   | |||||||
| @@ -22,6 +22,7 @@ interface VimCaret { | |||||||
|   val vimLeadSelectionOffset: Int |   val vimLeadSelectionOffset: Int | ||||||
|   var vimLastVisualOperatorRange: VisualChange? |   var vimLastVisualOperatorRange: VisualChange? | ||||||
|   val vimLine: Int |   val vimLine: Int | ||||||
|  |   val isPrimary: Boolean | ||||||
|   fun moveToOffset(offset: Int) |   fun moveToOffset(offset: Int) | ||||||
|   fun moveToOffsetNative(offset: Int) |   fun moveToOffsetNative(offset: Int) | ||||||
|   fun moveToLogicalPosition(logicalPosition: VimLogicalPosition) |   fun moveToLogicalPosition(logicalPosition: VimLogicalPosition) | ||||||
| @@ -43,17 +44,18 @@ interface VimCaret { | |||||||
| } | } | ||||||
|  |  | ||||||
| interface CaretRegisterStorage { | interface CaretRegisterStorage { | ||||||
|  |   // todo methods shouldn't have caret in signature | ||||||
|   /** |   /** | ||||||
|    * Stores text to caret's recordable (named/numbered/unnamed) register |    * Stores text to caret's recordable (named/numbered/unnamed) register | ||||||
|    */ |    */ | ||||||
|   fun storeText(editor: VimEditor, range: TextRange, type: SelectionType, isDelete: Boolean): Boolean |   fun storeText(caret: VimCaret, editor: VimEditor, range: TextRange, type: SelectionType, isDelete: Boolean): Boolean | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    * Gets text from caret's recordable register |    * Gets text from caret's recordable register | ||||||
|    * If the register is not recordable - global text state will be returned |    * If the register is not recordable - global text state will be returned | ||||||
|    */ |    */ | ||||||
|   fun getRegister(r: Char): Register? |   fun getRegister(caret: VimCaret, r: Char): Register? | ||||||
|  |  | ||||||
|   fun setKeys(register: Char, keys: List<KeyStroke>) |   fun setKeys(caret: VimCaret, register: Char, keys: List<KeyStroke>) | ||||||
|   fun saveRegister(r: Char, register: Register) |   fun saveRegister(caret: VimCaret, r: Char, register: Register) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -9,15 +9,15 @@ import javax.swing.KeyStroke | |||||||
|  |  | ||||||
| abstract class VimCaretBase : VimCaret | abstract class VimCaretBase : VimCaret | ||||||
|  |  | ||||||
| open class CaretRegisterStorageBase(private val isCaretPrimary: Boolean) : CaretRegisterStorage, VimRegisterGroupBase() { | open class CaretRegisterStorageBase : CaretRegisterStorage, VimRegisterGroupBase() { | ||||||
|   override var lastRegisterChar: Char |   override var lastRegisterChar: Char | ||||||
|     get() { |     get() { | ||||||
|       return injector.registerGroup.lastRegisterChar |       return injector.registerGroup.lastRegisterChar | ||||||
|     } |     } | ||||||
|     set(_) {} |     set(_) {} | ||||||
|  |  | ||||||
|   override fun storeText(editor: VimEditor, range: TextRange, type: SelectionType, isDelete: Boolean): Boolean { |   override fun storeText(caret: VimCaret, editor: VimEditor, range: TextRange, type: SelectionType, isDelete: Boolean): Boolean { | ||||||
|     if (isCaretPrimary) { |     if (caret.isPrimary) { | ||||||
|       return injector.registerGroup.storeText(editor, range, type, isDelete) |       return injector.registerGroup.storeText(editor, range, type, isDelete) | ||||||
|     } |     } | ||||||
|     val register = lastRegisterChar |     val register = lastRegisterChar | ||||||
| @@ -27,15 +27,15 @@ open class CaretRegisterStorageBase(private val isCaretPrimary: Boolean) : Caret | |||||||
|     return super.storeText(editor, range, type, isDelete) |     return super.storeText(editor, range, type, isDelete) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   override fun getRegister(r: Char): Register? { |   override fun getRegister(caret: VimCaret, r: Char): Register? { | ||||||
|     if (isCaretPrimary || !RegisterConstants.RECORDABLE_REGISTERS.contains(r)) { |     if (caret.isPrimary || !RegisterConstants.RECORDABLE_REGISTERS.contains(r)) { | ||||||
|       return injector.registerGroup.getRegister(r) |       return injector.registerGroup.getRegister(r) | ||||||
|     } |     } | ||||||
|     return super.getRegister(r) ?: injector.registerGroup.getRegister(r) |     return super.getRegister(r) ?: injector.registerGroup.getRegister(r) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   override fun setKeys(register: Char, keys: List<KeyStroke>) { |   override fun setKeys(caret: VimCaret, register: Char, keys: List<KeyStroke>) { | ||||||
|     if (isCaretPrimary) { |     if (caret.isPrimary) { | ||||||
|       injector.registerGroup.setKeys(register, keys) |       injector.registerGroup.setKeys(register, keys) | ||||||
|     } |     } | ||||||
|     if (!RegisterConstants.RECORDABLE_REGISTERS.contains(register)) { |     if (!RegisterConstants.RECORDABLE_REGISTERS.contains(register)) { | ||||||
| @@ -44,8 +44,8 @@ open class CaretRegisterStorageBase(private val isCaretPrimary: Boolean) : Caret | |||||||
|     return super.setKeys(register, keys) |     return super.setKeys(register, keys) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   override fun saveRegister(r: Char, register: Register) { |   override fun saveRegister(caret: VimCaret, r: Char, register: Register) { | ||||||
|     if (isCaretPrimary) { |     if (caret.isPrimary) { | ||||||
|       injector.registerGroup.saveRegister(r, register) |       injector.registerGroup.saveRegister(r, register) | ||||||
|     } |     } | ||||||
|     if (!RegisterConstants.RECORDABLE_REGISTERS.contains(r)) { |     if (!RegisterConstants.RECORDABLE_REGISTERS.contains(r)) { | ||||||
|   | |||||||
| @@ -158,7 +158,7 @@ abstract class VimChangeGroupBase : VimChangeGroup { | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     if (type == null || |     if (type == null || | ||||||
|       operatorArguments.mode.inInsertMode || caret.registerStorage.storeText(editor, updatedRange, type, true) || |       operatorArguments.mode.inInsertMode || caret.registerStorage.storeText(caret, editor, updatedRange, type, true) || | ||||||
|       caret != editor.primaryCaret() // sticky tape for VIM-2703 todo remove in the next release |       caret != editor.primaryCaret() // sticky tape for VIM-2703 todo remove in the next release | ||||||
|     ) { |     ) { | ||||||
|       val startOffsets = updatedRange.startOffsets |       val startOffsets = updatedRange.startOffsets | ||||||
|   | |||||||
| @@ -24,6 +24,7 @@ import com.maddyhome.idea.vim.key.KeyMapping | |||||||
| import com.maddyhome.idea.vim.key.KeyMappingLayer | import com.maddyhome.idea.vim.key.KeyMappingLayer | ||||||
| import com.maddyhome.idea.vim.key.MappingInfo | import com.maddyhome.idea.vim.key.MappingInfo | ||||||
| import com.maddyhome.idea.vim.key.MappingOwner | import com.maddyhome.idea.vim.key.MappingOwner | ||||||
|  | import com.maddyhome.idea.vim.key.OperatorFunction | ||||||
| import com.maddyhome.idea.vim.key.ShortcutOwnerInfo | import com.maddyhome.idea.vim.key.ShortcutOwnerInfo | ||||||
| import com.maddyhome.idea.vim.vimscript.model.expressions.Expression | import com.maddyhome.idea.vim.vimscript.model.expressions.Expression | ||||||
| import javax.swing.KeyStroke | import javax.swing.KeyStroke | ||||||
| @@ -73,4 +74,5 @@ interface VimKeyGroup { | |||||||
|  |  | ||||||
|   val shortcutConflicts: MutableMap<KeyStroke, ShortcutOwnerInfo> |   val shortcutConflicts: MutableMap<KeyStroke, ShortcutOwnerInfo> | ||||||
|   val savedShortcutConflicts: MutableMap<KeyStroke, ShortcutOwnerInfo> |   val savedShortcutConflicts: MutableMap<KeyStroke, ShortcutOwnerInfo> | ||||||
|  |   var operatorFunction: OperatorFunction? | ||||||
| } | } | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import com.maddyhome.idea.vim.key.KeyMapping | |||||||
| import com.maddyhome.idea.vim.key.KeyMappingLayer | import com.maddyhome.idea.vim.key.KeyMappingLayer | ||||||
| import com.maddyhome.idea.vim.key.MappingInfo | import com.maddyhome.idea.vim.key.MappingInfo | ||||||
| import com.maddyhome.idea.vim.key.MappingOwner | import com.maddyhome.idea.vim.key.MappingOwner | ||||||
|  | import com.maddyhome.idea.vim.key.OperatorFunction | ||||||
| import com.maddyhome.idea.vim.key.RequiredShortcut | import com.maddyhome.idea.vim.key.RequiredShortcut | ||||||
| import com.maddyhome.idea.vim.key.RootNode | import com.maddyhome.idea.vim.key.RootNode | ||||||
| import com.maddyhome.idea.vim.key.ShortcutOwnerInfo | import com.maddyhome.idea.vim.key.ShortcutOwnerInfo | ||||||
| @@ -24,6 +25,8 @@ abstract class VimKeyGroupBase : VimKeyGroup { | |||||||
|   val keyRoots: MutableMap<MappingMode, CommandPartNode<VimActionsInitiator>> = EnumMap(MappingMode::class.java) |   val keyRoots: MutableMap<MappingMode, CommandPartNode<VimActionsInitiator>> = EnumMap(MappingMode::class.java) | ||||||
|   val keyMappings: MutableMap<MappingMode, KeyMapping> = EnumMap(MappingMode::class.java) |   val keyMappings: MutableMap<MappingMode, KeyMapping> = EnumMap(MappingMode::class.java) | ||||||
|  |  | ||||||
|  |   override var operatorFunction: OperatorFunction? = null | ||||||
|  |  | ||||||
|   override fun removeKeyMapping(modes: Set<MappingMode>, keys: List<KeyStroke>) { |   override fun removeKeyMapping(modes: Set<MappingMode>, keys: List<KeyStroke>) { | ||||||
|     modes.map { getKeyMapping(it) }.forEach { it.delete(keys) } |     modes.map { getKeyMapping(it) }.forEach { it.delete(keys) } | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -83,7 +83,7 @@ interface VimSearchHelper { | |||||||
|     spaceWords: Boolean, |     spaceWords: Boolean, | ||||||
|   ): Int |   ): Int | ||||||
|  |  | ||||||
|   fun findNextWord(editor: VimEditor, searchFrom: Int, count: Int, bigWord: Boolean): Int |   fun findNextWord(editor: VimEditor, searchFrom: Int, count: Int, bigWord: Boolean): Long | ||||||
|  |  | ||||||
|   fun findPattern( |   fun findPattern( | ||||||
|     editor: VimEditor, |     editor: VimEditor, | ||||||
|   | |||||||
| @@ -0,0 +1,117 @@ | |||||||
|  | package com.maddyhome.idea.vim.api | ||||||
|  |  | ||||||
|  | import com.maddyhome.idea.vim.helper.CharacterHelper | ||||||
|  | import com.maddyhome.idea.vim.helper.CharacterHelper.charType | ||||||
|  | import kotlin.math.abs | ||||||
|  | import kotlin.math.min | ||||||
|  |  | ||||||
|  | abstract class VimSearchHelperBase : VimSearchHelper { | ||||||
|  |   override fun findNextWord(editor: VimEditor, searchFrom: Int, count: Int, bigWord: Boolean): Long { | ||||||
|  |     return findNextWord(editor.charsSequence(), searchFrom.toLong(), editor.fileSize(), count, bigWord, false) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   companion object { | ||||||
|  |     fun findNextWord( | ||||||
|  |       chars: CharSequence, | ||||||
|  |       pos: Long, | ||||||
|  |       size: Long, | ||||||
|  |       count: Int, | ||||||
|  |       bigWord: Boolean, | ||||||
|  |       spaceWords: Boolean, | ||||||
|  |     ): Long { | ||||||
|  |       var _count = count | ||||||
|  |       val step = if (_count >= 0) 1 else -1 | ||||||
|  |       _count = abs(_count) | ||||||
|  |       var res = pos | ||||||
|  |       for (i in 0 until _count) { | ||||||
|  |         res = findNextWordOne(chars, res, size, step, bigWord, spaceWords) | ||||||
|  |         if (res == pos || res == 0L || res == size - 1) { | ||||||
|  |           break | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       return res | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // TODO: 18.08.2022 Make private | ||||||
|  |     fun findNextWordOne( | ||||||
|  |       chars: CharSequence, | ||||||
|  |       pos: Long, | ||||||
|  |       size: Long, | ||||||
|  |       step: Int, | ||||||
|  |       bigWord: Boolean, | ||||||
|  |       spaceWords: Boolean, | ||||||
|  |     ): Long { | ||||||
|  |       var found = false | ||||||
|  |       var _pos = if (pos < size) pos else min(size, (chars.length - 1).toLong()) | ||||||
|  |       // For back searches, skip any current whitespace so we start at the end of a word | ||||||
|  |       if (step < 0 && _pos > 0) { | ||||||
|  |         if (charType(chars[_pos - 1], bigWord) === CharacterHelper.CharacterType.WHITESPACE && !spaceWords) { | ||||||
|  |           _pos = skipSpace(chars, _pos - 1, step, size) + 1 | ||||||
|  |         } | ||||||
|  |         if (_pos > 0 && charType(chars[_pos], bigWord) !== charType(chars[_pos - 1], bigWord)) { | ||||||
|  |           _pos += step | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       var res = _pos | ||||||
|  |       if (_pos < 0 || _pos >= size) { | ||||||
|  |         return _pos | ||||||
|  |       } | ||||||
|  |       var type = charType(chars[_pos], bigWord) | ||||||
|  |       if (type === CharacterHelper.CharacterType.WHITESPACE && step < 0 && _pos > 0 && !spaceWords) { | ||||||
|  |         type = charType(chars[_pos - 1], bigWord) | ||||||
|  |       } | ||||||
|  |       _pos += step | ||||||
|  |       while (_pos in 0 until size && !found) { | ||||||
|  |         val newType = charType(chars[_pos], bigWord) | ||||||
|  |         if (newType !== type) { | ||||||
|  |           if (newType === CharacterHelper.CharacterType.WHITESPACE && step >= 0 && !spaceWords) { | ||||||
|  |             _pos = skipSpace(chars, _pos, step, size) | ||||||
|  |             res = _pos | ||||||
|  |           } else if (step < 0) { | ||||||
|  |             res = _pos + 1 | ||||||
|  |           } else { | ||||||
|  |             res = _pos | ||||||
|  |           } | ||||||
|  |           type = charType(chars[res], bigWord) | ||||||
|  |           found = true | ||||||
|  |         } | ||||||
|  |         _pos += step | ||||||
|  |       } | ||||||
|  |       if (found) { | ||||||
|  |         if (res < 0) { // (pos <= 0) | ||||||
|  |           res = 0 | ||||||
|  |         } else if (res >= size) { // (pos >= size) | ||||||
|  |           res = size - 1 | ||||||
|  |         } | ||||||
|  |       } else if (_pos <= 0) { | ||||||
|  |         res = 0 | ||||||
|  |       } else if (_pos >= size) { | ||||||
|  |         res = size | ||||||
|  |       } | ||||||
|  |       return res | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Skip whitespace starting with the supplied position. | ||||||
|  |      * | ||||||
|  |      * An empty line is considered a whitespace break. | ||||||
|  |      */ | ||||||
|  |     // TODO: 18.08.2022 Make private | ||||||
|  |     fun skipSpace(chars: CharSequence, offset: Long, step: Int, size: Long): Long { | ||||||
|  |       var _offset = offset | ||||||
|  |       var prev = 0.toChar() | ||||||
|  |       while (_offset in 0 until size) { | ||||||
|  |         val c = chars[_offset.toInt()] | ||||||
|  |         if (c == '\n' && c == prev) break | ||||||
|  |         if (charType(c, false) !== CharacterHelper.CharacterType.WHITESPACE) break | ||||||
|  |         prev = c | ||||||
|  |         _offset += step | ||||||
|  |       } | ||||||
|  |       return if (_offset < size) _offset else size - 1 | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     operator fun CharSequence.get(index: Long): Char { | ||||||
|  |       return this[index.toInt()] | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -95,7 +95,7 @@ abstract class VimMachineBase : VimMachine { | |||||||
|     val operatedText = editor.deleteDryRun(range) ?: return null |     val operatedText = editor.deleteDryRun(range) ?: return null | ||||||
|  |  | ||||||
|     val normalizedRange = operatedText.toNormalizedTextRange(editor) |     val normalizedRange = operatedText.toNormalizedTextRange(editor) | ||||||
|     caret.registerStorage.storeText(editor, normalizedRange, operatedText.toType(), true) |     caret.registerStorage.storeText(caret, editor, normalizedRange, operatedText.toType(), true) | ||||||
|     (editor as MutableVimEditor).delete(range) |     (editor as MutableVimEditor).delete(range) | ||||||
|  |  | ||||||
|     val start = normalizedRange.startOffset |     val start = normalizedRange.startOffset | ||||||
|   | |||||||
| @@ -12,4 +12,5 @@ fun Int.toMotion(): Motion.AbsoluteOffset { | |||||||
| } | } | ||||||
|  |  | ||||||
| fun Int.toMotionOrError(): Motion = if (this < 0) Motion.Error else Motion.AbsoluteOffset(this) | fun Int.toMotionOrError(): Motion = if (this < 0) Motion.Error else Motion.AbsoluteOffset(this) | ||||||
|  | fun Long.toMotionOrError(): Motion = if (this < 0) Motion.Error else Motion.AbsoluteOffset(this.toInt()) | ||||||
| fun Int.toMotionOrNoMotion(): Motion = if (this < 0) Motion.NoMotion else Motion.AbsoluteOffset(this) | fun Int.toMotionOrNoMotion(): Motion = if (this < 0) Motion.NoMotion else Motion.AbsoluteOffset(this) | ||||||
|   | |||||||
| @@ -132,7 +132,7 @@ sealed class VisualOperatorActionHandler : EditorActionHandlerBase(false) { | |||||||
|     logger.debug { "Count of selection segments: ${selections.size}" } |     logger.debug { "Count of selection segments: ${selections.size}" } | ||||||
|     logger.debug { selections.values.joinToString("\n") { vimSelection -> "Caret: $vimSelection" } } |     logger.debug { selections.values.joinToString("\n") { vimSelection -> "Caret: $vimSelection" } } | ||||||
|  |  | ||||||
|     val commandWrapper = VisualStartFinishWrapper(editor, cmd) |     val commandWrapper = VisualStartFinishWrapper(editor, cmd, this) | ||||||
|     commandWrapper.start() |     commandWrapper.start() | ||||||
|  |  | ||||||
|     val res = arrayOf(true) |     val res = arrayOf(true) | ||||||
| @@ -230,7 +230,11 @@ sealed class VisualOperatorActionHandler : EditorActionHandlerBase(false) { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private class VisualStartFinishWrapper(private val editor: VimEditor, private val cmd: Command) { |   private class VisualStartFinishWrapper( | ||||||
|  |     private val editor: VimEditor, | ||||||
|  |     private val cmd: Command, | ||||||
|  |     private val visualOperatorActionHandler: VisualOperatorActionHandler | ||||||
|  |   ) { | ||||||
|     private val visualChanges = mutableMapOf<VimCaret, VisualChange?>() |     private val visualChanges = mutableMapOf<VimCaret, VisualChange?>() | ||||||
|  |  | ||||||
|     fun start() { |     fun start() { | ||||||
| @@ -256,9 +260,13 @@ sealed class VisualOperatorActionHandler : EditorActionHandlerBase(false) { | |||||||
|     fun finish(res: Boolean) { |     fun finish(res: Boolean) { | ||||||
|       logger.debug("Finish visual command. Result: $res") |       logger.debug("Finish visual command. Result: $res") | ||||||
|  |  | ||||||
|       if (CommandFlags.FLAG_MULTIKEY_UNDO !in cmd.flags && CommandFlags.FLAG_EXPECT_MORE !in cmd.flags) { |       if (visualOperatorActionHandler.id != "VimVisualOperatorAction" || | ||||||
|         logger.debug("Not multikey undo - exit visual") |         injector.keyGroup.operatorFunction?.postProcessSelection() != false | ||||||
|         editor.exitVisualModeNative() |       ) { | ||||||
|  |         if (CommandFlags.FLAG_MULTIKEY_UNDO !in cmd.flags && CommandFlags.FLAG_EXPECT_MORE !in cmd.flags) { | ||||||
|  |           logger.debug("Not multikey undo - exit visual") | ||||||
|  |           editor.exitVisualModeNative() | ||||||
|  |         } | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       if (res) { |       if (res) { | ||||||
|   | |||||||
| @@ -15,22 +15,23 @@ | |||||||
|  * You should have received a copy of the GNU General Public License |  * You should have received a copy of the GNU General Public License | ||||||
|  * along with this program. If not, see <https://www.gnu.org/licenses/>. |  * along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
|  */ |  */ | ||||||
|  | package com.maddyhome.idea.vim.key | ||||||
| 
 | 
 | ||||||
| package com.maddyhome.idea.vim.key; | import com.maddyhome.idea.vim.api.ExecutionContext | ||||||
| 
 | import com.maddyhome.idea.vim.api.VimEditor | ||||||
| import com.intellij.openapi.actionSystem.DataContext; | import com.maddyhome.idea.vim.command.SelectionType | ||||||
| import com.intellij.openapi.editor.Editor; |  | ||||||
| import com.maddyhome.idea.vim.command.SelectionType; |  | ||||||
| import org.jetbrains.annotations.NotNull; |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * @author vlan |  * @author vlan | ||||||
|  */ |  */ | ||||||
| public interface OperatorFunction { | interface OperatorFunction { | ||||||
|   /** |   /** | ||||||
|    * The value of 'operatorfunc' to be used as the operator function in 'g@'. |    * The value of 'operatorfunc' to be used as the operator function in 'g@'. | ||||||
|    * <p> |    * | ||||||
|  |    * | ||||||
|    * Make sure to synchronize your function properly using read/write actions. |    * Make sure to synchronize your function properly using read/write actions. | ||||||
|    */ |    */ | ||||||
|   boolean apply(@NotNull Editor editor, @NotNull DataContext context, @NotNull SelectionType selectionType); |   fun apply(editor: VimEditor, context: ExecutionContext, selectionType: SelectionType): Boolean | ||||||
|  | 
 | ||||||
|  |   fun postProcessSelection(): Boolean = true | ||||||
| } | } | ||||||
| @@ -21,8 +21,8 @@ interface VimPut { | |||||||
|     editor: VimEditor, |     editor: VimEditor, | ||||||
|     context: ExecutionContext, |     context: ExecutionContext, | ||||||
|     data: PutData, |     data: PutData, | ||||||
|     updateVisualMarks: Boolean = false, |  | ||||||
|     operatorArguments: OperatorArguments, |     operatorArguments: OperatorArguments, | ||||||
|  |     updateVisualMarks: Boolean = false, | ||||||
|   ): Boolean |   ): Boolean | ||||||
|  |  | ||||||
|   fun putTextForCaret(editor: VimEditor, caret: VimCaret, context: ExecutionContext, data: PutData, updateVisualMarks: Boolean = false): Boolean |   fun putTextForCaret(editor: VimEditor, caret: VimCaret, context: ExecutionContext, data: PutData, updateVisualMarks: Boolean = false): Boolean | ||||||
|   | |||||||
| @@ -25,8 +25,8 @@ abstract class VimPutBase : VimPut { | |||||||
|     editor: VimEditor, |     editor: VimEditor, | ||||||
|     context: ExecutionContext, |     context: ExecutionContext, | ||||||
|     data: PutData, |     data: PutData, | ||||||
|     updateVisualMarks: Boolean, |  | ||||||
|     operatorArguments: OperatorArguments, |     operatorArguments: OperatorArguments, | ||||||
|  |     updateVisualMarks: Boolean, | ||||||
|   ): Boolean { |   ): Boolean { | ||||||
|     val additionalData = collectPreModificationData(editor, data) |     val additionalData = collectPreModificationData(editor, data) | ||||||
|     deleteSelectedText(editor, data, operatorArguments) |     deleteSelectedText(editor, data, operatorArguments) | ||||||
| @@ -67,15 +67,18 @@ abstract class VimPutBase : VimPut { | |||||||
|     val caretsAndSelections = data.visualSelection?.caretsAndSelections ?: return |     val caretsAndSelections = data.visualSelection?.caretsAndSelections ?: return | ||||||
|     val selection = caretsAndSelections[currentCaret] ?: caretsAndSelections.firstOrNull()?.value ?: return |     val selection = caretsAndSelections[currentCaret] ?: caretsAndSelections.firstOrNull()?.value ?: return | ||||||
|  |  | ||||||
|     var fistIndex = selection.vimStart |     val leftIndex = min(selection.vimStart, selection.vimEnd) | ||||||
|     val lastIndex = fistIndex + textLength - 1 |     val rightIndex = leftIndex + textLength - 1 | ||||||
|  |  | ||||||
|     if (wasTextInsertedLineWise(text)) { |     val rangeForMarks = if (wasTextInsertedLineWise(text)) { | ||||||
|       // here we skip the \n char before the inserted text |       // here we skip the \n char after the inserted text | ||||||
|       fistIndex += 1 |       TextRange(leftIndex, rightIndex - 1) | ||||||
|  |     } else { | ||||||
|  |       TextRange(leftIndex, rightIndex) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     injector.markGroup.setVisualSelectionMarks(editor, TextRange(fistIndex, lastIndex)) |     editor.vimLastSelectionType = SelectionType.CHARACTER_WISE | ||||||
|  |     injector.markGroup.setVisualSelectionMarks(editor, rangeForMarks) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   protected fun deleteSelectedText(editor: VimEditor, data: PutData, operatorArguments: OperatorArguments) { |   protected fun deleteSelectedText(editor: VimEditor, data: PutData, operatorArguments: OperatorArguments) { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user