Add initial FreeBSD support (#20480)

This PR adds initial support for FreeBSD
(https://github.com/zed-industries/zed/issues/15309). While there is
still work left to be done, it seems to be usable. As discussed by
@syobocat (https://github.com/zed-industries/zed/discussions/10247), the
changes were just adding ```target_os = "freebsd"``` to wherever it
checks if the OS is Linux.


![image](https://github.com/user-attachments/assets/80ea5b29-047f-4cbd-8263-42e5fa6c94b7)

Needs to be build with ```RUSTFLAGS="-C link-dead-code"```

Known Issues:
- There's an issue in ```crates/project/src/environment.rs``` where a
command fails because ```/bin/sh``` on FreeBSD doesn't support the
```-l``` option.

![image](https://github.com/user-attachments/assets/c3c38633-160f-4f47-8840-e3da67f6ebc8)
- The file/folder choosers provided by the ```ashpd``` crate don't work
on FreeBSD (at least with KDE). This isn't that bad since a fallback
dialog is used.

![image](https://github.com/user-attachments/assets/29373006-1eb9-4ed0-bd52-2d0047fab418)
 - Moving to trash won't work.
- Numerous tests fail (when running on FreeBSD). While I haven't looked
into this much, it appears that the corresponding features seem to work
fine.

Release Notes:

- Added initial support for FreeBSD
This commit is contained in:
Caleb Heydon 2024-11-11 12:39:05 -05:00 committed by GitHub
parent b5da1198f5
commit a47759fd03
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
34 changed files with 139 additions and 96 deletions

View file

@ -29,7 +29,7 @@ serde.workspace = true
util.workspace = true util.workspace = true
tempfile.workspace = true tempfile.workspace = true
[target.'cfg(target_os = "linux")'.dependencies] [target.'cfg(any(target_os = "linux", target_os = "freebsd"))'.dependencies]
exec.workspace = true exec.workspace = true
fork.workspace = true fork.workspace = true

View file

@ -1,4 +1,7 @@
#![cfg_attr(any(target_os = "linux", target_os = "windows"), allow(dead_code))] #![cfg_attr(
any(target_os = "linux", target_os = "freebsd", target_os = "windows"),
allow(dead_code)
)]
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use clap::Parser; use clap::Parser;
@ -88,7 +91,7 @@ fn parse_path_with_position(argument_str: &str) -> anyhow::Result<String> {
fn main() -> Result<()> { fn main() -> Result<()> {
// Exit flatpak sandbox if needed // Exit flatpak sandbox if needed
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
{ {
flatpak::try_restart_to_host(); flatpak::try_restart_to_host();
flatpak::ld_extra_libs(); flatpak::ld_extra_libs();
@ -106,7 +109,7 @@ fn main() -> Result<()> {
} }
let args = Args::parse(); let args = Args::parse();
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
let args = flatpak::set_bin_if_no_escape(args); let args = flatpak::set_bin_if_no_escape(args);
let app = Detect::detect(args.zed.as_deref()).context("Bundle detection")?; let app = Detect::detect(args.zed.as_deref()).context("Bundle detection")?;
@ -220,7 +223,7 @@ fn main() -> Result<()> {
Ok(()) Ok(())
} }
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
mod linux { mod linux {
use std::{ use std::{
env, env,
@ -344,7 +347,7 @@ mod linux {
} }
} }
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
mod flatpak { mod flatpak {
use std::ffi::OsString; use std::ffi::OsString;
use std::path::PathBuf; use std::path::PathBuf;

View file

@ -100,7 +100,7 @@ pub fn os_name() -> String {
{ {
"macOS".to_string() "macOS".to_string()
} }
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
{ {
format!("Linux {}", gpui::guess_compositor()) format!("Linux {}", gpui::guess_compositor())
} }
@ -129,7 +129,7 @@ pub fn os_version() -> String {
.to_string() .to_string()
} }
} }
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
{ {
use std::path::Path; use std::path::Path;

View file

@ -2564,7 +2564,7 @@ impl Editor {
cx.invalidate_character_coordinates(); cx.invalidate_character_coordinates();
// Copy selections to primary selection buffer // Copy selections to primary selection buffer
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
if local { if local {
let selections = self.selections.all::<usize>(cx); let selections = self.selections.all::<usize>(cx);
let buffer_handle = self.buffer.read(cx).read(cx); let buffer_handle = self.buffer.read(cx).read(cx);

View file

@ -650,12 +650,14 @@ impl EditorElement {
cx.stop_propagation(); cx.stop_propagation();
} else if end_selection && pending_nonempty_selections { } else if end_selection && pending_nonempty_selections {
cx.stop_propagation(); cx.stop_propagation();
} else if cfg!(target_os = "linux") && event.button == MouseButton::Middle { } else if cfg!(any(target_os = "linux", target_os = "freebsd"))
&& event.button == MouseButton::Middle
{
if !text_hitbox.is_hovered(cx) || editor.read_only(cx) { if !text_hitbox.is_hovered(cx) || editor.read_only(cx) {
return; return;
} }
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
if EditorSettings::get_global(cx).middle_click_paste { if EditorSettings::get_global(cx).middle_click_paste {
if let Some(text) = cx.read_from_primary().and_then(|item| item.text()) { if let Some(text) = cx.read_from_primary().and_then(|item| item.text()) {
let point_for_position = let point_for_position =

View file

@ -36,7 +36,7 @@ const WASI_ADAPTER_URL: &str =
const WASI_SDK_URL: &str = "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-21/"; const WASI_SDK_URL: &str = "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-21/";
const WASI_SDK_ASSET_NAME: Option<&str> = if cfg!(target_os = "macos") { const WASI_SDK_ASSET_NAME: Option<&str> = if cfg!(target_os = "macos") {
Some("wasi-sdk-21.0-macos.tar.gz") Some("wasi-sdk-21.0-macos.tar.gz")
} else if cfg!(target_os = "linux") { } else if cfg!(any(target_os = "linux", target_os = "freebsd")) {
Some("wasi-sdk-21.0-linux.tar.gz") Some("wasi-sdk-21.0-linux.tar.gz")
} else if cfg!(target_os = "windows") { } else if cfg!(target_os = "windows") {
Some("wasi-sdk-21.0.m-mingw.tar.gz") Some("wasi-sdk-21.0.m-mingw.tar.gz")

View file

@ -43,7 +43,7 @@ notify = "6.1.1"
[target.'cfg(target_os = "windows")'.dependencies] [target.'cfg(target_os = "windows")'.dependencies]
windows.workspace = true windows.workspace = true
[target.'cfg(target_os = "linux")'.dependencies] [target.'cfg(any(target_os = "linux", target_os = "freebsd"))'.dependencies]
ashpd.workspace = true ashpd.workspace = true
[dev-dependencies] [dev-dependencies]

View file

@ -1,15 +1,15 @@
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
mod mac_watcher; mod mac_watcher;
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
pub mod linux_watcher; pub mod linux_watcher;
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use git::GitHostingProviderRegistry; use git::GitHostingProviderRegistry;
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
use ashpd::desktop::trash; use ashpd::desktop::trash;
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
use std::fs::File; use std::fs::File;
#[cfg(unix)] #[cfg(unix)]
use std::os::fd::AsFd; use std::os::fd::AsFd;
@ -217,7 +217,7 @@ impl FileHandle for std::fs::File {
Ok(path) Ok(path)
} }
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
fn current_path(&self, _: &Arc<dyn Fs>) -> Result<PathBuf> { fn current_path(&self, _: &Arc<dyn Fs>) -> Result<PathBuf> {
let fd = self.as_fd(); let fd = self.as_fd();
let fd_path = format!("/proc/self/fd/{}", fd.as_raw_fd()); let fd_path = format!("/proc/self/fd/{}", fd.as_raw_fd());
@ -391,7 +391,7 @@ impl Fs for RealFs {
Ok(()) Ok(())
} }
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
async fn trash_file(&self, path: &Path, _options: RemoveOptions) -> Result<()> { async fn trash_file(&self, path: &Path, _options: RemoveOptions) -> Result<()> {
let file = File::open(path)?; let file = File::open(path)?;
match trash::trash_file(&file.as_fd()).await { match trash::trash_file(&file.as_fd()).await {
@ -423,7 +423,7 @@ impl Fs for RealFs {
self.trash_file(path, options).await self.trash_file(path, options).await
} }
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
async fn trash_dir(&self, path: &Path, options: RemoveOptions) -> Result<()> { async fn trash_dir(&self, path: &Path, options: RemoveOptions) -> Result<()> {
self.trash_file(path, options).await self.trash_file(path, options).await
} }
@ -468,7 +468,7 @@ impl Fs for RealFs {
async fn atomic_write(&self, path: PathBuf, data: String) -> Result<()> { async fn atomic_write(&self, path: PathBuf, data: String) -> Result<()> {
smol::unblock(move || { smol::unblock(move || {
let mut tmp_file = if cfg!(target_os = "linux") { let mut tmp_file = if cfg!(any(target_os = "linux", target_os = "freebsd")) {
// Use the directory of the destination as temp dir to avoid // Use the directory of the destination as temp dir to avoid
// invalid cross-device link error, and XDG_CACHE_DIR for fallback. // invalid cross-device link error, and XDG_CACHE_DIR for fallback.
// See https://github.com/zed-industries/zed/pull/8437 for more details. // See https://github.com/zed-industries/zed/pull/8437 for more details.
@ -634,7 +634,7 @@ impl Fs for RealFs {
) )
} }
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
async fn watch( async fn watch(
&self, &self,
path: &Path, path: &Path,
@ -781,7 +781,7 @@ impl Fs for RealFs {
} }
} }
#[cfg(not(target_os = "linux"))] #[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
impl Watcher for RealWatcher { impl Watcher for RealWatcher {
fn add(&self, _: &Path) -> Result<()> { fn add(&self, _: &Path) -> Result<()> {
Ok(()) Ok(())

View file

@ -85,7 +85,10 @@ impl Watcher for LinuxWatcher {
pub struct GlobalWatcher { pub struct GlobalWatcher {
// two mutexes because calling inotify.add triggers an inotify.event, which needs watchers. // two mutexes because calling inotify.add triggers an inotify.event, which needs watchers.
#[cfg(target_os = "linux")]
pub(super) inotify: Mutex<notify::INotifyWatcher>, pub(super) inotify: Mutex<notify::INotifyWatcher>,
#[cfg(target_os = "freebsd")]
pub(super) inotify: Mutex<notify::KqueueWatcher>,
pub(super) watchers: Mutex<Vec<Box<dyn Fn(&notify::Event) + Send + Sync>>>, pub(super) watchers: Mutex<Vec<Box<dyn Fn(&notify::Event) + Send + Sync>>>,
} }

View file

@ -139,10 +139,10 @@ media.workspace = true
metal = "0.29" metal = "0.29"
objc = "0.2" objc = "0.2"
[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies] [target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))'.dependencies]
pathfinder_geometry = "0.5" pathfinder_geometry = "0.5"
[target.'cfg(target_os = "linux")'.dependencies] [target.'cfg(any(target_os = "linux", target_os = "freebsd"))'.dependencies]
# Always used # Always used
flume = "0.11" flume = "0.11"
oo7 = "0.3.0" oo7 = "0.3.0"

View file

@ -614,7 +614,7 @@ impl AppContext {
/// Writes data to the primary selection buffer. /// Writes data to the primary selection buffer.
/// Only available on Linux. /// Only available on Linux.
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
pub fn write_to_primary(&self, item: ClipboardItem) { pub fn write_to_primary(&self, item: ClipboardItem) {
self.platform.write_to_primary(item) self.platform.write_to_primary(item)
} }
@ -626,7 +626,7 @@ impl AppContext {
/// Reads data from the primary selection buffer. /// Reads data from the primary selection buffer.
/// Only available on Linux. /// Only available on Linux.
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
pub fn read_from_primary(&self) -> Option<ClipboardItem> { pub fn read_from_primary(&self) -> Option<ClipboardItem> {
self.platform.read_from_primary() self.platform.read_from_primary()
} }

View file

@ -33,11 +33,16 @@ impl KeyContext {
let mut context = Self::default(); let mut context = Self::default();
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
context.set("os", "macos"); context.set("os", "macos");
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
context.set("os", "linux"); context.set("os", "linux");
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
context.set("os", "windows"); context.set("os", "windows");
#[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))] #[cfg(not(any(
target_os = "macos",
target_os = "linux",
target_os = "freebsd",
target_os = "windows"
)))]
context.set("os", "unknown"); context.set("os", "unknown");
context context
} }

View file

@ -4,14 +4,17 @@
mod app_menu; mod app_menu;
mod keystroke; mod keystroke;
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
mod linux; mod linux;
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
mod mac; mod mac;
#[cfg(any( #[cfg(any(
all(target_os = "linux", any(feature = "x11", feature = "wayland")), all(
any(target_os = "linux", target_os = "freebsd"),
any(feature = "x11", feature = "wayland")
),
target_os = "windows", target_os = "windows",
feature = "macos-blade" feature = "macos-blade"
))] ))]
@ -57,7 +60,7 @@ use uuid::Uuid;
pub use app_menu::*; pub use app_menu::*;
pub use keystroke::*; pub use keystroke::*;
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
pub(crate) use linux::*; pub(crate) use linux::*;
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
pub(crate) use mac::*; pub(crate) use mac::*;
@ -72,7 +75,7 @@ pub(crate) fn current_platform(headless: bool) -> Rc<dyn Platform> {
Rc::new(MacPlatform::new(headless)) Rc::new(MacPlatform::new(headless))
} }
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
pub(crate) fn current_platform(headless: bool) -> Rc<dyn Platform> { pub(crate) fn current_platform(headless: bool) -> Rc<dyn Platform> {
if headless { if headless {
return Rc::new(HeadlessClient::new()); return Rc::new(HeadlessClient::new());
@ -92,7 +95,7 @@ pub(crate) fn current_platform(headless: bool) -> Rc<dyn Platform> {
/// Return which compositor we're guessing we'll use. /// Return which compositor we're guessing we'll use.
/// Does not attempt to connect to the given compositor /// Does not attempt to connect to the given compositor
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
#[inline] #[inline]
pub fn guess_compositor() -> &'static str { pub fn guess_compositor() -> &'static str {
if std::env::var_os("ZED_HEADLESS").is_some() { if std::env::var_os("ZED_HEADLESS").is_some() {
@ -192,10 +195,10 @@ pub(crate) trait Platform: 'static {
fn set_cursor_style(&self, style: CursorStyle); fn set_cursor_style(&self, style: CursorStyle);
fn should_auto_hide_scrollbars(&self) -> bool; fn should_auto_hide_scrollbars(&self) -> bool;
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
fn write_to_primary(&self, item: ClipboardItem); fn write_to_primary(&self, item: ClipboardItem);
fn write_to_clipboard(&self, item: ClipboardItem); fn write_to_clipboard(&self, item: ClipboardItem);
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
fn read_from_primary(&self) -> Option<ClipboardItem>; fn read_from_primary(&self) -> Option<ClipboardItem>;
fn read_from_clipboard(&self) -> Option<ClipboardItem>; fn read_from_clipboard(&self) -> Option<ClipboardItem>;
@ -508,7 +511,10 @@ pub(crate) enum AtlasKey {
impl AtlasKey { impl AtlasKey {
#[cfg_attr( #[cfg_attr(
all(target_os = "linux", not(any(feature = "x11", feature = "wayland"))), all(
any(target_os = "linux", target_os = "freebsd"),
not(any(feature = "x11", feature = "wayland"))
),
allow(dead_code) allow(dead_code)
)] )]
pub(crate) fn texture_kind(&self) -> AtlasTextureKind { pub(crate) fn texture_kind(&self) -> AtlasTextureKind {
@ -572,7 +578,10 @@ pub(crate) struct AtlasTextureId {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(C)] #[repr(C)]
#[cfg_attr( #[cfg_attr(
all(target_os = "linux", not(any(feature = "x11", feature = "wayland"))), all(
any(target_os = "linux", target_os = "freebsd"),
not(any(feature = "x11", feature = "wayland"))
),
allow(dead_code) allow(dead_code)
)] )]
pub(crate) enum AtlasTextureKind { pub(crate) enum AtlasTextureKind {
@ -603,7 +612,10 @@ pub(crate) struct PlatformInputHandler {
} }
#[cfg_attr( #[cfg_attr(
all(target_os = "linux", not(any(feature = "x11", feature = "wayland"))), all(
any(target_os = "linux", target_os = "freebsd"),
not(any(feature = "x11", feature = "wayland"))
),
allow(dead_code) allow(dead_code)
)] )]
impl PlatformInputHandler { impl PlatformInputHandler {
@ -625,7 +637,7 @@ impl PlatformInputHandler {
.flatten() .flatten()
} }
#[cfg_attr(target_os = "linux", allow(dead_code))] #[cfg_attr(any(target_os = "linux", target_os = "freebsd"), allow(dead_code))]
fn text_for_range(&mut self, range_utf16: Range<usize>) -> Option<String> { fn text_for_range(&mut self, range_utf16: Range<usize>) -> Option<String> {
self.cx self.cx
.update(|cx| self.handler.text_for_range(range_utf16, cx)) .update(|cx| self.handler.text_for_range(range_utf16, cx))
@ -814,7 +826,10 @@ pub struct WindowOptions {
/// The variables that can be configured when creating a new window /// The variables that can be configured when creating a new window
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr( #[cfg_attr(
all(target_os = "linux", not(any(feature = "x11", feature = "wayland"))), all(
any(target_os = "linux", target_os = "freebsd"),
not(any(feature = "x11", feature = "wayland"))
),
allow(dead_code) allow(dead_code)
)] )]
pub(crate) struct WindowParams { pub(crate) struct WindowParams {
@ -825,17 +840,17 @@ pub(crate) struct WindowParams {
pub titlebar: Option<TitlebarOptions>, pub titlebar: Option<TitlebarOptions>,
/// The kind of window to create /// The kind of window to create
#[cfg_attr(target_os = "linux", allow(dead_code))] #[cfg_attr(any(target_os = "linux", target_os = "freebsd"), allow(dead_code))]
pub kind: WindowKind, pub kind: WindowKind,
/// Whether the window should be movable by the user /// Whether the window should be movable by the user
#[cfg_attr(target_os = "linux", allow(dead_code))] #[cfg_attr(any(target_os = "linux", target_os = "freebsd"), allow(dead_code))]
pub is_movable: bool, pub is_movable: bool,
#[cfg_attr(target_os = "linux", allow(dead_code))] #[cfg_attr(any(target_os = "linux", target_os = "freebsd"), allow(dead_code))]
pub focus: bool, pub focus: bool,
#[cfg_attr(target_os = "linux", allow(dead_code))] #[cfg_attr(any(target_os = "linux", target_os = "freebsd"), allow(dead_code))]
pub show: bool, pub show: bool,
#[cfg_attr(feature = "wayland", allow(dead_code))] #[cfg_attr(feature = "wayland", allow(dead_code))]
@ -1341,7 +1356,7 @@ impl ClipboardString {
.and_then(|m| serde_json::from_str(m).ok()) .and_then(|m| serde_json::from_str(m).ok())
} }
#[cfg_attr(target_os = "linux", allow(dead_code))] #[cfg_attr(any(target_os = "linux", target_os = "freebsd"), allow(dead_code))]
pub(crate) fn text_hash(text: &str) -> u64 { pub(crate) fn text_hash(text: &str) -> u64 {
let mut hasher = SeaHasher::new(); let mut hasher = SeaHasher::new();
text.hash(&mut hasher); text.hash(&mut hasher);

View file

@ -134,7 +134,7 @@ impl Keystroke {
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
str.push_str("cmd-"); str.push_str("cmd-");
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
str.push_str("super-"); str.push_str("super-");
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
@ -234,7 +234,7 @@ impl std::fmt::Display for Keystroke {
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
f.write_char('⌘')?; f.write_char('⌘')?;
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
f.write_char('❖')?; f.write_char('❖')?;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]

View file

@ -162,7 +162,7 @@ impl WindowAppearance {
} }
} }
#[cfg_attr(target_os = "linux", allow(dead_code))] #[cfg_attr(any(target_os = "linux", target_os = "freebsd"), allow(dead_code))]
fn set_native(&mut self, cs: ColorScheme) { fn set_native(&mut self, cs: ColorScheme) {
*self = Self::from_native(cs); *self = Self::from_native(cs);
} }

View file

@ -28,7 +28,7 @@ pub(crate) struct TestPlatform {
active_display: Rc<dyn PlatformDisplay>, active_display: Rc<dyn PlatformDisplay>,
active_cursor: Mutex<CursorStyle>, active_cursor: Mutex<CursorStyle>,
current_clipboard_item: Mutex<Option<ClipboardItem>>, current_clipboard_item: Mutex<Option<ClipboardItem>>,
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
current_primary_item: Mutex<Option<ClipboardItem>>, current_primary_item: Mutex<Option<ClipboardItem>>,
pub(crate) prompts: RefCell<TestPrompts>, pub(crate) prompts: RefCell<TestPrompts>,
pub opened_url: RefCell<Option<String>>, pub opened_url: RefCell<Option<String>>,
@ -59,7 +59,7 @@ impl TestPlatform {
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
let text_system = Arc::new(crate::platform::mac::MacTextSystem::new()); let text_system = Arc::new(crate::platform::mac::MacTextSystem::new());
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
let text_system = Arc::new(crate::platform::linux::CosmicTextSystem::new()); let text_system = Arc::new(crate::platform::linux::CosmicTextSystem::new());
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
@ -76,7 +76,7 @@ impl TestPlatform {
active_display: Rc::new(TestDisplay::new()), active_display: Rc::new(TestDisplay::new()),
active_window: Default::default(), active_window: Default::default(),
current_clipboard_item: Mutex::new(None), current_clipboard_item: Mutex::new(None),
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
current_primary_item: Mutex::new(None), current_primary_item: Mutex::new(None),
weak: weak.clone(), weak: weak.clone(),
opened_url: Default::default(), opened_url: Default::default(),
@ -291,7 +291,7 @@ impl Platform for TestPlatform {
false false
} }
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
fn write_to_primary(&self, item: ClipboardItem) { fn write_to_primary(&self, item: ClipboardItem) {
*self.current_primary_item.lock() = Some(item); *self.current_primary_item.lock() = Some(item);
} }
@ -300,7 +300,7 @@ impl Platform for TestPlatform {
*self.current_clipboard_item.lock() = Some(item); *self.current_clipboard_item.lock() = Some(item);
} }
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
fn read_from_primary(&self) -> Option<ClipboardItem> { fn read_from_primary(&self) -> Option<ClipboardItem> {
self.current_primary_item.lock().clone() self.current_primary_item.lock().clone()
} }

View file

@ -41,7 +41,10 @@ impl Scene {
} }
#[cfg_attr( #[cfg_attr(
all(target_os = "linux", not(any(feature = "x11", feature = "wayland"))), all(
any(target_os = "linux", target_os = "freebsd"),
not(any(feature = "x11", feature = "wayland"))
),
allow(dead_code) allow(dead_code)
)] )]
pub fn paths(&self) -> &[Path<ScaledPixels>] { pub fn paths(&self) -> &[Path<ScaledPixels>] {
@ -135,7 +138,10 @@ impl Scene {
} }
#[cfg_attr( #[cfg_attr(
all(target_os = "linux", not(any(feature = "x11", feature = "wayland"))), all(
any(target_os = "linux", target_os = "freebsd"),
not(any(feature = "x11", feature = "wayland"))
),
allow(dead_code) allow(dead_code)
)] )]
pub(crate) fn batches(&self) -> impl Iterator<Item = PrimitiveBatch> { pub(crate) fn batches(&self) -> impl Iterator<Item = PrimitiveBatch> {
@ -167,7 +173,10 @@ impl Scene {
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Default)] #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Default)]
#[cfg_attr( #[cfg_attr(
all(target_os = "linux", not(any(feature = "x11", feature = "wayland"))), all(
any(target_os = "linux", target_os = "freebsd"),
not(any(feature = "x11", feature = "wayland"))
),
allow(dead_code) allow(dead_code)
)] )]
pub(crate) enum PrimitiveKind { pub(crate) enum PrimitiveKind {
@ -225,7 +234,10 @@ impl Primitive {
} }
#[cfg_attr( #[cfg_attr(
all(target_os = "linux", not(any(feature = "x11", feature = "wayland"))), all(
any(target_os = "linux", target_os = "freebsd"),
not(any(feature = "x11", feature = "wayland"))
),
allow(dead_code) allow(dead_code)
)] )]
struct BatchIterator<'a> { struct BatchIterator<'a> {
@ -415,7 +427,10 @@ impl<'a> Iterator for BatchIterator<'a> {
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr( #[cfg_attr(
all(target_os = "linux", not(any(feature = "x11", feature = "wayland"))), all(
any(target_os = "linux", target_os = "freebsd"),
not(any(feature = "x11", feature = "wayland"))
),
allow(dead_code) allow(dead_code)
)] )]
pub(crate) enum PrimitiveBatch<'a> { pub(crate) enum PrimitiveBatch<'a> {

View file

@ -344,7 +344,7 @@ impl Default for TextStyle {
TextStyle { TextStyle {
color: black(), color: black(),
// todo(linux) make this configurable or choose better default // todo(linux) make this configurable or choose better default
font_family: if cfg!(target_os = "linux") { font_family: if cfg!(any(target_os = "linux", target_os = "freebsd")) {
"FreeMono".into() "FreeMono".into()
} else if cfg!(target_os = "windows") { } else if cfg!(target_os = "windows") {
"Segoe UI".into() "Segoe UI".into()

View file

@ -1240,7 +1240,7 @@ impl<'a> WindowContext<'a> {
/// that currently owns the mouse cursor. /// that currently owns the mouse cursor.
/// On mac, this is equivalent to `is_window_active`. /// On mac, this is equivalent to `is_window_active`.
pub fn is_window_hovered(&self) -> bool { pub fn is_window_hovered(&self) -> bool {
if cfg!(target_os = "linux") { if cfg!(any(target_os = "linux", target_os = "freebsd")) {
self.window.hovered.get() self.window.hovered.get()
} else { } else {
self.is_window_active() self.is_window_active()

View file

@ -475,7 +475,7 @@ impl MarkdownElement {
} }
} else if markdown.selection.pending { } else if markdown.selection.pending {
markdown.selection.pending = false; markdown.selection.pending = false;
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
{ {
let text = rendered_text let text = rendered_text
.text_for_range(markdown.selection.start..markdown.selection.end); .text_for_range(markdown.selection.start..markdown.selection.end);

View file

@ -20,7 +20,7 @@ pub fn config_dir() -> &'static PathBuf {
.join("Zed"); .join("Zed");
} }
if cfg!(target_os = "linux") { if cfg!(any(target_os = "linux", target_os = "freebsd")) {
return if let Ok(flatpak_xdg_config) = std::env::var("FLATPAK_XDG_CONFIG_HOME") { return if let Ok(flatpak_xdg_config) = std::env::var("FLATPAK_XDG_CONFIG_HOME") {
flatpak_xdg_config.into() flatpak_xdg_config.into()
} else { } else {
@ -41,7 +41,7 @@ pub fn support_dir() -> &'static PathBuf {
return home_dir().join("Library/Application Support/Zed"); return home_dir().join("Library/Application Support/Zed");
} }
if cfg!(target_os = "linux") { if cfg!(any(target_os = "linux", target_os = "freebsd")) {
return if let Ok(flatpak_xdg_data) = std::env::var("FLATPAK_XDG_DATA_HOME") { return if let Ok(flatpak_xdg_data) = std::env::var("FLATPAK_XDG_DATA_HOME") {
flatpak_xdg_data.into() flatpak_xdg_data.into()
} else { } else {
@ -76,7 +76,7 @@ pub fn temp_dir() -> &'static PathBuf {
.join("Zed"); .join("Zed");
} }
if cfg!(target_os = "linux") { if cfg!(any(target_os = "linux", target_os = "freebsd")) {
return if let Ok(flatpak_xdg_cache) = std::env::var("FLATPAK_XDG_CACHE_HOME") { return if let Ok(flatpak_xdg_cache) = std::env::var("FLATPAK_XDG_CACHE_HOME") {
flatpak_xdg_cache.into() flatpak_xdg_cache.into()
} else { } else {

View file

@ -128,10 +128,10 @@ impl Connection {
&mut remaining_sql_ptr, &mut remaining_sql_ptr,
); );
#[cfg(not(target_os = "linux"))] #[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
let offset = sqlite3_error_offset(temp_connection.sqlite3); let offset = sqlite3_error_offset(temp_connection.sqlite3);
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
let offset = 0; let offset = 0;
( (
@ -149,10 +149,10 @@ impl Connection {
&mut remaining_sql_ptr, &mut remaining_sql_ptr,
); );
#[cfg(not(target_os = "linux"))] #[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
let offset = sqlite3_error_offset(self.sqlite3); let offset = sqlite3_error_offset(self.sqlite3);
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
let offset = 0; let offset = 0;
( (
@ -408,7 +408,7 @@ mod test {
); );
} }
#[cfg(not(target_os = "linux"))] #[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
#[test] #[test]
fn test_sql_has_syntax_errors() { fn test_sql_has_syntax_errors() {
let connection = Connection::open_memory(Some("test_sql_has_syntax_errors")); let connection = Connection::open_memory(Some("test_sql_has_syntax_errors"));

View file

@ -1,7 +1,7 @@
use proc_macro::{Delimiter, Span, TokenStream, TokenTree}; use proc_macro::{Delimiter, Span, TokenStream, TokenTree};
use syn::Error; use syn::Error;
#[cfg(not(target_os = "linux"))] #[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
static SQLITE: std::sync::LazyLock<sqlez::thread_safe_connection::ThreadSafeConnection> = static SQLITE: std::sync::LazyLock<sqlez::thread_safe_connection::ThreadSafeConnection> =
std::sync::LazyLock::new(|| { std::sync::LazyLock::new(|| {
sqlez::thread_safe_connection::ThreadSafeConnection::new( sqlez::thread_safe_connection::ThreadSafeConnection::new(
@ -16,10 +16,10 @@ static SQLITE: std::sync::LazyLock<sqlez::thread_safe_connection::ThreadSafeConn
pub fn sql(tokens: TokenStream) -> TokenStream { pub fn sql(tokens: TokenStream) -> TokenStream {
let (spans, sql) = make_sql(tokens); let (spans, sql) = make_sql(tokens);
#[cfg(not(target_os = "linux"))] #[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
let error = SQLITE.sql_has_syntax_error(sql.trim()); let error = SQLITE.sql_has_syntax_error(sql.trim());
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
let error: Option<(String, usize)> = None; let error: Option<(String, usize)> = None;
let formatted_sql = sqlformat::format(&sql, &sqlformat::QueryParams::None, Default::default()); let formatted_sql = sqlformat::format(&sql, &sqlformat::QueryParams::None, Default::default());

View file

@ -818,7 +818,7 @@ impl Terminal {
selection.update(point, AlacDirection::Right); selection.update(point, AlacDirection::Right);
term.selection = Some(selection); term.selection = Some(selection);
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
if let Some(selection_text) = term.selection_to_string() { if let Some(selection_text) = term.selection_to_string() {
cx.write_to_primary(ClipboardItem::new_string(selection_text)); cx.write_to_primary(ClipboardItem::new_string(selection_text));
} }
@ -831,7 +831,7 @@ impl Terminal {
InternalEvent::SetSelection(selection) => { InternalEvent::SetSelection(selection) => {
term.selection = selection.as_ref().map(|(sel, _)| sel.clone()); term.selection = selection.as_ref().map(|(sel, _)| sel.clone());
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
if let Some(selection_text) = term.selection_to_string() { if let Some(selection_text) = term.selection_to_string() {
cx.write_to_primary(ClipboardItem::new_string(selection_text)); cx.write_to_primary(ClipboardItem::new_string(selection_text));
} }
@ -852,7 +852,7 @@ impl Terminal {
selection.update(point, side); selection.update(point, side);
term.selection = Some(selection); term.selection = Some(selection);
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
if let Some(selection_text) = term.selection_to_string() { if let Some(selection_text) = term.selection_to_string() {
cx.write_to_primary(ClipboardItem::new_string(selection_text)); cx.write_to_primary(ClipboardItem::new_string(selection_text));
} }
@ -1512,7 +1512,7 @@ impl Terminal {
.push_back(InternalEvent::SetSelection(Some((sel, point)))); .push_back(InternalEvent::SetSelection(Some((sel, point))));
} }
} }
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
MouseButton::Middle => { MouseButton::Middle => {
if let Some(item) = _cx.read_from_primary() { if let Some(item) = _cx.read_from_primary() {
let text = item.text().unwrap_or_default().to_string(); let text = item.text().unwrap_or_default().to_string();

View file

@ -75,7 +75,7 @@ impl Render for TitleBar {
let height = Self::height(cx); let height = Self::height(cx);
let supported_controls = cx.window_controls(); let supported_controls = cx.window_controls();
let decorations = cx.window_decorations(); let decorations = cx.window_decorations();
let titlebar_color = if cfg!(target_os = "linux") { let titlebar_color = if cfg!(any(target_os = "linux", target_os = "freebsd")) {
if cx.is_window_active() && !self.should_move { if cx.is_window_active() && !self.should_move {
cx.theme().colors().title_bar_background cx.theme().colors().title_bar_background
} else { } else {

View file

@ -14,7 +14,7 @@ pub enum PlatformStyle {
impl PlatformStyle { impl PlatformStyle {
/// Returns the [`PlatformStyle`] for the current platform. /// Returns the [`PlatformStyle`] for the current platform.
pub const fn platform() -> Self { pub const fn platform() -> Self {
if cfg!(target_os = "linux") { if cfg!(any(target_os = "linux", target_os = "freebsd")) {
Self::Linux Self::Linux
} else if cfg!(target_os = "windows") { } else if cfg!(target_os = "windows") {
Self::Windows Self::Windows

View file

@ -57,7 +57,7 @@ impl<T: AsRef<Path>> PathExt for T {
/// does not have the user's home directory prefix, or if we are not on /// does not have the user's home directory prefix, or if we are not on
/// Linux or macOS, the original path is returned unchanged. /// Linux or macOS, the original path is returned unchanged.
fn compact(&self) -> PathBuf { fn compact(&self) -> PathBuf {
if cfg!(target_os = "linux") || cfg!(target_os = "macos") { if cfg!(any(target_os = "linux", target_os = "freebsd")) || cfg!(target_os = "macos") {
match self.as_ref().strip_prefix(home_dir().as_path()) { match self.as_ref().strip_prefix(home_dir().as_path()) {
Ok(relative_path) => { Ok(relative_path) => {
let mut shortened_path = PathBuf::new(); let mut shortened_path = PathBuf::new();
@ -699,7 +699,7 @@ mod tests {
] ]
.iter() .iter()
.collect(); .collect();
if cfg!(target_os = "linux") || cfg!(target_os = "macos") { if cfg!(any(target_os = "linux", target_os = "freebsd")) || cfg!(target_os = "macos") {
assert_eq!(path.compact().to_str(), Some("~/some_file.txt")); assert_eq!(path.compact().to_str(), Some("~/some_file.txt"));
} else { } else {
assert_eq!(path.compact().to_str(), path.to_str()); assert_eq!(path.compact().to_str(), path.to_str());

View file

@ -135,7 +135,7 @@ mod test {
} }
#[gpui::test] #[gpui::test]
#[cfg(not(target_os = "linux"))] #[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
async fn test_replace_mode(cx: &mut gpui::TestAppContext) { async fn test_replace_mode(cx: &mut gpui::TestAppContext) {
let mut cx: NeovimBackedTestContext = NeovimBackedTestContext::new(cx).await; let mut cx: NeovimBackedTestContext = NeovimBackedTestContext::new(cx).await;

View file

@ -228,9 +228,9 @@ impl VimGlobals {
} }
'*' => { '*' => {
self.registers.insert('"', content.clone()); self.registers.insert('"', content.clone());
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
cx.write_to_primary(content.into()); cx.write_to_primary(content.into());
#[cfg(not(target_os = "linux"))] #[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
cx.write_to_clipboard(content.into()); cx.write_to_clipboard(content.into());
} }
'"' => { '"' => {
@ -299,11 +299,11 @@ impl VimGlobals {
'_' | ':' | '.' | '#' | '=' => None, '_' | ':' | '.' | '#' | '=' => None,
'+' => cx.read_from_clipboard().map(|item| item.into()), '+' => cx.read_from_clipboard().map(|item| item.into()),
'*' => { '*' => {
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
{ {
cx.read_from_primary().map(|item| item.into()) cx.read_from_primary().map(|item| item.into())
} }
#[cfg(not(target_os = "linux"))] #[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
{ {
cx.read_from_clipboard().map(|item| item.into()) cx.read_from_clipboard().map(|item| item.into())
} }

View file

@ -842,7 +842,7 @@ async fn test_write_file(cx: &mut TestAppContext) {
.await .await
.unwrap(); .unwrap();
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
fs::linux_watcher::global(|_| {}).unwrap(); fs::linux_watcher::global(|_| {}).unwrap();
cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete()) cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())

View file

@ -126,7 +126,7 @@ windows.workspace = true
[target.'cfg(target_os = "windows")'.build-dependencies] [target.'cfg(target_os = "windows")'.build-dependencies]
winresource = "0.1" winresource = "0.1"
[target.'cfg(target_os = "linux")'.dependencies] [target.'cfg(any(target_os = "linux", target_os = "freebsd"))'.dependencies]
ashpd.workspace = true ashpd.workspace = true
[dev-dependencies] [dev-dependencies]

View file

@ -100,12 +100,12 @@ fn fail_to_open_window(e: anyhow::Error, _cx: &mut AppContext) {
eprintln!( eprintln!(
"Zed failed to open a window: {e:?}. See https://zed.dev/docs/linux for troubleshooting steps." "Zed failed to open a window: {e:?}. See https://zed.dev/docs/linux for troubleshooting steps."
); );
#[cfg(not(target_os = "linux"))] #[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
{ {
process::exit(1); process::exit(1);
} }
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
{ {
use ashpd::desktop::notification::{Notification, NotificationProxy, Priority}; use ashpd::desktop::notification::{Notification, NotificationProxy, Priority};
_cx.spawn(|_cx| async move { _cx.spawn(|_cx| async move {
@ -172,7 +172,7 @@ fn main() {
if *db::ZED_STATELESS || *release_channel::RELEASE_CHANNEL == ReleaseChannel::Dev { if *db::ZED_STATELESS || *release_channel::RELEASE_CHANNEL == ReleaseChannel::Dev {
false false
} else { } else {
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
{ {
crate::zed::listen_for_cli_connections(open_listener.clone()).is_err() crate::zed::listen_for_cli_connections(open_listener.clone()).is_err()
} }
@ -426,7 +426,7 @@ fn main() {
load_embedded_fonts(cx); load_embedded_fonts(cx);
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
crate::zed::linux_prompts::init(cx); crate::zed::linux_prompts::init(cx);
app_state.languages.set_theme(cx.theme().clone()); app_state.languages.set_theme(cx.theme().clone());
@ -942,7 +942,7 @@ fn init_logger() {
config_builder.set_time_offset(offset); config_builder.set_time_offset(offset);
} }
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
{ {
config_builder.add_filter_ignore_str("zbus"); config_builder.add_filter_ignore_str("zbus");
config_builder.add_filter_ignore_str("blade_graphics::hal::resource"); config_builder.add_filter_ignore_str("blade_graphics::hal::resource");

View file

@ -1,6 +1,6 @@
mod app_menus; mod app_menus;
pub mod inline_completion_registry; pub mod inline_completion_registry;
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
pub(crate) mod linux_prompts; pub(crate) mod linux_prompts;
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
pub(crate) mod mac_only_instance; pub(crate) mod mac_only_instance;
@ -153,7 +153,7 @@ pub fn initialize_workspace(
}) })
.detach(); .detach();
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
if let Err(e) = fs::linux_watcher::global(|_| {}) { if let Err(e) = fs::linux_watcher::global(|_| {}) {
let message = format!(db::indoc!{r#" let message = format!(db::indoc!{r#"
inotify_init returned {} inotify_init returned {}
@ -361,7 +361,7 @@ pub fn initialize_workspace(
}) })
.register_action(|_, _: &install_cli::Install, cx| { .register_action(|_, _: &install_cli::Install, cx| {
cx.spawn(|workspace, mut cx| async move { cx.spawn(|workspace, mut cx| async move {
if cfg!(target_os = "linux") { if cfg!(any(target_os = "linux", target_os = "freebsd")) {
let prompt = cx.prompt( let prompt = cx.prompt(
PromptLevel::Warning, PromptLevel::Warning,
"CLI should already be installed", "CLI should already be installed",

View file

@ -144,7 +144,7 @@ impl OpenListener {
} }
} }
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
pub fn listen_for_cli_connections(opener: OpenListener) -> Result<()> { pub fn listen_for_cli_connections(opener: OpenListener) -> Result<()> {
use release_channel::RELEASE_CHANNEL_NAME; use release_channel::RELEASE_CHANNEL_NAME;
use std::os::unix::net::UnixDatagram; use std::os::unix::net::UnixDatagram;