CoolFace
Apppublic

agentic-labs/lsproxy-tutorial

sourceHugging Faceagpl-3.0updated 2y agoView on Hugging Face
0likes
tutorial.py971 linesDownload Raw Back to root
1import marimo2 3__generated_with = "0.9.14"4app = marimo.App(width="medium")5 6 7@app.cell8def __():9    import requests10    import json11    import sys12    from typing import Dict, Any, Optional, List13 14    from lsproxy import GetReferencesRequest, FileRange, Position15 16    import marimo as mo17    return (18        Any,19        Dict,20        FileRange,21        GetReferencesRequest,22        List,23        Optional,24        Position,25        json,26        mo,27        requests,28        sys,29    )30 31 32@app.cell33def __(mo):34    mo.md("""### Welcome to the `lsproxy` tutorial! We'll be showing you how you can use `lsproxy` to easily navigate and search another codebase using python. Let's get started!\n> We will be using an open-source repo to demonstrate `lsproxy`. We chose [Trieve](https://github.com/devflowinc/trieve), a rust-based infrastructure solution for search, recommendations and RAG. They have rust for their backend, and typescript to run multiple frontend interfaces. We love their product and their team, check them out!""")35    return36 37 38@app.cell39def __(mo):40    mo.md("""<div style="height: 50px;"></div>""")41    return42 43 44@app.cell45def __(mo):46    # The first step is to create our API client47    from lsproxy import Lsproxy48 49    api_client = Lsproxy()50    mo.show_code()51    return Lsproxy, api_client52 53 54@app.cell55def __(mo):56    mo.md("""Other than starting the `lsproxy` docker container, no initialization is required to use `lsproxy`. Here, our "initialization" is just reading in the files to make the tutorial easier to navigate! Please click the button below to get started.""")57    return58 59 60@app.cell61def __(mo):62    start_button = mo.ui.run_button(label="Click to initialize")63    start_button64    return (start_button,)65 66 67@app.cell68def __(get_files, mo, start_button):69    # Reads all the files in the repo on initialization70    mo.stop(not start_button.value)71    file_symbol_dict = get_files()72    return (file_symbol_dict,)73 74 75@app.cell76def __(mo):77    mo.md("""<div style="height: 100px;"></div>""")78    return79 80 81@app.cell82def __(file_symbol_dict, mo):83    mo.stop(not file_symbol_dict)84 85    mo.md(86        """### `Example 1: Exploring symbols and their references in a file`\nYou'll see how easy it is to:\n\n- Get symbol definitions from a file.\n- Read the source code for any symbol.\n- Find references to the symbol across the codebase\n\n<p>Also note that we are only showing typescript and rust in this example, but we also support python!</p>\n---\n"""87    )88    return89 90 91@app.cell92def __(selections_ex1):93    selections_ex194    return95 96 97@app.cell98def __(code_language_select_ex1, file_dropdown_ex1, mo):99    # This is just for controlling the flow of this tutorial100    mo.stop(not file_dropdown_ex1.value)101    selected_file_first_time = True102    code_language_ex1 = code_language_select_ex1.value103    selected_file_ex1 = file_dropdown_ex1.value104    return code_language_ex1, selected_file_ex1, selected_file_first_time105 106 107@app.cell108def __(code_language_ex1, mo, selected_file_first_time):109    # This is just for controlling the flow of this tutorial110    mo.stop(not selected_file_first_time)111 112    mo.md(113        f"Note that you selected a file in {code_language_ex1}, but `lsproxy` wraps language servers for all the supported languages, and routes your request to the right one, so you don't have to worry about configuring servers for each language. Go ahead and try a different language!"114    )115    return116 117 118@app.cell119def __(api_client, mo, selected_file_ex1):120    # Retrieving the symbols defined in a file is just a single call121    symbols_ex1 = api_client.definitions_in_file(selected_file_ex1)122 123    mo.show_code()124    return (symbols_ex1,)125 126 127@app.cell128def __(mo, symbols_ex1):129    # Pack the data from the symbols into a tabular format130    table_data_ex1 = [131        {132            "name": symbol.name,133            "kind": symbol.kind,134            "start_line": symbol.identifier_position.position.line,135            "start_character": symbol.identifier_position.position.character,136            "num_lines": symbol.range.end.line - symbol.range.start.line + 1,137            "index": i,138        }139        for i, symbol in enumerate(symbols_ex1)140    ]141 142    # Create the table element to display143    symbol_table_ex1 = mo.ui.table(144        data=table_data_ex1,145        page_size=10,146        selection="single",147        label="Now, select a symbol to view code and references",148    )149    # Display the table150    symbol_table_ex1151    return symbol_table_ex1, table_data_ex1152 153 154@app.cell155def __(mo, symbol_table_ex1, symbols_ex1):156    mo.stop(not symbol_table_ex1.value)157    selected_symbol_ex1 = symbols_ex1[symbol_table_ex1.value[0].get("index")]158    return (selected_symbol_ex1,)159 160 161@app.cell162def __(163    FileRange,164    GetReferencesRequest,165    api_client,166    mo,167    selected_file_ex1,168    selected_symbol_ex1,169):170    # Read the source code for a particular range in a file by just asking for it!171    file_range_ex1 = FileRange(172        path=selected_file_ex1,173        start=selected_symbol_ex1.range.start,174        end=selected_symbol_ex1.range.end,175    )176    source_code_ex1 = api_client.read_source_code(file_range_ex1).source_code177 178    # Get references to the symbol and optionally include context lines surrounding the usage179    reference_request_ex1 = GetReferencesRequest(180        identifier_position=selected_symbol_ex1.identifier_position,181        include_code_context_lines=2,182    )183    reference_results_ex1 = api_client.find_references(reference_request_ex1)184    viewed_symbol = True185    mo.show_code()186    return (187        file_range_ex1,188        reference_request_ex1,189        reference_results_ex1,190        source_code_ex1,191        viewed_symbol,192    )193 194 195@app.cell196def __(197    code_language_ex1,198    mo,199    pretty_format_code_result,200    pretty_format_reference_results,201    reference_results_ex1,202    source_code_ex1,203):204    # Format the code and reference results for display205    code_text_ex1 = pretty_format_code_result(source_code_ex1, code_language_ex1)206    reference_text_ex1 = pretty_format_reference_results(207        reference_results_ex1, code_language_ex1208    )209 210    # Display the code and reference text211    mo.callout(212        mo.vstack(213            [214                mo.md(code_text_ex1),215                mo.md(reference_text_ex1),216            ]217        )218    )219    return code_text_ex1, reference_text_ex1220 221 222@app.cell223def __(mo, viewed_symbol):224    mo.stop(not viewed_symbol)225    example_2 = mo.ui.run_button(label="Click to move on to example 2: Exploring connections between files", full_width=True)226    example_2227    return (example_2,)228 229 230@app.cell231def __(mo):232    mo.md("""<div style="height: 100px;"></div>""")233    return234 235 236@app.cell237def __(example_2, mo):238    mo.stop(not example_2.value)239    ex2_unlocked = True240    return (ex2_unlocked,)241 242 243@app.cell244def __(ex2_unlocked, mo, selections_2):245    mo.stop(not ex2_unlocked)246    mo.vstack([247    mo.md("""### Example 2: Exploring connections between files\nThe examples above are similar to the kind of functionality you can find in your IDE, but having everything accessible with easy python functions means that you can compose these operations to be much more powerful.\n\nIn this example, we show:\n\n- Finding all the files that reference a given file\n- Tagging each file with the symbols it references\n\n---"""),248    selections_2,249    ])250    return251 252 253@app.cell254def __(ex2_unlocked, file_dropdown_2, mo):255    mo.stop(not ex2_unlocked)256    # Pull the symbols inside a file257    selected_file_ex2 = file_dropdown_2.value258    return (selected_file_ex2,)259 260 261@app.cell262def __(api_client, mo, selected_file_ex2):263    # As before we can get all of the symbols from a file264    symbols_ex2 = api_client.definitions_in_file(selected_file_ex2)265    mo.show_code()266    return (symbols_ex2,)267 268 269@app.cell270def __(271    GetReferencesRequest,272    api_client,273    mo,274    selected_file_ex2,275    symbols_ex2,276):277    # But now we can repeatedly look for references on EVERY symbol in the file and build up a graph of the references278    referenced_symbols_in_file_dict = {}279    for symbol in mo.status.progress_bar(280        symbols_ex2, title="Symbols processed", remove_on_exit=True281    ):282        reference_request_ex2 = GetReferencesRequest(283            identifier_position=symbol.identifier_position,284        )285        references_ex2 = api_client.find_references(reference_request_ex2).references286 287        # Save which symbols were referenced by which file288        for ref in references_ex2:289            referencing_file = ref.path290            if referencing_file != selected_file_ex2:291                referenced_symbols_in_file_dict.setdefault(292                    (selected_file_ex2, referencing_file), set()293                ).add(symbol.name)294    mo.show_code()295    return (296        ref,297        reference_request_ex2,298        referenced_symbols_in_file_dict,299        references_ex2,300        referencing_file,301        symbol,302    )303 304 305@app.cell306def __(ex2_unlocked, mo):307    mo.stop(not ex2_unlocked)308 309    mo.md(310        "From this information we can build a simple graph showing how a file's symbols are referenced by other files in the codebase."311    )312    return313 314 315@app.cell316def __(generate_reference_diagram, mo, referenced_symbols_in_file_dict):317    if not referenced_symbols_in_file_dict:318        mermaid_diagram = generate_reference_diagram("No external references found")319    else:320        mermaid_diagram = generate_reference_diagram(referenced_symbols_in_file_dict)321    diagram_shown = True322    mo.mermaid(mermaid_diagram)323    return diagram_shown, mermaid_diagram324 325 326@app.cell327def __(mo):328    mo.md("""<div style="height: 100px;"></div>""")329    return330 331 332@app.cell333def __(diagram_shown, mo):334    mo.stop(not diagram_shown)335    example_3 = mo.ui.run_button(label="Click to move on to example 3: Analyzing a change diff with call hierarchy.", full_width=True)336    example_3337    return (example_3,)338 339 340@app.cell341def __(example_3, mo):342    mo.stop(not example_3.value)343    mo.md(344        """### Example 3 (Advanced): Analyzing a change diff with call hierarchy.\n We can compose definitions and references to identify the full code paths that are affected by a particular change, and uncover ripple effects through the codebase.\n\n---"""345    )346    return347 348 349@app.cell350def __(example_3, mo):351    mo.stop(not example_3.value)352    import subprocess353    import io354    from pydantic import BaseModel355    mo.md("""Let's start with a diff of a change to the deletion logic in Trieve in this [PR](https://github.com/devflowinc/trieve/pull/2649)""")356    return BaseModel, io, subprocess357 358 359@app.cell360def __(mo, subprocess):361    parent_commit = "1910d6867877bfdd64ca822e266372335392a8be"362 363    # Load in the diff364    diff_text = subprocess.check_output(365        ["git", "diff", parent_commit], cwd="./trieve"366    ).decode("utf-8")367 368    mo.show_code(f"Output: Diff has {len(diff_text.splitlines())} lines")369    return diff_text, parent_commit370 371 372@app.cell373def __(example_3, mo):374    mo.stop(not example_3.value)375    mo.md("""First, we extract affected lines from the diff text.""")376    return377 378 379@app.cell380def __(Dict, List, Tuple, diff_text, io, mo):381    from unidiff import PatchSet382 383    def parse_diff(diff_text) -> Tuple[Dict[str, List[int]], str]:384        patch = PatchSet(io.StringIO(diff_text))385        affected_lines = {}386        for patched_file in patch:387            for hunk in patched_file:388                for line in hunk:389                    if line.is_added:390                        affected_lines.setdefault(patched_file.path, set()).add(391                            line.target_line_no392                        )393                    elif line.is_removed:394                        affected_lines.setdefault(patched_file.path, set()).add(395                            line.source_line_no396                        )397        return affected_lines398 399 400    affected_lines = parse_diff(diff_text)401 402    mo.show_code(403        f"Output: Diff contains {sum([len(lines) for lines in affected_lines.values()])} changed lines in {len(affected_lines)} files."404    )405    return PatchSet, affected_lines, parse_diff406 407 408@app.cell409def __(example_3, mo):410    mo.stop(not example_3.value)411    mo.md("""Then we define logic to \n\n 1. Find symbol definitions containing affected lines\n\n 2. Find references to these symbols.\n\n 3. Repeat with lines containing references, until we reach the end.\n\nWe also save the symbols that are direct parents of affected lines, so we can distinguish them from the symbols indirectly affected by the change.""")412    return413 414 415@app.cell416def __(BaseModel, GetReferencesRequest, List, Set, Tuple, api_client, mo):417    from lsproxy import FilePosition418 419 420    class HierarchyItem(BaseModel):421        name: str422        kind: str423        defined_at: FilePosition424        source_code: str425 426        def __hash__(self) -> int:427            return hash(428                (429                    self.defined_at.path,430                    self.defined_at.position.line,431                    self.defined_at.position.character,432                )433            )434 435 436    def get_symbols_containing_positions(437        target_positions: List[FilePosition],438    ) -> List[HierarchyItem]:439        file_path = target_positions[0].path440 441        #######################442        ### Get definitions ###443        #######################444        symbols = api_client.definitions_in_file(file_path)445        symbols_containing_position = {446            HierarchyItem(447                name=symbol.name,448                kind=symbol.kind,449                defined_at=symbol.identifier_position,450                source_code=api_client.read_source_code(symbol.range).source_code,451            )452            for symbol in symbols453            for target_position in target_positions454            if symbol.range.contains(target_position)455        }456        return symbols_containing_position457 458 459    def propagate_changes_through_codebase(symbols_changed_directly: List[FilePosition]):460        """461        Compute the chain of code symbols that touch the code at the starting positions.462        """463        nodes: Set[HierarchyItem] = set()464        edges: Set[Tuple[HierarchyItem, HierarchyItem]] = set()465 466        # Initialize with symbols that contain the starting positions467        stack = list(symbols_changed_directly)468 469        while stack:470            symbol = stack.pop()471 472            # If we've already processesed this symbol skip it473            if symbol in nodes:474                continue475            nodes.add(symbol)476 477            # For each symbol we find all its references478            references = api_client.find_references(479                GetReferencesRequest(480                    identifier_position=symbol.defined_at,481                    include_declaration=False,482                )483            ).references484 485            # Group them by file486            references_by_file = {}487            for ref in references:488                references_by_file.setdefault(ref.path, []).append(ref)489 490            # And then find symbols that contain the references so we can keep processing491            related_symbols = [492                sym493                for refs in references_by_file.values()494                for sym in get_symbols_containing_positions(refs)495            ]496 497            for related_symbol in related_symbols:498                if related_symbol != symbol:499                    edges.add((symbol, related_symbol))500                    stack.append(related_symbol)501 502        return nodes, edges503 504 505    mo.show_code()506    return (507        FilePosition,508        HierarchyItem,509        get_symbols_containing_positions,510        propagate_changes_through_codebase,511    )512 513 514@app.cell515def __(516    FilePosition,517    Position,518    affected_lines,519    api_client,520    get_symbols_containing_positions,521    mo,522    propagate_changes_through_codebase,523):524    affected_files = list(affected_lines.keys())525    lsp_files = api_client.list_files()526    affected_code_files = filter(lambda file: file in lsp_files, affected_files)527 528    symbols_changed_directly = set()529    for file in affected_code_files:530        # For all the affected lines, we figure out what symbol they belong to531        affected_positions = [532            FilePosition(path=file, position=Position(line=line, character=0))533            for line in affected_lines[file]534        ]535        symbols_changed_directly.update(get_symbols_containing_positions(affected_positions))536 537    # And then recursively follow the affected symbol through the codebase by following references538    all_nodes, all_edges = propagate_changes_through_codebase(symbols_changed_directly)539 540    mo.show_code()541    return (542        affected_code_files,543        affected_files,544        affected_positions,545        all_edges,546        all_nodes,547        file,548        lsp_files,549        symbols_changed_directly,550    )551 552 553@app.cell554def __(555    all_edges,556    all_nodes,557    hierarchy_to_mermaid,558    mo,559    symbols_changed_directly,560):561    mm = hierarchy_to_mermaid(all_nodes, all_edges, symbols_changed_directly)562    mo.vstack([563        mo.md("### Call graph of the code affected by the change.\n #### The white nodes are present in the diff, while the red ones are affected indirectly."),564        mo.mermaid(mm)565    ])566    return (mm,)567 568 569@app.cell570def __(HierarchyItem, Set, Tuple):571    def hierarchy_to_mermaid(572        nodes: Set[HierarchyItem],573        edges: Set[Tuple[HierarchyItem, HierarchyItem]],574        symbols_changed_directly: Set[HierarchyItem],575    ) -> str:576        """577        Convert hierarchy nodes and edges to a Mermaid diagram string with subgraphs by file.578        Uses hash codes as node identifiers. Nodes that were changed directly are colored red.579 580        Args:581            nodes: Set of HierarchyItem objects representing code symbols582            edges: Set of tuples containing (from_symbol, to_symbol) relationships583            symbols_changed_directly: Set of HierarchyItem objects that were changed directly584 585        Returns:586            str: Mermaid diagram representation of the hierarchy with file-based subgraphs587        """588        mermaid_lines = [589            "%%{",590            "  init: {",591            "    'flowchart': {",592            "      'rankSpacing': 100,",  # Increase vertical space between ranks593            "      'nodeSpacing': 50,",  # Increase horizontal space between nodes594            "      'padding': 20",  # Add padding around the entire diagram595            "    }",596            "  }",597            "}%%",598            "graph TD",599        ]600 601        # Track nodes that need red styling602        direct_node_ids = set()603        indirect_node_ids = set()604 605        # Group nodes by file606        nodes_by_file = {}607        for node in nodes:608            file_path = node.defined_at.path609            if file_path not in nodes_by_file:610                nodes_by_file[file_path] = []611            nodes_by_file[file_path].append(node)612 613            # Track node IDs that need to be colored red614            if node in symbols_changed_directly:615                direct_node_ids.add(f"node{abs(hash(node))}")616            else:617                indirect_node_ids.add(f"node{abs(hash(node))}")618 619        # Create subgraphs for each file620        for file_idx, (file_path, file_nodes) in enumerate(nodes_by_file.items()):621            # Create subgraph with unique ID622            subgraph_id = f"subgraph_{file_idx}"623            mermaid_lines.append(f"    subgraph {subgraph_id}[{file_path}]")624 625            # Add nodes for this file626            for node in file_nodes:627                # Escape quotes and special characters in names628                escaped_name = node.name.replace('"', '\\"')629                # Add kind as a suffix in italics630                label = f'"{escaped_name}<br><i>{node.kind}</i>"'631                # Use absolute value of hash to ensure positive ID632                node_id = f"node{abs(hash(node))}"633                mermaid_lines.append(f"        {node_id}[{label}]")634 635            # Close subgraph636            mermaid_lines.append("    end")637 638        # Add edges using hash IDs (outside subgraphs)639        for from_node, to_node in edges:640            from_id = f"node{abs(hash(from_node))}"641            to_id = f"node{abs(hash(to_node))}"642            mermaid_lines.append(f"    {from_id} --> {to_id}")643 644        # Add styling for red nodes645        for node_id in indirect_node_ids:646            mermaid_lines.append(f"    style {node_id} fill:#ffcccc,color:#000")647        for node_id in direct_node_ids:648            mermaid_lines.append(f"    style {node_id} fill:#ffffff,color:#000")649 650        return "\n".join(mermaid_lines)651    return (hierarchy_to_mermaid,)652 653 654@app.cell655def __(affected_lines, all_nodes, mo):656    diff_files = set(affected_lines.keys())657    call_hierarchy_files = set([n.defined_at.path for n in all_nodes])658    affected_files_not_in_diff = call_hierarchy_files - diff_files659    affected_files_not_in_diff_str = '\n'.join([f'{i+1}. {f}' for i, f in enumerate(affected_files_not_in_diff)])660    mo.md(f"We now see code paths crossing {len(affected_files_not_in_diff)} files that are not in the diff:\n\n{affected_files_not_in_diff_str}")661    return (662        affected_files_not_in_diff,663        affected_files_not_in_diff_str,664        call_hierarchy_files,665        diff_files,666    )667 668 669@app.cell670def __(mo):671    mo.md("""<div style="height: 400px;"></div>""")672    return673 674 675@app.cell676def __(mo):677    mo.md("""Thanks for trying `lsproxy`! See the README on our [github repo](https://github.com/agentic-labs/lsproxy) to run on your own code. Or if you want to play with the code in this example, you can use:\n\n```./examples/run.sh --edit```""")678    return679 680 681@app.cell682def __():683    # Appendix A: UI code to run the example684    return685 686 687@app.cell688def __(create_dropdowns, create_selector_dict, file_symbol_dict, mo):689    # UI Elements for the first example690    js_dropdown_1, rs_dropdown_1 = create_dropdowns(file_symbol_dict, "server/src/handlers/chunk_handler.rs: (65 symbols)")691    selector_dict_1 = create_selector_dict(js_dropdown_1, rs_dropdown_1)692    code_language_select_ex1 = mo.ui.radio(options=["typescript", "rust"], value="rust")693    return (694        code_language_select_ex1,695        js_dropdown_1,696        rs_dropdown_1,697        selector_dict_1,698    )699 700 701@app.cell702def __(code_language_select_ex1, mo, selector_dict_1):703    # Combining UI selections for the first example704    file_dropdown_ex1 = selector_dict_1[code_language_select_ex1.value]705    selections_ex1 = mo.hstack(706        [file_dropdown_ex1, code_language_select_ex1],707        gap=2,708        justify="end",709    )710    return file_dropdown_ex1, selections_ex1711 712 713@app.cell714def __(create_dropdowns, create_selector_dict, file_symbol_dict, mo):715    # UI Elements for the second example716    js_dropdown_2, rs_dropdown_2 = create_dropdowns(file_symbol_dict, "server/src/handlers/analytics_handler.rs: (15 symbols)")717    selector_dict_2 = create_selector_dict(js_dropdown_2, rs_dropdown_2)718    submit_button_2 = mo.ui.run_button(label="Find referenced files")719    code_language_select_ex2 = mo.ui.radio(options=["typescript", "rust"], value="rust")720    return (721        code_language_select_ex2,722        js_dropdown_2,723        rs_dropdown_2,724        selector_dict_2,725        submit_button_2,726    )727 728 729@app.cell730def __(code_language_select_ex2, mo, selector_dict_2):731    # Combining UI selections for the second example732    file_dropdown_2 = selector_dict_2[code_language_select_ex2.value]733    selections_2 = mo.hstack(734        [735            file_dropdown_2,736            code_language_select_ex2,737        ],738        gap=2,739        justify="end",740    )741    return file_dropdown_2, selections_2742 743 744@app.cell745def __():746    # Appendix B: Helper functions to create the UI code747    return748 749 750@app.cell751def __(api_client, mo):752    def get_files():753        file_dict = {}754        with mo.status.spinner():755            files = api_client.list_files()756        for file in mo.status.progress_bar(757            files, title="Files processed", remove_on_exit=True758        ):759            symbols = api_client.definitions_in_file(file)760            file_dict[file] = symbols761        return file_dict762    return (get_files,)763 764 765@app.cell766def __(create_lang_dropdown):767    def create_dropdowns(file_dict, value = None):768        file_with_symbol_count = [769            (file, len(symbols))770            for file, symbols in file_dict.items()771            if len(symbols) > 0772        ]773        file_with_symbol_count = sorted(774            file_with_symbol_count, key=lambda item: -item[1]775        )776        js_dropdown = create_lang_dropdown(777            file_with_symbol_count,778            ["ts", "tsx", "js", "jsx"],779            "Select a typescript/javascript file ->", None780        )781        rs_dropdown = create_lang_dropdown(782            file_with_symbol_count, ["rs"], "Select a rust file ->", value783        )784        return js_dropdown, rs_dropdown785    return (create_dropdowns,)786 787 788@app.cell789def __(mo):790    def create_lang_dropdown(file_symbol_dict, endings, label, value):791        file_options = {792            f"{file}: ({symbols} symbols)": file793            for file, symbols in file_symbol_dict794            if file.split(".")[-1] in endings795        }796        if value:797            return mo.ui.dropdown(options=file_options, label=label, value=value)798        else:799            return mo.ui.dropdown(options=file_options, label=label)800    return (create_lang_dropdown,)801 802 803@app.cell804def __():805    def create_selector_dict(js_dropdown, rs_dropdown):806        return {807            "typescript": js_dropdown,808            "rust": rs_dropdown,809        }810    return (create_selector_dict,)811 812 813@app.cell814def __():815    # Appendix C: Formatting functions for the text and mermaid charts816    return817 818 819@app.cell820def __():821    def pretty_format_code_result(code_result, code_language):822        return f"""### `Code`\n---\n```{code_language}\n\n{code_result}\n```\n"""823    return (pretty_format_code_result,)824 825 826@app.cell827def __():828    def pretty_format_reference_results(reference_results, code_language):829        # Header or no references830        ref_text = (831            ["\n### `References`\n---\n"]832            if reference_results.references833            else ["\n---\n### `No references found`"]834        )835        refs = {}836        for ref, context in zip(837            reference_results.references, reference_results.context838        ):839            # Split the code into it's lines and add an indicator for where the reference is840            code = context.source_code.split("\n")841            line_nums = range(context.range.start.line, context.range.end.line + 1)842            before_reference = filter(843                lambda num_code: num_code[0] < ref.position.line + 1,844                zip(line_nums, code),845            )846            after_reference = filter(847                lambda num_code: num_code[0] > ref.position.line,848                zip(line_nums, code),849            )850            code_with_line_nums = (851                list(before_reference)852                + [("", "_" * ref.position.character + "^")]853                + list(after_reference)854            )855 856            # Extend the list for the file with the new the (line_num, code) plus a separator857            file = ref.path858            refs.setdefault(file, []).extend(code_with_line_nums)859            refs[file].append(("@@@@@", "-----"))860 861        # For each file862        for ref_file, ref_lines in refs.items():863            ref_text.append(f"**{ref_file}**\n\n```{code_language}")864            ref_text.extend([f"{num:5}: {line}" for num, line in ref_lines])865            ref_text.append(f"```\n\n")866        return "\n".join(ref_text)867    return (pretty_format_reference_results,)868 869 870@app.cell871def __():872    def generate_reference_diagram(dependencies: dict, max_chars: int = 28) -> str:873        """874        Convert a dictionary of file dependencies and their referenced symbols into a Mermaid diagram string.875        Arrows point from referenced file back to source file through reference nodes.876        Args:877            dependencies: Dict where keys are tuples of (defined_file, referenced_file) and values are sets of referenced symbols878                         OR a string representing the root file path when there are no dependencies879            max_chars: Maximum length for displayed file paths, truncating from left if needed880        Returns:881            String containing the Mermaid diagram definition882        """883 884        def get_display_name(file_path: str) -> str:885            """Get display name for a file, truncating from left if needed."""886            if len(file_path) <= max_chars:887                return file_path888            return "..." + file_path[-(max_chars - 3) :]889 890        # Handle case where dependencies is just a root file string891        if isinstance(dependencies, str):892            return f"""graph LR893        root["{dependencies}"]894        classDef default fill:#f9f9f9,stroke:#333,stroke-width:2px,color:#000;895        classDef source fill:#e1f5fe,stroke:#0277bd,stroke-width:2px,color:#000;896        class root source;"""897 898        if not dependencies:899            return "graph LR\n    %% No dependencies to display"900 901        mermaid_lines = ["graph LR"]902        # Add styling with reduced padding903        mermaid_lines.extend(904            [905                "    %% Styling",906                "    classDef default fill:#f9f9f9,stroke:#333,stroke-width:2px,color:#000,max-width:none,text-overflow:clip,padding:0px;",907                "    classDef source fill:#e1f5fe,stroke:#0277bd,stroke-width:2px,color:#000,max-width:none,text-overflow:clip,padding:0px;",908                "    classDef reference fill:#e8e7ff,stroke:#6b69d6,stroke-width:2px,color:#000,max-width:none,text-overflow:clip,padding:0px;",909            ]910        )911 912        # Collect all unique files and create nodes913        unique_files = set()914        for defined_file, referenced_file in dependencies.keys():915            unique_files.add(defined_file)916            unique_files.add(referenced_file)917 918        # Create nodes for each unique file919        node_names = {}920        for idx, file in enumerate(unique_files):921            node_name = f"n{idx}"922            node_names[file] = node_name923            display_name = get_display_name(file)924            clean_name = display_name.replace('"', "&quot;")925            mermaid_lines.append(f'    {node_name}["{clean_name}"]')926 927        # Create reference nodes and connections928        for idx, ((defined_file, referenced_file), symbols) in enumerate(929            dependencies.items()930        ):931            from_node = node_names[defined_file]932            to_node = node_names[referenced_file]933            ref_node = f"ref{idx}"934 935            # Clean and truncate symbols936            cleaned_symbols = []937            for symbol in sorted(symbols):938                clean_symbol = str(symbol)939                clean_symbol = clean_symbol.replace('"', "&quot;")940                clean_symbol = clean_symbol.replace("<", "&lt;")941                clean_symbol = clean_symbol.replace(">", "&gt;")942                if len(clean_symbol) > 20:943                    clean_symbol = clean_symbol[:17] + "..."944                cleaned_symbols.append(clean_symbol)945 946            # Create symbol display with limited number of examples947            symbols_display = "<br/>" + "<br/>".join(cleaned_symbols)948            if len(cleaned_symbols) > 5:949                symbols_display = (950                    "<br/>" + "<br/>".join(cleaned_symbols[:5]) + "<br/>..."951                )952 953            # Add reference node and connections954            ref_node_def = f'    {ref_node}["{len(symbols)} refs{symbols_display}"]'955            mermaid_lines.append(ref_node_def)956            mermaid_lines.append(f"    {to_node} --> {ref_node} --> {from_node}")957            mermaid_lines.append(f"    class {ref_node} reference")958 959        mermaid_lines.extend(960            [961                f"    class {node_names[next(iter(dependencies))[0]]} source;",962            ]963        )964 965        return "\n".join(mermaid_lines)966    return (generate_reference_diagram,)967 968 969if __name__ == "__main__":970    app.run()971