-
Notifications
You must be signed in to change notification settings - Fork 3
Move parsing to frontend #7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,7 +8,6 @@ | |
| from typing import Dict, Any, Optional, List | ||
| import uuid | ||
|
|
||
| from ...services.parser.renpy_parser import RenPyParser, ChoiceNode, ChoiceNodeType | ||
| from ...services.database import DatabaseService | ||
| from ...models.exceptions import ResourceNotFoundException, DatabaseException | ||
| from ...services.websocket import connection_manager | ||
|
|
@@ -23,24 +22,6 @@ | |
|
|
||
| # Initialize services | ||
| db_service = DatabaseService() | ||
| parser = RenPyParser() | ||
|
|
||
| # Helper functions | ||
| def node_to_dict(node: ChoiceNode) -> Dict[str, Any]: | ||
| """Convert a ChoiceNode to a dictionary with line references for JSON serialization.""" | ||
| result = { | ||
| "id": str(id(node)), # Generate a unique ID using the object's memory address | ||
| "node_type": node.node_type.value if hasattr(node.node_type, "value") else str(node.node_type), | ||
| "label_name": node.label_name, | ||
| "start_line": node.start_line, | ||
| "end_line": node.end_line, | ||
| "children": [node_to_dict(child) for child in node.children] | ||
| } | ||
|
|
||
| if hasattr(node, "false_branch") and node.false_branch: | ||
| result["false_branch"] = [node_to_dict(opt) for opt in node.false_branch] | ||
|
|
||
| return result | ||
|
|
||
| # Routes | ||
| @scripts_router.post("/parse", response_model=Dict[str, Any]) | ||
|
|
@@ -51,14 +32,15 @@ async def parse_script( | |
| current_user: Dict[str, Any] = Depends(get_current_user) | ||
| ) -> Dict[str, Any]: | ||
| """ | ||
| Parse a RenPy script file and return its tree structure with line references. | ||
| Save a RenPy script file. | ||
| NOTE: Parsing is now handled on the frontend. This endpoint just saves the file. | ||
|
|
||
| Args: | ||
| file: The uploaded RenPy script file | ||
| project_id: ID of the project to associate the script with | ||
|
|
||
| Returns: | ||
| JSON representation of the parsed script tree with line references | ||
| JSON representation of the script metadata. Tree is empty. | ||
| """ | ||
| try: | ||
| # current_user now injected via Depends | ||
|
|
@@ -82,22 +64,11 @@ async def parse_script( | |
| if not has_access: | ||
| raise HTTPException(status_code=403, detail="Access denied to the specified project") | ||
|
|
||
| # Parse the content to check validity | ||
| temp_dir = Path(tempfile.gettempdir()) / "renpy_editor" / str(uuid.uuid4()) | ||
| temp_dir.mkdir(parents=True, exist_ok=True) | ||
|
|
||
| temp_file = temp_dir / file.filename | ||
| with open(temp_file, "wb") as f: | ||
| f.write(content) | ||
|
|
||
| # Parse the file to build the tree | ||
| parsed_tree = await parser.parse_async(str(temp_file)) | ||
|
|
||
| decoded_content = content.decode('utf-8') | ||
|
|
||
| # Check if script with this filename already exists in the project | ||
| existing_script = db_service.get_script_by_filename(project_id, file.filename) | ||
|
|
||
| decoded_content = content.decode('utf-8') | ||
|
|
||
| if existing_script: | ||
| # Update existing script | ||
| script_id = existing_script["id"] | ||
|
|
@@ -115,19 +86,16 @@ async def parse_script( | |
| user_id=current_user["id"] | ||
| ) | ||
|
|
||
| # Clean up temp file | ||
| background_tasks.add_task(shutil.rmtree, temp_dir, ignore_errors=True) | ||
|
|
||
| # Return result | ||
| # Return result with empty tree (frontend handles parsing) | ||
| result = { | ||
| "script_id": script_id, | ||
| "filename": file.filename, | ||
| "tree": node_to_dict(parsed_tree) | ||
| "tree": {} | ||
| } | ||
|
|
||
| return result | ||
| except Exception as e: | ||
| raise HTTPException(status_code=500, detail=f"Error parsing script: {str(e)}") | ||
| raise HTTPException(status_code=500, detail=f"Error saving script: {str(e)}") | ||
|
|
||
| @scripts_router.get("/node-content/{script_id}", response_model=Dict[str, Any]) | ||
| async def get_node_content( | ||
|
|
@@ -240,11 +208,10 @@ async def update_node_content( | |
| # Save changes to database | ||
| db_service.update_script(script_id, new_content, current_user["id"]) | ||
|
|
||
| # Parse updated script and broadcast new structure to collaborators | ||
| parsed_tree = parser.parse_text(new_content) | ||
| await connection_manager.broadcast_structure_update( | ||
| script_id, node_to_dict(parsed_tree) | ||
| ) | ||
| # Broadcast update | ||
| # Note: We no longer broadcast the full parsed tree because parsing is moved to frontend. | ||
| # Ideally, we should broadcast a "content_updated" event and let clients re-fetch or re-parse. | ||
| # For now, we omit the structure update broadcast. | ||
|
Comment on lines
+211
to
+214
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This change removes the only server-side trigger that tells other clients to reload the script graph after edits. The frontend only refreshes its parsed tree when it receives an Useful? React with 👍 / 👎.
Comment on lines
+211
to
+214
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Collaborative editing: no broadcast on content mutation. The comment acknowledges that tree broadcasting was removed, but there's no replacement event (e.g., 🤖 Prompt for AI Agents |
||
|
|
||
| # Calculate new end line | ||
| new_end_line = start_line + new_line_count - 1 | ||
|
|
@@ -314,31 +281,13 @@ async def insert_node( | |
| # Save changes to database | ||
| db_service.update_script(script_id, new_content, current_user["id"]) | ||
|
|
||
| # Re-parse the entire script to update the tree | ||
| # Create temp file for parsing | ||
| temp_dir = Path(tempfile.gettempdir()) / "renpy_editor" / str(uuid.uuid4()) | ||
| temp_dir.mkdir(parents=True, exist_ok=True) | ||
| temp_file = temp_dir / "temp_script.rpy" | ||
|
|
||
| with open(temp_file, "w", encoding="utf-8") as f: | ||
| f.write(new_content) | ||
|
|
||
| # Parse the updated file | ||
| parsed_tree = await parser.parse_async(str(temp_file)) | ||
|
|
||
| # Clean up temp file | ||
| shutil.rmtree(temp_dir, ignore_errors=True) | ||
|
|
||
| # Broadcast updated structure to other clients | ||
| await connection_manager.broadcast_structure_update( | ||
| script_id, node_to_dict(parsed_tree) | ||
| ) | ||
| # Note: We no longer broadcast the full parsed tree. | ||
|
|
||
| return { | ||
| "start_line": insertion_line, | ||
| "end_line": insertion_line + len(new_content_lines) - 1, | ||
| "line_count": len(new_content_lines), | ||
| "tree": node_to_dict(parsed_tree) | ||
| "tree": {} # Empty tree | ||
| } | ||
| except ResourceNotFoundException as e: | ||
| raise HTTPException(status_code=404, detail=str(e)) | ||
|
|
@@ -512,13 +461,13 @@ async def load_existing_script( | |
| current_user: Dict[str, Any] = Depends(get_current_user) | ||
| ) -> Dict[str, Any]: | ||
| """ | ||
| Load an existing script and return its parsed tree structure. | ||
| Load an existing script and return its content and empty tree. | ||
|
|
||
| Args: | ||
| script_id: The ID of the script to load | ||
|
|
||
| Returns: | ||
| JSON representation of the script with parsed tree | ||
| JSON representation of the script with content. Tree is empty. | ||
| """ | ||
| try: | ||
| # Get script from database | ||
|
|
@@ -534,36 +483,19 @@ async def load_existing_script( | |
| if not has_access: | ||
| raise HTTPException(status_code=403, detail="Access denied to this script") | ||
|
|
||
| # Parse the script content to build tree | ||
| temp_dir = Path(tempfile.gettempdir()) / "renpy_editor" / str(uuid.uuid4()) | ||
| temp_dir.mkdir(parents=True, exist_ok=True) | ||
|
|
||
| temp_file = temp_dir / script["filename"] | ||
| with open(temp_file, "w", encoding='utf-8') as f: | ||
| f.write(script["content"]) | ||
| # Return result | ||
| result = { | ||
| "script_id": script_id, | ||
| "filename": script["filename"], | ||
| "content": script["content"], | ||
| "tree": {} # Empty tree | ||
| } | ||
|
|
||
| try: | ||
| # Parse the file to build the tree | ||
| parsed_tree = await parser.parse_async(str(temp_file)) | ||
|
|
||
| # Return result | ||
| result = { | ||
| "script_id": script_id, | ||
| "filename": script["filename"], | ||
| "tree": node_to_dict(parsed_tree) | ||
| } | ||
|
|
||
| return result | ||
| finally: | ||
| # Clean up temp file | ||
| import shutil | ||
| shutil.rmtree(temp_dir, ignore_errors=True) | ||
| return result | ||
|
|
||
| except ResourceNotFoundException as e: | ||
| raise HTTPException(status_code=404, detail=str(e)) | ||
| except HTTPException: | ||
| raise | ||
| except Exception as e: | ||
| raise HTTPException(status_code=500, detail=f"Error loading script: {str(e)}") | ||
|
|
||
| # TODO: Add endpoints for version history retrieval - #issue/129 | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug:
HTTPExceptionis swallowed and re-raised as 500.Every other endpoint in this file has
except HTTPException: raisebefore the catch-allexcept Exception. This endpoint is missing it, soHTTPExceptions raised at Lines 48, 51, 58, and 65 (status codes 400 and 403) will be caught here and re-raised as a generic 500 error.🐛 Proposed fix
🧰 Tools
🪛 Ruff (0.14.14)
[warning] 97-97: Do not catch blind exception:
Exception(BLE001)
[warning] 98-98: Within an
exceptclause, raise exceptions withraise ... from errorraise ... from Noneto distinguish them from errors in exception handling(B904)
[warning] 98-98: Use explicit conversion flag
Replace with conversion flag
(RUF010)
🤖 Prompt for AI Agents