1
0
mirror of https://github.com/chylex/IntelliJ-IdeaVim.git synced 2025-02-22 20:46:02 +01:00

Move SearchHelper classes to kotlin file

This commit is contained in:
Alex Plate 2024-06-28 16:02:26 +03:00
parent 4d8e68d800
commit 16e92ddf60
No known key found for this signature in database
GPG Key ID: 0B97153C8FFEC09F
3 changed files with 380 additions and 414 deletions
src/main/java/com/maddyhome/idea/vim

View File

@ -34,10 +34,7 @@ import com.intellij.psi.search.ProjectScope;
import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.api.*;
import com.maddyhome.idea.vim.common.TextRange;
import com.maddyhome.idea.vim.helper.EditorHelper;
import com.maddyhome.idea.vim.helper.EditorHelperRt;
import com.maddyhome.idea.vim.helper.MessageHelper;
import com.maddyhome.idea.vim.helper.SearchHelper;
import com.maddyhome.idea.vim.helper.*;
import com.maddyhome.idea.vim.newapi.ExecuteExtensionKt;
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext;
import com.maddyhome.idea.vim.newapi.IjVimEditor;
@ -51,6 +48,7 @@ import java.util.Arrays;
import java.util.Collection;
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
import static com.maddyhome.idea.vim.helper.SearchHelperKtKt.countWords;
import static com.maddyhome.idea.vim.newapi.IjVimInjectorKt.globalIjOptions;
public class FileGroup extends VimFileBase {
@ -329,7 +327,7 @@ public class FileGroup extends VimFileBase {
msg.append("; Line ").append(lline + 1).append(" of ").append(total);
SearchHelper.CountPosition cp = SearchHelper.countWords(editor);
CountPosition cp = countWords(editor);
msg.append("; Word ").append(cp.getPosition()).append(" of ").append(cp.getCount());
@ -346,7 +344,7 @@ public class FileGroup extends VimFileBase {
vr.normalize();
int lines;
SearchHelper.CountPosition cp = SearchHelper.countWords(editor);
CountPosition cp = countWords(editor);
int words = cp.getCount();
int word = 0;
if (vr.isMultiple()) {
@ -356,7 +354,7 @@ public class FileGroup extends VimFileBase {
msg.append(cols).append(" Cols; ");
for (int i = 0; i < vr.size(); i++) {
cp = SearchHelper.countWords(editor, vr.getStartOffsets()[i], vr.getEndOffsets()[i] - 1);
cp = countWords(editor, vr.getStartOffsets()[i], vr.getEndOffsets()[i] - 1);
word += cp.getCount();
}
}
@ -366,7 +364,7 @@ public class FileGroup extends VimFileBase {
lines = elp.line - slp.line + 1;
cp = SearchHelper.countWords(editor, vr.getStartOffset(), vr.getEndOffset() - 1);
cp = countWords(editor, vr.getStartOffset(), vr.getEndOffset() - 1);
word = cp.getCount();
}

View File

@ -1,406 +0,0 @@
/*
* 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.helper;
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerEx;
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.spellchecker.SpellCheckerSeveritiesProvider;
import com.maddyhome.idea.vim.api.EngineEditorHelperKt;
import com.maddyhome.idea.vim.common.TextRange;
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;
import it.unimi.dsi.fastutil.ints.IntSortedSet;
import kotlin.Pair;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import static com.maddyhome.idea.vim.api.VimInjectorKt.*;
/**
* Helper methods for searching text
*/
public class SearchHelper {
/**
* This counts all the words in the file.
*/
public static @NotNull CountPosition countWords(@NotNull Editor editor) {
int size = EditorHelperRt.getFileSize(editor);
return countWords(editor, 0, size);
}
/**
* This counts all the words in the file.
*/
public static @NotNull CountPosition countWords(@NotNull Editor editor, int start, int end) {
int offset = editor.getCaretModel().getOffset();
final IjVimEditor vimEditor = new IjVimEditor(editor);
int count = 1;
int position = 0;
int last = -1;
int res = start;
while (true) {
res = injector.getSearchHelper().findNextWord(vimEditor, res, 1, true, false);
if (res == start || res == 0 || res > end || res == last) {
break;
}
count++;
if (res == offset) {
position = count;
}
else if (last < offset && res >= offset) {
if (count == 2) {
position = 1;
}
else {
position = count - 1;
}
}
last = res;
}
if (position == 0 && res == offset) {
position = count;
}
return new CountPosition(count, position);
}
public static @NotNull List<Pair<TextRange, NumberType>> findNumbersInRange(final @NotNull Editor editor,
@NotNull TextRange textRange,
final boolean alpha,
final boolean hex,
final boolean octal) {
List<Pair<TextRange, NumberType>> result = new ArrayList<>();
for (int i = 0; i < textRange.size(); i++) {
int startOffset = textRange.getStartOffsets()[i];
final int end = textRange.getEndOffsets()[i];
String text = EngineEditorHelperKt.getText(new IjVimEditor(editor), startOffset, end);
String[] textChunks = text.split("\\n");
int chunkStart = 0;
for (String chunk : textChunks) {
Pair<TextRange, NumberType> number = findNumberInText(chunk, 0, alpha, hex, octal);
if (number != null) {
result.add(new Pair<>(new TextRange(number.getFirst().getStartOffset() + startOffset + chunkStart,
number.getFirst().getEndOffset() + startOffset + chunkStart),
number.getSecond()));
}
chunkStart += 1 + chunk.length();
}
}
return result;
}
public static @Nullable Pair<TextRange, NumberType> findNumberUnderCursor(final @NotNull Editor editor,
@NotNull Caret caret,
final boolean alpha,
final boolean hex,
final boolean octal) {
int lline = caret.getLogicalPosition().line;
String text = new IjVimEditor(editor).getLineText(lline).toLowerCase();
int startLineOffset = new IjVimEditor(editor).getLineStartOffset(lline);
int posOnLine = caret.getOffset() - startLineOffset;
Pair<TextRange, NumberType> numberTextRange = findNumberInText(text, posOnLine, alpha, hex, octal);
if (numberTextRange == null) {
return null;
}
return new Pair<>(new TextRange(numberTextRange.getFirst().getStartOffset() + startLineOffset,
numberTextRange.getFirst().getEndOffset() + startLineOffset),
numberTextRange.getSecond());
}
/**
* Search for number in given text from start position
*
* @param textInRange - text to search in
* @param startPosOnLine - start offset to search
* @return - text range with number
*/
public static @Nullable Pair<TextRange, NumberType> findNumberInText(final @NotNull String textInRange,
int startPosOnLine,
final boolean alpha,
final boolean hex,
final boolean octal) {
if (logger.isDebugEnabled()) {
logger.debug("text=" + textInRange);
}
int pos = startPosOnLine;
int lineEndOffset = textInRange.length();
while (true) {
// Skip over current whitespace if any
while (pos < lineEndOffset && !isNumberChar(textInRange.charAt(pos), alpha, hex, octal, true)) {
pos++;
}
if (logger.isDebugEnabled()) logger.debug("pos=" + pos);
if (pos >= lineEndOffset) {
logger.debug("no number char on line");
return null;
}
boolean isHexChar = "abcdefABCDEF".indexOf(textInRange.charAt(pos)) >= 0;
if (hex) {
// Ox and OX handling
if (textInRange.charAt(pos) == '0' &&
pos < lineEndOffset - 1 &&
"xX".indexOf(textInRange.charAt(pos + 1)) >= 0) {
pos += 2;
}
else if ("xX".indexOf(textInRange.charAt(pos)) >= 0 && pos > 0 && textInRange.charAt(pos - 1) == '0') {
pos++;
}
logger.debug("checking hex");
final Pair<Integer, Integer> range = findRange(textInRange, pos, false, true, false, false);
int start = range.getFirst();
int end = range.getSecond();
// Ox and OX
if (start >= 2 && textInRange.substring(start - 2, start).equalsIgnoreCase("0x")) {
logger.debug("found hex");
return new Pair<>(new TextRange(start - 2, end), NumberType.HEX);
}
if (!isHexChar || alpha) {
break;
}
else {
pos++;
}
}
else {
break;
}
}
if (octal) {
logger.debug("checking octal");
final Pair<Integer, Integer> range = findRange(textInRange, pos, false, false, true, false);
int start = range.getFirst();
int end = range.getSecond();
if (end - start == 1 && textInRange.charAt(start) == '0') {
return new Pair<>(new TextRange(start, end), NumberType.DEC);
}
if (textInRange.charAt(start) == '0' &&
end > start &&
!(start > 0 && isNumberChar(textInRange.charAt(start - 1), false, false, false, true))) {
logger.debug("found octal");
return new Pair<>(new TextRange(start, end), NumberType.OCT);
}
}
if (alpha) {
if (logger.isDebugEnabled()) logger.debug("checking alpha for " + textInRange.charAt(pos));
if (isNumberChar(textInRange.charAt(pos), true, false, false, false)) {
if (logger.isDebugEnabled()) logger.debug("found alpha at " + pos);
return new Pair<>(new TextRange(pos, pos + 1), NumberType.ALPHA);
}
}
final Pair<Integer, Integer> range = findRange(textInRange, pos, false, false, false, true);
int start = range.getFirst();
int end = range.getSecond();
if (start > 0 && textInRange.charAt(start - 1) == '-') {
start--;
}
return new Pair<>(new TextRange(start, end), NumberType.DEC);
}
/**
* Searches for digits block that matches parameters
*/
private static @NotNull Pair<Integer, Integer> findRange(final @NotNull String text,
final int pos,
final boolean alpha,
final boolean hex,
final boolean octal,
final boolean decimal) {
int end = pos;
while (end < text.length() && isNumberChar(text.charAt(end), alpha, hex, octal, decimal || octal)) {
end++;
}
int start = pos;
while (start >= 0 && isNumberChar(text.charAt(start), alpha, hex, octal, decimal || octal)) {
start--;
}
if (start < end &&
(start == -1 ||
0 <= start &&
start < text.length() &&
!isNumberChar(text.charAt(start), alpha, hex, octal, decimal || octal))) {
start++;
}
if (octal) {
for (int i = start; i < end; i++) {
if (!isNumberChar(text.charAt(i), false, false, true, false)) return new Pair<>(0, 0);
}
}
return new Pair<>(start, end);
}
private static boolean isNumberChar(char ch, boolean alpha, boolean hex, boolean octal, boolean decimal) {
if (alpha && ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) {
return true;
}
else if (octal && (ch >= '0' && ch <= '7')) {
return true;
}
else if (hex && ((ch >= '0' && ch <= '9') || "abcdefABCDEF".indexOf(ch) >= 0)) {
return true;
}
else {
return decimal && (ch >= '0' && ch <= '9');
}
}
/**
* 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
*/
public static @Nullable TextRange findWordUnderCursor(@NotNull Editor editor, @NotNull Caret caret) {
final IjVimEditor vimEditor = new IjVimEditor(editor);
CharSequence chars = editor.getDocument().getCharsSequence();
int stop = EngineEditorHelperKt.getLineEndOffset(vimEditor, caret.getLogicalPosition().line, true);
int pos = caret.getOffset();
// 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());
int start = pos;
CharacterHelper.CharacterType[] types = new CharacterHelper.CharacterType[]{CharacterHelper.CharacterType.KEYWORD,
CharacterHelper.CharacterType.PUNCTUATION};
for (int i = 0; i < 2; i++) {
start = pos;
CharacterHelper.CharacterType type = CharacterHelper.charType(vimEditor, chars.charAt(start), false);
if (type == types[i]) {
// Search back for start of word
while (start > 0 && CharacterHelper.charType(vimEditor, chars.charAt(start - 1), false) == types[i]) {
start--;
}
}
else {
// Search forward for start of word
while (start < stop && CharacterHelper.charType(vimEditor, chars.charAt(start), false) != types[i]) {
start++;
}
}
if (start != stop) {
break;
}
}
if (start == stop) {
return null;
}
int end;
// Special case 1 character words because 'findNextWordEnd' returns one to many chars
if (start < stop &&
(start >= chars.length() - 1 ||
CharacterHelper.charType(vimEditor, chars.charAt(start + 1), false) != CharacterHelper.CharacterType.KEYWORD)) {
end = start + 1;
}
else {
end = injector.getSearchHelper().findNextWordEnd(vimEditor, start, 1, false, false) + 1;
}
return new TextRange(start, end);
}
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();
skip(offsetIterator, skipCount);
return offsetIterator.nextInt();
}
}
private static void skip(IntIterator iterator, final int n) {
if (n < 0) throw new IllegalArgumentException("Argument must be nonnegative: " + n);
int i = n;
while (i-- != 0 && iterator.hasNext()) iterator.nextInt();
}
public static class CountPosition {
public CountPosition(int count, int position) {
this.count = count;
this.position = position;
}
public int getCount() {
return count;
}
public int getPosition() {
return position;
}
private final int count;
private final int position;
}
private static final Logger logger = Logger.getInstance(SearchHelper.class.getName());
}

View File

@ -8,8 +8,25 @@
package com.maddyhome.idea.vim.helper
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerEx
import com.intellij.codeInsight.daemon.impl.HighlightInfo
import com.intellij.openapi.diagnostic.logger
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.getText
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 com.maddyhome.idea.vim.newapi.vim
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 java.util.*
/**
* Check ignorecase and smartcase options to see if a case insensitive search should be performed with the given pattern.
@ -36,3 +53,360 @@ private fun containsUpperCase(pattern: String): Boolean {
}
return false
}
/**
* This counts all the words in the file.
*/
public fun countWords(editor: Editor): CountPosition {
val size = editor.fileSize
return countWords(editor, 0, size)
}
/**
* This counts all the words in the file.
*/
public fun countWords(editor: Editor, start: Int, end: Int): CountPosition {
val offset = editor.caretModel.offset
val vimEditor = IjVimEditor(editor)
var count = 1
var position = 0
var last = -1
var res = start
while (true) {
res = injector.searchHelper.findNextWord(vimEditor, res, 1, true, false)
if (res == start || res == 0 || res > end || res == last) {
break
}
count++
if (res == offset) {
position = count
} else if (last < offset && res >= offset) {
position = if (count == 2) {
1
} else {
count - 1
}
}
last = res
}
if (position == 0 && res == offset) {
position = count
}
return CountPosition(count, position)
}
public fun findNumbersInRange(
editor: Editor,
textRange: TextRange,
alpha: Boolean,
hex: Boolean,
octal: Boolean,
): List<Pair<TextRange, NumberType>> {
val result: MutableList<Pair<TextRange, NumberType>> = ArrayList()
for (i in 0 until textRange.size()) {
val startOffset = textRange.startOffsets[i]
val end = textRange.endOffsets[i]
val text: String = editor.vim.getText(startOffset, end)
val textChunks = text.split("\\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
var chunkStart = 0
for (chunk in textChunks) {
val number = findNumberInText(chunk, 0, alpha, hex, octal)
if (number != null) {
result.add(
Pair(
TextRange(
number.first.startOffset + startOffset + chunkStart,
number.first.endOffset + startOffset + chunkStart
),
number.second
)
)
}
chunkStart += 1 + chunk.length
}
}
return result
}
public fun findNumberUnderCursor(
editor: Editor,
caret: Caret,
alpha: Boolean,
hex: Boolean,
octal: Boolean,
): Pair<TextRange, NumberType>? {
val lline = caret.logicalPosition.line
val text = IjVimEditor(editor).getLineText(lline).lowercase(Locale.getDefault())
val startLineOffset = IjVimEditor(editor).getLineStartOffset(lline)
val posOnLine = caret.offset - startLineOffset
val numberTextRange = findNumberInText(text, posOnLine, alpha, hex, octal) ?: return null
return Pair(
TextRange(
numberTextRange.first.startOffset + startLineOffset,
numberTextRange.first.endOffset + startLineOffset
),
numberTextRange.second
)
}
/**
* Search for number in given text from start position
*
* @param textInRange - text to search in
* @param startPosOnLine - start offset to search
* @return - text range with number
*/
public fun findNumberInText(
textInRange: String,
startPosOnLine: Int,
alpha: Boolean,
hex: Boolean,
octal: Boolean,
): Pair<TextRange, NumberType>? {
if (logger.isDebugEnabled) {
logger.debug("text=$textInRange")
}
var pos = startPosOnLine
val lineEndOffset = textInRange.length
while (true) {
// Skip over current whitespace if any
while (pos < lineEndOffset && !isNumberChar(textInRange[pos], alpha, hex, octal, true)) {
pos++
}
if (logger.isDebugEnabled) logger.debug("pos=$pos")
if (pos >= lineEndOffset) {
logger.debug("no number char on line")
return null
}
val isHexChar = "abcdefABCDEF".indexOf(textInRange[pos]) >= 0
if (hex) {
// Ox and OX handling
if (textInRange[pos] == '0' && pos < lineEndOffset - 1 && "xX".indexOf(textInRange[pos + 1]) >= 0) {
pos += 2
} else if ("xX".indexOf(textInRange[pos]) >= 0 && pos > 0 && textInRange[pos - 1] == '0') {
pos++
}
logger.debug("checking hex")
val range = findRange(textInRange, pos, false, true, false, false)
val start = range.first
val end = range.second
// Ox and OX
if (start >= 2 && textInRange.substring(start - 2, start).equals("0x", ignoreCase = true)) {
logger.debug("found hex")
return Pair(TextRange(start - 2, end), NumberType.HEX)
}
if (!isHexChar || alpha) {
break
} else {
pos++
}
} else {
break
}
}
if (octal) {
logger.debug("checking octal")
val range = findRange(textInRange, pos, false, false, true, false)
val start = range.first
val end = range.second
if (end - start == 1 && textInRange[start] == '0') {
return Pair(TextRange(start, end), NumberType.DEC)
}
if (textInRange[start] == '0' && end > start &&
!(start > 0 && isNumberChar(textInRange[start - 1], false, false, false, true))
) {
logger.debug("found octal")
return Pair(TextRange(start, end), NumberType.OCT)
}
}
if (alpha) {
if (logger.isDebugEnabled) logger.debug("checking alpha for " + textInRange[pos])
if (isNumberChar(textInRange[pos], true, false, false, false)) {
if (logger.isDebugEnabled) logger.debug("found alpha at $pos")
return Pair(TextRange(pos, pos + 1), NumberType.ALPHA)
}
}
val range = findRange(textInRange, pos, false, false, false, true)
var start = range.first
val end = range.second
if (start > 0 && textInRange[start - 1] == '-') {
start--
}
return Pair(TextRange(start, end), NumberType.DEC)
}
/**
* Searches for digits block that matches parameters
*/
private fun findRange(
text: String,
pos: Int,
alpha: Boolean,
hex: Boolean,
octal: Boolean,
decimal: Boolean,
): Pair<Int, Int> {
var end = pos
while (end < text.length && isNumberChar(text[end], alpha, hex, octal, decimal || octal)) {
end++
}
var start = pos
while (start >= 0 && isNumberChar(text[start], alpha, hex, octal, decimal || octal)) {
start--
}
if (start < end &&
(start == -1 ||
0 <= start && start < text.length &&
!isNumberChar(text[start], alpha, hex, octal, decimal || octal))
) {
start++
}
if (octal) {
for (i in start until end) {
if (!isNumberChar(text[i], false, false, true, false)) return Pair(0, 0)
}
}
return Pair(start, end)
}
private fun isNumberChar(ch: Char, alpha: Boolean, hex: Boolean, octal: Boolean, decimal: Boolean): Boolean {
return if (alpha && ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) {
true
} else if (octal && (ch >= '0' && ch <= '7')) {
true
} else if (hex && ((ch >= '0' && ch <= '9') || "abcdefABCDEF".indexOf(ch) >= 0)) {
true
} else {
decimal && (ch >= '0' && ch <= '9')
}
}
/**
* 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
*/
public 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)
}
public fun findMisspelledWords(
editor: Editor,
startOffset: Int,
endOffset: Int,
skipCount: Int,
offsetOrdering: IntComparator?,
): Int {
val project = editor.project ?: return -1
val offsets: IntSortedSet = IntRBTreeSet(offsetOrdering)
DaemonCodeAnalyzerEx.processHighlights(
editor.document, project, SpellCheckerSeveritiesProvider.TYPO,
startOffset, endOffset
) { highlight: HighlightInfo ->
if (highlight.severity === SpellCheckerSeveritiesProvider.TYPO) {
val offset = highlight.getStartOffset()
if (offset >= startOffset && offset <= endOffset) {
offsets.add(offset)
}
}
true
}
if (offsets.isEmpty()) {
return -1
}
if (skipCount >= offsets.size) {
return offsets.lastInt()
} else {
val offsetIterator: IntIterator = offsets.iterator()
skip(offsetIterator, skipCount)
return offsetIterator.nextInt()
}
}
private fun skip(iterator: IntIterator, n: Int) {
require(n >= 0) { "Argument must be nonnegative: $n" }
var i = n
while (i-- != 0 && iterator.hasNext()) iterator.nextInt()
}
public class CountPosition(public val count: Int, public val position: Int)
private val logger = logger<SearchLogger>()
private class SearchLogger