@@ -29,6 +29,43 @@ import timber.log.Timber
2929private const val MAX_RENDERED_STRING_RESULTS = 5_000
3030private const val MAX_RENDERED_STRING_CHARS = 4_096
3131private const val MAX_RENDERED_STRING_TOTAL_CHARS = 32_768
32+ internal const val MAX_SEARCHED_STRING_BYTES = 4 * 1024 * 1024
33+
34+ data class StringSearchInput (
35+ val bytes : ByteArray ,
36+ val originalSize : Long ,
37+ val isTruncated : Boolean
38+ )
39+
40+ internal fun buildStringSearchInput (
41+ previewBytes : ByteArray ,
42+ originalSize : Long ,
43+ maxBytes : Int = MAX_SEARCHED_STRING_BYTES
44+ ): StringSearchInput {
45+ return StringSearchInput (
46+ bytes = previewBytes,
47+ originalSize = originalSize,
48+ isTruncated = originalSize > maxBytes
49+ )
50+ }
51+
52+ internal fun buildStringSearchNotice (
53+ input : StringSearchInput ,
54+ resultsTruncated : Boolean
55+ ): String? {
56+ val parts = mutableListOf<String >()
57+ if (input.isTruncated) {
58+ parts + = " Searching strings in first ${input.bytes.size} bytes of ${input.originalSize} bytes"
59+ }
60+ if (resultsTruncated) {
61+ parts + = " Showing first $MAX_RENDERED_STRING_RESULTS results."
62+ }
63+ return when {
64+ parts.isEmpty() -> null
65+ parts.size == 1 -> parts.single()
66+ else -> parts.joinToString(" . " )
67+ }
68+ }
3269
3370internal fun clipFoundStringForRendering (
3471 result : FoundString ,
@@ -88,11 +125,20 @@ class StringTabData(val data: TabKind.FoundString) : PreparedTabData() {
88125 val isDone = _isDone as StateFlow <Boolean >
89126 private val _isTruncated = MutableStateFlow (false )
90127 val isTruncated = _isTruncated as StateFlow <Boolean >
128+ private val _notice = MutableStateFlow <String ?>(null )
129+ val notice = _notice as StateFlow <String ?>
91130 lateinit var analyzer: Analyzer
92131 override suspend fun prepare () {
93- val bytes = ProjectDataStorage .getFileContent(data.relPath)
132+ val fileSize = ProjectDataStorage .resolveToRead(data.relPath)?.length() ? : 0L
133+ val input = buildStringSearchInput(
134+ previewBytes = ProjectDataStorage .getFileContentPreview(
135+ data.relPath,
136+ MAX_SEARCHED_STRING_BYTES
137+ ),
138+ originalSize = fileSize
139+ )
94140 Timber .d(" Given relPath: ${data.relPath} " )
95- analyzer = Analyzer (bytes)
141+ analyzer = Analyzer (input. bytes)
96142 val accumulator = StringSearchResultAccumulator (MAX_RENDERED_STRING_RESULTS )
97143 analyzer.searchStrings(data.range.first, data.range.last) { p, t, fs ->
98144 fs?.let {
@@ -103,6 +149,7 @@ class StringTabData(val data: TabKind.FoundString) : PreparedTabData() {
103149 }
104150 if (p == t) { // done
105151 _isTruncated .value = accumulator.isTruncated
152+ _notice .value = buildStringSearchNotice(input, accumulator.isTruncated)
106153 _isDone .value = true
107154 }
108155 }
@@ -116,15 +163,13 @@ fun StringTab(data: TabData, viewModel: MainViewModel) {
116163 val preparedTabData: StringTabData = viewModel.getTabData(data)
117164 val strings = preparedTabData.strings
118165 val isDone = preparedTabData.isDone.collectAsState()
119- val isTruncated = preparedTabData.isTruncated .collectAsState()
166+ val notice = preparedTabData.notice .collectAsState()
120167 Column {
121168 Row {
122169 if (! isDone.value) {
123170 Icon (imageVector = Icons .Default .MoreVert , contentDescription = " Searching..." )
124171 }
125- if (isTruncated.value) {
126- Text (" Showing first $MAX_RENDERED_STRING_RESULTS results" )
127- }
172+ notice.value?.let { Text (it) }
128173 }
129174 TableView (
130175 titles = listOf (" Offset" to 100 .dp, " Length" to 50 .dp, " String" to 800 .dp),
0 commit comments