Repository navigation
Typing in the prompt of a long chat rescans the whole history for code fences #333
Description
Typing in the prompt of a long chat rescans the whole history for code fences
In a long chat (217 KB buffer, ~670 tool calls), typing in the prompt becomes noticeably sluggish. After I cleaned up the unrelated noise (see the end), about half of the CPU time per keystroke was in one place:
markdown-find-previous-prop
markdown-find-previous-block
markdown-syntax-propertize-fenced-block-constructs
markdown-syntax-propertize
markdown-syntax-propertize-fenced-block-constructs goes to start and calls markdown-find-previous-block. That runs previous-single-property-change for each fence-begin property, with no limit other than point-min. When a fence type doesn't occur in the buffer at all (tilde fences, YAML metadata), the search walks back to the very start of the chat. So the cost of every keystroke in the prompt grows with the size of the history.
Two paths trigger it on each keystroke:
jit-lock-after-change→eca-chat--font-lock-extend-region-function→markdown-font-lock-extend-region-function→markdown-syntax-propertize(~35%)syntax-ppss→syntax-propertize→eca-chat--syntax-propertize→markdown-syntax-propertize(~20%)
Path 2 is triggered by whatever calls syntax-ppss on insert. For me that was smartparens first. When I turned smartparens off in the chat, the same cost simply moved to Doom's electric-indent comment continuation. So it's not something a user can fix by disabling one minor mode.
Workaround I'm using
When the change starts inside the prompt, narrow to the prompt while the fence scan runs. The history above is already propertized and doesn't change while I type.
One thing that tripped me up: the eca-chat-prompt-area overlay is zero-length. It only marks where the prompt starts, so it has to be looked up across the buffer, not at start.
(defvar-local my/eca-chat--prompt-ov nil)(defun my/eca-chat--prompt-start ()
(unless (and my/eca-chat--prompt-ov (overlay-buffer my/eca-chat--prompt-ov))
(setq my/eca-chat--prompt-ov
(save-restriction
(widen)
(cl-find-if (lambda (o) (overlay-get o 'eca-chat-prompt-area))
(overlays-in (point-min) (point-max))))))
(and my/eca-chat--prompt-ov (overlay-start my/eca-chat--prompt-ov)))
(define-advice markdown-syntax-propertize-fenced-block-constructs
(:around (fn start end) eca-prompt-narrow)
(let ((ps (and (derived-mode-p 'eca-chat-mode) (my/eca-chat--prompt-start))))
(if (and ps (>= start ps))
(save-restriction
(narrow-to-region ps (point-max))
(funcall fn start end))
(funcall fn start end))))
Before and after, same chat, profiling while typing in the prompt:
| before | after | |
|---|---|---|
| markdown-find-previous-prop | 48% | 0% |
| all Lisp under self-insert-command | 38% | 11% |
What's left is mostly redisplay and GC, which is what I'd expect.
Suggestion
Advising markdown-mode from user config is obviously not the right place for this. ECA knows where the prompt starts, so eca-chat--syntax-propertize and eca-chat--font-lock-extend-region-function could narrow to the prompt (or clamp the region) when the change is inside it. That fixes both paths at once, and also anything else that calls syntax-ppss later.
Related: checkbox button leak
Before the above, the bigger problem was a markdown-mode bug: every change on a line with [ ] / [x] adds one more duplicate button overlay. Because ECA updates the task list all the time, my buffer collected ~34.7k duplicate overlays, and redisplay alone took 73% of CPU. I reported that to markdown-mode (https://github.com/jrblevin/markdown-mode/issues/953). Until it's fixed there, ECA could set markdown-make-gfm-checkboxes-buttons to nil in eca-chat-mode before the markdown setup runs. Clickable checkboxes in chat output don't do anything useful anyway, since a click only toggles the rendered text.
Activity
brdloush commentedon Oct 10, 2026
FYI jrblevin/markdown-mode#953 has already been fixed. Although that revision is not yet a part of doom emacs.
ericdallo commentedon Oct 14, 2026
I believe we should wait for this to be bumped in doom so? sounds better than do any workaround in eca-emacs