Skip to content

Commit 956cfc3

Browse files
committed
fea: Add select multiple files to merge log displays
1 parent 0440efa commit 956cfc3

File tree

2 files changed

+96
-4
lines changed

2 files changed

+96
-4
lines changed

ui/log/src/main/kotlin/me/gegenbauer/catspy/log/ui/tab/FileLogGuidancePanel.kt

Lines changed: 95 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,20 @@ package me.gegenbauer.catspy.log.ui.tab
33
import info.clearthought.layout.TableLayout
44
import info.clearthought.layout.TableLayoutConstants.FILL
55
import info.clearthought.layout.TableLayoutConstants.PREFERRED
6+
import kotlinx.coroutines.CoroutineScope
7+
import kotlinx.coroutines.Dispatchers
8+
import kotlinx.coroutines.GlobalScope
9+
import kotlinx.coroutines.async
10+
import kotlinx.coroutines.awaitAll
11+
import kotlinx.coroutines.channels.Channel
12+
import kotlinx.coroutines.coroutineScope
13+
import kotlinx.coroutines.delay
14+
import kotlinx.coroutines.flow.flow
15+
import kotlinx.coroutines.flow.onCompletion
16+
import kotlinx.coroutines.flow.onStart
17+
import kotlinx.coroutines.launch
18+
import kotlinx.coroutines.runBlocking
19+
import kotlinx.coroutines.withContext
620
import me.gegenbauer.catspy.file.toHumanReadableSize
721
import me.gegenbauer.catspy.iconset.GIcons
822
import me.gegenbauer.catspy.java.ext.EMPTY_STRING
@@ -26,6 +40,7 @@ import java.awt.event.ActionListener
2640
import java.awt.event.MouseAdapter
2741
import java.awt.event.MouseEvent
2842
import java.io.File
43+
import java.nio.charset.Charset
2944
import java.text.SimpleDateFormat
3045
import java.util.*
3146
import javax.swing.BorderFactory
@@ -76,16 +91,93 @@ class FileLogGuidancePanel(
7691
recentFilesPanel.setFiles(files)
7792
}
7893

94+
// 核心协程版实现
95+
private suspend fun mergeFilesWithCoroutine(
96+
sourceFiles: List<File>,
97+
targetFile: File,
98+
startMarkerTemplate: String = "── %s START ──", // 允许自定义标记格式
99+
endMarkerTemplate: String = "── %s END ──",
100+
markerCharset: String = "UTF-8"
101+
) = coroutineScope {
102+
require(sourceFiles.isNotEmpty()) { "Source files cannot be empty" }
103+
104+
// 准备目标文件
105+
targetFile.apply {
106+
parentFile?.mkdirs()
107+
delete()
108+
}
109+
110+
val channel = Channel<ByteArray>(capacity = 1024)
111+
val charset = Charset.forName(markerCharset)
112+
113+
// 启动单个写入协程
114+
val writerJob = launch(Dispatchers.IO) {
115+
targetFile.outputStream().buffered().use { os ->
116+
for (bytes in channel) {
117+
os.write(bytes)
118+
}
119+
}
120+
}
121+
122+
sourceFiles.asReversed().forEach { file ->
123+
launch(Dispatchers.IO) {
124+
try {
125+
// 写入开始标记
126+
val startMarker = startMarkerTemplate.format(file.name)
127+
channel.send(startMarker.toByteArray(charset))
128+
channel.send("\n".toByteArray(charset))
129+
130+
// 写入文件内容
131+
file.inputStream().buffered().use { ins ->
132+
val buffer = ByteArray(8192)
133+
var bytesRead: Int
134+
while (ins.read(buffer).also { bytesRead = it } != -1) {
135+
channel.send(buffer.copyOf(bytesRead))
136+
}
137+
}
138+
139+
// 写入结束标记
140+
val endMarker = endMarkerTemplate.format(file.name)
141+
channel.send("\n".toByteArray(charset))
142+
channel.send(endMarker.toByteArray(charset))
143+
channel.send("\n\n".toByteArray(charset)) // 文件间空行
144+
} catch (e: Exception) {
145+
// 错误标记
146+
channel.send("[ERROR processing ${file.name}: ${e.message}]".toByteArray(charset))
147+
throw e
148+
}
149+
}.join() // 保证文件顺序处理
150+
}
151+
152+
channel.close()
153+
writerJob.join()
154+
}
155+
79156
private fun onClickFileOpen() {
80157
val files = showSelectSingleFileDialog(
81158
findFrameFromParent(),
82159
STRINGS.ui.openFile,
83160
RecentLogFiles.getLastOpenDir(),
84161
)
85-
files.firstOrNull()?.let {
86-
onOpenFile(it)
87-
RecentLogFiles.onNewFileOpen(it.absolutePath)
162+
runBlocking {
163+
var openFile = File(files.first().absolutePath)
164+
if (files.size >=2){
165+
// 获取files第一个文件的地址
166+
openFile = File(files.first().parentFile.absolutePath+File.separator+"merge.log")
167+
168+
val mergeJob = GlobalScope.launch(Dispatchers.IO) {
169+
mergeFilesWithCoroutine(files, openFile)
170+
}
171+
mergeJob.join()
172+
print(">>> after last file size:"+openFile.length())
173+
}
174+
175+
files.firstOrNull()?.let {
176+
onOpenFile(openFile)
177+
RecentLogFiles.onNewFileOpen(openFile.absolutePath)
178+
}
88179
}
180+
89181
}
90182

91183
private inner class RecentFilesPanel : ScrollConstrainedScrollablePanel(

ui/utils/src/main/kotlin/me/gegenbauer/catspy/utils/ui/Dialogs.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ fun showSelectSingleFileDialog(
102102
frame,
103103
title,
104104
dir,
105-
false,
105+
true,
106106
)
107107
}
108108

0 commit comments

Comments
 (0)