Skip to content

LSP never starts for any fresh ssh:// session launched from a Windows host #2923

Description

@mommysgoodpuppy

Summary

When Fresh runs on Windows and connects to a Linux remote via fresh ssh://user@host/path, no
language server ever starts for remote files. A POSIX remote path can't be converted to a file:// URI by a
Windows-host build, so didOpen is never sent and the server is never spawned.

Repro

  1. On Windows, fresh ssh://user@linux-host/home/user/project
  2. Open a file with a configured LSP (any language pack with fresh.lsp).
  3. No server starts. ShowLspStatus reports nothing running.

Log:

WARN fresh::app::file_operations: No URI in metadata for file: /home/user/project/main.rs (failed to compute absolute path)

Cause

Window::notify_lsp_file_opened returns early when the buffer has no URI:

let Some(uri) = metadata.file_uri().cloned() else {
    tracing::warn!("No URI in metadata for file: {} (failed to compute absolute path)", path.display());
    return;
};

crates/fresh-editor/src/app/file_operations.rs:1448

The URI is computed in BufferMetadata::with_fileLspUri::from_host_path
fresh_core::file_uri::path_to_file_uri, which starts with:

if !path.is_absolute() { return None; }

crates/fresh-core/src/file_uri.rs:26

On Windows, Path::new("/home/user/project/main.rs").is_absolute() is false Windows
absoluteness requires a Prefix component (drive letter or UNC); a rooted-but-prefixless path has
has_root() == true but is_absolute() == false. Even without that guard, the #[cfg(windows)]
branch matches on Component::Prefix and returns None when prefix_kind is None
(file_uri.rs:58-61).

So the remote POSIX path is being interpreted with host path semantics. LspUri's
PathTranslation escape hatch doesn't help, because it's only populated for container authorities —
per the doc comment at crates/fresh-editor/src/app/types/buffer_meta.rs:172, it is "None for
local/SSH authorities"
. SSH assumes host and remote share path semantics, which holds Linux → Linux
and breaks Windows → Linux.

I also verified Linux → Linux, it works fine.

Suggested fix

Make URI computation path-style-aware rather than host-platform-aware:

  1. Add path_to_file_uri_posix / file_uri_to_path_posix in fresh-core/src/file_uri.rs that build
    and parse using /-splitting instead of Path::components(), so they behave identically
    regardless of the platform Fresh was compiled for.
  2. Carry a path style (posix / windows) on the Authority, set from the transport — SSH and
    kubectl-exec remotes are posix.
  3. Have LspUri::from_host_path / to_host_path and the root_uri construction in LspManager
    select the builder from that style instead of #[cfg].

The canonicalize() calls in display_name_for_path (buffer_meta.rs:317) already fall back
gracefully on failure, so they shouldn't need changes for this.

Broader note: there are ~39 is_absolute() call sites in fresh-editor/fresh-core that interpret
possibly-remote paths with host semantics. Most degrade gracefully; the URI path is the one that
hard-fails. A path-style abstraction would be the systematic fix, but the three steps above are the
contained change that restores remote LSP.

Environment

  • Fresh host: Windows
  • Remote: Linux (Termux/Android in my case, but the path semantics issue is generic)
  • Repro'd against b5b3dc7855c36b8ce6fcbbe3c0652d050f6f7cd0

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions