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

Compare commits

...

5 Commits

12 changed files with 229 additions and 58 deletions

View File

@ -11,7 +11,7 @@
ideaVersion=2023.3.2
downloadIdeaSources=true
instrumentPluginCode=true
version=chylex-1
version=chylex-24
javaVersion=17
remoteRobotVersion=0.11.21
antlrVersion=4.10.1

View File

@ -78,7 +78,6 @@ import java.math.BigInteger
import java.util.*
import java.util.function.Consumer
import kotlin.math.max
import kotlin.math.min
/**
* Provides all the insert/replace related functionality
@ -395,6 +394,7 @@ public class ChangeGroup : VimChangeGroupBase() {
context: ExecutionContext,
range: TextRange,
) {
val startPos = editor.offsetToBufferPosition(caret.offset.point)
val startOffset = editor.getLineStartForOffset(range.startOffset)
val endOffset = editor.getLineEndForOffset(range.endOffset)
val ijEditor = (editor as IjVimEditor).editor
@ -419,11 +419,7 @@ public class ChangeGroup : VimChangeGroupBase() {
}
}
val afterAction = {
val firstLine = editor.offsetToBufferPosition(
min(startOffset.toDouble(), endOffset.toDouble()).toInt()
).line
val newOffset = injector.motion.moveCaretToLineStartSkipLeading(editor, firstLine)
caret.moveToOffset(newOffset)
caret.moveToOffset(injector.motion.moveCaretToLineStartSkipLeading(editor, startPos.line))
restoreCursor(editor, caret, (caret as IjVimCaret).caret.logicalPosition.line)
}
if (project != null) {

View File

@ -9,6 +9,7 @@
package com.maddyhome.idea.vim.helper;
import com.google.common.collect.Lists;
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerEx;
import com.intellij.lang.CodeDocumentationAwareCommenter;
import com.intellij.lang.Commenter;
import com.intellij.lang.Language;
@ -16,15 +17,15 @@ import com.intellij.lang.LanguageCommenters;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Caret;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiComment;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.spellchecker.SpellCheckerSeveritiesProvider;
import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.api.EngineEditorHelperKt;
import com.maddyhome.idea.vim.api.VimEditor;
import com.maddyhome.idea.vim.state.mode.Mode;
import com.maddyhome.idea.vim.state.VimStateMachine;
import com.maddyhome.idea.vim.common.CharacterPosition;
import com.maddyhome.idea.vim.common.Direction;
import com.maddyhome.idea.vim.common.TextRange;
@ -32,6 +33,12 @@ import com.maddyhome.idea.vim.newapi.IjVimCaret;
import com.maddyhome.idea.vim.newapi.IjVimEditor;
import com.maddyhome.idea.vim.regexp.CharPointer;
import com.maddyhome.idea.vim.regexp.RegExp;
import com.maddyhome.idea.vim.state.VimStateMachine;
import com.maddyhome.idea.vim.state.mode.Mode;
import it.unimi.dsi.fastutil.ints.IntComparator;
import it.unimi.dsi.fastutil.ints.IntIterator;
import it.unimi.dsi.fastutil.ints.IntRBTreeSet;
import it.unimi.dsi.fastutil.ints.IntSortedSet;
import kotlin.Pair;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
@ -1523,6 +1530,42 @@ public class SearchHelper {
return PsiHelper.findMethodEnd(editor, caret.getOffset(), count);
}
public static int findMisspelledWords(@NotNull Editor editor,
int startOffset,
int endOffset,
int skipCount,
IntComparator offsetOrdering) {
Project project = editor.getProject();
if (project == null) {
return -1;
}
IntSortedSet offsets = new IntRBTreeSet(offsetOrdering);
DaemonCodeAnalyzerEx.processHighlights(editor.getDocument(), project, SpellCheckerSeveritiesProvider.TYPO,
startOffset, endOffset, highlight -> {
if (highlight.getSeverity() == SpellCheckerSeveritiesProvider.TYPO) {
int offset = highlight.getStartOffset();
if (offset >= startOffset && offset <= endOffset) {
offsets.add(offset);
}
}
return true;
});
if (offsets.isEmpty()) {
return -1;
}
if (skipCount >= offsets.size()) {
return offsets.lastInt();
}
else {
IntIterator offsetIterator = offsets.iterator();
offsetIterator.skip(skipCount);
return offsetIterator.nextInt();
}
}
private static @NotNull String parseMatchPairsOption(final VimEditor vimEditor) {
List<String> pairs = options(injector, vimEditor).getMatchpairs();
StringBuilder res = new StringBuilder();

View File

@ -95,41 +95,43 @@ internal object IdeaSpecifics {
if (VimPlugin.isNotEnabled()) return
val editor = editor
if (editor != null && action is ChooseItemAction && editor.vimStateMachine?.isRecording == true) {
val prevDocumentLength = completionPrevDocumentLength
val prevDocumentOffset = completionPrevDocumentOffset
if (editor != null) {
if (action is ChooseItemAction && editor.vimStateMachine?.isRecording == true) {
val prevDocumentLength = completionPrevDocumentLength
val prevDocumentOffset = completionPrevDocumentOffset
if (prevDocumentLength != null && prevDocumentOffset != null) {
val register = VimPlugin.getRegister()
val addedTextLength = editor.document.textLength - prevDocumentLength
val caretShift = addedTextLength - (editor.caretModel.primaryCaret.offset - prevDocumentOffset)
val leftArrow = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0)
if (prevDocumentLength != null && prevDocumentOffset != null) {
val register = VimPlugin.getRegister()
val addedTextLength = editor.document.textLength - prevDocumentLength
val caretShift = addedTextLength - (editor.caretModel.primaryCaret.offset - prevDocumentOffset)
val leftArrow = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0)
register.recordText(editor.document.getText(TextRange(prevDocumentOffset, prevDocumentOffset + addedTextLength)))
repeat(caretShift.coerceAtLeast(0)) {
register.recordKeyStroke(leftArrow)
register.recordText(editor.document.getText(TextRange(prevDocumentOffset, prevDocumentOffset + addedTextLength)))
repeat(caretShift.coerceAtLeast(0)) {
register.recordKeyStroke(leftArrow)
}
}
}
this.completionPrevDocumentLength = null
this.completionPrevDocumentOffset = null
}
//region Enter insert mode after surround with if
if (surrounderAction == action.javaClass.name && surrounderItems.any {
action.templatePresentation.text.endsWith(
it,
)
this.completionPrevDocumentLength = null
this.completionPrevDocumentOffset = null
}
) {
editor?.let {
val commandState = it.vim.vimStateMachine
//region Enter insert mode after surround with if
if (surrounderAction == action.javaClass.name && surrounderItems.any {
action.templatePresentation.text.endsWith(
it,
)
}
) {
val commandState = editor.vim.vimStateMachine
commandState.mode = Mode.NORMAL()
VimPlugin.getChange().insertBeforeCursor(it.vim, event.dataContext.vim)
KeyHandler.getInstance().reset(it.vim)
VimPlugin.getChange().insertBeforeCursor(editor.vim, event.dataContext.vim)
KeyHandler.getInstance().reset(editor.vim)
}
//endregion
injector.scroll.scrollCaretIntoView(editor.vim)
}
//endregion
this.editor = null
}

View File

@ -15,6 +15,8 @@ import com.maddyhome.idea.vim.api.VimSearchHelperBase
import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.helper.SearchHelper
import com.maddyhome.idea.vim.helper.SearchOptions
import it.unimi.dsi.fastutil.ints.IntComparator
import it.unimi.dsi.fastutil.ints.IntComparators
import java.util.*
@Service
@ -93,4 +95,26 @@ internal class IjVimSearchHelper : VimSearchHelperBase() {
): TextRange? {
return SearchHelper.findBlockRange(editor.ij, caret.ij, type, count, isOuter)
}
override fun findMisspelledWord(editor: VimEditor, caret: ImmutableVimCaret, count: Int): Int {
val startOffset: Int
val endOffset: Int
val skipCount: Int
val offsetOrdering: IntComparator
if (count < 0) {
startOffset = 0
endOffset = caret.offset.point - 1
skipCount = -count - 1
offsetOrdering = IntComparators.OPPOSITE_COMPARATOR
}
else {
startOffset = caret.offset.point + 1
endOffset = editor.ij.document.textLength
skipCount = count - 1
offsetOrdering = IntComparators.NATURAL_COMPARATOR
}
return SearchHelper.findMisspelledWords(editor.ij, startOffset, endOffset, skipCount, offsetOrdering)
}
}

View File

@ -333,7 +333,7 @@
* |[m| {@link com.maddyhome.idea.vim.action.motion.text.MotionMethodPreviousStartAction}
* |[p| {@link com.maddyhome.idea.vim.action.copy.PutVisualTextAfterCursorNoIndentAction}
* |[p| {@link com.maddyhome.idea.vim.action.copy.PutTextAfterCursorNoIndentAction}
* |[s| TO BE IMPLEMENTED
* |[s| {@link com.maddyhome.idea.vim.action.motion.text.MotionMisspelledWordPreviousAction}
* |[z| TO BE IMPLEMENTED
* |[{| {@link com.maddyhome.idea.vim.action.motion.text.MotionUnmatchedBraceOpenAction}
* |]_CTRL-D| TO BE IMPLEMENTED
@ -358,7 +358,7 @@
* |]m| {@link com.maddyhome.idea.vim.action.motion.text.MotionMethodNextStartAction}
* |]p| {@link com.maddyhome.idea.vim.action.copy.PutVisualTextAfterCursorNoIndentAction}
* |]p| {@link com.maddyhome.idea.vim.action.copy.PutTextAfterCursorNoIndentAction}
* |]s| TO BE IMPLEMENTED
* |]s| {@link com.maddyhome.idea.vim.action.motion.text.MotionMisspelledWordNextAction}
* |]z| TO BE IMPLEMENTED
* |]}| {@link com.maddyhome.idea.vim.action.motion.text.MotionUnmatchedBraceCloseAction}
*

View File

@ -94,6 +94,8 @@
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionMethodPreviousStartAction" mappingModes="NXO" keys="[m"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionMethodNextEndAction" mappingModes="NXO" keys="]M"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionMethodNextStartAction" mappingModes="NXO" keys="]m"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionMisspelledWordPreviousAction" mappingModes="NXO" keys="[s"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionMisspelledWordNextAction" mappingModes="NXO" keys="]s"/>
<!-- Text Objects -->
<vimAction implementation="com.maddyhome.idea.vim.action.motion.object.MotionOuterWordAction" mappingModes="XO" keys="aw"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.object.MotionOuterBigWordAction" mappingModes="XO" keys="aW"/>

View File

@ -448,6 +448,11 @@ abstract class VimTestCase {
return NeovimTesting.getMark(char)
}
protected fun assertRegister(char: Char, expected: String?) {
val actual = injector.registerGroup.getRegister(char)?.keys?.let(injector.parser::toKeyNotation)
assertEquals(expected, actual, "Wrong register contents")
}
protected fun assertState(modeAfter: Mode) {
assertMode(modeAfter)
assertCaretsVisualAttributes()

View File

@ -47,10 +47,7 @@ class MacroActionTest : VimTestCase() {
val editor = typeTextInFile(injector.parser.parseKeys("qa" + "3l" + "q"), "on<caret>e two three\n")
val commandState = editor.vim.vimStateMachine
kotlin.test.assertFalse(commandState.isRecording)
val registerGroup = VimPlugin.getRegister()
val register = registerGroup.getRegister('a')
assertNotNull<Any>(register)
kotlin.test.assertEquals("3l", register.text)
assertRegister('a', "3l")
}
@Test
@ -58,9 +55,7 @@ class MacroActionTest : VimTestCase() {
configureByText("")
enterCommand("imap pp hello")
typeText(injector.parser.parseKeys("qa" + "i" + "pp<Esc>" + "q"))
val register = VimPlugin.getRegister().getRegister('a')
assertNotNull<Any>(register)
kotlin.test.assertEquals("ipp<Esc>", injector.parser.toKeyNotation(register.keys))
assertRegister('a', "ipp<Esc>")
}
@Test
@ -68,7 +63,7 @@ class MacroActionTest : VimTestCase() {
typeTextInFile(injector.parser.parseKeys("qa" + "i" + "<C-K>OK<Esc>" + "q"), "")
val register = VimPlugin.getRegister().getRegister('a')
assertNotNull<Any>(register)
kotlin.test.assertEquals("i<C-K>OK<Esc>", injector.parser.toKeyNotation(register.keys))
assertRegister('a', "i<C-K>OK<Esc>")
}
@Test
@ -141,8 +136,8 @@ class MacroActionTest : VimTestCase() {
assertState("4\n5\n")
}
// Broken, see the resulting text
fun `ignore test macro with macro`() {
@Test
fun `test macro with macro`() {
val content = """
Lorem Ipsum
@ -152,16 +147,55 @@ class MacroActionTest : VimTestCase() {
Cras id tellus in ex imperdiet egestas.
""".trimIndent()
configureByText(content)
typeText(injector.parser.parseKeys("qa" + "l" + "q" + "qb" + "10@a" + "q" + "2@b"))
typeText(
injector.parser.parseKeys(
"qa" + "l" + "q" +
"qb" + "6@a" + "q" +
"^" + "3@b"
)
)
val startOffset = content.rangeOf("rocks").startOffset
assertRegister('b', "6@a")
assertState("""
Lorem Ipsum
waitAndAssert {
println(fixture.editor.caretModel.offset)
println(startOffset)
println()
startOffset == fixture.editor.caretModel.offset
}
Lorem ipsum dolor ${c}sit amet,
consectetur adipiscing elit
Sed in orci mauris.
Cras id tellus in ex imperdiet egestas.
""".trimIndent())
}
@Test
fun `test macro with macro with macro`() {
val content = """
Lorem Ipsum
${c}Lorem ipsum dolor sit amet,
consectetur adipiscing elit
Sed in orci mauris.
Cras id tellus in ex imperdiet egestas.
""".trimIndent()
configureByText(content)
typeText(
injector.parser.parseKeys(
"qa" + "l" + "q" +
"qb" + "3@a" + "q" +
"qc" + "2@b" + "q" +
"^" + "3@c"
)
)
assertRegister('b', "3@a")
assertRegister('c', "2@b")
assertState("""
Lorem Ipsum
Lorem ipsum dolor ${c}sit amet,
consectetur adipiscing elit
Sed in orci mauris.
Cras id tellus in ex imperdiet egestas.
""".trimIndent())
}
@Test

View File

@ -102,8 +102,9 @@ public class KeyHandler {
// If this is a "regular" character keystroke, get the character
val chKey: Char = if (key.keyChar == KeyEvent.CHAR_UNDEFINED) 0.toChar() else key.keyChar
// We only record unmapped keystrokes. If we've recursed to handle mapping, don't record anything.
var shouldRecord = handleKeyRecursionCount == 0 && editorState.isRecording
// We only record unmapped keystrokes.
// If we've recursed to handle mapping, or executing a macro, don't record anything.
var shouldRecord = handleKeyRecursionCount == 0 && editorState.isRecording && !injector.macro.isExecutingMacro
handleKeyRecursionCount++
try {
LOG.trace("Start key processing...")

View File

@ -0,0 +1,58 @@
/*
* Copyright 2003-2023 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/
package com.maddyhome.idea.vim.action.motion.text
import com.intellij.vim.annotations.CommandOrMotion
import com.intellij.vim.annotations.Mode
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.ImmutableVimCaret
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.CommandFlags
import com.maddyhome.idea.vim.command.MotionType
import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.handler.Motion
import com.maddyhome.idea.vim.handler.MotionActionHandler
import com.maddyhome.idea.vim.handler.toMotionOrError
import com.maddyhome.idea.vim.helper.enumSetOf
import java.util.*
@CommandOrMotion(keys = ["]s"], modes = [Mode.NORMAL, Mode.VISUAL, Mode.OP_PENDING])
public class MotionMisspelledWordNextAction : MotionActionHandler.ForEachCaret() {
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_SAVE_JUMP)
override fun getOffset(
editor: VimEditor,
caret: ImmutableVimCaret,
context: ExecutionContext,
argument: Argument?,
operatorArguments: OperatorArguments,
): Motion {
return injector.searchHelper.findMisspelledWord(editor, caret, operatorArguments.count1).toMotionOrError()
}
override val motionType: MotionType = MotionType.EXCLUSIVE
}
@CommandOrMotion(keys = ["[s"], modes = [Mode.NORMAL, Mode.VISUAL, Mode.OP_PENDING])
public class MotionMisspelledWordPreviousAction : MotionActionHandler.ForEachCaret() {
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_SAVE_JUMP)
override fun getOffset(
editor: VimEditor,
caret: ImmutableVimCaret,
context: ExecutionContext,
argument: Argument?,
operatorArguments: OperatorArguments,
): Motion {
return injector.searchHelper.findMisspelledWord(editor, caret, -operatorArguments.count1).toMotionOrError()
}
override val motionType: MotionType = MotionType.EXCLUSIVE
}

View File

@ -197,4 +197,10 @@ public interface VimSearchHelper {
count: Int,
isOuter: Boolean,
): TextRange?
public fun findMisspelledWord(
editor: VimEditor,
caret: ImmutableVimCaret,
count: Int,
): Int
}