1
0
mirror of https://github.com/chylex/IntelliJ-IdeaVim.git synced 2025-05-08 06:34:08 +02:00

Reduce multiple implementations to one

This commit is contained in:
Matt Ellis 2025-02-13 00:13:18 +00:00 committed by Alex Pláte
parent c4f56c40fd
commit 43a2bd98db
5 changed files with 68 additions and 124 deletions
src/main/java/com/maddyhome/idea/vim
vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api

View File

@ -34,7 +34,6 @@ import com.maddyhome.idea.vim.helper.SearchOptions
import com.maddyhome.idea.vim.helper.endOffsetInclusive
import com.maddyhome.idea.vim.helper.enumSetOf
import com.maddyhome.idea.vim.helper.exitVisualMode
import com.maddyhome.idea.vim.helper.findWordUnderCursor
import com.maddyhome.idea.vim.helper.inVisualMode
import com.maddyhome.idea.vim.helper.updateCaretsVisualAttributes
import com.maddyhome.idea.vim.helper.userData
@ -235,7 +234,7 @@ internal class VimMultipleCursorsExtension : VimExtension {
val text = if (editor.inVisualMode) {
primaryCaret.selectedText ?: return
} else {
val range = findWordUnderCursor(editor, primaryCaret) ?: return
val range = injector.searchHelper.findWordNearestCursor(editor.vim, primaryCaret.vim) ?: return
if (range.startOffset > primaryCaret.offset) return
IjVimEditor(editor).getText(range)
}
@ -300,7 +299,8 @@ internal class VimMultipleCursorsExtension : VimExtension {
}
private fun selectWordUnderCaret(editor: Editor, caret: Caret): TextRange? {
val range = findWordUnderCursor(editor, caret) ?: return null
// TODO: I think vim-multiple-cursors uses a text object rather than the star operator
val range = injector.searchHelper.findWordNearestCursor(editor.vim, caret.vim) ?: return null
if (range.startOffset > caret.offset) return null
enterVisualMode(editor.vim)

View File

@ -10,15 +10,10 @@ package com.maddyhome.idea.vim.helper
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerEx
import com.intellij.codeInsight.daemon.impl.HighlightInfo
import com.intellij.openapi.editor.Caret
import com.intellij.openapi.editor.Editor
import com.intellij.spellchecker.SpellCheckerSeveritiesProvider
import com.maddyhome.idea.vim.api.getLineEndOffset
import com.maddyhome.idea.vim.api.globalOptions
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.helper.CharacterHelper.charType
import com.maddyhome.idea.vim.newapi.IjVimEditor
import it.unimi.dsi.fastutil.ints.IntComparator
import it.unimi.dsi.fastutil.ints.IntIterator
import it.unimi.dsi.fastutil.ints.IntRBTreeSet
@ -50,64 +45,6 @@ private fun containsUpperCase(pattern: String): Boolean {
return false
}
/**
* Find the word under the cursor or the next word to the right of the cursor on the current line.
*
* @param editor The editor to find the word in
* @param caret The caret to find word under
* @return The text range of the found word or null if there is no word under/after the cursor on the line
*/
fun findWordUnderCursor(editor: Editor, caret: Caret): TextRange? {
val vimEditor = IjVimEditor(editor)
val chars = editor.document.charsSequence
val stop = vimEditor.getLineEndOffset(caret.logicalPosition.line, true)
val pos = caret.offset
// Technically the first condition is covered by the second one, but let it be
if (chars.length == 0 || chars.length <= pos) return null
//if (pos == chars.length() - 1) return new TextRange(chars.length() - 1, chars.length());
var start = pos
val types = arrayOf(
CharacterHelper.CharacterType.KEYWORD,
CharacterHelper.CharacterType.PUNCTUATION
)
for (i in 0..1) {
start = pos
val type = charType(vimEditor, chars[start], false)
if (type == types[i]) {
// Search back for start of word
while (start > 0 && charType(vimEditor, chars[start - 1], false) == types[i]) {
start--
}
} else {
// Search forward for start of word
while (start < stop && charType(vimEditor, chars[start], false) != types[i]) {
start++
}
}
if (start != stop) {
break
}
}
if (start == stop) {
return null
}
// Special case 1 character words because 'findNextWordEnd' returns one to many chars
val end = if (start < stop &&
(start >= chars.length - 1 ||
charType(vimEditor, chars[start + 1], false) != CharacterHelper.CharacterType.KEYWORD)
) {
start + 1
} else {
injector.searchHelper.findNextWordEnd(vimEditor, start, 1, false, false) + 1
}
return TextRange(start, end)
}
fun findMisspelledWords(
editor: Editor,
startOffset: Int,

View File

@ -13,7 +13,6 @@ import com.maddyhome.idea.vim.common.Direction
import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.ex.ExException
import com.maddyhome.idea.vim.ex.ranges.LineRange
import com.maddyhome.idea.vim.helper.CharacterHelper
import com.maddyhome.idea.vim.helper.Msg
import com.maddyhome.idea.vim.helper.SearchOptions
import com.maddyhome.idea.vim.helper.enumSetOf
@ -427,7 +426,7 @@ abstract class VimSearchGroupBase : VimSearchGroup {
whole: Boolean,
dir: Direction,
): Int {
val range: TextRange = findWordUnderCursor(editor, caret) ?: return -1
val range = injector.searchHelper.findWordNearestCursor(editor, caret) ?: return -1
val start = range.startOffset
val end = range.endOffset
@ -571,62 +570,6 @@ abstract class VimSearchGroupBase : VimSearchGroup {
}
/**
* Find the word under the cursor or the next word to the right of the cursor on the current line.
*
* @param editor The editor to find the word in
* @param caret The caret to find word under
* @return The text range of the found word or null if there is no word under/after the cursor on the line
*/
private fun findWordUnderCursor(
editor: VimEditor,
caret: ImmutableVimCaret,
): TextRange? {
val stop: Int = editor.getLineEndOffset(caret.getBufferPosition().line, true)
val pos: Int = caret.offset
// Technically the first condition is covered by the second one, but let it be
if (editor.text().isEmpty() || editor.text().length <= pos) return null
//if (pos == chars.length() - 1) return new TextRange(chars.length() - 1, chars.length());
var start = pos
val types = arrayOf(
CharacterHelper.CharacterType.KEYWORD,
CharacterHelper.CharacterType.PUNCTUATION
)
for (i in 0..1) {
start = pos
val type = CharacterHelper.charType(editor, editor.text()[start], false)
if (type === types[i]) {
// Search back for start of word
while (start > 0 && CharacterHelper.charType(editor, editor.text()[start - 1], false) === types[i]) {
start--
}
} else {
// Search forward for start of word
while (start < stop && CharacterHelper.charType(editor, editor.text()[start], false) !== types[i]) {
start++
}
}
if (start != stop) {
break
}
}
if (start == stop) {
return null
}
// Special case 1 character words because 'findNextWordEnd' returns one to many chars
val end: Int = if (start < stop &&
(start >= editor.text().length - 1 ||
CharacterHelper.charType(editor, editor.text()[start + 1], false) !== CharacterHelper.CharacterType.KEYWORD)
) {
start + 1
} else {
injector.searchHelper.findNextWordEnd(editor, start, 1, bigWord = false) + 1
}
return TextRange(start, end)
}
/****************************************************************************/
/* Substitute related methods */
/****************************************************************************/

View File

@ -195,6 +195,20 @@ interface VimSearchHelper {
ch: Char,
): Int
/**
* Find the word at or nearest to the current caret offset
*
* Note that this is not a word text object!
*
* This function is used to get the word to search for using the `*`/`#` and `g*`/`g#` operators. It will return:
* * the range of the keyword under the cursor
* * or the first keyword after the cursor on the current line,
* * or the non-blank word under the cursor,
* * or the first non-blank word after the cursor on the current line
* * or null, if none of the above are found
*/
fun findWordNearestCursor(editor: VimEditor, caret: ImmutableVimCaret): TextRange?
/**
* Find the range of the word text object at the location of the caret
*/

View File

@ -94,6 +94,56 @@ abstract class VimSearchHelperBase : VimSearchHelper {
}
}
override fun findWordNearestCursor(editor: VimEditor, caret: ImmutableVimCaret): TextRange? {
val chars = editor.text()
val stop = editor.getLineEndOffset(caret.getBufferPosition().line, true)
val pos = caret.offset
if (chars.isEmpty() || chars.length <= pos) return null
var start = pos
val types = arrayOf(
CharacterHelper.CharacterType.KEYWORD,
CharacterHelper.CharacterType.PUNCTUATION
)
for (i in 0..1) {
start = pos
val type = charType(editor, chars[start], false)
if (type == types[i]) {
// Search back for start of word
while (start > 0 && charType(editor, chars[start - 1], false) == types[i]) {
start--
}
} else {
// Search forward for start of word
while (start < stop && charType(editor, chars[start], false) != types[i]) {
start++
}
}
if (start != stop) {
break
}
}
if (start == stop) {
return null
}
// Special case 1 character words because 'findNextWordEnd' returns one to many chars
val end = if (start < stop &&
(start >= chars.length - 1 ||
charType(editor, chars[start + 1], false) != CharacterHelper.CharacterType.KEYWORD)
) {
start + 1
} else {
injector.searchHelper.findNextWordEnd(editor, start, 1, false, false) + 1
}
return TextRange(start, end)
}
override fun findNextWord(
editor: VimEditor,
searchFrom: Int,