1
0
mirror of https://github.com/chylex/IntelliJ-IdeaVim.git synced 2025-03-05 06:32:51 +01:00

Convert SearchHighlightsHelper to Kotlin

This commit is contained in:
Matt Ellis 2020-11-27 00:57:11 +00:00 committed by Alex Pláte
parent b13acaf823
commit 3c8b7e2de4
5 changed files with 215 additions and 241 deletions

View File

@ -61,6 +61,8 @@ import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;
import static com.maddyhome.idea.vim.helper.SearchHelperKtKt.shouldIgnoreCase;
@State(name = "VimSearchSettings", storages = {
@Storage(value = "$APP_CONFIG$/vim_settings_local.xml", roamingType = RoamingType.DISABLED)
})
@ -367,7 +369,7 @@ public class SearchGroup implements PersistentStateComponent<Element> {
private void highlightSearchLines(@NotNull Editor editor, int startLine, int endLine) {
if (lastSearch != null) {
final List<TextRange> results = findAll(editor, lastSearch, startLine, endLine,
SearchHelper.shouldIgnoreCase(lastSearch, lastIgnoreSmartCase));
shouldIgnoreCase(lastSearch, lastIgnoreSmartCase));
SearchHighlightsHelper.highlightSearchResults(editor, lastSearch, results, -1);
}
}
@ -432,7 +434,7 @@ public class SearchGroup implements PersistentStateComponent<Element> {
//RE sp;
RegExp sp;
RegExp.regmmatch_T regmatch = new RegExp.regmmatch_T();
regmatch.rmm_ic = SearchHelper.shouldIgnoreCase(pattern, searchOptions.contains(SearchOptions.IGNORE_SMARTCASE));
regmatch.rmm_ic = shouldIgnoreCase(pattern, searchOptions.contains(SearchOptions.IGNORE_SMARTCASE));
sp = new RegExp();
regmatch.regprog = sp.vim_regcomp(pattern, 1);
if (regmatch.regprog == null) {
@ -1002,7 +1004,7 @@ public class SearchGroup implements PersistentStateComponent<Element> {
}
/* the 'i' or 'I' flag overrules 'ignorecase' and 'smartcase' */
regmatch.rmm_ic = SearchHelper.shouldIgnoreCase(pattern != null ? pattern : "", false);
regmatch.rmm_ic = shouldIgnoreCase(pattern != null ? pattern : "", false);
if (do_ic == 'i') {
regmatch.rmm_ic = true;
}
@ -1206,6 +1208,7 @@ public class SearchGroup implements PersistentStateComponent<Element> {
/**
* Updates search highlights when the selected editor changes
*/
@SuppressWarnings("unused")
public static void fileEditorManagerSelectionChangedCallback(@NotNull FileEditorManagerEvent event) {
VimPlugin.getSearch().updateSearchHighlights();
}

View File

@ -2289,13 +2289,6 @@ public class SearchHelper {
return res.toString();
}
public static boolean shouldIgnoreCase(@NotNull String pattern, boolean ignoreSmartCase) {
boolean sc = !ignoreSmartCase && OptionsManager.INSTANCE.getSmartcase().isSet();
boolean ic = OptionsManager.INSTANCE.getIgnorecase().isSet();
return ic && !(sc && StringHelper.containsUpperCase(pattern));
}
public static class CountPosition {
public CountPosition(int count, int position) {
this.count = count;

View File

@ -20,6 +20,8 @@ package com.maddyhome.idea.vim.helper
import com.maddyhome.idea.vim.helper.SearchHelper.Direction
import com.maddyhome.idea.vim.helper.SearchHelper.findPositionOfFirstCharacter
import com.maddyhome.idea.vim.option.OptionsManager.ignorecase
import com.maddyhome.idea.vim.option.OptionsManager.smartcase
private data class State(val position: Int, val trigger: Char, val inQuote: Boolean?, val lastOpenSingleQuotePos: Int)
@ -161,3 +163,8 @@ private fun quoteChanges(chars: CharSequence, begin: Int) = sequence {
found = findPositionOfFirstCharacter(chars, i + Direction.FORWARD.toInt(), charsToSearch, false, Direction.FORWARD)
}
}
fun shouldIgnoreCase(pattern: String, ignoreSmartCase: Boolean): Boolean {
val sc = smartcase.isSet && !ignoreSmartCase
return ignorecase.isSet && !(sc && StringHelper.containsUpperCase(pattern))
}

View File

@ -16,259 +16,230 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.maddyhome.idea.vim.helper;
@file:JvmName("SearchHighlightsHelper")
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.editor.colors.EditorColors;
import com.intellij.openapi.editor.colors.EditorColorsScheme;
import com.intellij.openapi.editor.markup.*;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.ui.ColorUtil;
import com.maddyhome.idea.vim.common.TextRange;
import com.maddyhome.idea.vim.ex.ranges.LineRange;
import com.maddyhome.idea.vim.group.SearchGroup;
import com.maddyhome.idea.vim.option.OptionsManager;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
package com.maddyhome.idea.vim.helper
import java.awt.*;
import java.util.*;
import java.util.List;
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.EditorFactory
import com.intellij.openapi.editor.colors.EditorColors
import com.intellij.openapi.editor.colors.EditorColorsScheme
import com.intellij.openapi.editor.markup.*
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.project.ProjectManager
import com.intellij.ui.ColorUtil
import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.ex.ranges.LineRange
import com.maddyhome.idea.vim.group.SearchGroup
import com.maddyhome.idea.vim.group.SearchGroup.SearchOptions
import com.maddyhome.idea.vim.option.OptionsManager.hlsearch
import com.maddyhome.idea.vim.option.OptionsManager.wrapscan
import org.jetbrains.annotations.Contract
import java.awt.Color
import java.awt.Font
import java.util.*
public class SearchHighlightsHelper {
public static void updateSearchHighlights(@Nullable String pattern, boolean shouldIgnoreSmartCase, boolean showHighlights, boolean forceUpdate) {
updateSearchHighlights(pattern, shouldIgnoreSmartCase, showHighlights, -1, null, true, forceUpdate);
}
fun updateSearchHighlights(pattern: String?, shouldIgnoreSmartCase: Boolean, showHighlights: Boolean, forceUpdate: Boolean) {
updateSearchHighlights(pattern, shouldIgnoreSmartCase, showHighlights, -1, null, true, forceUpdate)
}
public static int updateIncsearchHighlights(@NotNull Editor editor, @NotNull String pattern, boolean forwards, int caretOffset, @Nullable LineRange searchRange) {
final int searchStartOffset = searchRange != null ? EditorHelper.getLineStartOffset(editor, searchRange.startLine) : caretOffset;
final boolean showHighlights = OptionsManager.INSTANCE.getHlsearch().isSet();
return updateSearchHighlights(pattern, false, showHighlights, searchStartOffset, searchRange, forwards, false);
}
fun updateIncsearchHighlights(editor: Editor, pattern: String, forwards: Boolean, caretOffset: Int, searchRange: LineRange?): Int {
val searchStartOffset = if (searchRange != null) EditorHelper.getLineStartOffset(editor, searchRange.startLine) else caretOffset
val showHighlights = hlsearch.isSet
return updateSearchHighlights(pattern, false, showHighlights, searchStartOffset, searchRange, forwards, false)
}
@NotNull
public static RangeHighlighter addSubstitutionConfirmationHighlight(@NotNull Editor editor, int start, int end) {
TextAttributes color = new TextAttributes(
editor.getColorsScheme().getColor(EditorColors.SELECTION_FOREGROUND_COLOR),
editor.getColorsScheme().getColor(EditorColors.SELECTION_BACKGROUND_COLOR),
editor.getColorsScheme().getColor(EditorColors.CARET_COLOR),
EffectType.ROUNDED_BOX, Font.PLAIN
);
return editor.getMarkupModel().addRangeHighlighter(start, end, HighlighterLayer.SELECTION,
color, HighlighterTargetArea.EXACT_RANGE);
}
fun addSubstitutionConfirmationHighlight(editor: Editor, start: Int, end: Int): RangeHighlighter {
val color = TextAttributes(
editor.colorsScheme.getColor(EditorColors.SELECTION_FOREGROUND_COLOR),
editor.colorsScheme.getColor(EditorColors.SELECTION_BACKGROUND_COLOR),
editor.colorsScheme.getColor(EditorColors.CARET_COLOR),
EffectType.ROUNDED_BOX, Font.PLAIN
)
return editor.markupModel.addRangeHighlighter(start, end, HighlighterLayer.SELECTION,
color, HighlighterTargetArea.EXACT_RANGE)
}
/**
* Refreshes current search highlights for all editors of currently active text editor/document
*/
private static int updateSearchHighlights(@Nullable String pattern, boolean shouldIgnoreSmartCase, boolean showHighlights,
int initialOffset, @Nullable LineRange searchRange, boolean forwards, boolean forceUpdate) {
int currentMatchOffset = -1;
ProjectManager projectManager = ProjectManager.getInstanceIfCreated();
if (projectManager == null) return currentMatchOffset;
Project[] projects = projectManager.getOpenProjects();
for (Project project : projects) {
Editor current = FileEditorManager.getInstance(project).getSelectedTextEditor();
Editor[] editors = current == null ? null : EditorFactory.getInstance().getEditors(current.getDocument(), project);
if (editors == null) {
continue;
/**
* Refreshes current search highlights for all editors of currently active text editor/document
*/
private fun updateSearchHighlights(pattern: String?, shouldIgnoreSmartCase: Boolean, showHighlights: Boolean,
initialOffset: Int, searchRange: LineRange?, forwards: Boolean, forceUpdate: Boolean): Int {
var currentMatchOffset = -1
val projectManager = ProjectManager.getInstanceIfCreated() ?: return currentMatchOffset
for (project in projectManager.openProjects) {
val current = FileEditorManager.getInstance(project).selectedTextEditor ?: continue
// [VERSION UPDATE] 202+ Use editors
val editors = EditorFactory.getInstance().getEditors(current.document, project) ?: continue
for (editor in editors) {
// Try to keep existing highlights if possible. Update if hlsearch has changed or if the pattern has changed.
// Force update for the situations where the text is the same, but the ignore case values have changed.
// E.g. Use `*` to search for a word (which ignores smartcase), then use `/<Up>` to search for the same pattern,
// which will match smartcase. Or changing the smartcase/ignorecase settings
if (shouldRemoveSearchHighlights(editor, pattern, showHighlights) || forceUpdate) {
removeSearchHighlights(editor)
}
for (final Editor editor : editors) {
// Try to keep existing highlights if possible. Update if hlsearch has changed or if the pattern has changed.
// Force update for the situations where the text is the same, but the ignore case values have changed.
// E.g. Use `*` to search for a word (which ignores smartcase), then use `/<Up>` to search for the same pattern,
// which will match smartcase. Or changing the smartcase/ignorecase settings
if (shouldRemoveSearchHighlights(editor, pattern, showHighlights) || forceUpdate) {
removeSearchHighlights(editor);
}
if (pattern == null) continue
if (shouldAddAllSearchHighlights(editor, pattern, showHighlights)) {
// hlsearch (+ incsearch/noincsearch)
final int startLine = searchRange == null ? 0 : searchRange.startLine;
final int endLine = searchRange == null ? -1 : searchRange.endLine;
List<TextRange> results = SearchGroup.findAll(editor, pattern, startLine, endLine,
SearchHelper.shouldIgnoreCase(pattern, shouldIgnoreSmartCase));
if (!results.isEmpty()) {
currentMatchOffset = findClosestMatch(editor, results, initialOffset, forwards);
highlightSearchResults(editor, pattern, results, currentMatchOffset);
}
UserDataManager.setVimLastSearch(editor, pattern);
if (shouldAddAllSearchHighlights(editor, pattern, showHighlights)) {
// hlsearch (+ incsearch/noincsearch)
val startLine = searchRange?.startLine ?: 0
val endLine = searchRange?.endLine ?: -1
val results = SearchGroup.findAll(editor, pattern, startLine, endLine, shouldIgnoreCase(pattern, shouldIgnoreSmartCase))
if (results.isNotEmpty()) {
currentMatchOffset = findClosestMatch(editor, results, initialOffset, forwards)
highlightSearchResults(editor, pattern, results, currentMatchOffset)
}
else if (shouldAddCurrentMatchSearchHighlight(pattern, showHighlights, initialOffset)) {
// nohlsearch + incsearch
final boolean wrap = OptionsManager.INSTANCE.getWrapscan().isSet();
final EnumSet<SearchGroup.SearchOptions> searchOptions = EnumSet.of(SearchGroup.SearchOptions.WHOLE_FILE);
if (wrap) searchOptions.add(SearchGroup.SearchOptions.WRAP);
if (shouldIgnoreSmartCase) searchOptions.add(SearchGroup.SearchOptions.IGNORE_SMARTCASE);
if (!forwards) searchOptions.add(SearchGroup.SearchOptions.BACKWARDS);
final TextRange result = SearchGroup.findIt(editor, pattern, initialOffset, 1, searchOptions);
if (result != null && pattern != null) {
currentMatchOffset = result.getStartOffset();
final List<TextRange> results = Collections.singletonList(result);
highlightSearchResults(editor, pattern, results, currentMatchOffset);
}
editor.vimLastSearch = pattern
} else if (shouldAddCurrentMatchSearchHighlight(pattern, showHighlights, initialOffset)) {
// nohlsearch + incsearch
val searchOptions = EnumSet.of(SearchOptions.WHOLE_FILE)
if (wrapscan.isSet) searchOptions.add(SearchOptions.WRAP)
if (shouldIgnoreSmartCase) searchOptions.add(SearchOptions.IGNORE_SMARTCASE)
if (!forwards) searchOptions.add(SearchOptions.BACKWARDS)
val result = SearchGroup.findIt(editor, pattern, initialOffset, 1, searchOptions)
if (result != null) {
currentMatchOffset = result.startOffset
val results = listOf(result)
highlightSearchResults(editor, pattern, results, currentMatchOffset)
}
else if (shouldMaintainCurrentMatchOffset(pattern, initialOffset)) {
// incsearch. If nothing has changed (e.g. we've edited offset values in `/foo/e+2`) make sure we return the
// current match offset so the caret remains at the current incsarch match
final Integer offset = UserDataManager.getVimIncsearchCurrentMatchOffset(editor);
if (offset != null) {
currentMatchOffset = offset;
}
} else if (shouldMaintainCurrentMatchOffset(pattern, initialOffset)) {
// incsearch. If nothing has changed (e.g. we've edited offset values in `/foo/e+2`) make sure we return the
// current match offset so the caret remains at the current incsarch match
val offset = editor.vimIncsearchCurrentMatchOffset
if (offset != null) {
currentMatchOffset = offset
}
}
}
return currentMatchOffset;
}
return currentMatchOffset
}
/**
* Remove current search highlights if hlSearch is false, or if the pattern is changed
*/
@Contract("_, _, false -> true; _, null, true -> false")
private static boolean shouldRemoveSearchHighlights(@NotNull Editor editor, @Nullable String newPattern, boolean hlSearch) {
return !hlSearch || (newPattern != null && !newPattern.equals(UserDataManager.getVimLastSearch(editor)));
/**
* Remove current search highlights if hlSearch is false, or if the pattern is changed
*/
@Contract("_, _, false -> true; _, null, true -> false")
private fun shouldRemoveSearchHighlights(editor: Editor, newPattern: String?, hlSearch: Boolean): Boolean {
return !hlSearch || newPattern != null && newPattern != editor.vimLastSearch
}
private fun removeSearchHighlights(editor: Editor) {
editor.vimLastSearch = null
val ehl = editor.vimLastHighlighters ?: return
for (rh in ehl) {
editor.markupModel.removeHighlighter(rh)
}
editor.vimLastHighlighters = null
}
private static void removeSearchHighlights(@NotNull Editor editor) {
UserDataManager.setVimLastSearch(editor, null);
/**
* Add search highlights if hlSearch is true and the pattern is changed
*/
@Contract("_, _, false -> false; _, null, true -> false")
private fun shouldAddAllSearchHighlights(editor: Editor, newPattern: String?, hlSearch: Boolean): Boolean {
return hlSearch && newPattern != null && newPattern != editor.vimLastSearch && newPattern != ""
}
Collection<RangeHighlighter> ehl = UserDataManager.getVimLastHighlighters(editor);
if (ehl == null) {
return;
}
for (RangeHighlighter rh : ehl) {
editor.getMarkupModel().removeHighlighter(rh);
}
ehl.clear();
UserDataManager.setVimLastHighlighters(editor, null);
private fun findClosestMatch(editor: Editor, results: List<TextRange>, initialOffset: Int, forwards: Boolean): Int {
if (results.isEmpty() || initialOffset == -1) {
return -1
}
/**
* Add search highlights if hlSearch is true and the pattern is changed
*/
@Contract("_, _, false -> false; _, null, true -> false")
private static boolean shouldAddAllSearchHighlights(@NotNull Editor editor, @Nullable String newPattern, boolean hlSearch) {
return hlSearch && newPattern != null && !newPattern.equals(UserDataManager.getVimLastSearch(editor)) && !Objects.equals(newPattern, "");
val size = editor.fileSize
val max = Collections.max(results) { r1: TextRange, r2: TextRange ->
val d1 = distance(r1, initialOffset, forwards, size)
val d2 = distance(r2, initialOffset, forwards, size)
if (d1 < 0 && d2 >= 0) {
return@max Int.MAX_VALUE
}
d2 - d1
}
private static int findClosestMatch(@NotNull Editor editor, @NotNull List<TextRange> results, int initialOffset, boolean forwards) {
if (results.isEmpty() || initialOffset == -1) {
return -1;
}
final int size = EditorHelperRt.getFileSize(editor);
final TextRange max = Collections.max(results, (r1, r2) -> {
final int d1 = distance(r1, initialOffset, forwards, size);
final int d2 = distance(r2, initialOffset, forwards, size);
if (d1 < 0 && d2 >= 0) {
return Integer.MAX_VALUE;
}
return d2 - d1;
});
if (!OptionsManager.INSTANCE.getWrapscan().isSet()) {
final int start = max.getStartOffset();
if (forwards && start < initialOffset) {
return -1;
}
else if (start >= initialOffset) {
return -1;
}
}
return max.getStartOffset();
}
private static int distance(@NotNull TextRange range, int pos, boolean forwards, int size) {
final int start = range.getStartOffset();
if (start <= pos) {
return forwards ? size - pos + start : pos - start;
}
else {
return forwards ? start - pos : pos + size - start;
if (!wrapscan.isSet) {
val start = max.startOffset
if (forwards && start < initialOffset) {
return -1
} else if (start >= initialOffset) {
return -1
}
}
return max.startOffset
}
public static void highlightSearchResults(@NotNull Editor editor, @NotNull String pattern, List<TextRange> results,
int currentMatchOffset) {
Collection<RangeHighlighter> highlighters = UserDataManager.getVimLastHighlighters(editor);
if (highlighters == null) {
highlighters = new ArrayList<>();
UserDataManager.setVimLastHighlighters(editor, highlighters);
}
for (TextRange range : results) {
final boolean current = range.getStartOffset() == currentMatchOffset;
final RangeHighlighter highlighter = highlightMatch(editor, range.getStartOffset(), range.getEndOffset(), current, pattern);
highlighters.add(highlighter);
}
UserDataManager.setVimIncsearchCurrentMatchOffset(editor, currentMatchOffset);
}
private static @NotNull RangeHighlighter highlightMatch(@NotNull Editor editor, int start, int end, boolean current, String tooltip) {
TextAttributes attributes = editor.getColorsScheme().getAttributes(EditorColors.TEXT_SEARCH_RESULT_ATTRIBUTES);
if (current) {
// This mimics what IntelliJ does with the Find live preview
attributes = attributes.clone();
attributes.setEffectType(EffectType.ROUNDED_BOX);
attributes.setEffectColor(editor.getColorsScheme().getColor(EditorColors.CARET_COLOR));
}
if (attributes.getErrorStripeColor() == null) {
attributes.setErrorStripeColor(getFallbackErrorStripeColor(attributes, editor.getColorsScheme()));
}
final RangeHighlighter highlighter = editor.getMarkupModel().addRangeHighlighter(start, end, HighlighterLayer.SELECTION - 1,
attributes, HighlighterTargetArea.EXACT_RANGE);
highlighter.setErrorStripeTooltip(tooltip);
return highlighter;
}
/**
* Return a valid error stripe colour based on editor background
*
* <p>Based on HighlightManager#addRangeHighlight behaviour, which we can't use because it will hide highlights
* when hitting Escape.</p>
*/
private static @Nullable Color getFallbackErrorStripeColor(TextAttributes attributes, EditorColorsScheme colorsScheme) {
if (attributes.getBackgroundColor() != null) {
boolean isDark = ColorUtil.isDark(colorsScheme.getDefaultBackground());
return isDark ? attributes.getBackgroundColor().brighter() : attributes.getBackgroundColor().darker();
}
return null;
}
/**
* Add search highlight for current match if hlsearch is false and we're performing incsearch highlights
*/
@Contract("_, true, _ -> false")
private static boolean shouldAddCurrentMatchSearchHighlight(@Nullable String pattern, boolean hlSearch, int initialOffset) {
return !hlSearch && isIncrementalSearchHighlights(initialOffset) && pattern != null && pattern.length() > 0;
}
/**
* Keep the current match offset if the pattern is still valid and we're performing incremental search highlights
* This will keep the caret position when editing the offset in e.g. `/foo/e+1`
*/
@Contract("null, _ -> false")
private static boolean shouldMaintainCurrentMatchOffset(@Nullable String pattern, int initialOffset) {
return pattern != null && pattern.length() > 0 && isIncrementalSearchHighlights(initialOffset);
}
/**
* initialOffset is only valid if we're highlighting incsearch
*/
@Contract(pure = true)
private static boolean isIncrementalSearchHighlights(int initialOffset) {
return initialOffset != -1;
private fun distance(range: TextRange, pos: Int, forwards: Boolean, size: Int): Int {
val start = range.startOffset
return if (start <= pos) {
if (forwards) size - pos + start else pos - start
} else {
if (forwards) start - pos else pos + size - start
}
}
fun highlightSearchResults(editor: Editor, pattern: String, results: List<TextRange>, currentMatchOffset: Int) {
var highlighters = editor.vimLastHighlighters
if (highlighters == null) {
highlighters = mutableListOf()
editor.vimLastHighlighters = highlighters
}
for (range in results) {
val current = range.startOffset == currentMatchOffset
val highlighter = highlightMatch(editor, range.startOffset, range.endOffset, current, pattern)
highlighters.add(highlighter)
}
editor.vimIncsearchCurrentMatchOffset = currentMatchOffset
}
private fun highlightMatch(editor: Editor, start: Int, end: Int, current: Boolean, tooltip: String): RangeHighlighter {
var attributes = editor.colorsScheme.getAttributes(EditorColors.TEXT_SEARCH_RESULT_ATTRIBUTES)
if (current) {
// This mimics what IntelliJ does with the Find live preview
attributes = attributes.clone()
attributes.effectType = EffectType.ROUNDED_BOX
attributes.effectColor = editor.colorsScheme.getColor(EditorColors.CARET_COLOR)
}
if (attributes.errorStripeColor == null) {
attributes.errorStripeColor = getFallbackErrorStripeColor(attributes, editor.colorsScheme)
}
val highlighter = editor.markupModel.addRangeHighlighter(start, end, HighlighterLayer.SELECTION - 1,
attributes, HighlighterTargetArea.EXACT_RANGE)
highlighter.errorStripeTooltip = tooltip
return highlighter
}
/**
* Return a valid error stripe colour based on editor background
*
*
* Based on HighlightManager#addRangeHighlight behaviour, which we can't use because it will hide highlights
* when hitting Escape.
*/
private fun getFallbackErrorStripeColor(attributes: TextAttributes, colorsScheme: EditorColorsScheme): Color? {
if (attributes.backgroundColor != null) {
val isDark = ColorUtil.isDark(colorsScheme.defaultBackground)
return if (isDark) attributes.backgroundColor.brighter() else attributes.backgroundColor.darker()
}
return null
}
/**
* Add search highlight for current match if hlsearch is false and we're performing incsearch highlights
*/
@Contract("_, true, _ -> false")
private fun shouldAddCurrentMatchSearchHighlight(pattern: String?, hlSearch: Boolean, initialOffset: Int): Boolean {
return !hlSearch && isIncrementalSearchHighlights(initialOffset) && pattern != null && pattern.isNotEmpty()
}
/**
* Keep the current match offset if the pattern is still valid and we're performing incremental search highlights
* This will keep the caret position when editing the offset in e.g. `/foo/e+1`
*/
@Contract("null, _ -> false")
private fun shouldMaintainCurrentMatchOffset(pattern: String?, initialOffset: Int): Boolean {
return pattern != null && pattern.isNotEmpty() && isIncrementalSearchHighlights(initialOffset)
}
/**
* initialOffset is only valid if we're highlighting incsearch
*/
@Contract(pure = true)
private fun isIncrementalSearchHighlights(initialOffset: Int) = initialOffset != -1

View File

@ -80,7 +80,7 @@ fun unInitializeEditor(editor: Editor) {
}
var Editor.vimLastSearch: String? by userData()
var Editor.vimLastHighlighters: Collection<RangeHighlighter>? by userData()
var Editor.vimLastHighlighters: MutableCollection<RangeHighlighter>? by userData()
var Editor.vimIncsearchCurrentMatchOffset: Int? by userData()
/***
* @see :help visualmode()