Skip to content

Commit ec69c1f

Browse files
committed
[WIP] feat(multi-client): Multi-client support for the application
This implements only the bare minimum to allow the multi-client support to be tested. To test it open three terminal windows. - In window 1 run: ./s - In window 2 run: ./c - In window 3 run: ./c You will see the UI appear in windows 2 and 3. You can open the same file in both windows and observe changes in one window appear in the other. TODO: - Support more than two clients. - Support dynamically attaching and detaching clients. - Prevent reading from terminal while client is suspended. - Support sending current directory, stdin from the client to the server. - Wait until Helix switches to Termina (helix-editor#13307), and then reimplement this on top of Termina. For now this depends on a forked version of Crossterm which is available at: https://github.com/pcc/crossterm/tree/client-server-rebase From a brief look at the Termina code it seems easier to extend Termina to support this than it was to extend Crossterm.
1 parent 27cdb9e commit ec69c1f

File tree

16 files changed

+677
-217
lines changed

16 files changed

+677
-217
lines changed

Cargo.lock

Lines changed: 51 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,6 @@ repository = "https://github.com/helix-editor/helix"
5757
homepage = "https://helix-editor.com"
5858
license = "MPL-2.0"
5959
rust-version = "1.82"
60+
61+
[patch.crates-io]
62+
crossterm = { path = 'crossterm' }

c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/bin/sh
2+
export HELIX_RUNTIME=$PWD/runtime
3+
target/release/hx --client /tmp/hx "$@"

helix-core/src/position.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ use std::{
44
ops::{Add, AddAssign, Sub, SubAssign},
55
};
66

7+
use serde::{Deserialize, Serialize};
8+
79
use helix_stdx::rope::RopeSliceExt;
810

911
use crate::{
@@ -16,7 +18,7 @@ use crate::{
1618
};
1719

1820
/// Represents a single point in a text buffer. Zero indexed.
19-
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
21+
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
2022
pub struct Position {
2123
pub row: usize,
2224
pub col: usize,

helix-stdx/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,13 @@ bitflags.workspace = true
2121
once_cell = "1.21"
2222
regex-automata = "0.4.9"
2323
unicode-segmentation.workspace = true
24+
tokio = { version = "1.38.0", features = ["net"] }
2425

2526
[target.'cfg(windows)'.dependencies]
2627
windows-sys = { version = "0.59", features = ["Win32_Foundation", "Win32_Security", "Win32_Security_Authorization", "Win32_Storage_FileSystem", "Win32_System_Threading"] }
2728

2829
[target.'cfg(unix)'.dependencies]
29-
rustix = { version = "1.0", features = ["fs"] }
30+
rustix = { version = "1.0", features = ["fs", "net"] }
3031

3132
[dev-dependencies]
3233
tempfile.workspace = true

helix-stdx/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@ pub mod faccess;
33
pub mod path;
44
pub mod range;
55
pub mod rope;
6+
pub mod socket;
67

78
pub use range::Range;

helix-stdx/src/socket.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
use rustix::fd::AsFd;
2+
use rustix::net::{
3+
recvmsg, sendmsg, RecvAncillaryBuffer, RecvAncillaryMessage, RecvFlags, SendAncillaryBuffer,
4+
SendAncillaryMessage, SendFlags,
5+
};
6+
use std::fs::File;
7+
use std::io::IoSlice;
8+
use std::io::{self, IoSliceMut};
9+
use std::mem::MaybeUninit;
10+
11+
pub fn write_fd<Fd: AsFd>(socket: Fd, file: &File) -> io::Result<()> {
12+
let mut space = [MaybeUninit::uninit(); rustix::cmsg_space!(ScmRights(1))];
13+
let mut buf = SendAncillaryBuffer::new(&mut space);
14+
let fd_arr = [file.as_fd()];
15+
buf.push(SendAncillaryMessage::ScmRights(&fd_arr));
16+
sendmsg(socket, &[IoSlice::new(&[0])], &mut buf, SendFlags::empty())?;
17+
Ok(())
18+
}
19+
20+
pub fn read_fd<Fd: AsFd>(socket: Fd) -> io::Result<File> {
21+
let mut space = [MaybeUninit::uninit(); rustix::cmsg_space!(ScmRights(1))];
22+
let mut buf = RecvAncillaryBuffer::new(&mut space);
23+
let mut recv_buf = [0];
24+
recvmsg(
25+
socket,
26+
&mut [IoSliceMut::new(&mut recv_buf)],
27+
&mut buf,
28+
RecvFlags::empty(),
29+
)?;
30+
if let Some(RecvAncillaryMessage::ScmRights(mut fd)) = buf.drain().next() {
31+
if let Some(fd) = fd.next() {
32+
return Ok(fd.into());
33+
}
34+
}
35+
Err(io::Error::new(io::ErrorKind::Other, "did not receive fd"))
36+
}

helix-term/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ serde = { version = "1.0", features = ["derive"] }
9090
# ripgrep for global search
9191
grep-regex = "0.1.13"
9292
grep-searcher = "0.1.14"
93+
rmp-serde = "1.3.0"
94+
tokio-util = { version = "0.7.15", features = ["io", "io-util"] }
9395

9496
[target.'cfg(not(windows))'.dependencies] # https://github.com/vorner/signal-hook/issues/100
9597
signal-hook-tokio = { version = "0.3", features = ["futures-v0_3"] }

0 commit comments

Comments
 (0)