1
0
mirror of https://github.com/chylex/IntelliJ-IdeaVim.git synced 2024-10-18 01:42:46 +02:00

Compare commits

..

46 Commits

Author SHA1 Message Date
Alex Plate
85a1fbe89e
Update links to github accounts 2022-10-11 09:23:07 +03:00
Alex Plate
142550a1f8
Use 2022.2.2 version of IJ by default 2022-10-04 09:48:35 +03:00
Alex Plate
e3d3b73903
Fix incompatiility with futur versions of IJ 2022-10-04 09:48:18 +03:00
Alex Plate
45f18ff91c Update formatting 2022-10-02 00:26:29 +00:00
Alex Pláte
2103163207
Update AUTHORS information 2022-09-29 09:10:19 +03:00
filipp
19dd49670c Statistic 2022-09-26 11:51:56 +03:00
Alex Plate
e738a1a821 Update formatting 2022-09-25 00:20:49 +00:00
filipp
6e0f301fb8 Fix(VIM-2757) Wrong gv behavior 2022-09-22 04:08:39 +03:00
filipp
c76b8db293 Fix(VIM-2718) Copied value is not inserted 2022-09-21 03:00:53 +03:00
Alex Plate
9fa4ca8fb3 Update formatting 2022-09-18 00:20:25 +00:00
Alex Plate
871b60fe8d
Revert "[New Typing Handler]: Switch j command to new typing handler"
This reverts commit 43a79dba
2022-09-12 09:30:22 +03:00
Alex Plate
6715a5b61f
Revert "[New Typing Handler]: Support d"
This reverts commit c32c62ea
2022-09-12 09:29:46 +03:00
Alex Plate
d7d91f1cc5
Add some tests for dd 2022-09-12 09:28:27 +03:00
Alex Plate
9f00dbd6f4 Update formatting 2022-09-11 00:19:01 +00:00
Alex Plate
f95cf3d671 Update changelog 2022-09-10 21:26:01 +00:00
filipp
7fbc17624f Fix(VIM-2718): Fixed case where the primary caret was changed 2022-09-11 00:24:06 +03:00
Alex Plate
b9c2ea37cb Update changelog 2022-09-09 14:21:51 +00:00
Alex Plate
ca0db15e01
Fix(VIM-2749): Fix :tabn and :tabN commands 2022-09-09 17:20:09 +03:00
Alex Plate
c32c62eacc
[New Typing Handler]: Support d 2022-09-06 13:13:31 +03:00
Alex Plate
43a79dbad4
[New Typing Handler]: Switch j command to new typing handler 2022-09-05 15:22:10 +03:00
Alex Plate
2829a13187
Update gradle plugin 2022-09-05 12:32:08 +03:00
Alex Plate
efc8c9207d Update changelog 2022-08-29 06:37:12 +00:00
Alex Plate
183ed10592
Fix(VIM-2744): Fix undo from ex line 2022-08-29 09:31:55 +03:00
Alex Plate
926b47a31e
Update gradle plugin 2022-08-29 09:31:34 +03:00
Alex Plate
d272c919ea
Fill missing authors 2022-08-25 11:59:16 +03:00
Alex Plate
f6e7d04fd5
Fix accessing bookmarks
Tests were failing because IdeaVim group is created by default by project name
2022-08-25 11:59:05 +03:00
Alex Plate
ccdff4f087
TC: Fix missing import 2022-08-25 11:23:22 +03:00
Alex Plate
ff14303e88
TC: add tests for stable version of idea 2022-08-25 11:12:50 +03:00
Alex Plate
48a592340b
Update test fixture 2022-08-23 15:27:00 +03:00
Alex Plate
da8f5f3231
Fix handler call from put command 2022-08-23 15:26:58 +03:00
Alex Plate
f8fa8b73fa
Move some methods to vim engine 2022-08-23 15:26:55 +03:00
Alex Plate
aee126b625
Formatting 2022-08-23 15:26:53 +03:00
Alex Plate
396ac86939
Fix strange issue on ideavimrc reload 2022-08-23 15:26:50 +03:00
Alex Plate
81816f903f Update formatting 2022-08-21 00:17:52 +00:00
Alex Plate
06a85b784b Update changelog 2022-08-17 07:14:42 +00:00
Alex Plate
7f1e3bb155
Fix(VIM-1758): Commentary plugin in rider 2022-08-17 10:13:04 +03:00
Alex Plate
241f554133
Fix(VIM-1903): Autoindent now works in rider 2022-08-17 10:11:19 +03:00
Alex Plate
9498d0779c
Revert "Refactoring IdeaVim to use editor actions instead of registering shortcuts in the editor"
This reverts commit b12fd5100f.
2022-08-15 18:59:35 +03:00
Alex Plate
b12fd5100f
Refactoring IdeaVim to use editor actions instead of registering shortcuts in the editor 2022-08-15 18:42:32 +03:00
Alex Plate
92f622430d
Revert "Add a hidden gem"
This reverts commit 362b9a5c3a.
2022-08-15 17:22:31 +03:00
Alex Plate
ef518f5b23
Revert "Rename hidden gem to neovim"
This reverts commit 4fd1a25557.
2022-08-15 17:22:31 +03:00
Alex Plate
7acb17ebdb
Small update 2022-08-15 17:22:31 +03:00
Alex Plate
479a7dbbaf Update formatting 2022-08-14 00:17:52 +00:00
Alex Plate
4fd1a25557
Rename hidden gem to neovim 2022-08-12 12:14:17 +03:00
Alex Plate
f32d42e625
Nice knowledge popup 2022-08-12 12:10:57 +03:00
Alex Plate
362b9a5c3a
Add a hidden gem 2022-08-12 10:49:55 +03:00
53 changed files with 669 additions and 574 deletions

View File

@ -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)

View File

@ -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")

View File

@ -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)
   

View File

@ -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:

View File

@ -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(

View File

@ -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

View File

@ -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
} }

View File

@ -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 */

View File

@ -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

View File

@ -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) {

View File

@ -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)
} }

View File

@ -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)
} }
} }
} }

View File

@ -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

View File

@ -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");

View File

@ -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()) {

View File

@ -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();
} }

View File

@ -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

View File

@ -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)

View File

@ -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
} }

View File

@ -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
* *

View File

@ -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

View File

@ -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

View File

@ -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?,

View File

@ -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)
} }
} }

View File

@ -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")

View File

@ -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()
} }
} }

View File

@ -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
} }

View File

@ -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,

View File

@ -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")
}
} }

View File

@ -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)
// } // }
//} // }

View File

@ -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"),

View File

@ -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())
)
}
}
} }

View File

@ -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

View File

@ -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"))

View File

@ -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()
) )
} }

View File

@ -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")

View File

@ -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"));
} }

View File

@ -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),

View File

@ -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),

View File

@ -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)
} }

View File

@ -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)) {

View File

@ -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

View File

@ -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?
} }

View File

@ -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) }
} }

View File

@ -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,

View File

@ -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()]
}
}
}

View File

@ -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

View File

@ -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)

View File

@ -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) {

View File

@ -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
} }

View File

@ -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

View File

@ -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) {