You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Selecting a method in the MethodPickerPopup (the popup the Object Browser's Patch Method… action opens) now automatically flips the lower pane into Patches mode before filling the form. Previously the form was populated under whichever tab the user was on — usually Output, because the same row's Inspect action put them there a click ago — and the user had to find their way back to Patches by hand to see what just happened. The flip closes the loop so the click on Patch Method, the row pick in the popup, and the populated patch form read as one continuous gesture. Existing Pull-Original-on-fill behaviour (added with the row context menu in #60) is unchanged: by the time the form is visible, the body editor either holds the pulled source or the status label carries a non-blocking Pull-failure hint. Closes #64.
Added (Compile / Validate Only)
New toolbar ✓ Validate button (and Ctrl+Shift+Enter / Cmd+Shift+Enter shortcut) compiles the current snippet without running it. Diagnostics flow into Output and the gutter the same way a failed Run does, but the assembly is never loaded into the AppDomain, the wrapper method is never invoked, runtime log capture never starts, and ReplEngine.LastResult (the _ carry-over) is unchanged. Useful when the snippet has side effects, drives a long experiment, or contains an as-yet-untested patch body the user wants to syntax-check before risking it against live Editor / Play Mode state. Closes #66.
A clean compile prints ✓ Validate OK (N ms) into Output and sets the summary chip to Validate OK; a failure prints the same diagnostic lines the failed-Run branch emits and paints gutter markers, so the user can scan the same surfaces they're already used to. The OK line is appended afterRenderResult's find-overlay rebuild signal, so the success branch raises RaiseRebuilt a second time (plus a ScrollOutputToBottom) — an open Ctrl+F search picks the new row up immediately instead of holding a stale hit list until the user nudges the query. The CompileError branch doesn't need that extra signal because diagnostics are appended inside RenderResult, before its single rebuild. Run behaviour is unchanged — Validate sits next to Execute, not in front of it.
New public API ReplEngine.Validate(string userCode, ReplOptions options = null) returns a ReplResult (Success with Value == null on a clean compile, CompileError with diagnostics otherwise). The pipeline mirrors Execute's wrap + parse + compile path so diagnostics line up against the same wrapped source — but stops at compilation.Emit(memoryStream): the IL bytes are produced (so emit-only errors like unsafe-IL violations still surface) but the stream is discarded, Assembly.Load is never called, the compile cache is bypassed (caching off the dry path would blur the "this is a dry check" contract), and the dynamic-assembly counter on the toolbar asm-badge stays put. Assembly name uses a ReplValidate_* prefix vs. Execute's ReplDynamic_* so a developer eyeballing a trace can tell which path produced a given diagnostic.
The cooperative-cancel first-run dialog (which Run shows once per workstation before the very first execution) is intentionally skipped on the Validate path — Validate can't loop forever or hang the Editor since user code never runs, so the warning would just train users to dismiss it.
Added (Insert into Code helpers)
Insert into Code is a new context-menu action across three surfaces: Output tree rows, Object Browser rows, and Watch rows. Each one drops a small C# snippet straight into the Code editor — no clipboard round-trip — so the user can pick a value from the inspection UI, fold it into a snippet, and Run / Validate it without retyping the path. Closes #65.
Output tree's action emits return <ExpressionPath>; against the node's safe path (return _.inventory.items[0].count;). It shares the path-staleness gate with Copy Path / Add Watch: when ReplEngine.LastResult no longer points at the tree's root the menu entry picks up the " (stale — \_` rebound)"suffix and disables, so an old tree can't insert a path that would silently resolve against a different object. Object Browser's action reuses the existingBuildBrowserInspectSnippetbuilder (MonoBehaviour →InstanceIDToObject(...) as T, ScriptableObject → LoadAssetAtPath(...), Singleton → comment template), gated on CSharpTypeName.IsRenderable(type). Watch row's action routes through a new WatchEvaluator.WrapAsReturnStatementhelper that mirrorsEvaluateOne's historic statement-detection rule: a watch typed as return Foo();(with space *or* tab afterreturn, with or without trailing semicolon) is treated as a complete statement and emitted as-is with one trailing ;; otherwise the stored expression is wrapped to return ;. Without that dispatch a row whose stored text already starts with returnwould land in the Code editor as the syntactically invalidreturn return Foo();;`, and the two paths (evaluator / insert helper) would have drifted apart. The row's right-click menu only contains this one entry today, since the rest of the row's actions (toggle / remove) already have explicit controls.
Insertion policy: when the editor is empty or holds the default starter (the user has nothing to lose), the snippet replaces the buffer; otherwise it appends on a fresh line. No confirmation dialog and no auto-run — Run / Validate stay user-driven so the inserted snippet gets at least one glance before it executes. A one-line 📝 Inserted into Code: <preview> toast lands in Output so the user can spot the change without scrolling the Code editor by hand.
New CodeEditorView.AppendSnippet(string) and CodeEditorView.ReplaceCode(string) are the public insertion API. AppendSnippet trims trailing newlines off the existing buffer before joining so the result is exactly "<existing>\n<snippet>" regardless of whether the user left their draft with a trailing newline; ReplaceCode wipes outright. The host's InsertSnippetIntoCode is the single chokepoint every UI action routes through — Output tree, Object Browser, and Watch row all hit it — so the replace-vs-append policy and the toast confirmation stay consistent across surfaces. WatchPanelView exposes an OnInsertSnippetRequestedAction<string> hook the host wires to that chokepoint; the row menu greys out the action when the hook is null so a panel instantiated in isolation (tests, future surfaces without a Code editor sibling) shows a disabled entry instead of failing silently.
Added (Named pins for live objects)
Pin as… is a new context-menu action on Object Browser rows and Output tree nodes. The user supplies a C# identifier (e.g. player, manager, popup) and the live value is bound to that name for the rest of the session. Snippets and Watch expressions can then reference the name unqualified — return player.transform.position;, manager.Count, etc. — exactly like the existing _ carry-over, just under a user-chosen handle. Closes #63.
Pins live in process memory only: a domain reload, an Editor restart, or Reset Project Data clears every pin. The new Tools / Roslyn REPL / Pins… window lists the live pins (name + type + value preview) with per-pin ✕ and a global Clear all, and the panel header restates the session-only contract so the user doesn't go looking for a "persist pins" toggle that intentionally doesn't exist.
Names go through PinStore.ValidateName before storage: valid C# identifiers only (letter / _ first, letter / digit / _ continue), C# keywords rejected, and the wrapper's reserved tokens (_, ct, plus ReplCodeWrapper.ClassName / ReplCodeWrapper.MethodName — __ReplScript and __Run today) rejected so a pin can't silently mask ReplEngine.LastResult, the cancellation token, or the wrapper method itself. A pin named __Run would otherwise produce two same-named members inside the wrapper class (the injected static property + the method the engine compiles into) and every Run / Watch compile would fail until the user found and removed it; the validator now blocks that name up front, and the reserved set pulls from the wrapper constants directly so a future rename flows through automatically. The "Pin as…" prompt reuses the new shared StringPromptDialog (extracted from SnippetLibraryWindow's private nested class so two callers don't have to duplicate IMGUI plumbing); failure messages from ValidateName surface as inline warnings rather than aborting the flow.
Wrapper integration: ReplCodeWrapper.Wrap emits one public static dynamic <pinName> => PinStore.Get("<pinName>"); line per pin alongside the existing _ / ct statics. Names are spliced into source without quoting since ValidateName already rejects anything that wouldn't be a legal identifier. PinStore.Changed is hooked once-per-domain by ReplEngine.EnsureAssemblyLoadHook and routed straight to InvalidateCompileCache — pin set is part of the wrapper source, so the cached MethodInfo would otherwise hand back a stale pre-edit wrapper after every pin mutation. WatchPanelView subscribes to PinStore.Changed too and re-runs the evaluator on every mutation, so a watch row like player.transform.position doesn't stay stuck on a compile error after player is pinned (or keep showing the prior pin's value after player is dropped) until the next Run forces a refresh.
Reset Project Data's confirmation dialog now lists N named pin(s) and clears them alongside _; the post-clear "Cleared N items" tally includes the pin count, and the early-return "Nothing to clear" branch checks PinStore.Count == 0 so a single dangling pin still routes through the clean path. The success and partial-failure result dialogs also name pins in their summary so a Reset that wiped only a pin doesn't read as "Cleared 1 item across [unrelated stores]" — the user sees which surface the count came from.