Skip to content

WIP: prepl backend (architectural exercise, not for merge)#3899

Draft
bbatsov wants to merge 32 commits intomasterfrom
prepl-support
Draft

WIP: prepl backend (architectural exercise, not for merge)#3899
bbatsov wants to merge 32 commits intomasterfrom
prepl-support

Conversation

@bbatsov
Copy link
Copy Markdown
Member

@bbatsov bbatsov commented May 3, 2026

This branch prototypes a Clojure prepl (clojure.core.server/io-prepl) backend for CIDER. It is not intended for merge; it's a thinking-out-loud branch left as a reference artifact.

1. Useful as a separation exercise. Adding a second client forced the question "what's actually nREPL-specific in CIDER vs. what's general?". The eval-handler keyword API (#3892, already merged) was step one; building a second consumer surfaced a few more leaky-abstraction warts. nrepl--mark-id-completed quietly assumed the request tables existed on every connection buffer. cider-jack-in-tools was entirely nREPL-shaped (params, middleware injection). The eval-* user commands called nREPL request functions directly rather than going through an abstraction. Even with prepl shelved, those are real findings.

2. Prototyping confirmed prepl isn't a good fit. Two reasons:

  • Dispatch complexity. Every eval-shaped command grew a backend branch. Source-buffer integration needed cider-current-backend. The jack-in registry needed a :backend slot. None of this is hard individually; all of it is overhead future features would have to keep paying.
  • Capability mismatch. The features people pick CIDER over clj for (debugger, structured stacktrace, inspector, profiler, test runner UI) are the same ones prepl can't host. A prepl-on-CIDER user would get a degraded experience compared to nREPL-on-CIDER while paying the same dispatch tax in the codebase.

3. Things worth bringing forward independently:

  • nrepl--mark-id-completed tolerating nil request tables. A small defensive fix, correct even without a second backend.
  • dev/design/prepl-support.md documents "we considered prepl, here's what it'd cost". Worth keeping as a reference for the next time the question comes up.
  • The free-port helper (make-network-process :server t :host "127.0.0.1" :service t) is generic enough to live in cider-util.el if a second consumer ever shows up.

Leaving the branch archived. If demand ever materializes (a serious Basilisp prepl push, say), the work is on file.

bbatsov added 30 commits April 29, 2026 22:14
Sketch implementation on the prepl-support feature branch.  Not yet
wired into cider-connect / cider-jack-in proper; the goal of this
commit is to show that the architecture works against a synthetic
input stream, with no JVM dependency in CI.

New files:

- lisp/cider-conn.el          cl-defgenerics for the connection-protocol
                              boundary: cider-send-eval, -eval-sync, -op,
                              cider-supports-op-p, cider-conn-interrupt,
                              cider-conn-close.

- lisp/cider-conn-nrepl.el    Thin wrappers implementing the generics on
                              top of the existing nREPL request layer.
                              Existing call sites unchanged for now.

- lisp/cider-prepl.el         Process filter that incrementally reads
                              EDN responses from a Clojure prepl
                              (`clojure.core.server/io-prepl'), demuxes
                              by tag (:out/:err/:tap/:ret/:exception),
                              and feeds them into nrepl-make-eval-handler
                              via a synthesized response dict.  FIFO of
                              pending eval handlers handles ordering-
                              based correlation.  Sketch
                              cider-connect-prepl as a manual entry
                              point.

- test/cider-prepl-tests.el   Five Buttercup specs driving the filter
                              with synthetic input: tag routing,
                              :exception path, partial-chunk buffering,
                              two-evals-in-flight demuxing, stray-
                              response handling.

Also update dev/design/prepl-support.md to use the agreed-on
`cider-send-*' naming throughout (we no longer prefix with `-conn-'
for wire methods; only the lifecycle methods keep the prefix to avoid
colliding with existing user commands like `cider-interrupt').

Real find from the test: prepl connection buffers need the same
nrepl-pending/completed-requests hashes the eval handler bookkeeping
expects.  Fixed in both `cider-connect-prepl' and the test helper.
…der-connection.el

Original draft used names that collided with existing CIDER conventions:

- `lisp/cider-conn.el' was visually too close to the existing
  `lisp/cider-connection.el'.  Reader had to remember which was the
  abstraction layer and which was the manager.
- `lisp/cider-conn-nrepl.el' read as two namespaces mashed together
  for what was really just thin nREPL glue.

Both replaced.  The new layout:

- `lisp/cider-backend.el'    The `cl-defgeneric' boundary.  Defines
                             `cider-send-eval' / `cider-send-eval-sync' /
                             `cider-send-op' (wire methods, dropping the
                             redundant `-conn-' tag), plus
                             `cider-supports-op-p',
                             `cider-backend-interrupt',
                             `cider-backend-close', the
                             `cider-backend-type' buffer-local, and the
                             `cider-backend-op-unsupported' error
                             symbol.

- `lisp/cider-connection.el' Existing surface, plus the nREPL
                             `cl-defmethod' block at the tail.  These
                             are short wrappers around the existing
                             nrepl-client.el functions and belong next
                             to the rest of CIDER's nREPL-side glue.

- `lisp/cider-prepl.el'      prepl protocol decoding + prepl
                             `cl-defmethod' block.  prepl is a
                             self-contained protocol implementation, so
                             it justifies its own file.

Wire methods keep `cider-send-*' (the disambiguator from the dense
`cider-eval-*' user-command namespace).  Lifecycle methods get
`cider-backend-*' to disambiguate from `cider-interrupt' /
`cider-close-buffer' user commands.

Tests still pass: 552/554 specs (same as before the rename, the 2
skipped are platform-gated).
…uffers

Two things land together because they're entangled:

1. Mark nREPL connection buffers with `cider-backend-type = nrepl' in
   `cider-repl-create'.  Without this, the nREPL `cl-defmethod's added
   in the previous commit don't dispatch (they assert on the type).
   The new generics still aren't called by anything in tree, so this
   is invisible until the migration starts -- but it's a prerequisite.

2. Sketch `cider-prepl--info-via-eval' as the first eval-form fallback
   for the `info' op.  Wires up an `op-fallbacks' alist so adding more
   ops (Step 3 of dev/design/prepl-support.md) is a one-line entry +
   helper function.

Pattern for fallbacks:

  - Build a Clojure form from PARAMS that gathers the data.
  - Wrap the user HANDLER with an intermediate eval-handler that
    parses the eval `:ret', reshapes it into the response dict the
    op's nREPL response would have used, and feeds it to HANDLER.
  - Call `cider-send-eval' with the form + intermediate handler.

`cider-send-op' on a prepl conn now dispatches through the alist;
unknown ops still signal `cider-backend-op-unsupported'.
`cider-supports-op-p' checks the alist.

Two real bugs found by writing the test:

- `plist-get' uses `eq', so it can't look up string-keyed op params.
  Added a small `cider-prepl--params-get' helper using `equal'.
- The prepl filter normally dispatches handlers from inside
  `with-current-buffer (process-buffer proc)' so the eval-handler's
  bookkeeping reaches the connection's buffer-local hashes.  The
  test's spy on `cider-send-eval' didn't replicate that and tripped
  over `nrepl--mark-id-completed' looking up nil.  Spy fake now does
  the same buffer setup.

Tests: 7/7 prepl specs pass, 554/556 in the full suite (+2 new).
Three pieces, in one commit because they're small and they make the
prepl prototype usable end-to-end:

1. Two more eval-form fallbacks following the same pattern as `info'.

   - `apropos': `(mapv str (clojure.repl/apropos #"query"))'
     -> response with `apropos-matches' slot.
   - `ns-vars': `(mapv (comp str key) (ns-publics 'foo))'
     -> response with `ns-vars' slot.

   Both are simpler than `info' (no metadata gathering), so the
   shared boilerplate factored out into a `cider-prepl--simple-via-eval'
   helper.  Each fallback now amounts to ~5 LOC: a Clojure form
   string, a reshape function, an alist entry.

2. `cider-prepl-eval-string': minimal user-facing eval command.
   Reads CODE from the minibuffer, sends to the current prepl
   connection via `cider-send-eval-sync', echoes the value (or
   the err on failure) to the minibuffer.  No REPL UI yet -- this
   is the smallest thing that lets a user actually try the
   prototype against a running prepl.

3. Sesman registration in `cider-connect-prepl'.  The connection
   buffer now sets `sesman-system' to `CIDER' and registers itself
   via `sesman-add-object', so prepl connections show up alongside
   nREPL ones in `sesman-current-sessions'.  Full sesman integration
   (e.g. project-aware connection resolution) is later work; this
   is just enough for `cider-prepl-current-conn' to find the
   connection and for `cider-quit'-style commands to discover it.

Tests: 11/11 prepl specs pass, 558/560 in the full suite (+4).
Three thin wrappers on top of `cider-prepl-eval-string', mirroring the
shape of `cider-eval-region' / `cider-eval-last-sexp' /
`cider-eval-defun-at-point' on the nREPL side.  Each just resolves a
text region in the current buffer and forwards.

Factored the value/err display logic into `cider-prepl--display-result'
and the connection check into `cider-prepl--ensure-conn' so all four
commands share one path.

While writing tests for these I tripped over a real design issue with
the `cl-defmethod' dispatch -- both nREPL and prepl methods specialize
on `(conn buffer)', so cl picks whichever was defined last (load
order), not whichever matches the runtime `cider-backend-type'.
Today prepl wins by load order, but that's luck, not real dispatch.
The tests work around it by spying at the wrapper boundary
(`cider-prepl-eval-string') instead of the cl-defgeneric.  Documented
as Open Question #0 in dev/design/prepl-support.md with two proposed
fixes; needs to land before any in-tree migration uses these
generics.
Three more user-facing commands rounding out the basic prepl session:

- `cider-prepl-load-file': sends `(load-file "...")' to the prepl.
  Caveat noted in the docstring that the path must be resolvable by
  the prepl process.

- `cider-prepl-quit': closes the connection by delegating to the
  generic `cider-backend-close', so the same key (when bound) works
  regardless of backend.

- `cider-prepl-doc': uses the existing `info' op fallback to look up
  a symbol's docstring and prints it to the echo area.  Reads from
  the symbol at point as the default.

`cider-prepl-doc' is the first command that actually exercises the
op-fallback layer end-to-end (build a Clojure form, dispatch through
the prepl filter, parse the result, hand back to the user-facing
command).  It does so synchronously by polling the response with
`accept-process-output' and a 5s deadline -- a more polished
implementation would pop a *cider-doc*-style buffer and accept the
response asynchronously, but the sketch is enough to confirm the
shape works.

Tests: 17/17 prepl specs pass; 564/566 in the full suite.
…spatch

Two changes coming together because they fed each other:

1. New test/utils/cider-prepl-mock-server.el: a minimal in-Emacs
   stand-in for `clojure.core.server/io-prepl'.  Listens on an
   ephemeral TCP port; each line of input maps to a small canned
   table of `:tag/:val' EDN responses.  Added three integration
   tests that connect via `cider-connect-prepl' to the mock and run
   eval forms end-to-end (value, stdout capture, exception path).
   No JVM needed -- works in CI.

2. The integration tests immediately surfaced the dispatch bug
   flagged as Open Question #0 in the design doc.  Both backends'
   `cl-defmethod' specialized on `(conn buffer)', so cl picked
   whichever was defined last (the nREPL one happened to win),
   not the runtime `cider-backend-type'.

   Rather than the originally-sketched `(eql 'foo)' specializer
   approach, replace the cl-generic layer entirely with a plain
   dispatch table:

   - `cider-backend.el' now defines `cider-backend-impl' struct (one
     slot per protocol method) and `cider-backend-register'.
   - `cider-send-eval' / `-eval-sync' / `-op' / `cider-supports-op-p' /
     `cider-backend-interrupt' / `-close' are plain defuns that look
     up `cider-backend-type', fetch the impl, and forward.
   - Each backend (cider-connection.el for nREPL, cider-prepl.el for
     prepl) defines plain `*--send-eval' helpers and registers via
     `cider-backend-register'.

   Simpler than the cl-generic approach, easier to extend (one
   register call per backend), and crucially: dispatch actually
   honors `cider-backend-type' now.

Tests: 20/20 prepl specs pass, 567/569 in the full suite.  The three
new mock-server integration specs each take ~15ms because they
exercise the real network round-trip.

Updated dev/design/prepl-support.md to mark Open Question #0
resolved with the actual fix that landed.
User-facing documentation page at doc/.../repl/prepl.adoc plus a
navigation entry.  Covers what works, what doesn't, how to start an
io-prepl from a deps.edn alias, the op-fallback table for the three
ops we currently translate, and a short architecture pointer to the
files involved.

Front-loaded the experimental warning so nobody mistakes this for
production-quality.  Honest list of limitations: no interrupt
(prepl-level), no clj+cljs bundling (nREPL+Piggieback shape), no
REPL prompt buffer yet, no jack-in.
Three more entries in the eval-form fallback table, all following the
established `cider-prepl--simple-via-eval' shape:

- `ns-list': `(mapv (comp str ns-name) (all-ns))' -> {"ns-list": [...]}.
- `source': `(clojure.repl/source-fn (symbol ...))' -> {"source": "..."}.
  Wraps in `(or ... "")' so a missing source returns an empty string
  rather than an unparseable nil-as-edn-string.
- `macroexpand': `(pr-str (macroexpand-1 'code))' by default; honors
  the `expander' param for `macroexpand' / `macroexpand-all' (the
  -all variant requires `clojure.walk', which the form requires
  inline).

Six fallbacks now: info, apropos, ns-vars, ns-list, source, macroexpand.
That covers the most-used non-trivial nREPL ops.  More can join the
table without touching the dispatcher.

Updated the doc page's fallback table.

Tests: 24/24 prepl specs, 571/573 in the full suite.
Three small additions, all in the same area of the connection
lifecycle:

1. Process sentinel.  If the prepl socket closes (server crash,
   network drop, or the user explicitly closes it), `cider-prepl--
   sentinel' fires.  Any handlers still in `cider-prepl--pending-evals'
   that were waiting for a `:ret'/`:exception' get a synthesized
   `err' + `eval-error'/`done' so callers don't hang forever.

2. `cider-prepl--connect-params' buffer-local that records the
   original host/port at connect time, so we can reconnect with the
   same parameters later.

3. `cider-prepl-restart': close the current connection and reconnect
   using the recorded params.  Uses the public
   `cider-backend-close' generic for the close, so the restart logic
   doesn't leak prepl-internal cleanup details.

New spec drops the server process under a pending eval and verifies
the handler gets drained with the right shape (err + eval-error +
done).
Adds a basic interactive REPL surface so prepl connections aren't
purely echo-area driven.  Built on comint-mode for prompt protection,
multi-line input, and history; the input-sender hands typed forms
to `cider-send-eval' rather than `process-send-string' so responses
still flow through our protocol decoder.

Two specs: mode-on-connect + inline render of an eval response.
Three small wins for the prepl REPL buffer:

- Keymap: C-c C-q (quit), C-c M-r (restart), C-c C-d C-d (doc),
  C-c C-o (clear-output).  Mirrors the corresponding cider-repl-mode
  bindings on a much smaller surface.
- `cider-prepl-clear-output' wipes the buffer up to the active prompt,
  matching the standard CIDER REPL behavior.
- Value output now goes through `cider-font-lock-as-clojure', so
  result strings get the same syntax highlighting as in cider-repl.
`:tap'-tagged responses from io-prepl can arrive at any time -- they
correspond to whatever the prepl process happens to call `tap>' on,
not the eval in flight.  Routing them onto the head pending handler
(the previous behavior) was wrong: tap values from background work
showed up as fake stdout for an unrelated eval.

Now every `:tap' is appended to `*cider-prepl-tap <conn>*', a lazily
created buffer linked back to its connection via a buffer-local
pointer.  C-c M-t / cider-prepl-show-tap-buffer pops it up.
Three more nREPL ops now have eval-form fallbacks on prepl:

- `eldoc' resolves the var, packages :arglists/:doc/:type.  Enough
  for the standard eldoc display.
- `complete' prefix-filters `ns-map' (publics + refers) and returns
  `[{:candidate ... :type ...}]'.  No compliment-style context, but
  good enough for trivial completion in a REPL.
- `classpath' just splits `java.class.path' on the path separator.

Also adds `cider-prepl--hash->dict-form', a small helper that wraps
the existing key/value flattener with the leading `dict' symbol, for
ops (like complete) that need a list of fully-formed dicts inside a
parent dict.
Spawns a JVM with `clojure.core.server/io-prepl' on a free port
(or `cider-jack-in-prepl-port' if set), waits for the port to be
reachable, and runs `cider-connect-prepl' to attach.

Deliberately not extending the existing `cider-jack-in-tools'
registry: that shape is nREPL-flavored (params, middleware
injection, etc.).  prepl gets its own narrow command for now.
A future tools-registry generalization could subsume both --
noted in the file's header.
Prepl was setting up `nrepl-pending-requests' / `nrepl-completed-requests'
purely so the eval-handler returned by `nrepl-make-eval-handler' wouldn't
crash when its done-branch called `nrepl--mark-id-completed'.

Move the tolerance into nrepl--mark-id-completed itself: when the
pending table is nil (because the buffer's backend isn't nREPL),
the function is a no-op.  Prepl no longer touches those tables.

The eval-handler shape is already a documented reuse point; this
makes the abstraction honest -- backends only opt in to the parts
they actually need.
Three swaps:

- `cider-prepl--params-get' was reimplementing equal-keyed plist-get;
  drop it for `nrepl-plist-get'.
- `cider-prepl--hash->dict' returned a flat kv-list; `--hash->dict-form'
  wrapped it in `(dict ...)'.  Replace the pair with one helper that
  builds an `nrepl-dict' directly via `nrepl-dict-put' (still strips
  the leading `:' from parseedn keyword keys -- the only reason we
  can't call `nrepl-dict-from-hash' verbatim).
- `cider-prepl--simple-via-eval' reshape-fn now returns a full dict
  rather than a kv-list-to-be-spliced.  The single-purpose info-via-
  eval shrinks from 40 lines of bespoke handler plumbing down to a
  call to simple-via-eval with `cider-prepl--hash->dict' as the
  reshape-fn -- structurally the same as eldoc.

All inlined `(dict ...)' constructions go through `nrepl-dict' so the
shape stays in one place.
The FIFO previously used `(append list (list new))', which is O(n)
in the queue depth on every send.  In normal interactive use the
depth is 0-1 so it's invisible, but bulk operations (load-file
splitting forms, scripted eval-region runs) make it quadratic.

Use the `queue' package directly (CIDER already depends on it via
nrepl-client.el).  Same flat plist entries, just behind the queue
struct -- enqueue/dequeue are constant-time.

Required `queue' explicitly in cider-prepl.el rather than relying
on the nrepl-client transitive load.
- `cider-prepl-doc' was polling for up to 5s with 50ms ticks just so
  it could `message' the result.  The op handler is invoked
  asynchronously per response anyway -- track shown/done state in
  closures and fire the message inline.  No blocking, no deadline.
- `cider-prepl--close' now kills the per-connection tap buffer
  alongside the connection.  The tap buffer was lazily linked via a
  buffer-local pointer but never torn down, so it leaked across
  reconnects.
- `cider-prepl--send-eval-sync' was busy-polling at 50ms; pass the
  process to `accept-process-output' with a 1s timeout so the call
  blocks until that specific process produces output.
- Drop redundant `(buffer-live-p)' / `(process-live-p)' guards in
  the comint-side helpers and `--close' -- the surrounding flow
  guarantees the values are good, and `kill-buffer' tears down a
  process all by itself.
Comment fluff that referenced the feature branch, design-doc Step
numbers, or "polishing comes later" framing didn't belong in the
shipped code -- it was meta about the change, not about why the
code looks the way it does.

Also moved the `cider-prepl--connect-params' defvar to the
buffer-local state section so it sits next to the other locals
and reads top-down rather than referenced-then-declared.
Silences byte-compile warnings about defcustoms not specifying a
containing group.
io-prepl tags every `:ret'/`:exception' response with the namespace
the eval ran in.  Stash that on the connection buffer, plumb it
through the eval handler's `:on-ns' callback, and use it as the
prefix for subsequent prompts.

So `(in-ns 'foo)' now flips the prompt from `user=> ' to `foo=> ',
matching how the standard cider-repl-mode behaves.

Mock server gains a `:tag/ns' suffix syntax in canned responses so
end-to-end tests can exercise namespace transitions without
spinning up a JVM.
Two small wins:

- `cider-prepl-set-ns' (C-c M-n) -- mirrors `cider-repl-set-ns'.
  Reads a namespace from the minibuffer (default: source-buffer ns)
  and sends `(in-ns 'foo)' through the regular eval path.  Combined
  with the prompt-tracking change, this gives the standard ns-switch
  ergonomics on prepl.
- `cider-prepl-current-conn' now goes through the sesman-linked
  sessions for the current buffer first, falling back to a buffer-
  list scan only when no session is linked.  The same machinery
  cider-current-repl uses, just narrower (filters by
  cider-backend-type).
- CHANGELOG.md: experimental-feature bullet listing the new commands
  and pointing at the prepl page.
- repl/prepl.adoc: add `cider-prepl-set-ns', clear-output, and
  show-tap-buffer to the commands list; add a keybinding table.
- dev/design/prepl-support.md: mark open questions 1, 2, 5 as
  resolved (sync semantics, tap routing, EDN reading); update 3 to
  reflect the partial jack-in implementation; leave 4 (registry
  generalization) explicitly deferred.
`cider-prepl-mode' bound RET to comint's default `comint-send-input',
which submits whatever's after the prompt as a single form.  Typing
`(let [x' RET sent the fragment to the prepl, which would block on
the unfinished read.

`cider-prepl-return' (now bound to RET) parses the input region with
the same `cider-repl--input-complete-p' the standard CIDER REPL uses:
balanced -> submit; unbalanced -> newline-and-indent so the user can
keep typing.  Outside the input region, defer to comint's default
(copy-old-input).
When a process chunk arrives and we have nothing pending in the
partial-line accumulator, the existing `(concat acc string)' was
copying STRING for no reason.  Branch on the empty case so the
common path (clean line boundaries) avoids the copy.
If the underlying socket has dropped (server crash, network blip,
explicit `cider-prepl-quit'), `process-send-string' on the dead
process previously raised a generic "process not running" Emacs
error.  Detect the case up front and `user-error' with the actual
remediation.
Pure docstring change to make the registry forward-compatible with
non-nREPL backends.  Default `:backend' is `nrepl'; `:server-args-fn'
is the prepl-backend hook for building server-startup argv.

Mark `:params-var' / `:inject-fn' / `:jack-in-type' as nREPL-only --
they don't apply to prepl entries (prepl has no middleware injection
and no clj/cljs distinction in the same session).
- `cider-jack-in-prepl' now reads its tool spec from the registry:
  binary via `:command-var' / `:default-command-fn', argv builder
  via `:server-args-fn'.  Defaults to a new `clojure-cli-prepl'
  tool entry, but accepts any registered prepl-backend tool.
- `cider-jack-in-universal' dispatches on `:backend': nREPL tools
  go through the existing `cider-jack-in-clj' / `-cljs' paths,
  prepl tools go through `cider-jack-in-prepl'.
- Register `clojure-cli-prepl' (deps.edn, prefix arg 6).  No
  `:project-files' on the entry -- a deps.edn project may be
  jacked in either way, and project-file detection picks one,
  so prepl is opt-in via the prefix arg or the explicit command.

Backward compatible: existing third-party tool registrations omit
`:backend' and pick up the `nrepl' default.
bbatsov added 2 commits May 2, 2026 17:06
`cider-current-backend' (in cider-session.el) inspects the linked
sesman session and returns `nrepl' / `prepl' / nil, with nREPL
priority when both are linked.

Plumbed into:

- cider-eval-last-sexp
- cider-eval-region
- cider-eval-defun-at-point (skipped when DEBUG-IT is on -- prepl
  has no debugger fallback)
- cider-load-file
- cider-doc

A Clojure source buffer connected only to a prepl session now
responds to the regular keybindings (C-x C-e, C-M-x, C-c C-k,
C-c C-d C-d) without the user having to reach for the cider-prepl-*
siblings.  No nREPL path changes for users with an nREPL session.
`start-file-process' instead of `start-process' so a remote
`default-directory' transparently spawns the JVM on the remote
host.  For local jack-in the call is identical to before.

Free-port discovery is still local-only (`make-network-process'
binds locally); when `default-directory' is remote and
`cider-jack-in-prepl-port' is 0, error up front so the user knows
to set a fixed port instead of seeing an opaque "address in use"
later.

Two new customs to handle the network shape:

- `cider-jack-in-prepl-bind-address' threads through to io-prepl's
  `:address' arg.  Defaults to nil (= io-prepl default of
  127.0.0.1).  Setting "0.0.0.0" makes the remote JVM reachable
  directly from the local Emacs.
- `cider-jack-in-prepl-host' overrides the connect-side host.
  Auto-detect uses the TRAMP host when remote, 127.0.0.1
  otherwise -- override to "127.0.0.1" when an SSH tunnel
  forwards the port locally.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant