diff --git a/src/request_tab_widget.py b/src/request_tab_widget.py index 6d2ecde..f1ddd59 100644 --- a/src/request_tab_widget.py +++ b/src/request_tab_widget.py @@ -41,6 +41,8 @@ class RequestTabWidget(Gtk.Box): self.selected_environment_id = selected_environment_id self.project_manager = None # Will be injected from window self.environment_dropdown = None # Will be created if project_id is set + self.undefined_variables = set() # Track undefined variables for visual feedback + self._update_indicators_timeout_id = None # Debounce timer for indicator updates # Build the UI self._build_ui() @@ -374,6 +376,11 @@ class RequestTabWidget(Gtk.Box): row.set_header(key, value) row.connect('remove-requested', self._on_header_remove) row.connect('changed', self._on_request_changed) + + # Connect to variable indicator updates (debounced) + if self.project_id: + row.connect('changed', lambda r: self._schedule_indicator_update()) + self.headers_listbox.append(row) if key or value: @@ -395,6 +402,11 @@ class RequestTabWidget(Gtk.Box): body_buffer = self.body_sourceview.get_buffer() body_buffer.connect("changed", self._on_request_changed) + # Setup variable indicator updates (debounced) + if self.project_id: + self.url_entry.connect("changed", lambda w: self._schedule_indicator_update()) + body_buffer.connect("changed", lambda b: self._schedule_indicator_update()) + def _on_request_changed(self, widget, *args): """Mark this tab as modified.""" if not self.original_request: @@ -644,6 +656,9 @@ class RequestTabWidget(Gtk.Box): else: self.environment_dropdown.set_selected(0) # Default to "None" + # Update indicators after environment is set + self._update_variable_indicators() + def _on_environment_changed(self, dropdown, _param): """Handle environment selection change.""" if not hasattr(self, 'environment_ids'): @@ -652,6 +667,8 @@ class RequestTabWidget(Gtk.Box): selected_index = dropdown.get_selected() if selected_index < len(self.environment_ids): self.selected_environment_id = self.environment_ids[selected_index] + # Update visual indicators when environment changes + self._update_variable_indicators() def get_selected_environment(self): """Get the currently selected environment object.""" @@ -675,3 +692,110 @@ class RequestTabWidget(Gtk.Box): return env return None + + def _detect_undefined_variables(self): + """Detect undefined variables in the current request. Returns set of undefined variable names.""" + # Only detect if environment is selected + env = self.get_selected_environment() + if not env: + return set() + + # Get current request from UI + request = self.get_request() + + # Use VariableSubstitution to detect undefined variables + from .variable_substitution import VariableSubstitution + _, undefined = VariableSubstitution.substitute_request(request, env) + + return undefined + + def _schedule_indicator_update(self): + """Schedule an indicator update with debouncing.""" + # Cancel existing timeout + if self._update_indicators_timeout_id: + GLib.source_remove(self._update_indicators_timeout_id) + + # Schedule new update after 500ms + self._update_indicators_timeout_id = GLib.timeout_add(500, self._update_variable_indicators_timeout) + + def _update_variable_indicators_timeout(self): + """Timeout callback for updating indicators.""" + self._update_variable_indicators() + self._update_indicators_timeout_id = None + return False # Don't repeat + + def _update_variable_indicators(self): + """Update visual indicators for undefined variables.""" + # Detect undefined variables + self.undefined_variables = self._detect_undefined_variables() + + # Update URL entry + self._update_url_indicator() + + # Update headers + self._update_header_indicators() + + # Update body + self._update_body_indicators() + + def _update_url_indicator(self): + """Update warning indicator on URL entry.""" + from .variable_substitution import VariableSubstitution + + url_text = self.url_entry.get_text() + url_vars = set(VariableSubstitution.find_variables(url_text)) + undefined_in_url = url_vars & self.undefined_variables + + if undefined_in_url: + self.url_entry.add_css_class("warning") + tooltip = "Undefined variables: " + ", ".join(sorted(undefined_in_url)) + self.url_entry.set_tooltip_text(tooltip) + else: + self.url_entry.remove_css_class("warning") + self.url_entry.set_tooltip_text("") + + def _update_header_indicators(self): + """Update warning indicators on header rows.""" + from .variable_substitution import VariableSubstitution + + # Iterate through all header rows + child = self.headers_listbox.get_first_child() + while child: + if hasattr(child, 'key_entry') and hasattr(child, 'value_entry'): + # Check key + key_text = child.key_entry.get_text() + key_vars = set(VariableSubstitution.find_variables(key_text)) + undefined_in_key = key_vars & self.undefined_variables + + if undefined_in_key: + child.key_entry.add_css_class("warning") + else: + child.key_entry.remove_css_class("warning") + + # Check value + value_text = child.value_entry.get_text() + value_vars = set(VariableSubstitution.find_variables(value_text)) + undefined_in_value = value_vars & self.undefined_variables + + if undefined_in_value: + child.value_entry.add_css_class("warning") + else: + child.value_entry.remove_css_class("warning") + + child = child.get_next_sibling() + + def _update_body_indicators(self): + """Update warning indicators in body text.""" + # For body, we use a simpler approach: just set tooltip if there are undefined vars + from .variable_substitution import VariableSubstitution + + buffer = self.body_sourceview.get_buffer() + body_text = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), False) + body_vars = set(VariableSubstitution.find_variables(body_text)) + undefined_in_body = body_vars & self.undefined_variables + + if undefined_in_body: + tooltip = "Undefined variables: " + ", ".join(sorted(undefined_in_body)) + self.body_sourceview.set_tooltip_text(tooltip) + else: + self.body_sourceview.set_tooltip_text("")