perf(vscode): fix input lag in long conversations#2550
Conversation
📋 Review SummaryThis PR addresses a critical performance issue (5+ second input lag in long conversations) by extracting the message list into a 🔍 General Feedback
🎯 Specific Feedback🟡 High
🟢 Medium
🔵 Low
✅ Highlights
|
Code Coverage Summary
CLI Package - Full Text ReportCore Package - Full Text ReportFor detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run. |
|
I re-checked this PR against the latest The two high-risk notes do not apply to the current PR head: I also verified the current impact surface with a temporary local merge against the latest |
tanzhenxin
left a comment
There was a problem hiding this comment.
Great catch on the input lag fix! Performance improvements like this make a real difference in daily use. Thanks for tracking this down and fixing it. Approved — merge at your leisure! ⚡
Extract message list into a React.memo component to prevent re-rendering the entire chat history on every keystroke. - Extract MessageList as a memoized component - Wrap UserMessage, AssistantMessage, ThinkingMessage with React.memo - Stabilize onFileClick callback with useCallback - Remove console.log from render path - Wrap handleToggleThinking with useCallback Fixes #2395 Made-with: Cursor
45de456 to
a6884c0
Compare
Merged 6 upstream commits while preserving HopCode architecture: Features synced from upstream: - feat(mcp): OSC 52 copy hotkey for OAuth authorization URL (QwenLM#3393) Press 'c' during OAuth to copy URL via terminal clipboard, works over SSH - feat(cli): early input capture to prevent keystroke loss during startup (QwenLM#3319) Buffers keystrokes during REPL init, replays once UI is mounted - perf(vscode): fix input lag in long conversations via React.memo (QwenLM#2550) MessageList/UserMessage/AssistantMessage wrapped with React.memo - feat(vscode-ide-companion): agent execution tool display (QwenLM#2590) Render dedicated agent execution cards in webview - fix(build): invoke tsx via node --import instead of npx (QwenLM#3237) Fixes bun compatibility for generate:settings-schema script - ci(stale): enable 35+35 stale/close PR policy (QwenLM#3375) Conflict resolution: - packages/vscode-ide-companion/.../toolcalls/index.tsx: kept @hoptrendy/webui, added ToolCallData to imports (upstream added it) - All @qwen-code/* import paths preserved as @hoptrendy/* (HopCode arch) - HopCode branding, version, and CI workflows preserved Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…LM#2550) Extract message list into a React.memo component to prevent re-rendering the entire chat history on every keystroke. - Extract MessageList as a memoized component - Wrap UserMessage, AssistantMessage, ThinkingMessage with React.memo - Stabilize onFileClick callback with useCallback - Remove console.log from render path - Wrap handleToggleThinking with useCallback Fixes QwenLM#2395 Made-with: Cursor
TLDR
Fix severe input lag (5+ second delay) in VS Code IDE Companion when conversations get long. The root cause is that every keystroke triggers a full
Appre-render including the entire chat message list (O(n) cost). By extracting the message list into aReact.memocomponent and memoizing individual message components, keystroke handling is reduced to O(1).Screenshots / Video Demo
N/A — no user-facing change, this is a performance optimization.
Before: In long conversations (many turns), each keystroke triggers a full
Appre-render → reconciles all message DOM nodes → latency grows linearly with message count (5+ seconds).After: Each keystroke only updates
inputTextstate →MessageListprops (allMessages,onFileClick) are unchanged →React.memoskips the entire message list re-render → constant O(1) response time.Dive Deeper
Root Cause Analysis
inputTextstate lives onApp— every keystroke callssetInputTextwhich re-renders the entireAppsubtreeReact.memo—UserMessage,AssistantMessage,ThinkingMessageare plain FCs that fully reconcile on every parent renderonFileClickrecreated inside.map()on every render — defeats the existingmemoonMessageContentconsole.login render path — serializes the entire message array on every keystrokeFix Summary
MessageListas aReact.memocomponentUserMessage/AssistantMessage/ThinkingMessagewithReact.memohandleFileClicktoApptop-level withuseCallbackconsole.log('[App] Rendering messages:', allMessages)handleToggleThinkingwithuseCallbackReviewer Test Plan
@file completion and/command completion still workTesting Matrix
Linked issues / bugs
Fixes #2395