|
| 1 | +"""LLDB Rust pretty-printer bootstrap for CodeLLDB. |
| 2 | +
|
| 3 | +Loads official Rust formatters (lldb_lookup + lldb_commands) without hardcoded toolchain paths. |
| 4 | +Adds summaries for core collection types and adjusts string length. Safe to re-run. |
| 5 | +""" |
| 6 | +from __future__ import annotations |
| 7 | +import os, sys, shutil, subprocess |
| 8 | + |
| 9 | +def log(*parts: object) -> None: |
| 10 | + print("[lldb_rust_init]", *parts) |
| 11 | + |
| 12 | +# 1) Ensure ~/.cargo/bin on PATH so rustc is discoverable when launched via CodeLLDB. |
| 13 | +home = os.environ.get("HOME", "") |
| 14 | +if home: |
| 15 | + cargo_bin = os.path.join(home, ".cargo", "bin") |
| 16 | + path = os.environ.get("PATH", "") |
| 17 | + if cargo_bin not in path.split(":"): |
| 18 | + os.environ["PATH"] = cargo_bin + (":" + path if path else "") |
| 19 | + log("PATH prepended with", cargo_bin) |
| 20 | +else: |
| 21 | + log("HOME unset; skipping PATH prepend") |
| 22 | + |
| 23 | +# 2) Locate rustc & sysroot. |
| 24 | +rustc = shutil.which("rustc") |
| 25 | +if not rustc: |
| 26 | + log("rustc NOT FOUND; aborting formatter init") |
| 27 | + raise SystemExit |
| 28 | +log("rustc ->", rustc) |
| 29 | +try: |
| 30 | + sysroot = subprocess.check_output([rustc, "--print", "sysroot"], text=True).strip() |
| 31 | +except Exception as e: # noqa: BLE001 |
| 32 | + log("Failed to get sysroot:", e) |
| 33 | + raise SystemExit |
| 34 | +log("sysroot ->", sysroot) |
| 35 | + |
| 36 | +etc_dir = os.path.join(sysroot, "lib", "rustlib", "etc") |
| 37 | +if not os.path.isdir(etc_dir): |
| 38 | + log("Missing etc dir:", etc_dir) |
| 39 | + raise SystemExit |
| 40 | +log("Loading Rust formatters from", etc_dir) |
| 41 | + |
| 42 | +# 3) Import lldb_lookup & source lldb_commands via LLDB command API. |
| 43 | +if etc_dir not in sys.path: |
| 44 | + sys.path.append(etc_dir) |
| 45 | +try: |
| 46 | + import lldb_lookup # type: ignore |
| 47 | + log("Imported lldb_lookup OK") |
| 48 | +except Exception as e: # noqa: BLE001 |
| 49 | + log("Import lldb_lookup FAILED:", e) |
| 50 | + raise SystemExit |
| 51 | + |
| 52 | +# Acquire lldb debugger object from injected global 'lldb' (provided by CodeLLDB environment). |
| 53 | +try: |
| 54 | + import lldb # type: ignore |
| 55 | +except Exception as e: # noqa: BLE001 |
| 56 | + log("Unable to import lldb module (unexpected):", e) |
| 57 | + raise SystemExit |
| 58 | + |
| 59 | +dbg = lldb.debugger |
| 60 | + |
| 61 | +# Source the static commands file for additional summaries (matches rust-lldb behavior). |
| 62 | +commands_path = os.path.join(etc_dir, "lldb_commands") |
| 63 | +if os.path.isfile(commands_path): |
| 64 | + dbg.HandleCommand(f"command source -s 0 {commands_path}") |
| 65 | + log("Sourced", commands_path) |
| 66 | +else: |
| 67 | + log("Commands file not found:", commands_path) |
| 68 | + |
| 69 | +# Enable Rust category & increase max string summary length. |
| 70 | +dbg.HandleCommand("type category enable Rust") |
| 71 | +dbg.HandleCommand("settings set target.max-string-summary-length 2000") |
| 72 | + |
| 73 | +# Register Vec printers explicitly (defensive if commands file changes in future). |
| 74 | +dbg.HandleCommand( |
| 75 | + 'type synthetic add -l lldb_lookup.synthetic_lookup -x "^(alloc::([a-z_]+::)+)Vec<.+>$" --category Rust' |
| 76 | +) |
| 77 | +dbg.HandleCommand( |
| 78 | + 'type summary add -F lldb_lookup.summary_lookup -e -x -h "^(alloc::([a-z_]+::)+)Vec<.+>$" --category Rust' |
| 79 | +) |
| 80 | + |
| 81 | +# Provide a concise summary for URI types (lsp_types::Url / fluent_uri::Uri wrappers). |
| 82 | +# These commonly contain an inner String we want to display directly. |
| 83 | +# We attempt a regex that matches types ending with `::Uri` or `::Url` and which have a |
| 84 | +# single field referencing alloc::string::String. |
| 85 | +def uri_summary(val_obj): # noqa: D401 |
| 86 | + """LLDB summary callback for various Uri/Url wrapper types. |
| 87 | +
|
| 88 | + Tries to locate an inner alloc::string::String and return its contents. |
| 89 | + Falls back to existing lldb_lookup.summary_lookup if structure differs. |
| 90 | + """ |
| 91 | + try: |
| 92 | + # Heuristics: search immediate children then recurse one level. |
| 93 | + for child in val_obj.children: |
| 94 | + ty = child.type.name or "" |
| 95 | + if "alloc::string::String" in ty: |
| 96 | + # Use the default Rust String summary by delegating to lldb_lookup. |
| 97 | + import lldb_lookup # type: ignore |
| 98 | + return lldb_lookup.summary_lookup(child) |
| 99 | + # Recurse one level for wrappers like tuple struct or newtype. |
| 100 | + for child in val_obj.children: |
| 101 | + for gchild in child.children: |
| 102 | + ty = gchild.type.name or "" |
| 103 | + if "alloc::string::String" in ty: |
| 104 | + import lldb_lookup # type: ignore |
| 105 | + return lldb_lookup.summary_lookup(gchild) |
| 106 | + except Exception as e: # noqa: BLE001 |
| 107 | + return f"<uri err: {e}>" |
| 108 | + return "<uri>" |
| 109 | + |
| 110 | +try: |
| 111 | + dbg.HandleCommand( |
| 112 | + 'type summary add -e -x -F lldb_rust_init.uri_summary "^(.*(::)+)(Url|Uri)$" --category Rust' |
| 113 | + ) |
| 114 | + log("Registered custom Url/Uri summary") |
| 115 | +except Exception as e: # noqa: BLE001 |
| 116 | + log("Failed to register custom Url/Uri summary:", e) |
| 117 | + |
| 118 | +log("Rust formatter initialization complete") |
0 commit comments