Add a JSON-RPC server mode to F# Interactive - #20396
Conversation
✅ No release notes required |
This comment has been minimized.
This comment has been minimized.
|
An idea: we could initially make it an out of process part of VS, a fsi session wrapper alternative to fsmain. |
02f94fd to
fde33b0
Compare
This comment has been minimized.
This comment has been minimized.
fde33b0 to
93efea8
Compare
This comment has been minimized.
This comment has been minimized.
T-Gro
left a comment
There was a problem hiding this comment.
I do like the idea of a server mode.
That being said, I think the protocol must be validated by usage.
Is there any benefit in dotnet fsi using it (maybe some bonus arguments referring to a single shared session?)
Could the VS-FSI be ported to use it? (would be a great dogfooding test for feature completeness and API UX)
(would also love if @TheAngryByrd, @baronfel or @auduchinok could list here any wishes from an FSI JSON-RPC server API and protocol wise)
4c8c5fd to
b7e2742
Compare
T-Gro
left a comment
There was a problem hiding this comment.
🤖🕵️ Please shorten the description using this guidance. Focus on the problem and why the change is needed, in simplified technical English. Leave the implementation inventory to the Files tab and retain necessary caveats.
Every `sprintf`/`failwithf` in the server, the protocol and their tests becomes an interpolated string, keeping the format specifiers so the types are still checked. Doc comments that carry more than one paragraph get `<summary>` and `<remarks><para>`, with code references in `<c>` rather than backticks; `<pipe name>` inside the option's description is escaped, since it now sits in XML. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`{Guid.NewGuid():N}` reads better than `%s{Guid.NewGuid().ToString "N"}`
and drops the triple quotes the nested literal forced. The two remaining
concatenations in the harness — the pipe name and the server switch — go
the same way.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Plain `{expr}` throughout, with a .NET format string where the text needs
one — `FS{d.errorNumber:D4}` keeps the four-digit error number.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
On Unix a named pipe is a socket under $TMPDIR, and macOS caps socket paths at 104 characters; with its long $TMPDIR every session failed to start. A failed connection now also reports the session's stderr. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Path.GetTempPath() returns /var/folders/..., but the session reports /private/var/folders/... after chdir — the kernel resolves /var through /private when it canonicalises a path, and Path.GetFullPath does not. setPaths changes the working directory failed on every macOS CI run. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…rver options on the session EvalInteraction parsed one interaction and discarded the text after its first `;;`, although its documentation promises one or more. It now commits each completed interaction and parses the next from the same lexer, as the standard-input loop does, so a host can hand over a whole selection and a line directive at its top holds for all of it. The JSON-RPC pipe name and the host process id become members of FsiEvaluationSession, read from the options the session has already parsed — response files included — instead of a second scan of argv in the entry point. The console reader is gated on the server options rather than asserted absent. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…n split and print The pipe accepted any local client, and whoever connected first ran code as the user. It now opens with PipeOptions.CurrentUserOnly. The process the server watches is the one named on the command line, so a host that dies before connecting takes the session with it. The hand-written `;;` splitter is gone: EvalInteractionNonThrowing evaluates the whole text, so a verbatim string ending in a backslash or a `(*)` operator no longer misleads the server, and a line directive covers the entire selection. Values are formatted by the session's own printer under its print settings instead of `%A`, and a ToString that throws becomes the value's text rather than a failed interaction. Paths spliced into #load, #I and #silentCd are verbatim literals, so a quote or a trailing backslash in a path survives. Requests carry their parameters as one object, the shape the protocol documents. The server is compiled into the .NET fsi only: the .NET Framework fsi does not carry StreamJsonRpc and a source-only build cannot provide it. A session asked for the mode without it exits with a message instead of idling. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…in the VS setup The SDK lays out `dotnet fsi` from this package's lib folder, and the Visual Studio installer entries added before reached no SDK. The project file lists the closure once and feeds the nuspec through a token that is empty under a source-only build. A test checks the list against what fsi's build restores and starts a session from the staged files alone. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
xUnit's StartsWith and EndsWith default to the current culture. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…e the pipe race The remediation passed the two values to Option.orElse the wrong way round, so a later value was replaced by an earlier one: `41;; 42;;` gave 41. The interaction's own status already carries the merged value. Closing a corrupted control channel can find the pipe disposed already, when the session hangs up and the client's JsonRpc disposes it first. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Head branch was pushed to by a user without write access
9ac1e7c to
68e6f0a
Compare
F# Interactive gets a server mode,
--fsi-server-jsonrpc:<pipe name>. A host submits code over a named pipe and receives structured diagnostics, exceptions, evaluated values, and process information; program output remains on stdout/stderr. This removes prompt parsing and gives editors deterministic request boundaries and source positions. The existing Visual Studio window still uses its current transport; this PR provides the protocol and an end-to-end reference client.