fixing bugs in gui_2.py
This commit is contained in:
+164
-153
@@ -8,6 +8,7 @@ import sys
|
|||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import copy
|
import copy
|
||||||
|
import threading
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from tkinter import filedialog, Tk
|
from tkinter import filedialog, Tk
|
||||||
from typing import Optional, Any
|
from typing import Optional, Any
|
||||||
@@ -1769,133 +1770,144 @@ class App:
|
|||||||
if self._show_ast_inspector:
|
if self._show_ast_inspector:
|
||||||
imgui.open_popup('AST Inspector')
|
imgui.open_popup('AST Inspector')
|
||||||
self._show_ast_inspector = False
|
self._show_ast_inspector = False
|
||||||
expanded, opened = imgui.begin_popup_modal('AST Inspector', True, imgui.WindowFlags_.always_auto_resize)
|
|
||||||
if not opened:
|
|
||||||
return
|
|
||||||
if self.ui_inspecting_ast_file is None:
|
|
||||||
imgui.close_current_popup()
|
|
||||||
imgui.end_popup()
|
|
||||||
return
|
|
||||||
f_item = self.ui_inspecting_ast_file
|
|
||||||
f_path = f_item.path if hasattr(f_item, "path") else str(f_item)
|
|
||||||
|
|
||||||
if f_path != self._cached_ast_file_path:
|
|
||||||
outline = ""
|
|
||||||
try:
|
|
||||||
if f_path.lower().endswith('.py'):
|
|
||||||
outline = mcp_client.py_get_code_outline(f_path)
|
|
||||||
elif f_path.lower().endswith(('.c', '.h')):
|
|
||||||
outline = mcp_client.ts_c_get_code_outline(f_path)
|
|
||||||
else:
|
|
||||||
outline = mcp_client.ts_cpp_get_code_outline(f_path)
|
|
||||||
except Exception as e:
|
|
||||||
outline = f"Error fetching outline: {e}"
|
|
||||||
|
|
||||||
self._cached_ast_nodes = []
|
|
||||||
import re
|
|
||||||
pattern = re.compile(r'^(\s*)\[(.*?)\] (.*?) \(Lines (\d+)-(\d+)\)')
|
|
||||||
stack = [] # (indent, name)
|
|
||||||
for line in outline.splitlines():
|
|
||||||
m = pattern.match(line)
|
|
||||||
if m:
|
|
||||||
indent_str, kind, name, start_ln, end_ln = m.groups()
|
|
||||||
indent = len(indent_str)
|
|
||||||
while stack and stack[-1][0] >= indent:
|
|
||||||
stack.pop()
|
|
||||||
stack.append((indent, name))
|
|
||||||
full_path = '::'.join([s[1] for s in stack])
|
|
||||||
self._cached_ast_nodes.append({
|
|
||||||
'indent': indent,
|
|
||||||
'kind': kind,
|
|
||||||
'name': name,
|
|
||||||
'full_path': full_path,
|
|
||||||
'start_line': int(start_ln),
|
|
||||||
'end_line': int(end_ln)
|
|
||||||
})
|
|
||||||
try:
|
|
||||||
content = mcp_client.read_file(f_path)
|
|
||||||
self._cached_ast_file_lines = content.splitlines()
|
|
||||||
except Exception:
|
|
||||||
self._cached_ast_file_lines = ["Error loading file content."]
|
|
||||||
self._cached_ast_file_path = f_path
|
|
||||||
|
|
||||||
imgui.text(f"Inspecting AST: {f_path}")
|
#region: AST Inspector
|
||||||
imgui.separator()
|
expanded, opened = imgui.begin_popup_modal('AST Inspector', True, imgui.WindowFlags_.always_auto_resize)
|
||||||
|
if opened:
|
||||||
|
if expanded:
|
||||||
|
if self.ui_inspecting_ast_file is None:
|
||||||
|
imgui.close_current_popup()
|
||||||
|
else:
|
||||||
|
f_item = self.ui_inspecting_ast_file
|
||||||
|
f_path = f_item.path if hasattr(f_item, "path") else str(f_item)
|
||||||
|
|
||||||
|
if f_path != self._cached_ast_file_path:
|
||||||
|
outline = ""
|
||||||
|
try:
|
||||||
|
if f_path.lower().endswith('.py'):
|
||||||
|
outline = mcp_client.py_get_code_outline(f_path)
|
||||||
|
elif f_path.lower().endswith(('.c', '.h')):
|
||||||
|
outline = mcp_client.ts_c_get_code_outline(f_path)
|
||||||
|
else:
|
||||||
|
outline = mcp_client.ts_cpp_get_code_outline(f_path)
|
||||||
|
except Exception as e:
|
||||||
|
outline = f"Error fetching outline: {e}"
|
||||||
|
|
||||||
|
self._cached_ast_nodes = []
|
||||||
|
import re
|
||||||
|
pattern = re.compile(r'^(\s*)\[(.*?)\] (.*?) \(Lines (\d+)-(\d+)\)')
|
||||||
|
stack = [] # (indent, name)
|
||||||
|
for line in outline.splitlines():
|
||||||
|
m = pattern.match(line)
|
||||||
|
if m:
|
||||||
|
indent_str, kind, name, start_ln, end_ln = m.groups()
|
||||||
|
indent = len(indent_str)
|
||||||
|
while stack and stack[-1][0] >= indent:
|
||||||
|
stack.pop()
|
||||||
|
stack.append((indent, name))
|
||||||
|
full_path = '::'.join([s[1] for s in stack])
|
||||||
|
self._cached_ast_nodes.append({
|
||||||
|
'indent': indent,
|
||||||
|
'kind': kind,
|
||||||
|
'name': name,
|
||||||
|
'full_path': full_path,
|
||||||
|
'start_line': int(start_ln),
|
||||||
|
'end_line': int(end_ln)
|
||||||
|
})
|
||||||
|
try:
|
||||||
|
content = mcp_client.read_file(f_path)
|
||||||
|
self._cached_ast_file_lines = content.splitlines()
|
||||||
|
except Exception:
|
||||||
|
self._cached_ast_file_lines = ["Error loading file content."]
|
||||||
|
self._cached_ast_file_path = f_path
|
||||||
|
|
||||||
|
imgui.text(f"Inspecting AST: {f_path}")
|
||||||
|
imgui.separator()
|
||||||
|
|
||||||
|
#region: ast_dual_pane
|
||||||
|
if imgui.begin_table('ast_dual_pane', 2, imgui.TableFlags_.resizable | imgui.TableFlags_.borders_inner_v):
|
||||||
|
imgui.table_next_column()
|
||||||
|
|
||||||
|
#region: LEFT COLUMN (Tree) ---
|
||||||
|
if imgui.begin_child("ast_tree_scroll", imgui.ImVec2(0, 600), True):
|
||||||
|
if not self._cached_ast_nodes:
|
||||||
|
imgui.text("No AST nodes found or error fetching outline.")
|
||||||
|
else:
|
||||||
|
for node in self._cached_ast_nodes:
|
||||||
|
indent = node['indent']
|
||||||
|
kind = node['kind']
|
||||||
|
name = node['name']
|
||||||
|
full_path = node['full_path']
|
||||||
|
|
||||||
|
imgui.dummy(imgui.ImVec2(indent * 10, 0))
|
||||||
|
imgui.same_line()
|
||||||
|
imgui.text(f"[{kind}] {name}")
|
||||||
|
imgui.same_line(imgui.get_window_width() - 200)
|
||||||
|
|
||||||
|
current_mode = f_item.ast_mask.get(full_path, 'hide')
|
||||||
|
|
||||||
|
imgui.push_id(full_path)
|
||||||
|
if imgui.radio_button("Def", current_mode == 'def'):
|
||||||
|
f_item.ast_mask[full_path] = 'def'
|
||||||
|
imgui.same_line()
|
||||||
|
if imgui.radio_button("Sig", current_mode == 'sig'):
|
||||||
|
f_item.ast_mask[full_path] = 'sig'
|
||||||
|
imgui.same_line()
|
||||||
|
if imgui.radio_button("Hide", current_mode == 'hide'):
|
||||||
|
f_item.ast_mask[full_path] = 'hide'
|
||||||
|
imgui.pop_id()
|
||||||
|
imgui.end_child()
|
||||||
|
#endregion: LEFT COLUMN (Tree)
|
||||||
|
|
||||||
|
imgui.table_next_column()
|
||||||
|
|
||||||
|
#region: RIGHT COLUMN (Content) ---
|
||||||
|
if imgui.begin_child("ast_content_scroll", imgui.ImVec2(0, 600), True):
|
||||||
|
if not hasattr(self, '_cached_ast_file_lines') or not self._cached_ast_file_lines:
|
||||||
|
imgui.text("No file content loaded.")
|
||||||
|
else:
|
||||||
|
draw_list = imgui.get_window_draw_list()
|
||||||
|
for i, line_text in enumerate(self._cached_ast_file_lines):
|
||||||
|
line_num = i + 1
|
||||||
|
|
||||||
|
# Prioritize the most specific node (deepest indent) that covers the line
|
||||||
|
deepest_node = None
|
||||||
|
for node in self._cached_ast_nodes:
|
||||||
|
if node['start_line'] <= line_num <= node['end_line']:
|
||||||
|
if deepest_node is None or node['indent'] > deepest_node['indent']:
|
||||||
|
deepest_node = node
|
||||||
|
|
||||||
|
mode = 'hide'
|
||||||
|
if deepest_node:
|
||||||
|
mode = f_item.ast_mask.get(deepest_node['full_path'], 'hide')
|
||||||
|
|
||||||
|
pos = imgui.get_cursor_screen_pos()
|
||||||
|
line_height = imgui.get_text_line_height()
|
||||||
|
|
||||||
|
if mode == 'def':
|
||||||
|
# Green, alpha 0.2
|
||||||
|
draw_list.add_rect_filled(pos, imgui.ImVec2(pos.x + imgui.get_content_region_avail().x, pos.y + line_height), imgui.get_color_u32(vec4(0, 255, 0, 0.2)))
|
||||||
|
elif mode == 'sig':
|
||||||
|
# Blue, alpha 0.2
|
||||||
|
draw_list.add_rect_filled(pos, imgui.ImVec2(pos.x + imgui.get_content_region_avail().x, pos.y + line_height), imgui.get_color_u32(vec4(0, 0, 255, 0.2)))
|
||||||
|
|
||||||
|
imgui.text(f"{line_num:4} | {line_text}")
|
||||||
|
imgui.end_child()
|
||||||
|
#endregion: RIGHT COLUMN (Content) ---
|
||||||
|
imgui.end_table()
|
||||||
|
#endregion: ast_dual_pane
|
||||||
|
|
||||||
|
imgui.separator()
|
||||||
|
|
||||||
|
if imgui.button("Close", imgui.ImVec2(120, 0)):
|
||||||
|
self.ui_inspecting_ast_file = None
|
||||||
|
imgui.close_current_popup()
|
||||||
|
|
||||||
|
imgui.end_popup()
|
||||||
|
#endregion: AST Inspector
|
||||||
|
|
||||||
if imgui.begin_table('ast_dual_pane', 2, imgui.TableFlags_.resizable | imgui.TableFlags_.borders_inner_v):
|
if not opened:
|
||||||
imgui.table_next_column()
|
|
||||||
|
|
||||||
# --- LEFT COLUMN (Tree) ---
|
|
||||||
imgui.begin_child("ast_tree_scroll", imgui.ImVec2(0, 600), True)
|
|
||||||
if not self._cached_ast_nodes:
|
|
||||||
imgui.text("No AST nodes found or error fetching outline.")
|
|
||||||
else:
|
|
||||||
for node in self._cached_ast_nodes:
|
|
||||||
indent = node['indent']
|
|
||||||
kind = node['kind']
|
|
||||||
name = node['name']
|
|
||||||
full_path = node['full_path']
|
|
||||||
|
|
||||||
imgui.dummy(imgui.ImVec2(indent * 10, 0))
|
|
||||||
imgui.same_line()
|
|
||||||
imgui.text(f"[{kind}] {name}")
|
|
||||||
imgui.same_line(imgui.get_window_width() - 200)
|
|
||||||
|
|
||||||
current_mode = f_item.ast_mask.get(full_path, 'hide')
|
|
||||||
|
|
||||||
imgui.push_id(full_path)
|
|
||||||
if imgui.radio_button("Def", current_mode == 'def'):
|
|
||||||
f_item.ast_mask[full_path] = 'def'
|
|
||||||
imgui.same_line()
|
|
||||||
if imgui.radio_button("Sig", current_mode == 'sig'):
|
|
||||||
f_item.ast_mask[full_path] = 'sig'
|
|
||||||
imgui.same_line()
|
|
||||||
if imgui.radio_button("Hide", current_mode == 'hide'):
|
|
||||||
f_item.ast_mask[full_path] = 'hide'
|
|
||||||
imgui.pop_id()
|
|
||||||
imgui.end_child()
|
|
||||||
|
|
||||||
imgui.table_next_column()
|
|
||||||
|
|
||||||
# --- RIGHT COLUMN (Content) ---
|
|
||||||
imgui.begin_child("ast_content_scroll", imgui.ImVec2(0, 600), True)
|
|
||||||
if not hasattr(self, '_cached_ast_file_lines') or not self._cached_ast_file_lines:
|
|
||||||
imgui.text("No file content loaded.")
|
|
||||||
else:
|
|
||||||
draw_list = imgui.get_window_draw_list()
|
|
||||||
for i, line_text in enumerate(self._cached_ast_file_lines):
|
|
||||||
line_num = i + 1
|
|
||||||
|
|
||||||
# Prioritize the most specific node (deepest indent) that covers the line
|
|
||||||
deepest_node = None
|
|
||||||
for node in self._cached_ast_nodes:
|
|
||||||
if node['start_line'] <= line_num <= node['end_line']:
|
|
||||||
if deepest_node is None or node['indent'] > deepest_node['indent']:
|
|
||||||
deepest_node = node
|
|
||||||
|
|
||||||
mode = 'hide'
|
|
||||||
if deepest_node:
|
|
||||||
mode = f_item.ast_mask.get(deepest_node['full_path'], 'hide')
|
|
||||||
|
|
||||||
pos = imgui.get_cursor_screen_pos()
|
|
||||||
line_height = imgui.get_text_line_height()
|
|
||||||
|
|
||||||
if mode == 'def':
|
|
||||||
# Green, alpha 0.2
|
|
||||||
draw_list.add_rect_filled(pos, imgui.ImVec2(pos.x + imgui.get_content_region_avail().x, pos.y + line_height), imgui.get_color_u32(vec4(0, 255, 0, 0.2)))
|
|
||||||
elif mode == 'sig':
|
|
||||||
# Blue, alpha 0.2
|
|
||||||
draw_list.add_rect_filled(pos, imgui.ImVec2(pos.x + imgui.get_content_region_avail().x, pos.y + line_height), imgui.get_color_u32(vec4(0, 0, 255, 0.2)))
|
|
||||||
|
|
||||||
imgui.text(f"{line_num:4} | {line_text}")
|
|
||||||
imgui.end_child()
|
|
||||||
imgui.end_table()
|
|
||||||
|
|
||||||
imgui.separator()
|
|
||||||
if imgui.button("Close", imgui.ImVec2(120, 0)):
|
|
||||||
self.ui_inspecting_ast_file = None
|
self.ui_inspecting_ast_file = None
|
||||||
imgui.close_current_popup()
|
|
||||||
imgui.end_popup()
|
|
||||||
|
|
||||||
def _render_save_workspace_profile_modal(self) -> None:
|
def _render_save_workspace_profile_modal(self) -> None:
|
||||||
if self._show_save_workspace_profile_modal:
|
if self._show_save_workspace_profile_modal:
|
||||||
@@ -1929,26 +1941,25 @@ class App:
|
|||||||
def _render_add_context_files_modal(self) -> None:
|
def _render_add_context_files_modal(self) -> None:
|
||||||
if imgui.begin_popup_modal("Select Context Files", None, imgui.WindowFlags_.always_auto_resize)[0]:
|
if imgui.begin_popup_modal("Select Context Files", None, imgui.WindowFlags_.always_auto_resize)[0]:
|
||||||
imgui.text("Select files from project to add to context:")
|
imgui.text("Select files from project to add to context:")
|
||||||
imgui.begin_child("ctx_picker_list", imgui.ImVec2(600, 300), True)
|
if imgui.begin_child("ctx_picker_list", imgui.ImVec2(600, 300), True):
|
||||||
|
from src import models
|
||||||
from src import models
|
# Create a temporary selection set if not initialized
|
||||||
# Create a temporary selection set if not initialized
|
if not hasattr(self, '_ui_picker_selected'):
|
||||||
if not hasattr(self, '_ui_picker_selected'):
|
self._ui_picker_selected = set()
|
||||||
self._ui_picker_selected = set()
|
|
||||||
|
for f in self.files:
|
||||||
for f in self.files:
|
fpath = f.path if hasattr(f, 'path') else str(f)
|
||||||
fpath = f.path if hasattr(f, 'path') else str(f)
|
# Skip if already in context
|
||||||
# Skip if already in context
|
if any((cf.path if hasattr(cf, 'path') else str(cf)) == fpath for cf in self.context_files):
|
||||||
if any((cf.path if hasattr(cf, 'path') else str(cf)) == fpath for cf in self.context_files):
|
continue
|
||||||
continue
|
is_sel = fpath in self._ui_picker_selected
|
||||||
is_sel = fpath in self._ui_picker_selected
|
clicked, new_sel = imgui.checkbox(f"{fpath}##picker_{fpath}", is_sel)
|
||||||
clicked, new_sel = imgui.checkbox(f"{fpath}##picker_{fpath}", is_sel)
|
if clicked:
|
||||||
if clicked:
|
if new_sel:
|
||||||
if new_sel:
|
self._ui_picker_selected.add(fpath)
|
||||||
self._ui_picker_selected.add(fpath)
|
else:
|
||||||
else:
|
self._ui_picker_selected.discard(fpath)
|
||||||
self._ui_picker_selected.discard(fpath)
|
imgui.end_child()
|
||||||
imgui.end_child()
|
|
||||||
imgui.separator()
|
imgui.separator()
|
||||||
|
|
||||||
if imgui.button("Add Selected", imgui.ImVec2(120, 0)):
|
if imgui.button("Add Selected", imgui.ImVec2(120, 0)):
|
||||||
@@ -2993,11 +3004,11 @@ class App:
|
|||||||
self._file_stats_queue = []
|
self._file_stats_queue = []
|
||||||
if not hasattr(self, '_file_stats_worker_active'):
|
if not hasattr(self, '_file_stats_worker_active'):
|
||||||
self._file_stats_worker_active = False
|
self._file_stats_worker_active = False
|
||||||
|
|
||||||
if imgui.collapsing_header("Context Composition"):
|
if imgui.collapsing_header("Context Composition"):
|
||||||
total_lines = 0
|
total_lines = 0
|
||||||
total_ast = 0
|
total_ast = 0
|
||||||
|
|
||||||
missing_keys = []
|
missing_keys = []
|
||||||
for f in self.context_files:
|
for f in self.context_files:
|
||||||
f_path = f.path if hasattr(f, "path") else str(f)
|
f_path = f.path if hasattr(f, "path") else str(f)
|
||||||
@@ -3009,7 +3020,7 @@ class App:
|
|||||||
stats = self._file_stats_cache[cache_key]
|
stats = self._file_stats_cache[cache_key]
|
||||||
total_lines += stats.get("lines", 0)
|
total_lines += stats.get("lines", 0)
|
||||||
total_ast += stats.get("ast_elements", 0)
|
total_ast += stats.get("ast_elements", 0)
|
||||||
|
|
||||||
# Process one missing key per frame or spawn a worker
|
# Process one missing key per frame or spawn a worker
|
||||||
if missing_keys and not self._file_stats_worker_active:
|
if missing_keys and not self._file_stats_worker_active:
|
||||||
def _stats_worker():
|
def _stats_worker():
|
||||||
@@ -3020,12 +3031,11 @@ class App:
|
|||||||
self._file_stats_cache[key] = aggregate.compute_file_stats(path)
|
self._file_stats_cache[key] = aggregate.compute_file_stats(path)
|
||||||
finally:
|
finally:
|
||||||
self._file_stats_worker_active = False
|
self._file_stats_worker_active = False
|
||||||
|
|
||||||
import threading
|
|
||||||
threading.Thread(target=_stats_worker, daemon=True).start()
|
threading.Thread(target=_stats_worker, daemon=True).start()
|
||||||
|
|
||||||
#region: Batch Action Bar imgui.text("Batch:")
|
#region: Batch Action Bar imgui.text("Batch:")
|
||||||
imgui.same_line()
|
# imgui.same_line()
|
||||||
for mode in ["full", "summary", "skeleton", "outline", "masked", "none"]:
|
for mode in ["full", "summary", "skeleton", "outline", "masked", "none"]:
|
||||||
if imgui.button(f"{mode.capitalize()}##batch"):
|
if imgui.button(f"{mode.capitalize()}##batch"):
|
||||||
for f in self.context_files:
|
for f in self.context_files:
|
||||||
@@ -3067,9 +3077,9 @@ class App:
|
|||||||
#endregion: Batch Action Bar
|
#endregion: Batch Action Bar
|
||||||
|
|
||||||
imgui.dummy(imgui.ImVec2(0, 4))
|
imgui.dummy(imgui.ImVec2(0, 4))
|
||||||
|
|
||||||
grouped_files = aggregate.group_files_by_dir(self.context_files)
|
grouped_files = aggregate.group_files_by_dir(self.context_files)
|
||||||
|
|
||||||
if imgui.begin_table("ctx_comp_table", 2, imgui.TableFlags_.resizable | imgui.TableFlags_.borders):
|
if imgui.begin_table("ctx_comp_table", 2, imgui.TableFlags_.resizable | imgui.TableFlags_.borders):
|
||||||
imgui.table_setup_column("File", imgui.TableColumnFlags_.width_stretch)
|
imgui.table_setup_column("File", imgui.TableColumnFlags_.width_stretch)
|
||||||
imgui.table_setup_column("Flags", imgui.TableColumnFlags_.width_fixed, 200)
|
imgui.table_setup_column("Flags", imgui.TableColumnFlags_.width_fixed, 200)
|
||||||
@@ -3180,6 +3190,7 @@ class App:
|
|||||||
imgui.same_line()
|
imgui.same_line()
|
||||||
imgui.text_colored(imgui.ImVec4(1.0, 0.5, 0.0, 1.0), "[Slices Active]")
|
imgui.text_colored(imgui.ImVec4(1.0, 0.5, 0.0, 1.0), "[Slices Active]")
|
||||||
imgui.tree_pop()
|
imgui.tree_pop()
|
||||||
|
|
||||||
imgui.end_table()
|
imgui.end_table()
|
||||||
# Context Composition collasping header
|
# Context Composition collasping header
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user