mirror of
https://github.com/nyakang/nyaterm.git
synced 2026-11-06 08:07:34 +00:00
- Added `nyaterm-files` for local and remote file services, including SFTP and shell file backends. - Introduced `nyaterm-remote-ops` for system metrics, process management, and Docker operations. - Updated `AGENTS.md` to reflect the new crates and their responsibilities. - Ensured that the new crates remain independent of GPUI and desktop presentation types. - Updated `Cargo.toml` and `Cargo.lock` to include new dependencies and workspace members.
113 lines
5.9 KiB
Python
113 lines
5.9 KiB
Python
"""Apply reviewable diagnostic instrumentation to an isolated source checkout."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from pathlib import Path
|
|
|
|
|
|
TEMPLATES = Path(__file__).with_name("diagnostic")
|
|
|
|
|
|
def replace_once(text: str, old: str, new: str, label: str) -> str:
|
|
if text.count(old) != 1:
|
|
raise ValueError(f"diagnostic overlay anchor changed: {label}")
|
|
return text.replace(old, new, 1)
|
|
|
|
|
|
def enter_function(text: str, name: str, statement: str) -> str:
|
|
pattern = rf"(\bfn {re.escape(name)}\([^{{}}]*?\)(?:\s*->[^{{]+)?\s*\{{\n)"
|
|
text, count = re.subn(pattern, lambda match: match[0] + f" {statement}\n", text)
|
|
if count != 1:
|
|
raise ValueError(f"diagnostic function anchor changed: {name} ({count})")
|
|
return text
|
|
|
|
|
|
def record(stage: str) -> str:
|
|
return f"nyaterm_core::diagnostic_probe::record(nyaterm_core::diagnostic_probe::Stage::{stage});"
|
|
|
|
|
|
def apply_overlay(source: Path) -> list[str]:
|
|
# Prepare every edit before touching the checkout. An unfamiliar source
|
|
# revision must fail closed instead of producing partial instrumentation.
|
|
changes: dict[str, str] = {}
|
|
|
|
def read(path: str) -> str:
|
|
return changes[path] if path in changes else (source / path).read_text(encoding="utf-8")
|
|
|
|
def replace(path: str, old: str, new: str) -> None:
|
|
changes[path] = replace_once(read(path), old, new, path)
|
|
|
|
def enter(path: str, function: str, stage: str) -> None:
|
|
changes[path] = enter_function(read(path), function, record(stage))
|
|
|
|
replace("crates/nyaterm-core/src/lib.rs", "pub mod diagnostics;",
|
|
"pub mod diagnostics;\npub mod diagnostic_probe;")
|
|
replace("crates/nyaterm-app/src/lib.rs", "pub mod crash_report;",
|
|
"pub mod crash_report;\npub mod diagnostic_build;")
|
|
replace("crates/nyaterm-app/src/main.rs", "fn main() -> anyhow::Result<()> {",
|
|
"fn main() -> anyhow::Result<()> {\n"
|
|
" if nyaterm_app::diagnostic_build::run_self_test_if_requested()? {\n"
|
|
" return Ok(());\n }")
|
|
replace("crates/nyaterm-app/src/crash_report.rs",
|
|
' writeln!(writer, "Version: {}", env!("CARGO_PKG_VERSION"))?;',
|
|
' writeln!(writer, "Version: {}", env!("CARGO_PKG_VERSION"))?;\n'
|
|
" crate::diagnostic_build::write_metadata(writer)?;")
|
|
replace("crates/nyaterm-app/src/crash_report.rs",
|
|
' writeln!(writer, "\\nMessage:\\n{message}\\n\\nBacktrace:")?;',
|
|
' writeln!(writer, "\\nMessage:\\n{message}")?;\n'
|
|
" nyaterm_core::diagnostic_probe::write_snapshot(writer)?;\n"
|
|
' writeln!(writer, "\\nBacktrace:")?;')
|
|
|
|
editor = "crates/nyaterm-desktop/src/features/connections/connection_runtime/editor.rs"
|
|
enter(editor, "save_connection_editor", "SaveRequested")
|
|
enter(editor, "save_editor_connection", "SaveJobStarted")
|
|
enter(editor, "close_connection_editor", "EditorClosing")
|
|
replace(editor, " store.load_sessions()\n}",
|
|
" let result = store.load_sessions();\n"
|
|
f" if result.is_ok() {{ {record('SaveJobSucceeded')} }}\n result\n}}")
|
|
replace(editor, " Ok(Ok(sessions)) => {",
|
|
" Ok(Ok(sessions)) => {\n " + record("SaveApplied"))
|
|
window = "crates/nyaterm-desktop/src/features/connections/connection_runtime/window.rs"
|
|
replace(window, "window.defer(cx, |window, _| window.remove_window());",
|
|
"window.defer(cx, |window, _| {\n "
|
|
+ record("EditorWindowRemoving") + "\n window.remove_window();\n });")
|
|
enter("crates/nyaterm-desktop/src/features/session/session_runtime/background.rs",
|
|
"begin_background_session_start", "SessionStartRequested")
|
|
|
|
anchor = " let pair = pty_system\n .openpty(local_pty_size("
|
|
candidates = [path for path in (source / "crates/nyaterm-transport/src").rglob("*.rs")
|
|
if anchor in path.read_text(encoding="utf-8")]
|
|
if len(candidates) != 1:
|
|
raise ValueError(f"diagnostic PTY anchor changed: expected one implementation, got {len(candidates)}")
|
|
transport = candidates[0].relative_to(source).as_posix()
|
|
replace(transport, " let pair = pty_system\n .openpty(local_pty_size(",
|
|
" " + record("LocalPtyOpening")
|
|
+ "\n let pair = pty_system\n .openpty(local_pty_size(")
|
|
# The marker is reached only after spawn_command returned successfully.
|
|
replace(transport, " drop(pair.slave);",
|
|
" " + record("LocalShellSpawned") + "\n drop(pair.slave);")
|
|
|
|
element = "crates/nyaterm-terminal-gpui/src/element/mod.rs"
|
|
enter(element, "request_layout", "TerminalLayout")
|
|
enter(element, "prepaint", "TerminalPrepaint")
|
|
enter(element, "paint", "TerminalPaint")
|
|
changes[element] = enter_function(read(element), "prepaint",
|
|
"nyaterm_core::diagnostic_probe::geometry(self.font_size, self.cell_width, "
|
|
"self.cell_height, window.scale_factor());")
|
|
shape_pattern = r"(?m)^( +)(let line = Arc::new\(window\.text_system\(\)\.shape_line\([\s\S]*?^\1\)\);)"
|
|
changes[element], shape_count = re.subn(shape_pattern, lambda match:
|
|
match[1] + record("TerminalShape") + "\n" + match[1] + match[2]
|
|
+ "\n" + match[1] + record("TerminalShaped"), read(element))
|
|
if shape_count != 3:
|
|
raise ValueError(f"diagnostic shape anchors changed: expected 3, got {shape_count}")
|
|
|
|
for name, crate in [("diagnostic_probe", "nyaterm-core"), ("diagnostic_build", "nyaterm-app")]:
|
|
destination = f"crates/{crate}/src/{name}.rs"
|
|
if (source / destination).exists():
|
|
raise ValueError(f"diagnostic overlay already exists: {destination}")
|
|
changes[destination] = (TEMPLATES / f"{name}.rs").read_text(encoding="utf-8")
|
|
for path, content in changes.items():
|
|
(source / path).write_text(content, encoding="utf-8", newline="\n")
|
|
return list(changes)
|