Copy a shark:// link to a tab, and follow one back to it - #2950
Merged
Conversation
Anything on screen can now be handed to someone else as one line of text that puts them in front of it: right click a tab, "Copy link to this tab", and clicking the URL brings the app forward and opens that place in a new tab. Which is also how a script or an agent that has read a heap dump can point at what it found, rather than describing the way there. Every Place has a spelling, and it carries the whole place rather than a shorthand for it — a filtered object list arrives filtered, a page of leaks arrives with the same groups unfolded — so that any state the app can be in is reachable through a link. A link names a window, not a heap dump. The same dump open twice is what comparing two of them is, so a path would be ambiguous exactly when it matters, and a window id also settles what a link means once the window is gone: nothing, which is an empty window saying so rather than the same object in whichever other window had that file open. macOS is told at build time, through CFBundleURLTypes, and hands the URL to the running process. Windows and Linux have nothing like that in the packaging, so the app registers itself on first run and the OS delivers a link by starting a process with it — which then hands it to the run that has the window, over a loopback port each run publishes. Deliberately not single instance: several explorers open at once is how this app is used. Only macOS could be tried here, so the other two are best effort. The whole of the URL is unit tested either way, and the module's AGENTS.md records the trap that cost the most: a JVM launched from a shell script registers as net.java.openjdk.java whatever its wrapper says, so no link ever reaches a run from Gradle. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Copying a link was on a tab and nowhere else, so a link to the object under the pointer meant opening it in a tab first and right clicking that. It is now the second item of every menu that offers "open in a new tab" — a rectangle of the map, a row of the object list or of the leaks, a step of a chain, a field of the details panel, a starred object — and the only item on the two things that name a place without a choice of where to open it: a tab, and a button of the screen bar. Every one of them goes through one `copyLink` in `HeapDumpExplorer`, for the same reason opening goes through one `open`: five of them would drift. And the menu item is called "Copy link" everywhere rather than naming its surface, so that the same item doesn't read as five. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Right click a tab → Copy link to this tab → a
shark://URL. Click it anywhere — a chat message, an issue, a terminal — and Shark Explorer comes to the front with that place open in a new tab.Every
Placehas a spelling, and it carries the whole place rather than a shorthand: a filtered object list arrives filtered, a page of leaks arrives with the same groups unfolded. So any state the app can be in is reachable through a link — which is the point, since the next user of this is a tool or an agent printing "here is what I found" as a URL rather than as directions.A link names a window, not a heap dump
The same dump open twice is what comparing two of them is, so a path would be ambiguous exactly when it matters. A window id is not, and it also settles what a link means once the window has gone: nothing. Following one then opens an empty window saying so, rather than the same object in whichever other window happened to have that file open.
A link always opens a tab of its own and never replaces what was being read.
How each OS delivers one
shark://is oursCFBundleURLTypesin the packagedInfo.plistDesktop.setOpenURIHandlerHKCU\Software\Classes\shark, written on first run~/.local/share/applications/shark-explorer.desktop, written on first runFor the two that start a process per link,
DeepLinkPeersis how that process reaches the run that has the window: every run publishes a loopback port and a token under~/.shark-explorer/runs, and a run holding a link asks each of the others in turn. Deliberately not single-instance — several explorers open at once, one per piece of work, is how this app is used, and making the second run hand its command line to the first and exit would take that away. Nobody is in charge and nothing breaks when any of them is killed.The macOS handler goes through the same peers, because one installed app is handed every
shark://link on the machine, including ones naming a window of a run from source.All of the OS end is best effort: every failure is one line in the log, never a refusal to start.
Tested
DeepLinkTest— 23 tests over the URL itself: every place round-trips, every synthetic node id (ROOT_OBJECT_ID,UNREACHABLE_NODE_ID, pile ids,Long.MAX_VALUE, negatives), URL-hostile query text, deterministic output, and each error message. Node ids are the exact unsigned 64 bits rather thanhexObjectId, which masks to the low 32.ExplorerWindowTest— 8 more: a link reaches only the window it names, a place is dropped once its tab opens, two links are two tabs in order, a link to a window that has gone opens one saying so and it lands beside the others.ExplorerArgumentsTest,TabStripTest— a link on the command line is not a heap dump path; right clicking a tab copies the link to where that tab is; a link opens a tab of its own, in front.createDistributablebuild:open "shark://<id>/leaks", the filtered object list, starred, and a link to a closed window, all read back out of the run's log file.Windows and Linux could not be tried from here, so those two are best effort.
Only macOS could be verified, and here is why the other loop matters
shark/shark-explorer/AGENTS.mdgains a section on it: a JVM launched from a shell script registers with LaunchServices asnet.java.openjdk.javawhatever its wrapper's plist says, so no link ever reaches a./gradlew runorrunNamed. Only ajpackagelauncher gets its own bundle identity. Anything about links is a package, not a compile — the section has the loop, and says to read the result out of the log rather than off the screen, since following a link raises the app over whatever the person at the machine was doing.