diff --git a/src/jvmMain/kotlin/com/sunnychung/application/multiplatform/hellohttp/ux/CodeEditorView.kt b/src/jvmMain/kotlin/com/sunnychung/application/multiplatform/hellohttp/ux/CodeEditorView.kt index 1b8e66bd..07cd4b5e 100644 --- a/src/jvmMain/kotlin/com/sunnychung/application/multiplatform/hellohttp/ux/CodeEditorView.kt +++ b/src/jvmMain/kotlin/com/sunnychung/application/multiplatform/hellohttp/ux/CodeEditorView.kt @@ -34,6 +34,7 @@ import androidx.compose.ui.input.key.key import androidx.compose.ui.input.key.onPreviewKeyEvent import androidx.compose.ui.input.key.type import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.TextLayoutResult import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.font.FontFamily @@ -192,17 +193,18 @@ fun CodeEditorView( isCaseSensitive = false, isWholeWord = false )) } + var searchPattern by rememberLast(searchText, searchOptions) { mutableStateOf(null) } val scrollState = rememberScrollState() val searchBarFocusRequester = remember { FocusRequester() } val textFieldFocusRequester = remember { FocusRequester() } var searchResultViewIndex by rememberLast(text) { mutableStateOf(0) } var lastSearchResultViewIndex by rememberLast(text) { mutableStateOf(0) } - var searchResultRanges by rememberLast(text, searchText, searchOptions) { mutableStateOf?>(null) } + var searchResultRanges by rememberLast(text, searchPattern) { mutableStateOf?>(null) } var textLayoutResult by remember { mutableStateOf(null) } var textFieldSize by remember { mutableStateOf(null) } - if (searchText.isNotEmpty() && searchResultRanges == null) { + if (searchText.isNotEmpty() && searchPattern == null) { val regexOption = if (searchOptions.isCaseSensitive) setOf() else setOf(RegexOption.IGNORE_CASE) try { val pattern = if (searchOptions.isRegex) { @@ -212,7 +214,7 @@ fun CodeEditorView( } else { Pattern.quote(searchText).toRegex(regexOption) } - searchResultRanges = pattern.findAll(textValue.text).map { it.range }.sortedBy { it.start }.toList() + searchPattern = pattern searchResultViewIndex = 0 lastSearchResultViewIndex = -1 } catch (_: Throwable) {} @@ -237,14 +239,28 @@ fun CodeEditorView( } if (isSearchVisible) { - if (!searchResultRanges.isNullOrEmpty()) { + if (!searchResultRanges.isNullOrEmpty() && searchPattern != null) { visualTransformations += SearchHighlightTransformation( - highlightRanges = searchResultRanges!!, + searchPattern = searchPattern!!, currentIndex = searchResultViewIndex, colours = themeColours, ) } + if (searchPattern != null && searchResultRanges == null) { + try { + searchResultRanges = searchPattern!! + .findAll( + MultipleVisualTransformation(visualTransformations) + .filter(AnnotatedString(textValue.text)) + .text.text + ) + .map { it.range } + .sortedBy { it.start } + .toList() + } catch (_: Throwable) {} + } + if (lastSearchResultViewIndex != searchResultViewIndex && textLayoutResult != null && textFieldSize != null && searchResultRanges != null) { lastSearchResultViewIndex = searchResultViewIndex val index = searchResultRanges!!.getOrNull(searchResultViewIndex)?.start diff --git a/src/jvmMain/kotlin/com/sunnychung/application/multiplatform/hellohttp/ux/transformation/SearchHighlightTransformation.kt b/src/jvmMain/kotlin/com/sunnychung/application/multiplatform/hellohttp/ux/transformation/SearchHighlightTransformation.kt index 6bac19b1..52a15453 100644 --- a/src/jvmMain/kotlin/com/sunnychung/application/multiplatform/hellohttp/ux/transformation/SearchHighlightTransformation.kt +++ b/src/jvmMain/kotlin/com/sunnychung/application/multiplatform/hellohttp/ux/transformation/SearchHighlightTransformation.kt @@ -7,12 +7,14 @@ import androidx.compose.ui.text.input.TransformedText import androidx.compose.ui.text.input.VisualTransformation import com.sunnychung.application.multiplatform.hellohttp.ux.local.AppColor -class SearchHighlightTransformation(private val highlightRanges: List, private val currentIndex: Int?, colours: AppColor) : VisualTransformation { +class SearchHighlightTransformation(private val searchPattern: Regex, private val currentIndex: Int?, colours: AppColor) : VisualTransformation { val highlightStyle = SpanStyle(background = colours.backgroundInputFieldHighlight) val currentHighlightStyle = SpanStyle(background = colours.backgroundInputFieldHighlightEmphasize) override fun filter(text: AnnotatedString): TransformedText { val s = text.text + // highlightRanges must be recalculated at this point, because text offset can be changed by other VisualTransformation + val highlightRanges = searchPattern.findAll(s).map { it.range }.sortedBy { it.start }.toList() val spans = highlightRanges.mapIndexed { index, it -> val style = if (index == currentIndex) currentHighlightStyle else highlightStyle AnnotatedString.Range(style, it.start, it.endInclusive + 1)