chore: standardize string quotes and fix import ordering across multiple modules
This commit is contained in:
+8
-2
@@ -1,5 +1,11 @@
|
||||
from pr.ui.colors import Colors
|
||||
from pr.ui.rendering import highlight_code, render_markdown
|
||||
from pr.ui.display import display_tool_call, print_autonomous_header
|
||||
from pr.ui.rendering import highlight_code, render_markdown
|
||||
|
||||
__all__ = ['Colors', 'highlight_code', 'render_markdown', 'display_tool_call', 'print_autonomous_header']
|
||||
__all__ = [
|
||||
"Colors",
|
||||
"highlight_code",
|
||||
"render_markdown",
|
||||
"display_tool_call",
|
||||
"print_autonomous_header",
|
||||
]
|
||||
|
||||
+13
-13
@@ -1,14 +1,14 @@
|
||||
class Colors:
|
||||
RESET = '\033[0m'
|
||||
BOLD = '\033[1m'
|
||||
RED = '\033[91m'
|
||||
GREEN = '\033[92m'
|
||||
YELLOW = '\033[93m'
|
||||
BLUE = '\033[94m'
|
||||
MAGENTA = '\033[95m'
|
||||
CYAN = '\033[96m'
|
||||
GRAY = '\033[90m'
|
||||
WHITE = '\033[97m'
|
||||
BG_BLUE = '\033[44m'
|
||||
BG_GREEN = '\033[42m'
|
||||
BG_RED = '\033[41m'
|
||||
RESET = "\033[0m"
|
||||
BOLD = "\033[1m"
|
||||
RED = "\033[91m"
|
||||
GREEN = "\033[92m"
|
||||
YELLOW = "\033[93m"
|
||||
BLUE = "\033[94m"
|
||||
MAGENTA = "\033[95m"
|
||||
CYAN = "\033[96m"
|
||||
GRAY = "\033[90m"
|
||||
WHITE = "\033[97m"
|
||||
BG_BLUE = "\033[44m"
|
||||
BG_GREEN = "\033[42m"
|
||||
BG_RED = "\033[41m"
|
||||
|
||||
+106
-60
@@ -1,5 +1,6 @@
|
||||
import difflib
|
||||
from typing import List, Tuple, Dict, Optional
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
|
||||
from .colors import Colors
|
||||
|
||||
|
||||
@@ -19,8 +20,13 @@ class DiffStats:
|
||||
|
||||
|
||||
class DiffLine:
|
||||
def __init__(self, line_type: str, content: str, old_line_num: Optional[int] = None,
|
||||
new_line_num: Optional[int] = None):
|
||||
def __init__(
|
||||
self,
|
||||
line_type: str,
|
||||
content: str,
|
||||
old_line_num: Optional[int] = None,
|
||||
new_line_num: Optional[int] = None,
|
||||
):
|
||||
self.line_type = line_type
|
||||
self.content = content
|
||||
self.old_line_num = old_line_num
|
||||
@@ -28,27 +34,27 @@ class DiffLine:
|
||||
|
||||
def format(self, show_line_nums: bool = True) -> str:
|
||||
color = {
|
||||
'add': Colors.GREEN,
|
||||
'delete': Colors.RED,
|
||||
'context': Colors.GRAY,
|
||||
'header': Colors.CYAN,
|
||||
'stats': Colors.BLUE
|
||||
"add": Colors.GREEN,
|
||||
"delete": Colors.RED,
|
||||
"context": Colors.GRAY,
|
||||
"header": Colors.CYAN,
|
||||
"stats": Colors.BLUE,
|
||||
}.get(self.line_type, Colors.RESET)
|
||||
|
||||
prefix = {
|
||||
'add': '+ ',
|
||||
'delete': '- ',
|
||||
'context': ' ',
|
||||
'header': '',
|
||||
'stats': ''
|
||||
}.get(self.line_type, ' ')
|
||||
"add": "+ ",
|
||||
"delete": "- ",
|
||||
"context": " ",
|
||||
"header": "",
|
||||
"stats": "",
|
||||
}.get(self.line_type, " ")
|
||||
|
||||
if show_line_nums and self.line_type in ('add', 'delete', 'context'):
|
||||
old_num = str(self.old_line_num) if self.old_line_num else ' '
|
||||
new_num = str(self.new_line_num) if self.new_line_num else ' '
|
||||
if show_line_nums and self.line_type in ("add", "delete", "context"):
|
||||
old_num = str(self.old_line_num) if self.old_line_num else " "
|
||||
new_num = str(self.new_line_num) if self.new_line_num else " "
|
||||
line_num_str = f"{Colors.YELLOW}{old_num:>4} {new_num:>4}{Colors.RESET} "
|
||||
else:
|
||||
line_num_str = ''
|
||||
line_num_str = ""
|
||||
|
||||
return f"{line_num_str}{color}{prefix}{self.content}{Colors.RESET}"
|
||||
|
||||
@@ -57,8 +63,9 @@ class DiffDisplay:
|
||||
def __init__(self, context_lines: int = 3):
|
||||
self.context_lines = context_lines
|
||||
|
||||
def create_diff(self, old_content: str, new_content: str,
|
||||
filename: str = "file") -> Tuple[List[DiffLine], DiffStats]:
|
||||
def create_diff(
|
||||
self, old_content: str, new_content: str, filename: str = "file"
|
||||
) -> Tuple[List[DiffLine], DiffStats]:
|
||||
old_lines = old_content.splitlines(keepends=True)
|
||||
new_lines = new_content.splitlines(keepends=True)
|
||||
|
||||
@@ -67,31 +74,38 @@ class DiffDisplay:
|
||||
stats.files_changed = 1
|
||||
|
||||
diff = difflib.unified_diff(
|
||||
old_lines, new_lines,
|
||||
old_lines,
|
||||
new_lines,
|
||||
fromfile=f"a/{filename}",
|
||||
tofile=f"b/{filename}",
|
||||
n=self.context_lines
|
||||
n=self.context_lines,
|
||||
)
|
||||
|
||||
old_line_num = 0
|
||||
new_line_num = 0
|
||||
|
||||
for line in diff:
|
||||
if line.startswith('---') or line.startswith('+++'):
|
||||
diff_lines.append(DiffLine('header', line.rstrip()))
|
||||
elif line.startswith('@@'):
|
||||
diff_lines.append(DiffLine('header', line.rstrip()))
|
||||
if line.startswith("---") or line.startswith("+++"):
|
||||
diff_lines.append(DiffLine("header", line.rstrip()))
|
||||
elif line.startswith("@@"):
|
||||
diff_lines.append(DiffLine("header", line.rstrip()))
|
||||
old_line_num, new_line_num = self._parse_hunk_header(line)
|
||||
elif line.startswith('+'):
|
||||
elif line.startswith("+"):
|
||||
stats.insertions += 1
|
||||
diff_lines.append(DiffLine('add', line[1:].rstrip(), None, new_line_num))
|
||||
diff_lines.append(
|
||||
DiffLine("add", line[1:].rstrip(), None, new_line_num)
|
||||
)
|
||||
new_line_num += 1
|
||||
elif line.startswith('-'):
|
||||
elif line.startswith("-"):
|
||||
stats.deletions += 1
|
||||
diff_lines.append(DiffLine('delete', line[1:].rstrip(), old_line_num, None))
|
||||
diff_lines.append(
|
||||
DiffLine("delete", line[1:].rstrip(), old_line_num, None)
|
||||
)
|
||||
old_line_num += 1
|
||||
elif line.startswith(' '):
|
||||
diff_lines.append(DiffLine('context', line[1:].rstrip(), old_line_num, new_line_num))
|
||||
elif line.startswith(" "):
|
||||
diff_lines.append(
|
||||
DiffLine("context", line[1:].rstrip(), old_line_num, new_line_num)
|
||||
)
|
||||
old_line_num += 1
|
||||
new_line_num += 1
|
||||
|
||||
@@ -101,15 +115,20 @@ class DiffDisplay:
|
||||
|
||||
def _parse_hunk_header(self, header: str) -> Tuple[int, int]:
|
||||
try:
|
||||
parts = header.split('@@')[1].strip().split()
|
||||
old_start = int(parts[0].split(',')[0].replace('-', ''))
|
||||
new_start = int(parts[1].split(',')[0].replace('+', ''))
|
||||
parts = header.split("@@")[1].strip().split()
|
||||
old_start = int(parts[0].split(",")[0].replace("-", ""))
|
||||
new_start = int(parts[1].split(",")[0].replace("+", ""))
|
||||
return old_start, new_start
|
||||
except (IndexError, ValueError):
|
||||
return 0, 0
|
||||
|
||||
def render_diff(self, diff_lines: List[DiffLine], stats: DiffStats,
|
||||
show_line_nums: bool = True, show_stats: bool = True) -> str:
|
||||
def render_diff(
|
||||
self,
|
||||
diff_lines: List[DiffLine],
|
||||
stats: DiffStats,
|
||||
show_line_nums: bool = True,
|
||||
show_stats: bool = True,
|
||||
) -> str:
|
||||
output = []
|
||||
|
||||
if show_stats:
|
||||
@@ -124,10 +143,15 @@ class DiffDisplay:
|
||||
if show_stats:
|
||||
output.append(f"\n{Colors.BOLD}{Colors.BLUE}{'=' * 60}{Colors.RESET}\n")
|
||||
|
||||
return '\n'.join(output)
|
||||
return "\n".join(output)
|
||||
|
||||
def display_file_diff(self, old_content: str, new_content: str,
|
||||
filename: str = "file", show_line_nums: bool = True) -> str:
|
||||
def display_file_diff(
|
||||
self,
|
||||
old_content: str,
|
||||
new_content: str,
|
||||
filename: str = "file",
|
||||
show_line_nums: bool = True,
|
||||
) -> str:
|
||||
diff_lines, stats = self.create_diff(old_content, new_content, filename)
|
||||
|
||||
if not diff_lines:
|
||||
@@ -135,8 +159,13 @@ class DiffDisplay:
|
||||
|
||||
return self.render_diff(diff_lines, stats, show_line_nums)
|
||||
|
||||
def display_side_by_side(self, old_content: str, new_content: str,
|
||||
filename: str = "file", width: int = 80) -> str:
|
||||
def display_side_by_side(
|
||||
self,
|
||||
old_content: str,
|
||||
new_content: str,
|
||||
filename: str = "file",
|
||||
width: int = 80,
|
||||
) -> str:
|
||||
old_lines = old_content.splitlines()
|
||||
new_lines = new_content.splitlines()
|
||||
|
||||
@@ -144,40 +173,57 @@ class DiffDisplay:
|
||||
output = []
|
||||
|
||||
output.append(f"\n{Colors.BOLD}{Colors.BLUE}{'=' * width}{Colors.RESET}")
|
||||
output.append(f"{Colors.BOLD}{Colors.BLUE}SIDE-BY-SIDE COMPARISON: {filename}{Colors.RESET}")
|
||||
output.append(
|
||||
f"{Colors.BOLD}{Colors.BLUE}SIDE-BY-SIDE COMPARISON: {filename}{Colors.RESET}"
|
||||
)
|
||||
output.append(f"{Colors.BOLD}{Colors.BLUE}{'=' * width}{Colors.RESET}\n")
|
||||
|
||||
half_width = (width - 5) // 2
|
||||
|
||||
for tag, i1, i2, j1, j2 in matcher.get_opcodes():
|
||||
if tag == 'equal':
|
||||
for i, (old_line, new_line) in enumerate(zip(old_lines[i1:i2], new_lines[j1:j2])):
|
||||
if tag == "equal":
|
||||
for i, (old_line, new_line) in enumerate(
|
||||
zip(old_lines[i1:i2], new_lines[j1:j2])
|
||||
):
|
||||
old_display = old_line[:half_width].ljust(half_width)
|
||||
new_display = new_line[:half_width].ljust(half_width)
|
||||
output.append(f"{Colors.GRAY}{old_display} | {new_display}{Colors.RESET}")
|
||||
elif tag == 'replace':
|
||||
output.append(
|
||||
f"{Colors.GRAY}{old_display} | {new_display}{Colors.RESET}"
|
||||
)
|
||||
elif tag == "replace":
|
||||
max_lines = max(i2 - i1, j2 - j1)
|
||||
for i in range(max_lines):
|
||||
old_line = old_lines[i1 + i] if i1 + i < i2 else ""
|
||||
new_line = new_lines[j1 + i] if j1 + i < j2 else ""
|
||||
old_display = old_line[:half_width].ljust(half_width)
|
||||
new_display = new_line[:half_width].ljust(half_width)
|
||||
output.append(f"{Colors.RED}{old_display}{Colors.RESET} | {Colors.GREEN}{new_display}{Colors.RESET}")
|
||||
elif tag == 'delete':
|
||||
output.append(
|
||||
f"{Colors.RED}{old_display}{Colors.RESET} | {Colors.GREEN}{new_display}{Colors.RESET}"
|
||||
)
|
||||
elif tag == "delete":
|
||||
for old_line in old_lines[i1:i2]:
|
||||
old_display = old_line[:half_width].ljust(half_width)
|
||||
output.append(f"{Colors.RED}{old_display} | {' ' * half_width}{Colors.RESET}")
|
||||
elif tag == 'insert':
|
||||
output.append(
|
||||
f"{Colors.RED}{old_display} | {' ' * half_width}{Colors.RESET}"
|
||||
)
|
||||
elif tag == "insert":
|
||||
for new_line in new_lines[j1:j2]:
|
||||
new_display = new_line[:half_width].ljust(half_width)
|
||||
output.append(f"{' ' * half_width} | {Colors.GREEN}{new_display}{Colors.RESET}")
|
||||
output.append(
|
||||
f"{' ' * half_width} | {Colors.GREEN}{new_display}{Colors.RESET}"
|
||||
)
|
||||
|
||||
output.append(f"\n{Colors.BOLD}{Colors.BLUE}{'=' * width}{Colors.RESET}\n")
|
||||
return '\n'.join(output)
|
||||
return "\n".join(output)
|
||||
|
||||
|
||||
def display_diff(old_content: str, new_content: str, filename: str = "file",
|
||||
format_type: str = "unified", context_lines: int = 3) -> str:
|
||||
def display_diff(
|
||||
old_content: str,
|
||||
new_content: str,
|
||||
filename: str = "file",
|
||||
format_type: str = "unified",
|
||||
context_lines: int = 3,
|
||||
) -> str:
|
||||
displayer = DiffDisplay(context_lines)
|
||||
|
||||
if format_type == "side-by-side":
|
||||
@@ -191,9 +237,9 @@ def get_diff_stats(old_content: str, new_content: str) -> Dict[str, int]:
|
||||
_, stats = displayer.create_diff(old_content, new_content)
|
||||
|
||||
return {
|
||||
'insertions': stats.insertions,
|
||||
'deletions': stats.deletions,
|
||||
'modifications': stats.modifications,
|
||||
'total_changes': stats.total_changes,
|
||||
'files_changed': stats.files_changed
|
||||
"insertions": stats.insertions,
|
||||
"deletions": stats.deletions,
|
||||
"modifications": stats.modifications,
|
||||
"total_changes": stats.total_changes,
|
||||
"files_changed": stats.files_changed,
|
||||
}
|
||||
|
||||
+5
-4
@@ -1,8 +1,6 @@
|
||||
import json
|
||||
import time
|
||||
from typing import Dict, Any
|
||||
from pr.ui.colors import Colors
|
||||
|
||||
|
||||
def display_tool_call(tool_name, arguments, status="running", result=None):
|
||||
if status == "running":
|
||||
return
|
||||
@@ -15,8 +13,11 @@ def display_tool_call(tool_name, arguments, status="running", result=None):
|
||||
|
||||
print(f"{Colors.GRAY}{line}{Colors.RESET}")
|
||||
|
||||
|
||||
def print_autonomous_header(task):
|
||||
print(f"{Colors.BOLD}Task:{Colors.RESET} {task}")
|
||||
print(f"{Colors.GRAY}r will work continuously until the task is complete.{Colors.RESET}")
|
||||
print(
|
||||
f"{Colors.GRAY}r will work continuously until the task is complete.{Colors.RESET}"
|
||||
)
|
||||
print(f"{Colors.GRAY}Press Ctrl+C twice to interrupt.{Colors.RESET}\n")
|
||||
print(f"{Colors.BOLD}{'═' * 80}{Colors.RESET}\n")
|
||||
|
||||
+60
-38
@@ -1,12 +1,20 @@
|
||||
from typing import List, Dict, Optional
|
||||
from datetime import datetime
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
from .colors import Colors
|
||||
from .progress import ProgressBar
|
||||
|
||||
|
||||
class EditOperation:
|
||||
def __init__(self, op_type: str, filepath: str, start_pos: int = 0,
|
||||
end_pos: int = 0, content: str = "", old_content: str = ""):
|
||||
def __init__(
|
||||
self,
|
||||
op_type: str,
|
||||
filepath: str,
|
||||
start_pos: int = 0,
|
||||
end_pos: int = 0,
|
||||
content: str = "",
|
||||
old_content: str = "",
|
||||
):
|
||||
self.op_type = op_type
|
||||
self.filepath = filepath
|
||||
self.start_pos = start_pos
|
||||
@@ -18,40 +26,46 @@ class EditOperation:
|
||||
|
||||
def format_operation(self) -> str:
|
||||
op_colors = {
|
||||
'INSERT': Colors.GREEN,
|
||||
'REPLACE': Colors.YELLOW,
|
||||
'DELETE': Colors.RED,
|
||||
'WRITE': Colors.BLUE
|
||||
"INSERT": Colors.GREEN,
|
||||
"REPLACE": Colors.YELLOW,
|
||||
"DELETE": Colors.RED,
|
||||
"WRITE": Colors.BLUE,
|
||||
}
|
||||
|
||||
color = op_colors.get(self.op_type, Colors.RESET)
|
||||
status_icon = {
|
||||
'pending': '○',
|
||||
'in_progress': '◐',
|
||||
'completed': '●',
|
||||
'failed': '✗'
|
||||
}.get(self.status, '○')
|
||||
"pending": "○",
|
||||
"in_progress": "◐",
|
||||
"completed": "●",
|
||||
"failed": "✗",
|
||||
}.get(self.status, "○")
|
||||
|
||||
return f"{color}{status_icon} [{self.op_type}]{Colors.RESET} {self.filepath}"
|
||||
|
||||
def format_details(self, show_content: bool = True) -> str:
|
||||
output = [self.format_operation()]
|
||||
|
||||
if self.op_type in ('INSERT', 'REPLACE'):
|
||||
output.append(f" {Colors.GRAY}Position: {self.start_pos}-{self.end_pos}{Colors.RESET}")
|
||||
if self.op_type in ("INSERT", "REPLACE"):
|
||||
output.append(
|
||||
f" {Colors.GRAY}Position: {self.start_pos}-{self.end_pos}{Colors.RESET}"
|
||||
)
|
||||
|
||||
if show_content:
|
||||
if self.old_content:
|
||||
lines = self.old_content.split('\n')
|
||||
preview = lines[0][:60] + ('...' if len(lines[0]) > 60 or len(lines) > 1 else '')
|
||||
lines = self.old_content.split("\n")
|
||||
preview = lines[0][:60] + (
|
||||
"..." if len(lines[0]) > 60 or len(lines) > 1 else ""
|
||||
)
|
||||
output.append(f" {Colors.RED}- {preview}{Colors.RESET}")
|
||||
|
||||
if self.content:
|
||||
lines = self.content.split('\n')
|
||||
preview = lines[0][:60] + ('...' if len(lines[0]) > 60 or len(lines) > 1 else '')
|
||||
lines = self.content.split("\n")
|
||||
preview = lines[0][:60] + (
|
||||
"..." if len(lines[0]) > 60 or len(lines) > 1 else ""
|
||||
)
|
||||
output.append(f" {Colors.GREEN}+ {preview}{Colors.RESET}")
|
||||
|
||||
return '\n'.join(output)
|
||||
return "\n".join(output)
|
||||
|
||||
|
||||
class EditTracker:
|
||||
@@ -76,11 +90,13 @@ class EditTracker:
|
||||
|
||||
def get_stats(self) -> Dict[str, int]:
|
||||
stats = {
|
||||
'total': len(self.operations),
|
||||
'completed': sum(1 for op in self.operations if op.status == 'completed'),
|
||||
'pending': sum(1 for op in self.operations if op.status == 'pending'),
|
||||
'in_progress': sum(1 for op in self.operations if op.status == 'in_progress'),
|
||||
'failed': sum(1 for op in self.operations if op.status == 'failed')
|
||||
"total": len(self.operations),
|
||||
"completed": sum(1 for op in self.operations if op.status == "completed"),
|
||||
"pending": sum(1 for op in self.operations if op.status == "pending"),
|
||||
"in_progress": sum(
|
||||
1 for op in self.operations if op.status == "in_progress"
|
||||
),
|
||||
"failed": sum(1 for op in self.operations if op.status == "failed"),
|
||||
}
|
||||
return stats
|
||||
|
||||
@@ -88,7 +104,7 @@ class EditTracker:
|
||||
if not self.operations:
|
||||
return 0.0
|
||||
stats = self.get_stats()
|
||||
return (stats['completed'] / stats['total']) * 100
|
||||
return (stats["completed"] / stats["total"]) * 100
|
||||
|
||||
def display_progress(self) -> str:
|
||||
if not self.operations:
|
||||
@@ -96,26 +112,30 @@ class EditTracker:
|
||||
|
||||
output = []
|
||||
output.append(f"\n{Colors.BOLD}{Colors.BLUE}{'=' * 60}{Colors.RESET}")
|
||||
output.append(f"{Colors.BOLD}{Colors.BLUE}EDIT OPERATIONS PROGRESS{Colors.RESET}")
|
||||
output.append(
|
||||
f"{Colors.BOLD}{Colors.BLUE}EDIT OPERATIONS PROGRESS{Colors.RESET}"
|
||||
)
|
||||
output.append(f"{Colors.BOLD}{Colors.BLUE}{'=' * 60}{Colors.RESET}\n")
|
||||
|
||||
stats = self.get_stats()
|
||||
completion = self.get_completion_percentage()
|
||||
self.get_completion_percentage()
|
||||
|
||||
progress_bar = ProgressBar(total=stats['total'], width=40)
|
||||
progress_bar.current = stats['completed']
|
||||
progress_bar = ProgressBar(total=stats["total"], width=40)
|
||||
progress_bar.current = stats["completed"]
|
||||
bar_display = progress_bar._get_bar_display()
|
||||
|
||||
output.append(f"Progress: {bar_display}")
|
||||
output.append(f"{Colors.BLUE}Total: {stats['total']}, Completed: {stats['completed']}, "
|
||||
f"Pending: {stats['pending']}, Failed: {stats['failed']}{Colors.RESET}\n")
|
||||
output.append(
|
||||
f"{Colors.BLUE}Total: {stats['total']}, Completed: {stats['completed']}, "
|
||||
f"Pending: {stats['pending']}, Failed: {stats['failed']}{Colors.RESET}\n"
|
||||
)
|
||||
|
||||
output.append(f"{Colors.BOLD}Recent Operations:{Colors.RESET}")
|
||||
for i, op in enumerate(self.operations[-5:], 1):
|
||||
output.append(f"{i}. {op.format_operation()}")
|
||||
|
||||
output.append(f"\n{Colors.BOLD}{Colors.BLUE}{'=' * 60}{Colors.RESET}\n")
|
||||
return '\n'.join(output)
|
||||
return "\n".join(output)
|
||||
|
||||
def display_timeline(self, show_content: bool = False) -> str:
|
||||
if not self.operations:
|
||||
@@ -134,18 +154,20 @@ class EditTracker:
|
||||
|
||||
stats = self.get_stats()
|
||||
output.append(f"{Colors.BOLD}Summary:{Colors.RESET}")
|
||||
output.append(f"{Colors.BLUE}Total operations: {stats['total']}, "
|
||||
f"Completed: {stats['completed']}, Failed: {stats['failed']}{Colors.RESET}")
|
||||
output.append(
|
||||
f"{Colors.BLUE}Total operations: {stats['total']}, "
|
||||
f"Completed: {stats['completed']}, Failed: {stats['failed']}{Colors.RESET}"
|
||||
)
|
||||
|
||||
output.append(f"\n{Colors.BOLD}{Colors.BLUE}{'=' * 60}{Colors.RESET}\n")
|
||||
return '\n'.join(output)
|
||||
return "\n".join(output)
|
||||
|
||||
def display_summary(self) -> str:
|
||||
if not self.operations:
|
||||
return f"{Colors.GRAY}No edits to summarize{Colors.RESET}"
|
||||
|
||||
stats = self.get_stats()
|
||||
files_modified = len(set(op.filepath for op in self.operations))
|
||||
files_modified = len({op.filepath for op in self.operations})
|
||||
|
||||
output = []
|
||||
output.append(f"\n{Colors.BOLD}{Colors.GREEN}{'=' * 60}{Colors.RESET}")
|
||||
@@ -156,7 +178,7 @@ class EditTracker:
|
||||
output.append(f"{Colors.GREEN}Total Operations: {stats['total']}{Colors.RESET}")
|
||||
output.append(f"{Colors.GREEN}Successful: {stats['completed']}{Colors.RESET}")
|
||||
|
||||
if stats['failed'] > 0:
|
||||
if stats["failed"] > 0:
|
||||
output.append(f"{Colors.RED}Failed: {stats['failed']}{Colors.RESET}")
|
||||
|
||||
output.append(f"\n{Colors.BOLD}Operations by Type:{Colors.RESET}")
|
||||
@@ -168,7 +190,7 @@ class EditTracker:
|
||||
output.append(f" {op_type}: {count}")
|
||||
|
||||
output.append(f"\n{Colors.BOLD}{Colors.GREEN}{'=' * 60}{Colors.RESET}\n")
|
||||
return '\n'.join(output)
|
||||
return "\n".join(output)
|
||||
|
||||
def clear(self):
|
||||
self.operations.clear()
|
||||
|
||||
+16
-16
@@ -1,31 +1,31 @@
|
||||
import json
|
||||
import sys
|
||||
from typing import Any, Dict, List
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
|
||||
class OutputFormatter:
|
||||
|
||||
def __init__(self, format_type: str = 'text', quiet: bool = False):
|
||||
def __init__(self, format_type: str = "text", quiet: bool = False):
|
||||
self.format_type = format_type
|
||||
self.quiet = quiet
|
||||
|
||||
def output(self, data: Any, message_type: str = 'response'):
|
||||
if self.quiet and message_type not in ['error', 'result']:
|
||||
def output(self, data: Any, message_type: str = "response"):
|
||||
if self.quiet and message_type not in ["error", "result"]:
|
||||
return
|
||||
|
||||
if self.format_type == 'json':
|
||||
if self.format_type == "json":
|
||||
self._output_json(data, message_type)
|
||||
elif self.format_type == 'structured':
|
||||
elif self.format_type == "structured":
|
||||
self._output_structured(data, message_type)
|
||||
else:
|
||||
self._output_text(data, message_type)
|
||||
|
||||
def _output_json(self, data: Any, message_type: str):
|
||||
output = {
|
||||
'type': message_type,
|
||||
'timestamp': datetime.now().isoformat(),
|
||||
'data': data
|
||||
"type": message_type,
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"data": data,
|
||||
}
|
||||
print(json.dumps(output, indent=2))
|
||||
|
||||
@@ -46,24 +46,24 @@ class OutputFormatter:
|
||||
print(data)
|
||||
|
||||
def error(self, message: str):
|
||||
if self.format_type == 'json':
|
||||
self._output_json({'error': message}, 'error')
|
||||
if self.format_type == "json":
|
||||
self._output_json({"error": message}, "error")
|
||||
else:
|
||||
print(f"Error: {message}", file=sys.stderr)
|
||||
|
||||
def success(self, message: str):
|
||||
if not self.quiet:
|
||||
if self.format_type == 'json':
|
||||
self._output_json({'success': message}, 'success')
|
||||
if self.format_type == "json":
|
||||
self._output_json({"success": message}, "success")
|
||||
else:
|
||||
print(message)
|
||||
|
||||
def info(self, message: str):
|
||||
if not self.quiet:
|
||||
if self.format_type == 'json':
|
||||
self._output_json({'info': message}, 'info')
|
||||
if self.format_type == "json":
|
||||
self._output_json({"info": message}, "info")
|
||||
else:
|
||||
print(message)
|
||||
|
||||
def result(self, data: Any):
|
||||
self.output(data, 'result')
|
||||
self.output(data, "result")
|
||||
|
||||
+14
-8
@@ -1,6 +1,6 @@
|
||||
import sys
|
||||
import time
|
||||
import threading
|
||||
import time
|
||||
|
||||
|
||||
class ProgressIndicator:
|
||||
@@ -30,15 +30,15 @@ class ProgressIndicator:
|
||||
self.running = False
|
||||
if self.thread:
|
||||
self.thread.join(timeout=1.0)
|
||||
sys.stdout.write('\r' + ' ' * (len(self.message) + 10) + '\r')
|
||||
sys.stdout.write("\r" + " " * (len(self.message) + 10) + "\r")
|
||||
sys.stdout.flush()
|
||||
|
||||
def _animate(self):
|
||||
spinner = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
|
||||
spinner = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
|
||||
idx = 0
|
||||
|
||||
while self.running:
|
||||
sys.stdout.write(f'\r{spinner[idx]} {self.message}...')
|
||||
sys.stdout.write(f"\r{spinner[idx]} {self.message}...")
|
||||
sys.stdout.flush()
|
||||
idx = (idx + 1) % len(spinner)
|
||||
time.sleep(0.1)
|
||||
@@ -62,14 +62,20 @@ class ProgressBar:
|
||||
else:
|
||||
percent = int((self.current / self.total) * 100)
|
||||
|
||||
filled = int((self.current / self.total) * self.width) if self.total > 0 else self.width
|
||||
bar = '█' * filled + '░' * (self.width - filled)
|
||||
filled = (
|
||||
int((self.current / self.total) * self.width)
|
||||
if self.total > 0
|
||||
else self.width
|
||||
)
|
||||
bar = "█" * filled + "░" * (self.width - filled)
|
||||
|
||||
sys.stdout.write(f'\r{self.description}: |{bar}| {percent}% ({self.current}/{self.total})')
|
||||
sys.stdout.write(
|
||||
f"\r{self.description}: |{bar}| {percent}% ({self.current}/{self.total})"
|
||||
)
|
||||
sys.stdout.flush()
|
||||
|
||||
if self.current >= self.total:
|
||||
sys.stdout.write('\n')
|
||||
sys.stdout.write("\n")
|
||||
|
||||
def finish(self):
|
||||
self.current = self.total
|
||||
|
||||
+51
-38
@@ -1,90 +1,103 @@
|
||||
import re
|
||||
from pr.ui.colors import Colors
|
||||
|
||||
from pr.config import LANGUAGE_KEYWORDS
|
||||
from pr.ui.colors import Colors
|
||||
|
||||
|
||||
def highlight_code(code, language=None, syntax_highlighting=True):
|
||||
if not syntax_highlighting:
|
||||
return code
|
||||
|
||||
if not language:
|
||||
if 'def ' in code or 'import ' in code:
|
||||
language = 'python'
|
||||
elif 'function ' in code or 'const ' in code:
|
||||
language = 'javascript'
|
||||
elif 'public ' in code or 'class ' in code:
|
||||
language = 'java'
|
||||
if "def " in code or "import " in code:
|
||||
language = "python"
|
||||
elif "function " in code or "const " in code:
|
||||
language = "javascript"
|
||||
elif "public " in code or "class " in code:
|
||||
language = "java"
|
||||
|
||||
if language and language in LANGUAGE_KEYWORDS:
|
||||
keywords = LANGUAGE_KEYWORDS[language]
|
||||
for keyword in keywords:
|
||||
pattern = r'\b' + re.escape(keyword) + r'\b'
|
||||
pattern = r"\b" + re.escape(keyword) + r"\b"
|
||||
code = re.sub(pattern, f"{Colors.BLUE}{keyword}{Colors.RESET}", code)
|
||||
|
||||
code = re.sub(r'"([^"]*)"', f'{Colors.GREEN}"\\1"{Colors.RESET}', code)
|
||||
code = re.sub(r"'([^']*)'", f"{Colors.GREEN}'\\1'{Colors.RESET}", code)
|
||||
|
||||
code = re.sub(r'#(.*)$', f'{Colors.GRAY}#\\1{Colors.RESET}', code, flags=re.MULTILINE)
|
||||
code = re.sub(r'//(.*)$', f'{Colors.GRAY}//\\1{Colors.RESET}', code, flags=re.MULTILINE)
|
||||
code = re.sub(
|
||||
r"#(.*)$", f"{Colors.GRAY}#\\1{Colors.RESET}", code, flags=re.MULTILINE
|
||||
)
|
||||
code = re.sub(
|
||||
r"//(.*)$", f"{Colors.GRAY}//\\1{Colors.RESET}", code, flags=re.MULTILINE
|
||||
)
|
||||
|
||||
return code
|
||||
|
||||
|
||||
def render_markdown(text, syntax_highlighting=True):
|
||||
if not syntax_highlighting:
|
||||
return text
|
||||
|
||||
code_blocks = []
|
||||
|
||||
def extract_code_block(match):
|
||||
lang = match.group(1) or ''
|
||||
lang = match.group(1) or ""
|
||||
code = match.group(2)
|
||||
highlighted_code = highlight_code(code.strip('\n'), lang, syntax_highlighting)
|
||||
highlighted_code = highlight_code(code.strip("\n"), lang, syntax_highlighting)
|
||||
placeholder = f"%%CODEBLOCK{len(code_blocks)}%%"
|
||||
full_block = f'{Colors.GRAY}```{lang}{Colors.RESET}\n{highlighted_code}\n{Colors.GRAY}```{Colors.RESET}'
|
||||
full_block = f"{Colors.GRAY}```{lang}{Colors.RESET}\n{highlighted_code}\n{Colors.GRAY}```{Colors.RESET}"
|
||||
code_blocks.append(full_block)
|
||||
return placeholder
|
||||
|
||||
text = re.sub(r'```(\w*)\n(.*?)\n?```', extract_code_block, text, flags=re.DOTALL)
|
||||
text = re.sub(r"```(\w*)\n(.*?)\n?```", extract_code_block, text, flags=re.DOTALL)
|
||||
|
||||
inline_codes = []
|
||||
|
||||
def extract_inline_code(match):
|
||||
code = match.group(1)
|
||||
placeholder = f"%%INLINECODE{len(inline_codes)}%%"
|
||||
inline_codes.append(f'{Colors.YELLOW}{code}{Colors.RESET}')
|
||||
inline_codes.append(f"{Colors.YELLOW}{code}{Colors.RESET}")
|
||||
return placeholder
|
||||
|
||||
text = re.sub(r'`([^`]+)`', extract_inline_code, text)
|
||||
text = re.sub(r"`([^`]+)`", extract_inline_code, text)
|
||||
|
||||
lines = text.split('\n')
|
||||
lines = text.split("\n")
|
||||
processed_lines = []
|
||||
for line in lines:
|
||||
if line.startswith('### '):
|
||||
line = f'{Colors.BOLD}{Colors.GREEN}{line[4:]}{Colors.RESET}'
|
||||
elif line.startswith('## '):
|
||||
line = f'{Colors.BOLD}{Colors.BLUE}{line[3:]}{Colors.RESET}'
|
||||
elif line.startswith('# '):
|
||||
line = f'{Colors.BOLD}{Colors.MAGENTA}{line[2:]}{Colors.RESET}'
|
||||
elif line.startswith('> '):
|
||||
line = f'{Colors.CYAN}> {line[2:]}{Colors.RESET}'
|
||||
elif re.match(r'^\s*[\*\-\+]\s', line):
|
||||
match = re.match(r'^(\s*)([\*\-\+])(\s+.*)', line)
|
||||
if line.startswith("### "):
|
||||
line = f"{Colors.BOLD}{Colors.GREEN}{line[4:]}{Colors.RESET}"
|
||||
elif line.startswith("## "):
|
||||
line = f"{Colors.BOLD}{Colors.BLUE}{line[3:]}{Colors.RESET}"
|
||||
elif line.startswith("# "):
|
||||
line = f"{Colors.BOLD}{Colors.MAGENTA}{line[2:]}{Colors.RESET}"
|
||||
elif line.startswith("> "):
|
||||
line = f"{Colors.CYAN}> {line[2:]}{Colors.RESET}"
|
||||
elif re.match(r"^\s*[\*\-\+]\s", line):
|
||||
match = re.match(r"^(\s*)([\*\-\+])(\s+.*)", line)
|
||||
if match:
|
||||
line = f"{match.group(1)}{Colors.YELLOW}{match.group(2)}{Colors.RESET}{match.group(3)}"
|
||||
elif re.match(r'^\s*\d+\.\s', line):
|
||||
match = re.match(r'^(\s*)(\d+\.)(\s+.*)', line)
|
||||
elif re.match(r"^\s*\d+\.\s", line):
|
||||
match = re.match(r"^(\s*)(\d+\.)(\s+.*)", line)
|
||||
if match:
|
||||
line = f"{match.group(1)}{Colors.YELLOW}{match.group(2)}{Colors.RESET}{match.group(3)}"
|
||||
processed_lines.append(line)
|
||||
text = '\n'.join(processed_lines)
|
||||
text = "\n".join(processed_lines)
|
||||
|
||||
text = re.sub(r'\[(.*?)\]\((.*?)\)', f'{Colors.BLUE}\\1{Colors.RESET}{Colors.GRAY}(\\2){Colors.RESET}', text)
|
||||
text = re.sub(r'~~(.*?)~~', f'{Colors.GRAY}\\1{Colors.RESET}', text)
|
||||
text = re.sub(r'\*\*(.*?)\*\*', f'{Colors.BOLD}\\1{Colors.RESET}', text)
|
||||
text = re.sub(r'__(.*?)__', f'{Colors.BOLD}\\1{Colors.RESET}', text)
|
||||
text = re.sub(r'\*(.*?)\*', f'{Colors.CYAN}\\1{Colors.RESET}', text)
|
||||
text = re.sub(r'_(.*?)_', f'{Colors.CYAN}\\1{Colors.RESET}', text)
|
||||
text = re.sub(
|
||||
r"\[(.*?)\]\((.*?)\)",
|
||||
f"{Colors.BLUE}\\1{Colors.RESET}{Colors.GRAY}(\\2){Colors.RESET}",
|
||||
text,
|
||||
)
|
||||
text = re.sub(r"~~(.*?)~~", f"{Colors.GRAY}\\1{Colors.RESET}", text)
|
||||
text = re.sub(r"\*\*(.*?)\*\*", f"{Colors.BOLD}\\1{Colors.RESET}", text)
|
||||
text = re.sub(r"__(.*?)__", f"{Colors.BOLD}\\1{Colors.RESET}", text)
|
||||
text = re.sub(r"\*(.*?)\*", f"{Colors.CYAN}\\1{Colors.RESET}", text)
|
||||
text = re.sub(r"_(.*?)_", f"{Colors.CYAN}\\1{Colors.RESET}", text)
|
||||
|
||||
for i, code in enumerate(inline_codes):
|
||||
text = text.replace(f'%%INLINECODE{i}%%', code)
|
||||
text = text.replace(f"%%INLINECODE{i}%%", code)
|
||||
for i, block in enumerate(code_blocks):
|
||||
text = text.replace(f'%%CODEBLOCK{i}%%', block)
|
||||
text = text.replace(f"%%CODEBLOCK{i}%%", block)
|
||||
|
||||
return text
|
||||
|
||||
Reference in New Issue
Block a user