fix(terminal+lsp): SHELL=$(POSIX-fallback) + skip selection-restore on empty buffer (v0.7.43)
All checks were successful
boundary-lint / PR boundary-claim (Lint (push) Has been skipped
boundary-lint / ban-list lint (Lint (push) Successful in 19s
boundary-lint / duplication-deadline (Layer 1/2) (push) Successful in 19s
Release Publish (Gitea session_helper) / verify-release-tag (push) Successful in 17s
Release Publish (Gitea session_helper) / publish-linux-x86_64 (push) Successful in 4m47s
ci / rust debug (push) Successful in 1m47s
ci / rust release (push) Successful in 2m24s
ci / test-health gate (push) Successful in 16s
ci / mutation test (broker) (push) Successful in 1m14s
ci / python (push) Successful in 1m41s
All checks were successful
boundary-lint / PR boundary-claim (Lint (push) Has been skipped
boundary-lint / ban-list lint (Lint (push) Successful in 19s
boundary-lint / duplication-deadline (Layer 1/2) (push) Successful in 19s
Release Publish (Gitea session_helper) / verify-release-tag (push) Successful in 17s
Release Publish (Gitea session_helper) / publish-linux-x86_64 (push) Successful in 4m47s
ci / rust debug (push) Successful in 1m47s
ci / rust release (push) Successful in 2m24s
ci / test-health gate (push) Successful in 16s
ci / mutation test (broker) (push) Successful in 1m14s
ci / python (push) Successful in 1m41s
Two follow-ups to v0.7.42 user reports.
1. Terminal: ``zsh:1: permission denied:`` exit 126
---------------------------------------------------
v0.7.42 dropped the ``${SHELL:-/bin/sh}`` fallback assuming sshd
populates ``$SHELL`` in every login shell, but ``ssh -t host cmd``
runs the user's login shell with ``-c`` (NON-login mode); on some
remotes ``$SHELL`` is unset there, so ``exec "$SHELL" -il`` becomes
``exec "" -il`` → ``permission denied:`` exit 126.
Reinstate the fallback via POSIX ``if [ -z "$SHELL" ]; then
SHELL=/bin/sh; fi`` instead of ``${SHELL:-...}`` so the parser-bug
class that produced ``zsh:1: unknown exec flag -/`` in v0.7.31+ is
still avoided.
2. LSP: cross-file goto-def to unhydrated placeholder lands at (0,0)
--------------------------------------------------------------------
When LSP-pyright / rust-analyzer return a definition target whose
local cache copy is still a 0-byte placeholder, Sublime's
``window.open_file(path:42:5, ENCODED_POSITION)`` cannot place the
caret at row 42 col 5 — that row doesn't exist in an empty buffer —
and clamps to ``(0, 0)``. ``_apply_hydrate_result`` then captured
that ``(0, 0)`` selection before revert and restored it after,
overriding whatever position Sublime defers / re-applies once the
buffer has content. Net result: user lands at the file top instead
of the definition.
Skip capture/restore entirely when the pre-revert buffer was empty.
For the empty-pre-revert case the captured selection is always
``(0, 0)`` — restoring it can only override a Sublime-side deferred
placement, never recover the LSP target — so dropping the restore
is at least as good as before and lets any deferred ENCODED_POSITION
take effect.
The non-empty branch (e21b3a4 cross-file caret fix for already-
hydrated buffers) is unchanged.
Tests
-----
Python 1368 pass; Rust 486 pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "sessions-sublime"
|
||||
version = "0.7.42"
|
||||
version = "0.7.43"
|
||||
description = "Sublime-facing Python code for Sessions."
|
||||
requires-python = ">=3.8"
|
||||
license = {text = "MIT"}
|
||||
|
||||
12
rust/Cargo.lock
generated
12
rust/Cargo.lock
generated
@@ -221,7 +221,7 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
|
||||
|
||||
[[package]]
|
||||
name = "local_bridge"
|
||||
version = "0.7.42"
|
||||
version = "0.7.43"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"glob",
|
||||
@@ -432,7 +432,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "session_helper"
|
||||
version = "0.7.42"
|
||||
version = "0.7.43"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"notify",
|
||||
@@ -443,7 +443,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "session_protocol"
|
||||
version = "0.7.42"
|
||||
version = "0.7.43"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"serde",
|
||||
@@ -452,14 +452,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sessions_askpass"
|
||||
version = "0.7.42"
|
||||
version = "0.7.43"
|
||||
dependencies = [
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sessions_native"
|
||||
version = "0.7.42"
|
||||
version = "0.7.43"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"notify",
|
||||
@@ -773,7 +773,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "workspace_identity"
|
||||
version = "0.7.42"
|
||||
version = "0.7.43"
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
|
||||
@@ -12,7 +12,7 @@ resolver = "2"
|
||||
[workspace.package]
|
||||
edition = "2024"
|
||||
license = "MIT"
|
||||
version = "0.7.42"
|
||||
version = "0.7.43"
|
||||
authors = ["Myeongseon Choi <key262yek@gmail.com>"]
|
||||
repository = "https://git.teahaven.kr/sublime-rs/sessions"
|
||||
homepage = "https://git.teahaven.kr/sublime-rs/sessions"
|
||||
|
||||
@@ -1677,19 +1677,36 @@ def _apply_hydrate_result(
|
||||
# returns None / unexpected types) just let revert behave as
|
||||
# it always has — better to lose the cursor than to crash the
|
||||
# hydrate finish callback.
|
||||
#
|
||||
# Empty-pre-revert guard: when the LSP target was a 0-byte
|
||||
# placeholder (cache stub never fetched), Sublime can't place
|
||||
# the caret at row 42 col 5 — that row doesn't exist in an
|
||||
# empty buffer — so the caret clamps to (0, 0) and our
|
||||
# captured selection becomes ``[(0, 0)]``. Restoring that
|
||||
# after revert overrides whatever position Sublime defers /
|
||||
# re-applies once the buffer has content, leaving the user at
|
||||
# the file top. Skip capture/restore entirely when the buffer
|
||||
# had no content; downstream LSP-side caret placement (if
|
||||
# any) wins, and we are no worse than landing at (0, 0).
|
||||
view_size_fn = getattr(current, "size", None)
|
||||
try:
|
||||
buffer_size = view_size_fn() if callable(view_size_fn) else 0
|
||||
except (RuntimeError, AttributeError, TypeError):
|
||||
buffer_size = 0
|
||||
captured_selections: List[Tuple[int, int]] = []
|
||||
sel_fn = getattr(current, "sel", None)
|
||||
if callable(sel_fn):
|
||||
try:
|
||||
regions = sel_fn()
|
||||
if regions is not None:
|
||||
for region in regions:
|
||||
a = getattr(region, "a", None)
|
||||
b = getattr(region, "b", None)
|
||||
if isinstance(a, int) and isinstance(b, int):
|
||||
captured_selections.append((a, b))
|
||||
except (RuntimeError, AttributeError, TypeError):
|
||||
captured_selections = []
|
||||
if isinstance(buffer_size, int) and buffer_size > 0:
|
||||
sel_fn = getattr(current, "sel", None)
|
||||
if callable(sel_fn):
|
||||
try:
|
||||
regions = sel_fn()
|
||||
if regions is not None:
|
||||
for region in regions:
|
||||
a = getattr(region, "a", None)
|
||||
b = getattr(region, "b", None)
|
||||
if isinstance(a, int) and isinstance(b, int):
|
||||
captured_selections.append((a, b))
|
||||
except (RuntimeError, AttributeError, TypeError):
|
||||
captured_selections = []
|
||||
run_command = getattr(current, "run_command", None)
|
||||
if callable(run_command):
|
||||
run_command("revert")
|
||||
@@ -6957,11 +6974,21 @@ class SessionsOpenRemoteTerminalCommand(sublime_plugin.WindowCommand):
|
||||
# form re-tripped some zsh setups in v0.7.31+ with
|
||||
# ``zsh:1: unknown exec flag -/`` — the parameter expansion
|
||||
# split such that the literal ``-/bin/sh`` reached ``exec`` as
|
||||
# a flag instead of expanding to ``/bin/sh``. sshd populates
|
||||
# ``$SHELL`` from the user's passwd entry in every login
|
||||
# session, so the fallback was redundant; quoting ``"$SHELL"``
|
||||
# also handles the rare path-with-spaces case.
|
||||
remote_invocation = 'cd {}; exec "$SHELL" -il'.format(shlex.quote(remote_root))
|
||||
# a flag instead of expanding to ``/bin/sh``.
|
||||
#
|
||||
# POSIX ``if [ -z "$SHELL" ]; then SHELL=/bin/sh; fi`` fallback
|
||||
# not ``${SHELL:-...}``: ``ssh -t host cmd`` runs the user's
|
||||
# login shell with ``-c`` (NON-login mode), so depending on the
|
||||
# remote ``/etc/passwd`` and any ``.zshenv`` quirks ``$SHELL``
|
||||
# may be unset. v0.7.42 dropped the fallback assuming sshd
|
||||
# always populates it, but that broke users with non-standard
|
||||
# shell configs (``zsh:1: permission denied:`` exit 126 from
|
||||
# ``exec ""``). Reinstate the fallback in a form that avoids
|
||||
# the ``:-`` parser bug entirely.
|
||||
remote_invocation = (
|
||||
'cd {}; if [ -z "$SHELL" ]; then SHELL=/bin/sh; fi; '
|
||||
'exec "$SHELL" -il'.format(shlex.quote(remote_root))
|
||||
)
|
||||
# ``panel_name`` makes Terminus open the shell as a panel
|
||||
# docked at the bottom of the active window. Without it
|
||||
# Terminus defaults to a new tab in the editor pane group,
|
||||
|
||||
@@ -452,13 +452,17 @@ def test_open_remote_terminal_opens_transient_terminus_pane(
|
||||
# prefix was dropped — it confused interactive zsh on some macOS →
|
||||
# Linux setups (``zsh: bad option: -/``). The ``${SHELL:-/bin/sh}``
|
||||
# default form re-tripped the same class of zsh setups in v0.7.31+
|
||||
# (``zsh:1: unknown exec flag -/``); ``"$SHELL"`` quoted is enough
|
||||
# because sshd populates ``$SHELL`` from passwd in every login.
|
||||
# (``zsh:1: unknown exec flag -/``). v0.7.42 dropped the fallback
|
||||
# entirely on the assumption sshd populates ``$SHELL``; that broke
|
||||
# users where ``ssh -t host cmd`` runs the login shell in non-login
|
||||
# ``-c`` mode and ``$SHELL`` is empty (``permission denied:`` exit
|
||||
# 126). v0.7.43 reinstates the fallback via POSIX ``if`` instead of
|
||||
# ``:-`` so the parser-bug class is avoided.
|
||||
assert args["cmd"] == [
|
||||
"ssh",
|
||||
"-t",
|
||||
"prod",
|
||||
'cd /srv/app; exec "$SHELL" -il',
|
||||
'cd /srv/app; if [ -z "$SHELL" ]; then SHELL=/bin/sh; fi; exec "$SHELL" -il',
|
||||
]
|
||||
# ``auto_close=False`` so an unexpected shell exit (dotfile error,
|
||||
# missing remote root, SSH drop) keeps the pane visible long enough
|
||||
|
||||
Reference in New Issue
Block a user