class_name LogBuffer extends RichTextLabel ## Enhanced message buffer with retroactive filtering capabilities ## Stores all messages and applies filters dynamically without losing data const LoggieConsoleSettings = preload("res://addons/loggie-console/resources/loggie_console_settings.gd") const DomainColorManager = preload("res://addons/loggie-console/scripts/domain_color_manager.gd") # Signals signal filter_changed(visible_count: int, total_count: int) # Internal message storage var _all_messages: Array[LogMessage] = [] var _filtered_messages: Array[LogMessage] = [] var _current_filters: FilterState # Domain color manager reference var _color_manager: DomainColorManager # Memory management constants const DEFAULT_MAX_MESSAGES: int = 5000 const CLEANUP_PERCENTAGE: float = 0.2 # Remove 20% of messages when limit reached # Memory management @export var max_messages: int = DEFAULT_MAX_MESSAGES ## Internal LogMessage class for storing complete message data class LogMessage: var msg: LoggieMsg var preprocessed_content: String var msg_type: LoggieEnums.MsgType var log_level: LoggieEnums.LogLevel var timestamp: float var enhanced_content: String func _init(p_msg: LoggieMsg, p_content: String, p_type: LoggieEnums.MsgType, p_level: LoggieEnums.LogLevel) -> void: msg = p_msg preprocessed_content = p_content msg_type = p_type log_level = p_level timestamp = Time.get_unix_time_from_system() ## Filter state for retroactive filtering class FilterState: var enabled_domains: Array[String] = [] var min_log_level: LoggieEnums.LogLevel = LoggieEnums.LogLevel.DEBUG var text_search: String = "" var show_stack_only: bool = false func _init() -> void: # Initialize to show ERROR and above by default (lower numbers = higher priority) min_log_level = LoggieEnums.LogLevel.DEBUG func _ready() -> void: _current_filters = FilterState.new() bbcode_enabled = true ## Set the domain color manager instance func set_color_manager(color_manager: DomainColorManager) -> void: _color_manager = color_manager ## Add a new log message to the buffer func add_message(msg: LoggieMsg, content: String, msg_type: LoggieEnums.MsgType, log_level: LoggieEnums.LogLevel) -> void: # Create and store the message var log_msg: LogMessage = LogMessage.new(msg, content, msg_type, log_level) log_msg.enhanced_content = _enhance_with_metadata(log_msg) _all_messages.append(log_msg) # Manage memory if needed _manage_memory() # Apply current filters and rebuild display _reapply_filters() ## Set new filter state and reapply filters func set_filters(filter_state: FilterState) -> void: _current_filters = filter_state _reapply_filters() ## Set text search filter func set_text_search(search: String) -> void: if _current_filters.text_search == search: return _current_filters.text_search = search _reapply_filters() ## Clear all messages func clear_all() -> void: _all_messages.clear() _filtered_messages.clear() text = "" clear() # Emit updated counts filter_changed.emit(0, 0) ## Get current message counts func get_message_counts() -> Dictionary: return { "total": _all_messages.size(), "filtered": _filtered_messages.size() } ## Saves current buffer state to persistent settings resource ## Param settings: The settings resource to populate with current state func save_settings_to_resource(settings: LoggieConsoleSettings) -> void: settings.filter_state.from_filter_state(_current_filters) settings.max_messages = max_messages ## Loads buffer state from persistent settings resource ## Note: Does not immediately apply filters to avoid triggering reapply before messages exist ## Param settings: The settings resource containing saved state func load_settings_from_resource(settings: LoggieConsoleSettings) -> void: if settings.filter_state: _current_filters = settings.filter_state.to_filter_state() max_messages = settings.max_messages ## Applies previously loaded filter state to existing messages ## Call this after load_settings_from_resource() and message loading is complete func apply_loaded_filters() -> void: _reapply_filters() ## Private methods func _reapply_filters() -> void: _filtered_messages.clear() for msg in _all_messages: if msg != null and _message_passes_filters(msg, _current_filters): _filtered_messages.append(msg) _rebuild_display() # Emit filter change signal filter_changed.emit(_filtered_messages.size(), _all_messages.size()) func _message_passes_filters(msg: LogMessage, filters: FilterState) -> bool: # Domain check - if no domains are enabled, show no messages if filters.enabled_domains.size() == 0: return false # If domains are enabled, check if this message's domain is in the enabled list if not filters.enabled_domains.has(msg.msg.domain_name): return false # Level check - show messages at or above the minimum level (lower numbers = higher priority) if msg.log_level > filters.min_log_level: return false # Text search if not filters.text_search.is_empty(): var search_lower: String = filters.text_search.to_lower() if not msg.preprocessed_content.to_lower().contains(search_lower): return false # Stack filter if filters.show_stack_only and not msg.msg.appends_stack: return false return true func _rebuild_display() -> void: # Clear current display text = "" clear() # Add all filtered messages for msg in _filtered_messages: text += msg.enhanced_content + "\n" func _enhance_with_metadata(msg: LogMessage) -> String: var metadata_parts: Array[String] = [] metadata_parts.append("[color=%s]%s [/color]" % [Color.DIM_GRAY.to_html(false), Time.get_time_string_from_unix_time(int(msg.timestamp))]) if msg.msg.domain_name and not msg.msg.domain_name.is_empty(): var domain_color_html: String = _color_manager.get_domain_color_html(msg.msg.domain_name) metadata_parts.append("[color=%s]%s[/color]" % [domain_color_html, msg.msg.domain_name]) if msg.msg.domain_name and not msg.msg.domain_name.is_empty(): metadata_parts.append("[color=%s][%s] [/color]" % [_get_color_for_log_level(msg.log_level).to_html(false), LoggieEnums.LogLevel.keys()[msg.log_level]]) return "%s %s" % [" ".join(metadata_parts), msg.msg.content[0]] func _get_color_for_log_level(level: LoggieEnums.LogLevel) -> Color: match level: LoggieEnums.LogLevel.ERROR: return Color.RED LoggieEnums.LogLevel.WARN: return Color.ORANGE LoggieEnums.LogLevel.NOTICE: return Color.GREEN LoggieEnums.LogLevel.INFO: return Color.WHITE LoggieEnums.LogLevel.DEBUG, _: return Color.AQUA ## Manages memory by removing oldest messages when buffer limit exceeded ## Uses CLEANUP_PERCENTAGE constant to determine how many messages to remove func _manage_memory() -> void: if _all_messages.size() > max_messages: # Remove oldest messages based on cleanup percentage var remove_count: int = int(max_messages * CLEANUP_PERCENTAGE) _all_messages = _all_messages.slice(remove_count) # Note: This will cause filtered_messages indices to be invalid, # but _reapply_filters() will be called which rebuilds it