Includes prior-session patches (carry forward so the app compiles): - crates/gpui/build.rs: cross-compile manifest fix - crates/gpui/src/platform.rs: PlatformWindow::activate_with_token trait method - crates/gpui/src/window.rs: Window::activate_with_token public API - crates/gpui_linux/src/linux/wayland/window.rs: WaylandWindow::activate_with_token + activate() keyboard-serial fix Plus the focus-serial fix: - serial.rs: SerialKind::KeyboardEnter - client.rs: store wl_keyboard.enter serial; latest_serial_of() Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
218 lines
5.5 KiB
Rust
218 lines
5.5 KiB
Rust
use std::collections::BTreeSet;
|
|
|
|
const FILTERED_GIT_PROVIDER_HOSTNAMES: &[&str] = &[
|
|
"dev.azure.com",
|
|
"bitbucket.org",
|
|
"chromium.googlesource.com",
|
|
"codeberg.org",
|
|
"gitea.com",
|
|
"gitee.com",
|
|
"github.com",
|
|
"gist.github.com",
|
|
"gitlab.com",
|
|
"sourcehut.org",
|
|
"git.sr.ht",
|
|
];
|
|
|
|
pub fn parse_ssh_config_hosts(config: &str) -> BTreeSet<String> {
|
|
parse_host_blocks(config)
|
|
.into_iter()
|
|
.flat_map(HostBlock::non_git_provider_hosts)
|
|
.collect()
|
|
}
|
|
|
|
struct HostBlock {
|
|
aliases: BTreeSet<String>,
|
|
hostname: Option<String>,
|
|
}
|
|
|
|
impl HostBlock {
|
|
fn non_git_provider_hosts(self) -> impl Iterator<Item = String> {
|
|
let hostname = self.hostname;
|
|
let hostname_ref = hostname.as_deref().map(is_git_provider_domain);
|
|
self.aliases
|
|
.into_iter()
|
|
.filter(move |alias| !hostname_ref.unwrap_or_else(|| is_git_provider_domain(alias)))
|
|
}
|
|
}
|
|
|
|
fn parse_host_blocks(config: &str) -> Vec<HostBlock> {
|
|
let mut blocks = Vec::new();
|
|
let mut aliases = BTreeSet::new();
|
|
let mut hostname = None;
|
|
let mut needs_continuation = false;
|
|
|
|
for line in config.lines() {
|
|
let line = line.trim_start();
|
|
|
|
if needs_continuation {
|
|
needs_continuation = line.trim_end().ends_with('\\');
|
|
parse_hosts(line, &mut aliases);
|
|
continue;
|
|
}
|
|
|
|
let Some((keyword, value)) = split_keyword_and_value(line) else {
|
|
continue;
|
|
};
|
|
|
|
if keyword.eq_ignore_ascii_case("host") {
|
|
if !aliases.is_empty() {
|
|
blocks.push(HostBlock { aliases, hostname });
|
|
aliases = BTreeSet::new();
|
|
hostname = None;
|
|
}
|
|
parse_hosts(value, &mut aliases);
|
|
needs_continuation = line.trim_end().ends_with('\\');
|
|
} else if keyword.eq_ignore_ascii_case("hostname") {
|
|
hostname = value.split_whitespace().next().map(ToOwned::to_owned);
|
|
}
|
|
}
|
|
|
|
if !aliases.is_empty() {
|
|
blocks.push(HostBlock { aliases, hostname });
|
|
}
|
|
|
|
blocks
|
|
}
|
|
|
|
fn parse_hosts(line: &str, hosts: &mut BTreeSet<String>) {
|
|
hosts.extend(
|
|
line.split_whitespace()
|
|
.map(|field| field.trim_end_matches('\\'))
|
|
.filter(|field| !field.starts_with("!"))
|
|
.filter(|field| !field.contains("*"))
|
|
.filter(|field| *field != "\\")
|
|
.filter(|field| !field.is_empty())
|
|
.map(|field| field.to_owned()),
|
|
);
|
|
}
|
|
|
|
fn split_keyword_and_value(line: &str) -> Option<(&str, &str)> {
|
|
let keyword_end = line.find(char::is_whitespace).unwrap_or(line.len());
|
|
let keyword = &line[..keyword_end];
|
|
if keyword.is_empty() {
|
|
return None;
|
|
}
|
|
|
|
let value = line[keyword_end..].trim_start();
|
|
Some((keyword, value))
|
|
}
|
|
|
|
fn is_git_provider_domain(host: &str) -> bool {
|
|
let host = host.to_ascii_lowercase();
|
|
FILTERED_GIT_PROVIDER_HOSTNAMES.contains(&host.as_str())
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use indoc::indoc;
|
|
|
|
#[test]
|
|
fn test_thank_you_bjorn3() {
|
|
let hosts = indoc! {"
|
|
Host *
|
|
AddKeysToAgent yes
|
|
UseKeychain yes
|
|
IdentityFile ~/.ssh/id_ed25519
|
|
|
|
Host whatever.*
|
|
User another
|
|
|
|
Host !not_this
|
|
User not_me
|
|
|
|
Host something
|
|
HostName whatever.tld
|
|
|
|
Host linux bsd host3
|
|
User bjorn
|
|
|
|
Host rpi
|
|
user rpi
|
|
hostname rpi.local
|
|
|
|
Host \\
|
|
somehost \\
|
|
anotherhost
|
|
Hostname 192.168.3.3
|
|
"};
|
|
|
|
let expected_hosts = BTreeSet::from_iter([
|
|
"something".to_owned(),
|
|
"linux".to_owned(),
|
|
"host3".to_owned(),
|
|
"bsd".to_owned(),
|
|
"rpi".to_owned(),
|
|
"somehost".to_owned(),
|
|
"anotherhost".to_owned(),
|
|
]);
|
|
|
|
assert_eq!(expected_hosts, parse_ssh_config_hosts(hosts));
|
|
}
|
|
|
|
#[test]
|
|
fn filters_git_provider_domains_from_hostname() {
|
|
let hosts = indoc! {"
|
|
Host github-personal
|
|
HostName github.com
|
|
|
|
Host gitlab-work
|
|
HostName GITLAB.COM
|
|
|
|
Host local
|
|
HostName example.com
|
|
"};
|
|
|
|
assert_eq!(
|
|
BTreeSet::from_iter(["local".to_owned()]),
|
|
parse_ssh_config_hosts(hosts)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn falls_back_to_host_when_hostname_is_absent() {
|
|
let hosts = indoc! {"
|
|
Host github.com bitbucket.org keep-me
|
|
User git
|
|
"};
|
|
|
|
assert_eq!(
|
|
BTreeSet::from_iter(["keep-me".to_owned()]),
|
|
parse_ssh_config_hosts(hosts)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn does_not_fuzzy_match_host_aliases() {
|
|
let hosts = indoc! {"
|
|
Host GitHub GitLab Bitbucket GITHUB github
|
|
User git
|
|
"};
|
|
|
|
assert_eq!(
|
|
BTreeSet::from_iter([
|
|
"Bitbucket".to_owned(),
|
|
"GITHUB".to_owned(),
|
|
"GitHub".to_owned(),
|
|
"GitLab".to_owned(),
|
|
"github".to_owned(),
|
|
]),
|
|
parse_ssh_config_hosts(hosts)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn uses_hostname_before_host_filtering() {
|
|
let hosts = indoc! {"
|
|
Host github.com keep-me
|
|
HostName example.com
|
|
"};
|
|
|
|
assert_eq!(
|
|
BTreeSet::from_iter(["github.com".to_owned(), "keep-me".to_owned()]),
|
|
parse_ssh_config_hosts(hosts)
|
|
);
|
|
}
|
|
}
|