diff --git a/src/test/java/org/jetbrains/plugins/ideavim/regex/VimRegexEngineTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/regex/VimRegexEngineTest.kt new file mode 100644 index 000000000..6500dd3cc --- /dev/null +++ b/src/test/java/org/jetbrains/plugins/ideavim/regex/VimRegexEngineTest.kt @@ -0,0 +1,120 @@ +/* + * 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 org.jetbrains.plugins.ideavim.regex + +import com.maddyhome.idea.vim.api.injector +import com.maddyhome.idea.vim.common.TextRange +import com.maddyhome.idea.vim.mark.VimMark +import com.maddyhome.idea.vim.newapi.vim +import com.maddyhome.idea.vim.regexp.VimRegex +import org.jetbrains.plugins.ideavim.VimTestCase +import org.junit.jupiter.api.Test +import kotlin.test.assertEquals + +class VimRegexEngineTest : VimTestCase() { + private fun findAll(pattern: String): List<TextRange> { + val regex = VimRegex(pattern) + return regex.findAll(fixture.editor.vim).map { it.range } + } + + @Test + fun `test end of word at middle of text`() { + configureByText("Lorem Ipsum") + val result = findAll("Lorem\\>") + assertEquals(result, listOf(TextRange(0, 5))) + } + + @Test + fun `test end of word should fail`() { + configureByText("Lorem Ipsum") + val result = findAll("Lo\\>rem") + assertEquals(result, emptyList()) + } + + @Test + fun `test start of word at offset`() { + configureByText("Lorem Ipsum") + val result = findAll("\\<Ipsum") + assertEquals(result, listOf(TextRange(6, 11))) + } + + @Test + fun `test start of word should fail`() { + configureByText("Lorem Ipsum") + val result = findAll("Lo\\<rem") + assertEquals(result, emptyList()) + } + + @Test + fun `test end of word at end of text`() { + configureByText("Lorem Ipsum") + val result = findAll("Ipsum\\>") + assertEquals(result, listOf(TextRange(6, 11))) + } + + @Test + fun `test start of word at start of text`() { + configureByText("Lorem Ipsum") + val result = findAll("\\<Lorem") + assertEquals(result, listOf(TextRange(0, 5))) + } + + @Test + fun `test cursor and mark belong to the same cursor`() { + /* + In this test, there are two cursors, one at offset 3 and the other at 6. + The second cursor (at offset 6) has a mark 'm' at offset 0. + The pattern reads as "match the character at the cursor position that is after a mark 'm'". + Since the cursor and mark tokens have to "belong" to the same cursor, the resulting match + is at offset 6 (the offset of the second cursor), even though the first cursor appears first + in the text. + */ + configureByText("Lor${c}em ${c}Ipsum") + val editor = fixture.editor.vim + val mark = VimMark.create('m', 0, 0, editor.getPath(), editor.extractProtocol())!! + val secondCaret = editor.carets().maxByOrNull { it.offset.point }!! + secondCaret.markStorage.setMark(mark) + + val result = findAll("\\%>'m\\%#.") + assertEquals(result, listOf(TextRange(6, 7))) + } + + @Test + fun `test text at mark position`() { + configureByText("Lorem Ipsum") + val editor = fixture.editor.vim + val mark = VimMark.create('m', 0, 5, editor.getPath(), editor.extractProtocol())!! + injector.markService.setMark(editor.primaryCaret(), mark) + + val result = findAll("\\%'m...") + assertEquals(result, listOf(TextRange(5, 8))) + } + + @Test + fun `test text before mark position`() { + configureByText("Lorem Ipsum") + val editor = fixture.editor.vim + val mark = VimMark.create('m', 0, 5, editor.getPath(), editor.extractProtocol())!! + injector.markService.setMark(editor.primaryCaret(), mark) + + val result = findAll("\\%<'m...") + assertEquals(result, listOf(TextRange(0, 3), TextRange(3, 6))) + } + + @Test + fun `test text after mark position`() { + configureByText("Lorem Ipsum") + val editor = fixture.editor.vim + val mark = VimMark.create('m', 0, 5, editor.getPath(), editor.extractProtocol())!! + injector.markService.setMark(editor.primaryCaret(), mark) + + val result = findAll("\\%>'m...") + assertEquals(result, listOf(TextRange(6, 9))) + } +} \ No newline at end of file diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/regexp/engine/nfa/matcher/EndOfWordMatcher.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/regexp/engine/nfa/matcher/EndOfWordMatcher.kt index 4a33bcc4b..57ebd835c 100644 --- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/regexp/engine/nfa/matcher/EndOfWordMatcher.kt +++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/regexp/engine/nfa/matcher/EndOfWordMatcher.kt @@ -26,7 +26,7 @@ internal class EndOfWordMatcher : Matcher { ): MatcherResult { if (index > editor.text().length || index == 0) return MatcherResult.Failure - val isKeywordAtIndex = KeywordOptionHelper.isKeyword(editor, editor.text()[index]) + val isKeywordAtIndex = editor.text().getOrNull(index)?.let { KeywordOptionHelper.isKeyword(editor, it) } ?: false val isKeywordBeforeIndex = editor.text().getOrNull(index - 1)?.let { KeywordOptionHelper.isKeyword(editor, it) } ?: false return if (isKeywordBeforeIndex && !isKeywordAtIndex) MatcherResult.Success(0) else MatcherResult.Failure diff --git a/vim-engine/src/test/kotlin/com/maddyhome/idea/vim/regexp/internal/VimRegexEngineTest.kt b/vim-engine/src/test/kotlin/com/maddyhome/idea/vim/regexp/internal/VimRegexEngineTest.kt index be2447bf3..29349414e 100644 --- a/vim-engine/src/test/kotlin/com/maddyhome/idea/vim/regexp/internal/VimRegexEngineTest.kt +++ b/vim-engine/src/test/kotlin/com/maddyhome/idea/vim/regexp/internal/VimRegexEngineTest.kt @@ -839,54 +839,6 @@ class VimRegexEngineTest { ) } - @Test - fun `test start of word at start of text`() { - doTest( - "${START}Lorem$END Ipsum", - "\\<Lorem", - ) - } - - @Test - fun `test start of word at offset`() { - doTest( - "Lorem ${START}Ipsum$END", - "\\<Ipsum", - ) - } - - @Test - fun `test start of word should fail`() { - assertFailure( - "Lorem Ipsum", - "Lo\\<rem" - ) - } - - @Test - fun `test end of word at end of text`() { - doTest( - "Lorem ${START}Ipsum$END", - "Ipsum\\>", - ) - } - - @Test - fun `test end of word at middle of text`() { - doTest( - "${START}Lorem$END Ipsum", - "Lorem\\>", - ) - } - - @Test - fun `test end of word should fail`() { - assertFailure( - "Lorem Ipsum", - "Lo\\>rem" - ) - } - @Test fun `test collection with EOL`() { doTest( @@ -1678,29 +1630,7 @@ class VimRegexEngineTest { ) } - @Test - fun `test text at mark position`() { - doTest( - "Lorem ${START}${MARK('m')}Ips${END}um", - "\\%'m..." - ) - } - @Test - fun `test text before mark position`() { - doTest( - "${START}Lor${END}em ${MARK('m')}Ipsum", - "\\%<'m..." - ) - } - - @Test - fun `test text after mark position`() { - doTest( - "Lorem ${MARK('m')}I${START}psu${END}m", - "\\%>'m..." - ) - } @Test fun `test mark does not exist`() { @@ -1710,26 +1640,6 @@ class VimRegexEngineTest { ) } - @Test - fun `test cursor and mark belong to the same cursor`() { - /* - In this test, there are two cursors, one at offset 3 and the other at 6. - The second cursor (at offset 6) has a mark 'm' at offset 0. - The pattern reads as "match the character at the cursor position that is after a mark 'm'". - Since the cursor and mark tokens have to "belong" to the same cursor, the resulting match - is at offset 6 (the offset of the second cursor), even though the first cursor appears first - in the text. - */ - doTest( - "Lorem ${START}I${END}psum", - "\\%>'m\\%#.", - listOf( - mockCaret(3), - mockCaret(6, marks = mapOf(Pair('m', BufferPosition(0, 0)))) - ) - ) - } - @Test fun `test cursor and visual belong to the same cursor`() { doTest(