Add GitHub-style suggestion blocks for code review comments#199
Add GitHub-style suggestion blocks for code review comments#199takumi0706 wants to merge 9 commits intoyoshiko-pg:mainfrom
Conversation
Add TypeScript interface for parsed suggestion block data structure containing original code, suggested code, language, and position info.
- hasSuggestionBlock: Check if text contains suggestion block - parseSuggestionBlocks: Parse suggestion blocks with original code - createSuggestionTemplate: Generate suggestion template for editor - extractFirstSuggestion: Extract first suggestion code from body - formatSuggestionForPrompt: Format suggestion for AI prompt output Includes comprehensive test coverage (20 tests).
- Add selectedCode and language props - Add toolbar with "Add suggestion" button - Button inserts suggestion template into textarea - Button disabled when no code is selected
- Add getSelectedCodeContent() to extract code from selected lines - Pass selectedCode and language props to CommentForm - Include codeContent when adding comments for suggestion support
- Add SuggestionBlockRenderer component - Display original code with red background and "-" prefix - Display suggested code with green background and "+" prefix - Parse suggestion blocks using suggestionUtils
- Format suggestions with ORIGINAL/SUGGESTED blocks for AI prompts - Extract context text before suggestion blocks - Add tests for suggestion prompt formatting
- Replace type assertions with instanceof checks in SideBySideDiffChunk - Fix language detection to use file path instead of code content - Simplify hasSuggestionBlock regex without global flag
Pass codeSnapshot.content to formatCommentPrompt so that ORIGINAL block is included in the generated prompt for suggestion comments.
|
@yoshiko-pg Oops, sorry! I thought I had pasted the UI and prompt, but it turns out I was still editing 😅 |
|
@takumi0706 Thank you! |
yoshiko-pg
left a comment
There was a problem hiding this comment.
@takumi0706 Thank you for waiting! I've reviewed it!
It seems there are multiple instances of dead code, so I'd appreciate it if you could remove them first, and then address the other comments!
Please let me know if there are any contents that are difficult to handle.
| const end = textarea.selectionEnd; | ||
| const before = body.slice(0, start); | ||
| const after = body.slice(end); | ||
| const newBody = before + (before && !before.endsWith('\n') ? '\n' : '') + template + after; |
There was a problem hiding this comment.
When inserting a suggestion in the middle of existing text, after is appended immediately after the closing fence. If after doesn't start with \n, the closing ``` and trailing text can end up on the same line, breaking the suggestion block.
Could we add a trailing newline when needed (similar to the leading newline logic)?
There was a problem hiding this comment.
Fixed. Added a newline between the template and after text when after doesn't start with \n, so the closing fence won't merge with trailing text.
src/utils/commentFormatting.ts
Outdated
|
|
||
| // Check if body contains a suggestion block | ||
| if (hasSuggestionBlock(body)) { | ||
| const suggestedCode = extractFirstSuggestion(body); |
There was a problem hiding this comment.
This only uses extractFirstSuggestion() and keeps text before the first block, so text after the first suggestion (and additional suggestion blocks) is dropped.
Could we parse all suggestion blocks and preserve surrounding text in order?
There was a problem hiding this comment.
Replaced extractFirstSuggestion() with parseSuggestionBlocks() and now iterate over all blocks, preserving text between and after them 👍
| </div> | ||
| ); | ||
| })} | ||
| </div> |
There was a problem hiding this comment.
Could we reuse the existing diff line renderer here instead of hand-building +/- rows?
This duplicates diff-row rendering logic and can drift from the main file diff view (styles, highlighting behavior, future UI changes).
There was a problem hiding this comment.
The existing DiffLineRow is -based with many required props, so direct reuse wasn't practical here. Instead, extracted a shared SuggestionDiffLine component that uses the same Tailwind diff classes (bg-diff-addition-bg / bg-diff-deletion-bg) to keep the visual style consistent!
src/utils/suggestionUtils.ts
Outdated
| /** | ||
| * Parse all suggestion blocks from a comment body | ||
| */ | ||
| export function parseSuggestionBlocks( |
There was a problem hiding this comment.
parseSuggestionBlocks currently mixes parsing with injected view context (originalCode/language). Would it be simpler to keep this function as a pure parser of body only, and attach rendering context at the caller side?
There was a problem hiding this comment.
Refactored to a pure parser — now only takes body and returns { suggestedCode, startIndex, endIndex }. Rendering context (originalCode) is attached at the caller side.
src/utils/suggestionUtils.ts
Outdated
| /** | ||
| * Format a suggestion for prompt output with ORIGINAL/SUGGESTED structure | ||
| */ | ||
| export function formatSuggestionForPrompt( |
There was a problem hiding this comment.
formatSuggestionForPrompt looks unused in runtime code. Can we remove unused helper APIs for now and keep this module minimal until we have a concrete caller?
There was a problem hiding this comment.
Removed. The equivalent functionality is already covered by formatCommentPrompt() in commentFormatting.ts.
src/types/diff.ts
Outdated
| startIndex: number; // Start position in the comment body | ||
| endIndex: number; // End position in the comment body | ||
| } | ||
|
|
There was a problem hiding this comment.
SuggestionBlock seems to be an internal shape used only by suggestion parsing/rendering utilities. Would it be better to keep this type local to that module instead of placing it in global diff types?
There was a problem hiding this comment.
Moved SuggestionBlock to suggestionUtils.ts and removed it from global diff types.
src/utils/suggestionUtils.ts
Outdated
| /** | ||
| * Create a suggestion template with the given code | ||
| */ | ||
| export function createSuggestionTemplate(code: string, _language?: string): string { |
There was a problem hiding this comment.
createSuggestionTemplate(code, _language?) accepts a language argument that is currently unused. If we don't plan to use it soon, could we drop it from the signature and simplify the call sites?
There was a problem hiding this comment.
Dropped the language parameter from the signature🫳
src/client/components/DiffChunk.tsx
Outdated
| onSubmit={handleSubmitComment} | ||
| onCancel={handleCancelComment} | ||
| selectedCode={getSelectedCodeContent()} | ||
| language={filename ? getLanguageFromPath(filename) : undefined} |
There was a problem hiding this comment.
language={filename ? getLanguageFromPath(filename) : undefined} is passed into CommentForm, but this value is not used in the current suggestion flow. This looks like dead prop plumbing; could we remove the prop (and related imports) to keep the implementation minimal?
There was a problem hiding this comment.
Removed the language prop from CommentForm and cleaned up related imports in DiffChunk and SideBySideDiffChunk🧹
- Remove dead code: formatSuggestionForPrompt, extractFirstSuggestion - Remove unused language parameter from createSuggestionTemplate - Remove language prop plumbing from DiffChunk/SideBySideDiffChunk/CommentForm - Localize SuggestionBlock type to suggestionUtils module - Make parseSuggestionBlocks a pure body-only parser - Handle all suggestion blocks in prompt output (not just the first) - Extract SuggestionDiffLine component for suggestion diff rendering - Fix newline insertion position in CommentForm suggestion template
|
@yoshiko-pg I've addressed all the comments. For the diff line renderer, I wasn't able to directly reuse DiffLineRow since it's -based with many required props, but I extracted a shared SuggestionDiffLine component using the same Tailwind diff classes to keep the visual style consistent. Happy to adjust if you had a different approach in mind. Thanks again for taking the time to review — really appreciate it!❤️🔥 |
yoshiko-pg
left a comment
There was a problem hiding this comment.
@takumi0706 Thank you for your quick response!
I've confirmed that it has been corrected!
I've just commented on one point that I overlooked; if you could address that, I'd like to merge it!
| [comments], | ||
| ); | ||
|
|
||
| const generateAllCommentsPrompt = useCallback((): string => { |
There was a problem hiding this comment.
I think generateAllCommentsPrompt() still drops suggestion context for ORIGINAL: blocks. In transformedComments, we pass file/line/body/timestamp but not codeContent (from comment.codeSnapshot?.content), so suggestion prompts generated via "Copy All Comments" lose the original code context even though single-comment prompt generation keeps it.
And could we add a test for generateAllCommentsPrompt() with a suggestion comment that includes codeSnapshot.content? Right now the tests cover regular comments, but they don't assert that "Copy All Comments" preserves ORIGINAL + SUGGESTED output for suggestion blocks.
Summary
Testing
For Example
UI
Prompt
Closes #197