def determine_position( logria: 'Logria', messages_pointer: List[str]) -> Tuple[int, int]: # type: ignore """ Determine the start and end positions for a screen render """ if logria.stick_to_top: end = 0 rows = 0 for i in messages_pointer: if messages_pointer is logria.messages: # No processing needed for normal messages item: str = i elif messages_pointer is logria.matched_rows: # Grab the matched message item = logria.messages[i] # type: ignore # Determine if the message will fit in the window msg_lines = ceil(get_real_length(item) / logria.width) rows += msg_lines # If we can fit, increment the last row number if rows < logria.last_row and end < len(messages_pointer) - 1: end += 1 else: break logria.current_end = end # Save this row so we know where we are # When iterating backwards, we need to end at 0, so we must create a range # object like range(10, -1, -1) to generate a list that ends at 0 # If there are no messages, we want to not iterate later, so we change the # -1 to 0 so that we do not iterate at all return -1 if messages_pointer else 0, end # Early escape elif logria.stick_to_bottom: end = len(messages_pointer) - 1 elif logria.manually_controlled_line: if len(messages_pointer) < logria.last_row: # If have fewer messages than lines, just render it all end = len(messages_pointer) - 1 elif logria.current_end < logria.last_row: # If the last row we rendered comes before the last row we can render, # use all of the available rows end = logria.current_end elif logria.current_end < len(messages_pointer): # If we are looking at a valid line, render ends there end = logria.current_end else: # If we have over-scrolled, go back if logria.current_end > len(messages_pointer): logria.current_end = len(messages_pointer) # Since current_end can be zero, we have to use the number of messages end = len(messages_pointer) else: end = len(messages_pointer) logria.current_end = end # Save this row so we know where we are # Last index of a list is length - 1 start = max(-1, end - logria.last_row - 1) return start, end
def render_text_in_output(self) -> None: """ Renders stream content in the output window If filters are inactive, we use `messages`. If they are active, we pull from `matched_rows` We write the whole message, regardless of length, because slicing a string allocates a new string """ # Store a pointer to the buffer of messages if self.func_handle is None: messages_pointer = self.messages else: # Ignore typing because we use different values depending on what this pointer is messages_pointer = self.matched_rows # type: ignore # Determine the start and end position of the render start, end = determine_position(self, messages_pointer) # Don't do anything if nothing changed; start at index 0 if not self.analytics_enabled and self.previous_render == (max( start, 0), end): return # Early escape self.previous_render = (max(start, 0), end) self.outwin.erase() current_row = self.last_row # The row we are currently rendering for i in range(end, start, -1): if messages_pointer is self.messages: # No processing needed for normal messages item = messages_pointer[i] elif messages_pointer is self.matched_rows: # Grab the matched message and optionally highlight it messages_idx = self.matched_rows[i] item = self.messages[messages_idx] if self.highlight_match: # Remove all color codes before applying highlighter item = re.sub(constants.ANSI_COLOR_PATTERN, '', item) match = re.search(self.regex_pattern, item) if match: start, end = match.span() matched_str = item[start:end] item = item.replace( matched_str, f'\u001b[35m{matched_str}\u001b[0m') # Find the correct start position current_row -= ceil(get_real_length(item) / self.width) if current_row < 0: break # Instead of window.addstr, handle colors color_handler.addstr(self.outwin, current_row, 0, item.rstrip()) self.outwin.refresh()
def render_text_in_output(self) -> None: """ Renders stream content in the output window If filters are inactive, we use `messages`. If they are active, we pull from `matched_rows` We write the whole message, regardless of length, because slicing a string allocates a new string """ # Store a pointer to the buffer of messages if self.func_handle is None: messages_pointer = self.messages else: messages_pointer = self.matched_rows # Determine the start and end position of the render start, end = self.determine_render_position(messages_pointer) # Don't do anything if nothing changed; start at index 0 if self.previous_render == messages_pointer[max(start, 0):end]: return self.previous_render = messages_pointer[max(start, 0):end] self.outwin.erase() current_row = self.last_row # The row we are currently rendering for i in range(end, start, -1): if messages_pointer is self.messages: # No processing needed for normal messages item = messages_pointer[i] elif messages_pointer is self.matched_rows: # Grab the matched message and optionally highlight it messages_idx = self.matched_rows[i] item = self.messages[messages_idx] if self.highlight_match: # Remove all color codes before applying highlighter item = re.sub(constants.ANSI_COLOR_PATTERN, '', item) item = re.sub(self.regex_pattern, f'\u001b[35m{self.regex_pattern}\u001b[0m', item.rstrip()) # Find the correct start position current_row -= ceil(get_real_length(item) / self.width) if current_row < 0: break # Instead of window.addstr, handle colors color_handler.addstr(self.outwin, current_row, 0, item.rstrip()) self.outwin.refresh()
def test_get_real_length_with_color_code(self): """ Test that we get the real length of a string with escape codes """ self.assertEqual( regex_generator.get_real_length('\u001b[0m word \u001b[32m'), 6)
def test_get_real_length(self): """ Test that we get the real length of a string """ self.assertEqual(regex_generator.get_real_length('word'), 4)