5050
5151; ;;; Response Display
5252
53+ (defvar-local pi-coding-agent--defer-history-postprocessing nil
54+ " Non-nil while replaying history with batched display post-processing.
55+ When set, per-message fontification and table decoration are skipped;
56+ `pi-coding-agent--postprocess-history-buffer' runs one consolidated pass after
57+ all history has been inserted." )
58+
59+ (defvar-local pi-coding-agent--streaming-table-candidate nil
60+ " Non-nil when recent streaming text may contain a markdown pipe table.
61+ Streaming table decoration is comparatively expensive because it queries the
62+ current message with tree-sitter. Most assistant text is not table content, so
63+ we track whether a pipe has appeared since the last decoration attempt and skip
64+ the query when no table can be present." )
65+
66+ (defun pi-coding-agent--history-postprocessing-deferred-p ()
67+ " Return non-nil when history display post-processing is currently deferred."
68+ pi-coding-agent--defer-history-postprocessing)
69+
70+ (defun pi-coding-agent--decorate-tables-unless-deferred (start end )
71+ " Decorate markdown tables between START and END unless deferred."
72+ (unless (pi-coding-agent--history-postprocessing-deferred-p)
73+ (pi-coding-agent--decorate-tables-in-region start end)))
74+
5375(defun pi-coding-agent--display-user-message (text &optional timestamp )
5476 " Display user message TEXT in the chat buffer.
5577If TIMESTAMP (Emacs time value) is provided, display it in the header."
@@ -58,7 +80,7 @@ If TIMESTAMP (Emacs time value) is provided, display it in the header."
5880 (concat " \n " (pi-coding-agent--make-separator " You" timestamp) " \n "
5981 text " \n " ))
6082 (with-current-buffer (pi-coding-agent--get-chat-buffer)
61- (pi-coding-agent--decorate-tables-in-region start (point-max )))))
83+ (pi-coding-agent--decorate-tables-unless-deferred start (point-max )))))
6284
6385(defun pi-coding-agent--display-agent-start ()
6486 " Display separator for new agent turn.
@@ -79,6 +101,7 @@ Note: status is set to `streaming' by the event handler."
79101 (setq pi-coding-agent--line-parse-state 'line-start )
80102 (setq pi-coding-agent--in-code-block nil )
81103 (setq pi-coding-agent--in-thinking-block nil )
104+ (setq pi-coding-agent--streaming-table-candidate nil )
82105 (pi-coding-agent--set-activity-phase " thinking" ))
83106
84107(defun pi-coding-agent--process-streaming-char (char state in-block )
@@ -178,8 +201,14 @@ fontification; tree-sitter re-parses at the C level on each insert."
178201 (goto-char (marker-position pi-coding-agent--streaming-marker))
179202 (insert transformed)
180203 (set-marker pi-coding-agent--streaming-marker (point ))))
181- ; ; After inserting text with completed lines, check for active table
182- (when (string-match-p " \n " delta)
204+ ; ; After inserting text with completed lines, check for active tables only
205+ ; ; if recent streaming text contained a pipe. This avoids a tree-sitter
206+ ; ; table query on every non-table newline in long assistant messages.
207+ (when (string-match-p " |" delta)
208+ (setq pi-coding-agent--streaming-table-candidate t ))
209+ (when (and pi-coding-agent--streaming-table-candidate
210+ (string-match-p " \n " delta))
211+ (setq pi-coding-agent--streaming-table-candidate nil )
183212 (pi-coding-agent--maybe-decorate-streaming-table)))))
184213
185214(defun pi-coding-agent--thinking-insert-position ()
@@ -1065,7 +1094,7 @@ which asks upfront before any buffers are touched."
10651094 (not (string-empty-p content)))
10661095 (let ((start (point-max )))
10671096 (pi-coding-agent--append-to-chat (concat " \n " content " \n " ))
1068- (pi-coding-agent--decorate-tables-in-region start (point-max )))
1097+ (pi-coding-agent--decorate-tables-unless-deferred start (point-max )))
10691098 ; ; Reset so next assistant message shows its header
10701099 (setq pi-coding-agent--assistant-header-shown nil )))
10711100
@@ -1128,7 +1157,8 @@ Updates buffer-local state and renders display updates."
11281157 (" text_end"
11291158 ; ; Text block ended — finalize any active table that may have
11301159 ; ; a trailing row without newline (backstop for streaming).
1131- (pi-coding-agent--maybe-decorate-streaming-table))
1160+ (pi-coding-agent--maybe-decorate-streaming-table)
1161+ (setq pi-coding-agent--streaming-table-candidate nil ))
11321162 (" thinking_start"
11331163 (pi-coding-agent--display-thinking-start))
11341164 (" thinking_delta"
@@ -2572,7 +2602,7 @@ TIMESTAMP is optional time when compaction occurred."
25722602 'face 'pi-coding-agent-tool-name )
25732603 (or summary " " ) " \n " ))
25742604 (with-current-buffer (pi-coding-agent--get-chat-buffer)
2575- (pi-coding-agent--decorate-tables-in-region start (point-max )))))
2605+ (pi-coding-agent--decorate-tables-unless-deferred start (point-max )))))
25762606
25772607(defun pi-coding-agent--handle-compaction-success (tokens-before summary &optional timestamp )
25782608 " Handle successful compaction: display result and notify user.
@@ -2694,6 +2724,19 @@ Uses the current buffer's completed-thinking display mode."
26942724 (puthash (plist-get msg :toolCallId ) msg index)))))
26952725 index))
26962726
2727+ (defun pi-coding-agent--postprocess-history-buffer ()
2728+ " Run consolidated display post-processing after history replay.
2729+ History replay inserts many small user/assistant chunks. Running fontification
2730+ and table decoration after each chunk is expensive in large sessions, so replay
2731+ defers those operations and performs one full-buffer pass here."
2732+ ; ; History replay should keep rendering even if markdown fontification trips
2733+ ; ; over a tree-sitter/runtime mismatch. Preserve debugger behavior when
2734+ ; ; `debug-on-error' is non-nil.
2735+ (condition-case-unless-debug nil
2736+ (font-lock-ensure (point-min ) (point-max ))
2737+ (error nil ))
2738+ (pi-coding-agent--decorate-tables-in-region (point-min ) (point-max )))
2739+
26972740(defun pi-coding-agent--render-history-text (text )
26982741 " Render TEXT as markdown content with proper isolation.
26992742Ensures markdown structures don't leak to subsequent content.
@@ -2705,10 +2748,11 @@ Display-only table decoration is applied after fontification."
27052748 ; ; History replay should keep rendering even if markdown
27062749 ; ; fontification trips over a tree-sitter/runtime mismatch.
27072750 ; ; Preserve debugger behavior when `debug-on-error' is non-nil.
2708- (condition-case-unless-debug nil
2709- (font-lock-ensure start (point-max ))
2710- (error nil ))
2711- (pi-coding-agent--decorate-tables-in-region start (point-max )))
2751+ (unless (pi-coding-agent--history-postprocessing-deferred-p)
2752+ (condition-case-unless-debug nil
2753+ (font-lock-ensure start (point-max ))
2754+ (error nil ))
2755+ (pi-coding-agent--decorate-tables-in-region start (point-max ))))
27122756 ; ; Two trailing newlines reset any open markdown list/paragraph context
27132757 (pi-coding-agent--append-to-chat " \n\n " ))))
27142758
@@ -3010,13 +3054,15 @@ Note: When called from async callbacks, pass CHAT-BUF explicitly."
30103054 (erase-buffer )
30113055 (insert (pi-coding-agent--format-startup-header) " \n " )
30123056 (when (vectorp messages)
3013- (pi-coding-agent--display-history-messages messages))
3057+ (let ((pi-coding-agent--defer-history-postprocessing t ))
3058+ (pi-coding-agent--display-history-messages messages)))
30143059 (goto-char (point-max ))
30153060 (unless (bolp ) (insert " \n " ))
30163061 (pi-coding-agent--set-message-start-marker nil )
30173062 (pi-coding-agent--set-streaming-marker nil )
30183063 (pi-coding-agent--update-hot-tail-boundary)
30193064 (pi-coding-agent--cool-completed-tool-blocks-outside-hot-tail)
3065+ (pi-coding-agent--postprocess-history-buffer)
30203066 (goto-char (point-max ))))))
30213067
30223068(provide 'pi-coding-agent-render )
0 commit comments