diff --git a/codeflash/lsp/lsp_logger.py b/codeflash/lsp/lsp_logger.py index 711431c19..33b932291 100644 --- a/codeflash/lsp/lsp_logger.py +++ b/codeflash/lsp/lsp_logger.py @@ -3,10 +3,10 @@ import logging import sys from dataclasses import dataclass -from typing import Any, Callable, Optional +from typing import Any, Callable from codeflash.lsp.helpers import is_LSP_enabled -from codeflash.lsp.lsp_message import LspTextMessage +from codeflash.lsp.lsp_message import LspTextMessage, message_delimiter root_logger = None @@ -43,16 +43,19 @@ def add_heading_tags(msg: str, tags: LspMessageTags) -> str: return msg -def extract_tags(msg: str) -> tuple[Optional[LspMessageTags], str]: +def extract_tags(msg: str) -> tuple[LspMessageTags, str]: delimiter = "|" - parts = msg.split(delimiter) - if len(parts) == 2: + first_delim_idx = msg.find(delimiter) + if first_delim_idx != -1 and msg.count(delimiter) == 1: + tags_str = msg[:first_delim_idx] + content = msg[first_delim_idx + 1 :] + tags = {tag.strip() for tag in tags_str.split(",")} message_tags = LspMessageTags() - tags = {tag.strip() for tag in parts[0].split(",")} - if "!lsp" in tags: - message_tags.not_lsp = True + # manually check and set to avoid repeated membership tests if "lsp" in tags: message_tags.lsp = True + if "!lsp" in tags: + message_tags.not_lsp = True if "force_lsp" in tags: message_tags.force_lsp = True if "loading" in tags: @@ -67,9 +70,9 @@ def extract_tags(msg: str) -> tuple[Optional[LspMessageTags], str]: message_tags.h3 = True if "h4" in tags: message_tags.h4 = True - return message_tags, delimiter.join(parts[1:]) + return message_tags, content - return None, msg + return LspMessageTags(), msg supported_lsp_log_levels = ("info", "debug") @@ -86,31 +89,32 @@ def enhanced_log( actual_log_fn(msg, *args, **kwargs) return - is_lsp_json_message = msg.startswith('{"type"') + is_lsp_json_message = msg.startswith(message_delimiter) and msg.endswith(message_delimiter) is_normal_text_message = not is_lsp_json_message - # extract tags only from the text messages (not the json ones) - tags, clean_msg = extract_tags(msg) if is_normal_text_message else (None, msg) + # Extract tags only from text messages + tags, clean_msg = extract_tags(msg) if is_normal_text_message else (LspMessageTags(), msg) lsp_enabled = is_LSP_enabled() - lsp_only = tags and tags.lsp - - if not lsp_enabled and not lsp_only: - # normal logging - actual_log_fn(clean_msg, *args, **kwargs) - return - - #### LSP mode #### - final_tags = tags if tags else LspMessageTags() - unsupported_level = level not in supported_lsp_log_levels - if not final_tags.force_lsp and (final_tags.not_lsp or unsupported_level): - return + # ---- Normal logging path ---- + if not tags.lsp: + if not lsp_enabled: # LSP disabled + actual_log_fn(clean_msg, *args, **kwargs) + return + if tags.not_lsp: # explicitly marked as not for LSP + actual_log_fn(clean_msg, *args, **kwargs) + return + if unsupported_level and not tags.force_lsp: # unsupported level + actual_log_fn(clean_msg, *args, **kwargs) + return + + # ---- LSP logging path ---- if is_normal_text_message: - clean_msg = add_heading_tags(clean_msg, final_tags) - clean_msg = add_highlight_tags(clean_msg, final_tags) - clean_msg = LspTextMessage(text=clean_msg, takes_time=final_tags.loading).serialize() + clean_msg = add_heading_tags(clean_msg, tags) + clean_msg = add_highlight_tags(clean_msg, tags) + clean_msg = LspTextMessage(text=clean_msg, takes_time=tags.loading).serialize() actual_log_fn(clean_msg, *args, **kwargs) diff --git a/codeflash/lsp/lsp_message.py b/codeflash/lsp/lsp_message.py index cfe15ef68..214927857 100644 --- a/codeflash/lsp/lsp_message.py +++ b/codeflash/lsp/lsp_message.py @@ -10,6 +10,9 @@ json_primitive_types = (str, float, int, bool) max_code_lines_before_collapse = 45 +# \u241F is the message delimiter becuase it can be more than one message sent over the same message, so we need something to separate each message +message_delimiter = "\u241f" + @dataclass class LspMessage: @@ -32,12 +35,8 @@ def type(self) -> str: def serialize(self) -> str: data = self._loop_through(asdict(self)) - # Important: keep type as the first key, for making it easy and fast for the client to know if this is a lsp message before parsing it ordered = {"type": self.type(), **data} - return ( - json.dumps(ordered) - + "\u241f" # \u241F is the message delimiter becuase it can be more than one message sent over the same message, so we need something to separate each message - ) + return message_delimiter + json.dumps(ordered) + message_delimiter @dataclass diff --git a/codeflash/optimization/function_optimizer.py b/codeflash/optimization/function_optimizer.py index 926adff95..2de5e5d0b 100644 --- a/codeflash/optimization/function_optimizer.py +++ b/codeflash/optimization/function_optimizer.py @@ -1865,7 +1865,6 @@ def line_profiler_step( self, code_context: CodeOptimizationContext, original_helper_code: dict[Path, str], candidate_index: int ) -> dict: try: - logger.info("Running line profiling to identify performance bottlenecks…") console.rule() test_env = self.get_test_env(