From 8c7c087931154fc4d6d7e5b40553b9c961271d1d Mon Sep 17 00:00:00 2001 From: Judy Date: Wed, 28 May 2025 21:33:28 +0900 Subject: [PATCH] feat: TODO multi-line --- pr_agent/algo/utils.py | 41 ++++++++++++++++++---- pr_agent/settings/pr_reviewer_prompts.toml | 2 +- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/pr_agent/algo/utils.py b/pr_agent/algo/utils.py index 49768168..d89cb212 100644 --- a/pr_agent/algo/utils.py +++ b/pr_agent/algo/utils.py @@ -1,5 +1,6 @@ from __future__ import annotations +import ast import copy import difflib import hashlib @@ -53,7 +54,7 @@ class ModelType(str, Enum): class TodoItem(TypedDict): relevant_file: str - line_number: int + line_range: Tuple[int, int] content: str @@ -222,25 +223,51 @@ def convert_to_markdown_v2(output_data: dict, value = emphasize_header(value.strip(), only_markdown=True) markdown_text += f"{value}\n\n" elif 'todo sections' in key_nice.lower(): + + def format_multiline_html_item(file: str, line_range: Tuple[int, int], content: str, url: str) -> str: + label = f"{file} [{line_range[0]}-{line_range[1]}]" if line_range[0] != line_range[1] else f"{file} [{line_range[0]}]" + first_line, *rest_lines = content.strip().split("\n") + if rest_lines: + rest = "
".join(rest_lines) + return f"
  • {label}: {first_line}
    {rest}
  • " + else: + return f"
  • {label}: {first_line}
  • " + def format_todo_item(todo_item: TodoItem) -> str: relevant_file = todo_item.get('relevant_file', '').strip() - line_number = todo_item.get('line_number', '') + line_range = todo_item.get('line_range', []) content = todo_item.get('content', '') reference_link = None + + if isinstance(line_range, str): + line_range = ast.literal_eval(line_range.strip()) try: - if git_provider and relevant_file and line_number: - reference_link = git_provider.get_line_link(relevant_file, line_number, line_number) + if git_provider and relevant_file and line_range: + reference_link = git_provider.get_line_link(relevant_file, line_range[0], line_range[1]) except Exception as e: get_logger().exception(f"Error generating link: {e}") - return f"{relevant_file} [{line_number}]: {content}" + line_str = f"[{line_range[0]}]" if line_range[0] == line_range[1] else f"[{line_range[0]}-{line_range[1]}]" + return f"{relevant_file} {line_str}: {content}" - file_line = f"{relevant_file} [{line_number}]" + line_str = f"[{line_range[0]}]" if line_range[0] == line_range[1] else f"[{line_range[0]}-{line_range[1]}]" + file_line = f"{relevant_file} {line_str}" if reference_link: if gfm_supported: file_line = f"{file_line}" else: file_line = f"[{file_line}]({reference_link})" - return f"{file_line}: {content}" if content.strip() else file_line + + content_lines = content.strip().split("\n") + # if TODO content is single-line : + if len(content_lines) == 1: + return f"{file_line}: {content_lines[0]}" + # else if TODO content is multi-line: + elif len(content_lines) > 1: + content_lines = "
    ".join(content_lines) + return f"{file_line}:
    \n{content_lines}\n
    " + # else if TODO content is empty: + else: + return file_line if gfm_supported: markdown_text += f"" diff --git a/pr_agent/settings/pr_reviewer_prompts.toml b/pr_agent/settings/pr_reviewer_prompts.toml index 29a73a49..c0bb8b1b 100644 --- a/pr_agent/settings/pr_reviewer_prompts.toml +++ b/pr_agent/settings/pr_reviewer_prompts.toml @@ -74,7 +74,7 @@ class KeyIssuesComponentLink(BaseModel): class TodoSection(BaseModel): relevant_file: str = Field(description="The file containing the TODO comment") - line_number: int = Field(description="The line number of the TODO comment") + line_range: Tuple[int, int] = Field(description="Start and end line numbers of the TODO comment (inclusive). Must be a tuple of two integers, e.g., (7, 7) for a single line or (7, 10) for a range. Do not use list format [7, 7].") content: str = Field(description="The content of the TODO comment. Only include actual TODO comments within code comments (e.g., lines starting with '#', '//', '/*', '