@@ -753,60 +753,20 @@ class DialogLogFragment : DialogFragment() {
753753 private fun updateLogWithColor (text : String , forcedColor : Int? ) {
754754 var parsedLog: CharSequence = AnsiColorParser .parse(text)
755755
756- // Lệnh `clear` (hoặc chương trình vẽ lại 1 phần màn hình như progress bar) sẽ phát ra
757- // mã CSI "Erase in Display" (ESC[nJ), được AnsiColorParser nhận diện ở trên. Vì log
758- // buffer ở đây chỉ append tuần tự (không theo dõi toạ độ cursor thật như terminal),
759- // ta xấp xỉ "vị trí cursor" như sau:
760- // - Nếu vừa gặp '\r' (pendingOverwrite = true, đang chờ ghi đè dòng hiện tại) -> cursor
761- // coi như đang ở ĐẦU dòng hiện tại (lineStart).
762- // - Ngược lại (append bình thường, chưa ghi đè gì) -> cursor coi như đang ở CUỐI buffer.
763- // Xấp xỉ này khớp chính xác với các chương trình dùng '\r' để vẽ lại dòng (progress
764- // bar, spinner...), nhưng sẽ không chính xác nếu chương trình dùng CUP (ESC[r;cH) để
765- // di chuyển cursor tự do - các mã di chuyển cursor này hiện chưa được theo dõi.
766- when (AnsiColorParser .consumePendingErase()) {
767- 2 , 3 -> {
768- // Xoá toàn bộ màn hình (lệnh `clear`)
769- synchronized(logBuffer) {
770- logBuffer.clear()
771- lineCount = 0
772- lineStart = 0
773- pendingOverwrite = false
774- markInvalidFrom(0 )
775- }
776- }
777- 0 -> {
778- // Xoá từ cursor (xấp xỉ) tới hết buffer
779- synchronized(logBuffer) {
780- val cursorPos = (if (pendingOverwrite) lineStart else logBuffer.length)
781- .coerceIn(0 , logBuffer.length)
782- if (cursorPos < logBuffer.length) {
783- logBuffer.delete(cursorPos, logBuffer.length)
784- markInvalidFrom(cursorPos)
785- }
786- pendingOverwrite = false
787- }
788- }
789- 1 -> {
790- // Xoá từ đầu buffer tới cursor (xấp xỉ). Vì phần đầu bị xoá, mọi toạ độ phía
791- // sau đều dịch chuyển - xử lý tương tự logic "cắt tỉa 5000 dòng" bên dưới.
792- synchronized(logBuffer) {
793- val cursorPos = (if (pendingOverwrite) lineStart else logBuffer.length)
794- .coerceIn(0 , logBuffer.length)
795- if (cursorPos > 0 ) {
796- var removedLines = 0
797- for (k in 0 until cursorPos) {
798- if (logBuffer[k] == ' \n ' ) removedLines++
799- }
800- logBuffer.delete(0 , cursorPos)
801- lineCount = (lineCount - removedLines).coerceAtLeast(0 )
802- lineStart = (lineStart - cursorPos).coerceAtLeast(0 )
803- uiAppliedLength = (uiAppliedLength - cursorPos).coerceAtLeast(0 )
804- uiInvalidFrom = 0
805- }
806- pendingOverwrite = false
807- }
756+ // Lệnh `clear` trong script sẽ phát ra mã CSI xoá màn hình (ESC[2J/ESC[3J), được
757+ // AnsiColorParser nhận diện ở trên. Xoá sạch logBuffer ngay tại đây (background
758+ // thread) và đánh dấu invalid từ vị trí 0 để lần flushToUi() kế tiếp thay thế TOÀN
759+ // BỘ nội dung cũ đang hiển thị bằng nội dung mới (thường là rỗng), giống hệt cách
760+ // xử lý ghi đè do '\r' - không cần đụng trực tiếp vào UI thread, tránh race với
761+ // pendingUiUpdate/flushToUi.
762+ if (AnsiColorParser .consumePendingClear()) {
763+ synchronized(logBuffer) {
764+ logBuffer.clear()
765+ lineCount = 0
766+ lineStart = 0
767+ pendingOverwrite = false
768+ markInvalidFrom(0 )
808769 }
809- else -> {}
810770 }
811771
812772 if (forcedColor != null && ! text.contains(" \u001B [" )) {
0 commit comments