logiguard fork: GPUI xdg-activation keyboard-focus serial fix
Some checks failed
Update All Top Ranking Issues / update_top_ranking_issues (push) Has been cancelled
Triage Project Sync (#84) / Sync triage project (push) Has been cancelled
release_nightly / notify_on_failure (push) Has been cancelled
release_nightly / check_style (push) Has been cancelled
release_nightly / run_tests_windows (push) Has been cancelled
release_nightly / clippy_windows (push) Has been cancelled
release_nightly / bundle_linux_aarch64 (push) Has been cancelled
release_nightly / bundle_linux_x86_64 (push) Has been cancelled
release_nightly / bundle_mac_aarch64 (push) Has been cancelled
release_nightly / bundle_mac_x86_64 (push) Has been cancelled
release_nightly / bundle_windows_aarch64 (push) Has been cancelled
release_nightly / bundle_windows_x86_64 (push) Has been cancelled
release_nightly / build_nix_linux_x86_64 (push) Has been cancelled
release_nightly / build_nix_mac_aarch64 (push) Has been cancelled
release_nightly / update_nightly_tag (push) Has been cancelled
Hotfix Review Monitor / check-hotfix-reviews (push) Has been cancelled
Stale PR Review Reminder / check-stale-prs (push) Has been cancelled
Update Weekly Top Ranking Issues / update_top_ranking_issues (push) Has been cancelled
Bump collab-staging Tag / update-collab-staging-tag (push) Has been cancelled
compliance_check / scheduled_compliance_check (push) Has been cancelled

Single-commit orphan branch: full zed-industries/zed @ 8c74db0 source tree
with a 3-file patch applied (no upstream history).

Patch (crates/gpui_linux/src/linux/wayland/):
  - serial.rs: add SerialKind::KeyboardEnter
  - client.rs: store wl_keyboard.enter serial; add latest_serial_of()
  - window.rs: activate() uses keyboard-enter serial (Mutter focus gate)

Mutter honors window activation only when the token carries the keyboard-
focus serial from wl_keyboard.enter; GPUI used a stale mouse-press serial.
See docs/tray-window-focus-wayland.md in logiguard.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Mohamad Khani
2026-07-14 02:22:17 +03:30
commit b72a46db68
3984 changed files with 1583326 additions and 0 deletions

43
crates/lsp/Cargo.toml Normal file
View File

@@ -0,0 +1,43 @@
[package]
name = "lsp"
version = "0.1.0"
edition.workspace = true
publish.workspace = true
license = "GPL-3.0-or-later"
[lints]
workspace = true
[lib]
path = "src/lsp.rs"
doctest = false
[features]
test-support = ["async-pipe", "gpui_util"]
[dependencies]
anyhow.workspace = true
async-pipe = { workspace = true, optional = true }
collections.workspace = true
gpui_util = { workspace = true, optional = true }
futures.workspace = true
gpui.workspace = true
log.workspace = true
lsp-types.workspace = true
parking_lot.workspace = true
postage.workspace = true
serde.workspace = true
serde_json.workspace = true
schemars.workspace = true
smol.workspace = true
util.workspace = true
release_channel.workspace = true
[dev-dependencies]
async-pipe.workspace = true
gpui_util.workspace = true
ctor.workspace = true
gpui = { workspace = true, features = ["test-support"] }
semver.workspace = true
util = { workspace = true, features = ["test-support"] }
zlog.workspace = true

1
crates/lsp/LICENSE-GPL Symbolic link
View File

@@ -0,0 +1 @@
../../LICENSE-GPL

View File

@@ -0,0 +1,158 @@
use std::str;
use std::sync::Arc;
use anyhow::{Context as _, Result};
use collections::HashMap;
use futures::{
AsyncBufReadExt, AsyncRead, AsyncReadExt as _,
channel::mpsc::{UnboundedReceiver, UnboundedSender, unbounded},
};
use gpui::{BackgroundExecutor, Task};
use log::warn;
use parking_lot::Mutex;
use smol::io::BufReader;
use crate::{
AnyResponse, CONTENT_LEN_HEADER, IoHandler, IoKind, NotificationOrRequest, RequestId,
ResponseHandler,
};
const HEADER_DELIMITER: &[u8; 4] = b"\r\n\r\n";
/// Handler for stdout of language server.
pub struct LspStdoutHandler {
pub(super) loop_handle: Task<Result<()>>,
pub(super) incoming_messages: UnboundedReceiver<NotificationOrRequest>,
}
async fn read_headers<Stdout>(reader: &mut BufReader<Stdout>, buffer: &mut Vec<u8>) -> Result<()>
where
Stdout: AsyncRead + Unpin + Send + 'static,
{
loop {
if buffer.len() >= HEADER_DELIMITER.len()
&& buffer[(buffer.len() - HEADER_DELIMITER.len())..] == HEADER_DELIMITER[..]
{
return Ok(());
}
if reader.read_until(b'\n', buffer).await? == 0 {
anyhow::bail!("cannot read LSP message headers");
}
}
}
impl LspStdoutHandler {
pub fn new<Input>(
stdout: Input,
response_handlers: Arc<Mutex<Option<HashMap<RequestId, ResponseHandler>>>>,
io_handlers: Arc<Mutex<HashMap<i32, IoHandler>>>,
cx: BackgroundExecutor,
) -> Self
where
Input: AsyncRead + Unpin + Send + 'static,
{
let (tx, notifications_channel) = unbounded();
let loop_handle = cx.spawn(Self::handler(stdout, tx, response_handlers, io_handlers));
Self {
loop_handle,
incoming_messages: notifications_channel,
}
}
async fn handler<Input>(
stdout: Input,
notifications_sender: UnboundedSender<NotificationOrRequest>,
response_handlers: Arc<Mutex<Option<HashMap<RequestId, ResponseHandler>>>>,
io_handlers: Arc<Mutex<HashMap<i32, IoHandler>>>,
) -> anyhow::Result<()>
where
Input: AsyncRead + Unpin + Send + 'static,
{
let mut stdout = BufReader::new(stdout);
let mut buffer = Vec::new();
loop {
buffer.clear();
read_headers(&mut stdout, &mut buffer).await?;
let headers = std::str::from_utf8(&buffer)?;
let message_len = headers
.split('\n')
.find(|line| line.starts_with(CONTENT_LEN_HEADER))
.and_then(|line| line.strip_prefix(CONTENT_LEN_HEADER))
.with_context(|| format!("invalid LSP message header {headers:?}"))?
.trim_end()
.parse()?;
buffer.resize(message_len, 0);
stdout.read_exact(&mut buffer).await?;
if let Ok(message) = str::from_utf8(&buffer) {
log::trace!("incoming message: {message}");
for handler in io_handlers.lock().values_mut() {
handler(IoKind::StdOut, message);
}
}
if let Ok(msg) = serde_json::from_slice::<NotificationOrRequest>(&buffer) {
notifications_sender.unbounded_send(msg)?;
} else if let Ok(AnyResponse {
id, error, result, ..
}) = serde_json::from_slice(&buffer)
{
let handler = {
response_handlers
.lock()
.as_mut()
.and_then(|handlers| handlers.remove(&id))
};
if let Some(handler) = handler {
if let Some(error) = error {
handler(Err(error)).await;
} else if let Some(result) = result {
handler(Ok(result.get().into())).await;
} else {
handler(Ok("null".into())).await;
}
}
} else {
warn!(
"failed to deserialize LSP message:\n{}",
std::str::from_utf8(&buffer)?
);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[gpui::test]
async fn test_read_headers() {
let mut buf = Vec::new();
let mut reader = smol::io::BufReader::new(b"Content-Length: 123\r\n\r\n" as &[u8]);
read_headers(&mut reader, &mut buf).await.unwrap();
assert_eq!(buf, b"Content-Length: 123\r\n\r\n");
let mut buf = Vec::new();
let mut reader = smol::io::BufReader::new(b"Content-Type: application/vscode-jsonrpc\r\nContent-Length: 1235\r\n\r\n{\"somecontent\":123}" as &[u8]);
read_headers(&mut reader, &mut buf).await.unwrap();
assert_eq!(
buf,
b"Content-Type: application/vscode-jsonrpc\r\nContent-Length: 1235\r\n\r\n"
);
let mut buf = Vec::new();
let mut reader = smol::io::BufReader::new(b"Content-Length: 1235\r\nContent-Type: application/vscode-jsonrpc\r\n\r\n{\"somecontent\":true}" as &[u8]);
read_headers(&mut reader, &mut buf).await.unwrap();
assert_eq!(
buf,
b"Content-Length: 1235\r\nContent-Type: application/vscode-jsonrpc\r\n\r\n"
);
}
}

2256
crates/lsp/src/lsp.rs Normal file

File diff suppressed because it is too large Load Diff