Merge branch 'main' into element-types

This commit is contained in:
Mikayla 2023-11-14 15:49:10 -08:00
commit 7f72df6dcf
No known key found for this signature in database
27 changed files with 3714 additions and 1471 deletions

26
Cargo.lock generated
View file

@ -3061,6 +3061,31 @@ dependencies = [
"workspace", "workspace",
] ]
[[package]]
name = "file_finder2"
version = "0.1.0"
dependencies = [
"collections",
"ctor",
"editor2",
"env_logger 0.9.3",
"fuzzy2",
"gpui2",
"language2",
"menu2",
"picker2",
"postage",
"project2",
"serde",
"serde_json",
"settings2",
"text2",
"theme2",
"ui2",
"util",
"workspace2",
]
[[package]] [[package]]
name = "filetime" name = "filetime"
version = "0.2.22" version = "0.2.22"
@ -11424,6 +11449,7 @@ dependencies = [
"editor2", "editor2",
"env_logger 0.9.3", "env_logger 0.9.3",
"feature_flags2", "feature_flags2",
"file_finder2",
"fs2", "fs2",
"fsevent", "fsevent",
"futures 0.3.28", "futures 0.3.28",

View file

@ -9168,6 +9168,10 @@ impl Editor {
cx.focus(&self.focus_handle) cx.focus(&self.focus_handle)
} }
pub fn is_focused(&self, cx: &WindowContext) -> bool {
self.focus_handle.is_focused(cx)
}
fn handle_focus_in(&mut self, cx: &mut ViewContext<Self>) { fn handle_focus_in(&mut self, cx: &mut ViewContext<Self>) {
if self.focus_handle.is_focused(cx) { if self.focus_handle.is_focused(cx) {
// todo!() // todo!()
@ -9379,8 +9383,8 @@ impl Render for Editor {
EditorMode::SingleLine => { EditorMode::SingleLine => {
TextStyle { TextStyle {
color: cx.theme().colors().text, color: cx.theme().colors().text,
font_family: "Zed Sans".into(), // todo!() font_family: settings.ui_font.family.clone(), // todo!()
font_features: FontFeatures::default(), font_features: settings.ui_font.features,
font_size: rems(0.875).into(), font_size: rems(0.875).into(),
font_weight: FontWeight::NORMAL, font_weight: FontWeight::NORMAL,
font_style: FontStyle::Normal, font_style: FontStyle::Normal,

View file

@ -1448,6 +1448,7 @@ impl EditorElement {
let snapshot = editor.snapshot(cx); let snapshot = editor.snapshot(cx);
let style = self.style.clone(); let style = self.style.clone();
let font_id = cx.text_system().font_id(&style.text.font()).unwrap(); let font_id = cx.text_system().font_id(&style.text.font()).unwrap();
let font_size = style.text.font_size.to_pixels(cx.rem_size()); let font_size = style.text.font_size.to_pixels(cx.rem_size());
let line_height = style.text.line_height_in_pixels(cx.rem_size()); let line_height = style.text.line_height_in_pixels(cx.rem_size());

View file

@ -0,0 +1,37 @@
[package]
name = "file_finder2"
version = "0.1.0"
edition = "2021"
publish = false
[lib]
path = "src/file_finder.rs"
doctest = false
[dependencies]
editor = { package = "editor2", path = "../editor2" }
collections = { path = "../collections" }
fuzzy = { package = "fuzzy2", path = "../fuzzy2" }
gpui = { package = "gpui2", path = "../gpui2" }
menu = { package = "menu2", path = "../menu2" }
picker = { package = "picker2", path = "../picker2" }
project = { package = "project2", path = "../project2" }
settings = { package = "settings2", path = "../settings2" }
text = { package = "text2", path = "../text2" }
util = { path = "../util" }
theme = { package = "theme2", path = "../theme2" }
ui = { package = "ui2", path = "../ui2" }
workspace = { package = "workspace2", path = "../workspace2" }
postage.workspace = true
serde.workspace = true
[dev-dependencies]
editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
language = { package = "language2", path = "../language2", features = ["test-support"] }
workspace = { package = "workspace2", path = "../workspace2", features = ["test-support"] }
theme = { package = "theme2", path = "../theme2", features = ["test-support"] }
serde_json.workspace = true
ctor.workspace = true
env_logger.workspace = true

File diff suppressed because it is too large Load diff

View file

@ -234,10 +234,10 @@ impl AppContext {
app_version: platform.app_version().ok(), app_version: platform.app_version().ok(),
}; };
Rc::new_cyclic(|this| AppCell { let app = Rc::new_cyclic(|this| AppCell {
app: RefCell::new(AppContext { app: RefCell::new(AppContext {
this: this.clone(), this: this.clone(),
platform, platform: platform.clone(),
app_metadata, app_metadata,
text_system, text_system,
flushing_effects: false, flushing_effects: false,
@ -269,12 +269,21 @@ impl AppContext {
layout_id_buffer: Default::default(), layout_id_buffer: Default::default(),
propagate_event: true, propagate_event: true,
}), }),
}) });
platform.on_quit(Box::new({
let cx = app.clone();
move || {
cx.borrow_mut().shutdown();
}
}));
app
} }
/// Quit the application gracefully. Handlers registered with `ModelContext::on_app_quit` /// Quit the application gracefully. Handlers registered with `ModelContext::on_app_quit`
/// will be given 100ms to complete before exiting. /// will be given 100ms to complete before exiting.
pub fn quit(&mut self) { pub fn shutdown(&mut self) {
let mut futures = Vec::new(); let mut futures = Vec::new();
for observer in self.quit_observers.remove(&()) { for observer in self.quit_observers.remove(&()) {
@ -292,8 +301,10 @@ impl AppContext {
{ {
log::error!("timed out waiting on app_will_quit"); log::error!("timed out waiting on app_will_quit");
} }
}
self.globals_by_type.clear(); pub fn quit(&mut self) {
self.platform.quit();
} }
pub fn app_metadata(&self) -> AppMetadata { pub fn app_metadata(&self) -> AppMetadata {

View file

@ -26,7 +26,7 @@ impl EntityId {
impl Display for EntityId { impl Display for EntityId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self) write!(f, "{}", self.as_u64())
} }
} }

View file

@ -1,8 +1,8 @@
use crate::{ use crate::{
div, AnyView, AnyWindowHandle, AppCell, AppContext, AsyncAppContext, BackgroundExecutor, div, Action, AnyView, AnyWindowHandle, AppCell, AppContext, AsyncAppContext,
Context, Div, EventEmitter, ForegroundExecutor, InputEvent, KeyDownEvent, Keystroke, Model, BackgroundExecutor, Context, Div, EventEmitter, ForegroundExecutor, InputEvent, KeyDownEvent,
ModelContext, Render, Result, Task, TestDispatcher, TestPlatform, View, ViewContext, Keystroke, Model, ModelContext, Render, Result, Task, TestDispatcher, TestPlatform, View,
VisualContext, WindowContext, WindowHandle, WindowOptions, ViewContext, VisualContext, WindowContext, WindowHandle, WindowOptions,
}; };
use anyhow::{anyhow, bail}; use anyhow::{anyhow, bail};
use futures::{Stream, StreamExt}; use futures::{Stream, StreamExt};
@ -14,6 +14,7 @@ pub struct TestAppContext {
pub background_executor: BackgroundExecutor, pub background_executor: BackgroundExecutor,
pub foreground_executor: ForegroundExecutor, pub foreground_executor: ForegroundExecutor,
pub dispatcher: TestDispatcher, pub dispatcher: TestDispatcher,
pub test_platform: Rc<TestPlatform>,
} }
impl Context for TestAppContext { impl Context for TestAppContext {
@ -77,17 +78,16 @@ impl TestAppContext {
let arc_dispatcher = Arc::new(dispatcher.clone()); let arc_dispatcher = Arc::new(dispatcher.clone());
let background_executor = BackgroundExecutor::new(arc_dispatcher.clone()); let background_executor = BackgroundExecutor::new(arc_dispatcher.clone());
let foreground_executor = ForegroundExecutor::new(arc_dispatcher); let foreground_executor = ForegroundExecutor::new(arc_dispatcher);
let platform = Rc::new(TestPlatform::new( let platform = TestPlatform::new(background_executor.clone(), foreground_executor.clone());
background_executor.clone(),
foreground_executor.clone(),
));
let asset_source = Arc::new(()); let asset_source = Arc::new(());
let http_client = util::http::FakeHttpClient::with_404_response(); let http_client = util::http::FakeHttpClient::with_404_response();
Self { Self {
app: AppContext::new(platform, asset_source, http_client), app: AppContext::new(platform.clone(), asset_source, http_client),
background_executor, background_executor,
foreground_executor, foreground_executor,
dispatcher: dispatcher.clone(), dispatcher: dispatcher.clone(),
test_platform: platform,
} }
} }
@ -96,7 +96,7 @@ impl TestAppContext {
} }
pub fn quit(&self) { pub fn quit(&self) {
self.app.borrow_mut().quit(); self.app.borrow_mut().shutdown();
} }
pub fn refresh(&mut self) -> Result<()> { pub fn refresh(&mut self) -> Result<()> {
@ -152,6 +152,21 @@ impl TestAppContext {
(view, VisualTestContext::from_window(*window.deref(), self)) (view, VisualTestContext::from_window(*window.deref(), self))
} }
pub fn simulate_new_path_selection(
&self,
select_path: impl FnOnce(&std::path::Path) -> Option<std::path::PathBuf>,
) {
self.test_platform.simulate_new_path_selection(select_path);
}
pub fn simulate_prompt_answer(&self, button_ix: usize) {
self.test_platform.simulate_prompt_answer(button_ix);
}
pub fn has_pending_prompt(&self) -> bool {
self.test_platform.has_pending_prompt()
}
pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut) -> Task<R> pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut) -> Task<R>
where where
Fut: Future<Output = R> + 'static, Fut: Future<Output = R> + 'static,
@ -199,6 +214,15 @@ impl TestAppContext {
} }
} }
pub fn dispatch_action<A>(&mut self, window: AnyWindowHandle, action: A)
where
A: Action,
{
window
.update(self, |_, cx| cx.dispatch_action(action.boxed_clone()))
.unwrap()
}
pub fn dispatch_keystroke( pub fn dispatch_keystroke(
&mut self, &mut self,
window: AnyWindowHandle, window: AnyWindowHandle,
@ -376,6 +400,13 @@ impl<'a> VisualTestContext<'a> {
pub fn from_window(window: AnyWindowHandle, cx: &'a mut TestAppContext) -> Self { pub fn from_window(window: AnyWindowHandle, cx: &'a mut TestAppContext) -> Self {
Self { cx, window } Self { cx, window }
} }
pub fn dispatch_action<A>(&mut self, action: A)
where
A: Action,
{
self.cx.dispatch_action(self.window, action)
}
} }
impl<'a> Context for VisualTestContext<'a> { impl<'a> Context for VisualTestContext<'a> {

View file

@ -14,7 +14,7 @@ use taffy::style::Overflow;
pub fn uniform_list<I, V, C>( pub fn uniform_list<I, V, C>(
id: I, id: I,
item_count: usize, item_count: usize,
f: impl 'static + Fn(&mut V, Range<usize>, &mut ViewContext<V>) -> SmallVec<[C; 64]>, f: impl 'static + Fn(&mut V, Range<usize>, &mut ViewContext<V>) -> Vec<C>,
) -> UniformList<V> ) -> UniformList<V>
where where
I: Into<ElementId>, I: Into<ElementId>,

View file

@ -3,8 +3,15 @@ use crate::{
PlatformDisplay, PlatformTextSystem, TestDisplay, TestWindow, WindowOptions, PlatformDisplay, PlatformTextSystem, TestDisplay, TestWindow, WindowOptions,
}; };
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use collections::VecDeque;
use futures::channel::oneshot;
use parking_lot::Mutex; use parking_lot::Mutex;
use std::{rc::Rc, sync::Arc}; use std::{
cell::RefCell,
path::PathBuf,
rc::{Rc, Weak},
sync::Arc,
};
pub struct TestPlatform { pub struct TestPlatform {
background_executor: BackgroundExecutor, background_executor: BackgroundExecutor,
@ -13,18 +20,60 @@ pub struct TestPlatform {
active_window: Arc<Mutex<Option<AnyWindowHandle>>>, active_window: Arc<Mutex<Option<AnyWindowHandle>>>,
active_display: Rc<dyn PlatformDisplay>, active_display: Rc<dyn PlatformDisplay>,
active_cursor: Mutex<CursorStyle>, active_cursor: Mutex<CursorStyle>,
pub(crate) prompts: RefCell<TestPrompts>,
weak: Weak<Self>,
}
#[derive(Default)]
pub(crate) struct TestPrompts {
multiple_choice: VecDeque<oneshot::Sender<usize>>,
new_path: VecDeque<(PathBuf, oneshot::Sender<Option<PathBuf>>)>,
} }
impl TestPlatform { impl TestPlatform {
pub fn new(executor: BackgroundExecutor, foreground_executor: ForegroundExecutor) -> Self { pub fn new(executor: BackgroundExecutor, foreground_executor: ForegroundExecutor) -> Rc<Self> {
TestPlatform { Rc::new_cyclic(|weak| TestPlatform {
background_executor: executor, background_executor: executor,
foreground_executor, foreground_executor,
prompts: Default::default(),
active_cursor: Default::default(), active_cursor: Default::default(),
active_display: Rc::new(TestDisplay::new()), active_display: Rc::new(TestDisplay::new()),
active_window: Default::default(), active_window: Default::default(),
} weak: weak.clone(),
})
}
pub(crate) fn simulate_new_path_selection(
&self,
select_path: impl FnOnce(&std::path::Path) -> Option<std::path::PathBuf>,
) {
let (path, tx) = self
.prompts
.borrow_mut()
.new_path
.pop_front()
.expect("no pending new path prompt");
tx.send(select_path(&path)).ok();
}
pub(crate) fn simulate_prompt_answer(&self, response_ix: usize) {
let tx = self
.prompts
.borrow_mut()
.multiple_choice
.pop_front()
.expect("no pending multiple choice prompt");
tx.send(response_ix).ok();
}
pub(crate) fn has_pending_prompt(&self) -> bool {
!self.prompts.borrow().multiple_choice.is_empty()
}
pub(crate) fn prompt(&self) -> oneshot::Receiver<usize> {
let (tx, rx) = oneshot::channel();
self.prompts.borrow_mut().multiple_choice.push_back(tx);
rx
} }
} }
@ -46,9 +95,7 @@ impl Platform for TestPlatform {
unimplemented!() unimplemented!()
} }
fn quit(&self) { fn quit(&self) {}
unimplemented!()
}
fn restart(&self) { fn restart(&self) {
unimplemented!() unimplemented!()
@ -88,7 +135,11 @@ impl Platform for TestPlatform {
options: WindowOptions, options: WindowOptions,
) -> Box<dyn crate::PlatformWindow> { ) -> Box<dyn crate::PlatformWindow> {
*self.active_window.lock() = Some(handle); *self.active_window.lock() = Some(handle);
Box::new(TestWindow::new(options, self.active_display.clone())) Box::new(TestWindow::new(
options,
self.weak.clone(),
self.active_display.clone(),
))
} }
fn set_display_link_output_callback( fn set_display_link_output_callback(
@ -118,15 +169,20 @@ impl Platform for TestPlatform {
fn prompt_for_paths( fn prompt_for_paths(
&self, &self,
_options: crate::PathPromptOptions, _options: crate::PathPromptOptions,
) -> futures::channel::oneshot::Receiver<Option<Vec<std::path::PathBuf>>> { ) -> oneshot::Receiver<Option<Vec<std::path::PathBuf>>> {
unimplemented!() unimplemented!()
} }
fn prompt_for_new_path( fn prompt_for_new_path(
&self, &self,
_directory: &std::path::Path, directory: &std::path::Path,
) -> futures::channel::oneshot::Receiver<Option<std::path::PathBuf>> { ) -> oneshot::Receiver<Option<std::path::PathBuf>> {
unimplemented!() let (tx, rx) = oneshot::channel();
self.prompts
.borrow_mut()
.new_path
.push_back((directory.to_path_buf(), tx));
rx
} }
fn reveal_path(&self, _path: &std::path::Path) { fn reveal_path(&self, _path: &std::path::Path) {
@ -141,9 +197,7 @@ impl Platform for TestPlatform {
unimplemented!() unimplemented!()
} }
fn on_quit(&self, _callback: Box<dyn FnMut()>) { fn on_quit(&self, _callback: Box<dyn FnMut()>) {}
unimplemented!()
}
fn on_reopen(&self, _callback: Box<dyn FnMut()>) { fn on_reopen(&self, _callback: Box<dyn FnMut()>) {
unimplemented!() unimplemented!()

View file

@ -1,15 +1,13 @@
use std::{
rc::Rc,
sync::{self, Arc},
};
use collections::HashMap;
use parking_lot::Mutex;
use crate::{ use crate::{
px, AtlasKey, AtlasTextureId, AtlasTile, Pixels, PlatformAtlas, PlatformDisplay, px, AtlasKey, AtlasTextureId, AtlasTile, Pixels, PlatformAtlas, PlatformDisplay,
PlatformInputHandler, PlatformWindow, Point, Scene, Size, TileId, WindowAppearance, PlatformInputHandler, PlatformWindow, Point, Scene, Size, TestPlatform, TileId,
WindowBounds, WindowOptions, WindowAppearance, WindowBounds, WindowOptions,
};
use collections::HashMap;
use parking_lot::Mutex;
use std::{
rc::{Rc, Weak},
sync::{self, Arc},
}; };
#[derive(Default)] #[derive(Default)]
@ -25,16 +23,22 @@ pub struct TestWindow {
current_scene: Mutex<Option<Scene>>, current_scene: Mutex<Option<Scene>>,
display: Rc<dyn PlatformDisplay>, display: Rc<dyn PlatformDisplay>,
input_handler: Option<Box<dyn PlatformInputHandler>>, input_handler: Option<Box<dyn PlatformInputHandler>>,
handlers: Mutex<Handlers>, handlers: Mutex<Handlers>,
platform: Weak<TestPlatform>,
sprite_atlas: Arc<dyn PlatformAtlas>, sprite_atlas: Arc<dyn PlatformAtlas>,
} }
impl TestWindow { impl TestWindow {
pub fn new(options: WindowOptions, display: Rc<dyn PlatformDisplay>) -> Self { pub fn new(
options: WindowOptions,
platform: Weak<TestPlatform>,
display: Rc<dyn PlatformDisplay>,
) -> Self {
Self { Self {
bounds: options.bounds, bounds: options.bounds,
current_scene: Default::default(), current_scene: Default::default(),
display, display,
platform,
input_handler: None, input_handler: None,
sprite_atlas: Arc::new(TestAtlas::new()), sprite_atlas: Arc::new(TestAtlas::new()),
handlers: Default::default(), handlers: Default::default(),
@ -89,7 +93,7 @@ impl PlatformWindow for TestWindow {
_msg: &str, _msg: &str,
_answers: &[&str], _answers: &[&str],
) -> futures::channel::oneshot::Receiver<usize> { ) -> futures::channel::oneshot::Receiver<usize> {
todo!() self.platform.upgrade().expect("platform dropped").prompt()
} }
fn activate(&self) { fn activate(&self) {

View file

@ -167,7 +167,7 @@ fn main() {
panic!("unexpected message"); panic!("unexpected message");
} }
cx.update(|cx| cx.quit()).ok(); cx.update(|cx| cx.shutdown()).ok();
}) })
.detach(); .detach();
}); });

View file

@ -2,7 +2,7 @@ use anyhow::{anyhow, bail, Context, Result};
use async_compression::futures::bufread::GzipDecoder; use async_compression::futures::bufread::GzipDecoder;
use async_tar::Archive; use async_tar::Archive;
use serde::Deserialize; use serde::Deserialize;
use smol::{fs, io::BufReader, process::Command}; use smol::{fs, io::BufReader, lock::Mutex, process::Command};
use std::process::{Output, Stdio}; use std::process::{Output, Stdio};
use std::{ use std::{
env::consts, env::consts,
@ -45,14 +45,19 @@ pub trait NodeRuntime: Send + Sync {
pub struct RealNodeRuntime { pub struct RealNodeRuntime {
http: Arc<dyn HttpClient>, http: Arc<dyn HttpClient>,
installation_lock: Mutex<()>,
} }
impl RealNodeRuntime { impl RealNodeRuntime {
pub fn new(http: Arc<dyn HttpClient>) -> Arc<dyn NodeRuntime> { pub fn new(http: Arc<dyn HttpClient>) -> Arc<dyn NodeRuntime> {
Arc::new(RealNodeRuntime { http }) Arc::new(RealNodeRuntime {
http,
installation_lock: Mutex::new(()),
})
} }
async fn install_if_needed(&self) -> Result<PathBuf> { async fn install_if_needed(&self) -> Result<PathBuf> {
let _lock = self.installation_lock.lock().await;
log::info!("Node runtime install_if_needed"); log::info!("Node runtime install_if_needed");
let arch = match consts::ARCH { let arch = match consts::ARCH {
@ -73,6 +78,9 @@ impl RealNodeRuntime {
.stdin(Stdio::null()) .stdin(Stdio::null())
.stdout(Stdio::null()) .stdout(Stdio::null())
.stderr(Stdio::null()) .stderr(Stdio::null())
.args(["--cache".into(), node_dir.join("cache")])
.args(["--userconfig".into(), node_dir.join("blank_user_npmrc")])
.args(["--globalconfig".into(), node_dir.join("blank_global_npmrc")])
.status() .status()
.await; .await;
let valid = matches!(result, Ok(status) if status.success()); let valid = matches!(result, Ok(status) if status.success());
@ -96,6 +104,11 @@ impl RealNodeRuntime {
archive.unpack(&node_containing_dir).await?; archive.unpack(&node_containing_dir).await?;
} }
// Note: Not in the `if !valid {}` so we can populate these for existing installations
_ = fs::create_dir(node_dir.join("cache")).await;
_ = fs::write(node_dir.join("blank_user_npmrc"), []).await;
_ = fs::write(node_dir.join("blank_global_npmrc"), []).await;
anyhow::Ok(node_dir) anyhow::Ok(node_dir)
} }
} }
@ -137,7 +150,17 @@ impl NodeRuntime for RealNodeRuntime {
let mut command = Command::new(node_binary); let mut command = Command::new(node_binary);
command.env("PATH", env_path); command.env("PATH", env_path);
command.arg(npm_file).arg(subcommand).args(args); command.arg(npm_file).arg(subcommand);
command.args(["--cache".into(), installation_path.join("cache")]);
command.args([
"--userconfig".into(),
installation_path.join("blank_user_npmrc"),
]);
command.args([
"--globalconfig".into(),
installation_path.join("blank_global_npmrc"),
]);
command.args(args);
if let Some(directory) = directory { if let Some(directory) = directory {
command.current_dir(directory); command.current_dir(directory);

View file

@ -58,7 +58,7 @@ impl<D: PickerDelegate> Picker<D> {
self.editor.update(cx, |editor, cx| editor.focus(cx)); self.editor.update(cx, |editor, cx| editor.focus(cx));
} }
fn select_next(&mut self, _: &menu::SelectNext, cx: &mut ViewContext<Self>) { pub fn select_next(&mut self, _: &menu::SelectNext, cx: &mut ViewContext<Self>) {
let count = self.delegate.match_count(); let count = self.delegate.match_count();
if count > 0 { if count > 0 {
let index = self.delegate.selected_index(); let index = self.delegate.selected_index();
@ -98,6 +98,15 @@ impl<D: PickerDelegate> Picker<D> {
} }
} }
pub fn cycle_selection(&mut self, cx: &mut ViewContext<Self>) {
let count = self.delegate.match_count();
let index = self.delegate.selected_index();
let new_index = if index + 1 == count { 0 } else { index + 1 };
self.delegate.set_selected_index(new_index, cx);
self.scroll_handle.scroll_to_item(new_index);
cx.notify();
}
fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) { fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
self.delegate.dismissed(cx); self.delegate.dismissed(cx);
} }
@ -137,6 +146,11 @@ impl<D: PickerDelegate> Picker<D> {
} }
} }
pub fn refresh(&mut self, cx: &mut ViewContext<Self>) {
let query = self.editor.read(cx).text(cx);
self.update_matches(query, cx);
}
pub fn update_matches(&mut self, query: String, cx: &mut ViewContext<Self>) { pub fn update_matches(&mut self, query: String, cx: &mut ViewContext<Self>) {
let update = self.delegate.update_matches(query, cx); let update = self.delegate.update_matches(query, cx);
self.matches_updated(cx); self.matches_updated(cx);

File diff suppressed because it is too large Load diff

View file

@ -35,6 +35,7 @@ pub(crate) fn one_dark() -> Theme {
id: "one_dark".to_string(), id: "one_dark".to_string(),
name: "One Dark".into(), name: "One Dark".into(),
appearance: Appearance::Dark, appearance: Appearance::Dark,
styles: ThemeStyles { styles: ThemeStyles {
system: SystemColors::default(), system: SystemColors::default(),
colors: ThemeColors { colors: ThemeColors {

View file

@ -19,6 +19,7 @@ const MIN_LINE_HEIGHT: f32 = 1.0;
#[derive(Clone)] #[derive(Clone)]
pub struct ThemeSettings { pub struct ThemeSettings {
pub ui_font_size: Pixels, pub ui_font_size: Pixels,
pub ui_font: Font,
pub buffer_font: Font, pub buffer_font: Font,
pub buffer_font_size: Pixels, pub buffer_font_size: Pixels,
pub buffer_line_height: BufferLineHeight, pub buffer_line_height: BufferLineHeight,
@ -120,6 +121,12 @@ impl settings::Settings for ThemeSettings {
let mut this = Self { let mut this = Self {
ui_font_size: defaults.ui_font_size.unwrap_or(16.).into(), ui_font_size: defaults.ui_font_size.unwrap_or(16.).into(),
ui_font: Font {
family: "Helvetica".into(),
features: Default::default(),
weight: Default::default(),
style: Default::default(),
},
buffer_font: Font { buffer_font: Font {
family: defaults.buffer_font_family.clone().unwrap().into(), family: defaults.buffer_font_family.clone().unwrap().into(),
features: defaults.buffer_font_features.clone().unwrap(), features: defaults.buffer_font_features.clone().unwrap(),

View file

@ -129,7 +129,7 @@ impl Icon {
#[derive(Component)] #[derive(Component)]
pub struct IconElement { pub struct IconElement {
icon: Icon, path: SharedString,
color: TextColor, color: TextColor,
size: IconSize, size: IconSize,
} }
@ -137,7 +137,15 @@ pub struct IconElement {
impl IconElement { impl IconElement {
pub fn new(icon: Icon) -> Self { pub fn new(icon: Icon) -> Self {
Self { Self {
icon, path: icon.path().into(),
color: TextColor::default(),
size: IconSize::default(),
}
}
pub fn from_path(path: impl Into<SharedString>) -> Self {
Self {
path: path.into(),
color: TextColor::default(), color: TextColor::default(),
size: IconSize::default(), size: IconSize::default(),
} }
@ -162,7 +170,7 @@ impl IconElement {
svg() svg()
.size(svg_size) .size(svg_size)
.flex_none() .flex_none()
.path(self.icon.path()) .path(self.path)
.text_color(self.color.color(cx)) .text_color(self.color.color(cx))
} }
} }

View file

@ -1,5 +1,6 @@
use gpui::{Div, ParentComponent, Render, SharedString, Styled, ViewContext}; use gpui::{Div, Render};
use theme2::ActiveTheme; use settings2::Settings;
use theme2::{ActiveTheme, ThemeSettings};
use crate::prelude::*; use crate::prelude::*;
use crate::{h_stack, v_stack, KeyBinding, Label, LabelSize, StyledExt, TextColor}; use crate::{h_stack, v_stack, KeyBinding, Label, LabelSize, StyledExt, TextColor};
@ -34,9 +35,10 @@ impl Render for TextTooltip {
type Element = Div<Self>; type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element { fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let ui_font = ThemeSettings::get_global(cx).ui_font.family.clone();
v_stack() v_stack()
.elevation_2(cx) .elevation_2(cx)
.font("Zed Sans") .font(ui_font)
.text_ui_sm() .text_ui_sm()
.text_color(cx.theme().colors().text) .text_color(cx.theme().colors().text)
.py_1() .py_1()

View file

@ -206,13 +206,14 @@ impl Render for Workspace {
.child(self.editor_1.clone())], .child(self.editor_1.clone())],
SplitDirection::Horizontal, SplitDirection::Horizontal,
); );
let ui_font = ThemeSettings::get_global(cx).ui_font.family.clone();
div() div()
.relative() .relative()
.size_full() .size_full()
.flex() .flex()
.flex_col() .flex_col()
.font("Zed Sans") .font(ui_font)
.gap_0() .gap_0()
.justify_start() .justify_start()
.items_start() .items_start()

View file

@ -1,7 +1,8 @@
use crate::{status_bar::StatusItemView, Axis, Workspace}; use crate::{status_bar::StatusItemView, Axis, Workspace};
use gpui::{ use gpui::{
div, Action, AnyView, AppContext, Div, Entity, EntityId, EventEmitter, FocusHandle, div, px, Action, AnyView, AppContext, Component, Div, Entity, EntityId, EventEmitter,
ParentComponent, Render, Styled, Subscription, View, ViewContext, WeakView, WindowContext, FocusHandle, ParentComponent, Render, Styled, Subscription, View, ViewContext, WeakView,
WindowContext,
}; };
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -429,7 +430,14 @@ impl Render for Dock {
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element { fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
if let Some(entry) = self.visible_entry() { if let Some(entry) = self.visible_entry() {
div().size_full().child(entry.panel.to_any()) let size = entry.panel.size(cx);
div()
.map(|this| match self.position().axis() {
Axis::Horizontal => this.w(px(size)).h_full(),
Axis::Vertical => this.h(px(size)).w_full(),
})
.child(entry.panel.to_any())
} else { } else {
div() div()
} }

View file

@ -71,6 +71,14 @@ impl ModalLayer {
cx.notify(); cx.notify();
} }
pub fn current_modal<V>(&self) -> Option<View<V>>
where
V: 'static,
{
let active_modal = self.active_modal.as_ref()?;
active_modal.modal.clone().downcast::<V>().ok()
}
} }
impl Render for ModalLayer { impl Render for ModalLayer {

View file

@ -2,7 +2,7 @@ use super::DraggedItem;
use crate::{Pane, SplitDirection, Workspace}; use crate::{Pane, SplitDirection, Workspace};
use gpui::{ use gpui::{
color::Color, color::Color,
elements::{Canvas, MouseEventHandler, ParentElement, Stack}, elements::{Canvas, MouseEventHandler, ParentComponent, Stack},
geometry::{rect::RectF, vector::Vector2F}, geometry::{rect::RectF, vector::Vector2F},
platform::MouseButton, platform::MouseButton,
scene::MouseUp, scene::MouseUp,

View file

@ -66,9 +66,10 @@ use std::{
sync::{atomic::AtomicUsize, Arc}, sync::{atomic::AtomicUsize, Arc},
time::Duration, time::Duration,
}; };
use theme2::ActiveTheme; use theme2::{ActiveTheme, ThemeSettings};
pub use toolbar::{ToolbarItemLocation, ToolbarItemView}; pub use toolbar::{ToolbarItemLocation, ToolbarItemView};
use ui::{h_stack, Button, ButtonVariant, KeyBinding, Label, TextColor, TextTooltip}; use ui::TextColor;
use ui::{h_stack, Button, ButtonVariant, KeyBinding, Label, TextTooltip};
use util::ResultExt; use util::ResultExt;
use uuid::Uuid; use uuid::Uuid;
pub use workspace_settings::{AutosaveSetting, WorkspaceSettings}; pub use workspace_settings::{AutosaveSetting, WorkspaceSettings};
@ -1765,50 +1766,50 @@ impl Workspace {
}) })
} }
// pub fn open_abs_path( pub fn open_abs_path(
// &mut self, &mut self,
// abs_path: PathBuf, abs_path: PathBuf,
// visible: bool, visible: bool,
// cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
// ) -> Task<anyhow::Result<Box<dyn ItemHandle>>> { ) -> Task<anyhow::Result<Box<dyn ItemHandle>>> {
// cx.spawn(|workspace, mut cx| async move { cx.spawn(|workspace, mut cx| async move {
// let open_paths_task_result = workspace let open_paths_task_result = workspace
// .update(&mut cx, |workspace, cx| { .update(&mut cx, |workspace, cx| {
// workspace.open_paths(vec![abs_path.clone()], visible, cx) workspace.open_paths(vec![abs_path.clone()], visible, cx)
// }) })
// .with_context(|| format!("open abs path {abs_path:?} task spawn"))? .with_context(|| format!("open abs path {abs_path:?} task spawn"))?
// .await; .await;
// anyhow::ensure!( anyhow::ensure!(
// open_paths_task_result.len() == 1, open_paths_task_result.len() == 1,
// "open abs path {abs_path:?} task returned incorrect number of results" "open abs path {abs_path:?} task returned incorrect number of results"
// ); );
// match open_paths_task_result match open_paths_task_result
// .into_iter() .into_iter()
// .next() .next()
// .expect("ensured single task result") .expect("ensured single task result")
// { {
// Some(open_result) => { Some(open_result) => {
// open_result.with_context(|| format!("open abs path {abs_path:?} task join")) open_result.with_context(|| format!("open abs path {abs_path:?} task join"))
// } }
// None => anyhow::bail!("open abs path {abs_path:?} task returned None"), None => anyhow::bail!("open abs path {abs_path:?} task returned None"),
// } }
// }) })
// } }
// pub fn split_abs_path( pub fn split_abs_path(
// &mut self, &mut self,
// abs_path: PathBuf, abs_path: PathBuf,
// visible: bool, visible: bool,
// cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
// ) -> Task<anyhow::Result<Box<dyn ItemHandle>>> { ) -> Task<anyhow::Result<Box<dyn ItemHandle>>> {
// let project_path_task = let project_path_task =
// Workspace::project_path_for_path(self.project.clone(), &abs_path, visible, cx); Workspace::project_path_for_path(self.project.clone(), &abs_path, visible, cx);
// cx.spawn(|this, mut cx| async move { cx.spawn(|this, mut cx| async move {
// let (_, path) = project_path_task.await?; let (_, path) = project_path_task.await?;
// this.update(&mut cx, |this, cx| this.split_path(path, cx))? this.update(&mut cx, |this, cx| this.split_path(path, cx))?
// .await .await
// }) })
// } }
pub fn open_path( pub fn open_path(
&mut self, &mut self,
@ -1835,37 +1836,37 @@ impl Workspace {
}) })
} }
// pub fn split_path( pub fn split_path(
// &mut self, &mut self,
// path: impl Into<ProjectPath>, path: impl Into<ProjectPath>,
// cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
// ) -> Task<Result<Box<dyn ItemHandle>, anyhow::Error>> { ) -> Task<Result<Box<dyn ItemHandle>, anyhow::Error>> {
// let pane = self.last_active_center_pane.clone().unwrap_or_else(|| { let pane = self.last_active_center_pane.clone().unwrap_or_else(|| {
// self.panes self.panes
// .first() .first()
// .expect("There must be an active pane") .expect("There must be an active pane")
// .downgrade() .downgrade()
// }); });
// if let Member::Pane(center_pane) = &self.center.root { if let Member::Pane(center_pane) = &self.center.root {
// if center_pane.read(cx).items_len() == 0 { if center_pane.read(cx).items_len() == 0 {
// return self.open_path(path, Some(pane), true, cx); return self.open_path(path, Some(pane), true, cx);
// } }
// } }
// let task = self.load_path(path.into(), cx); let task = self.load_path(path.into(), cx);
// cx.spawn(|this, mut cx| async move { cx.spawn(|this, mut cx| async move {
// let (project_entry_id, build_item) = task.await?; let (project_entry_id, build_item) = task.await?;
// this.update(&mut cx, move |this, cx| -> Option<_> { this.update(&mut cx, move |this, cx| -> Option<_> {
// let pane = pane.upgrade(cx)?; let pane = pane.upgrade()?;
// let new_pane = this.split_pane(pane, SplitDirection::Right, cx); let new_pane = this.split_pane(pane, SplitDirection::Right, cx);
// new_pane.update(cx, |new_pane, cx| { new_pane.update(cx, |new_pane, cx| {
// Some(new_pane.open_item(project_entry_id, true, cx, build_item)) Some(new_pane.open_item(project_entry_id, true, cx, build_item))
// }) })
// }) })
// .map(|option| option.ok_or_else(|| anyhow!("pane was dropped")))? .map(|option| option.ok_or_else(|| anyhow!("pane was dropped")))?
// }) })
// } }
pub(crate) fn load_path( pub(crate) fn load_path(
&mut self, &mut self,
@ -3029,10 +3030,10 @@ impl Workspace {
fn force_remove_pane(&mut self, pane: &View<Pane>, cx: &mut ViewContext<Workspace>) { fn force_remove_pane(&mut self, pane: &View<Pane>, cx: &mut ViewContext<Workspace>) {
self.panes.retain(|p| p != pane); self.panes.retain(|p| p != pane);
if true { self.panes
todo!() .last()
// cx.focus(self.panes.last().unwrap()); .unwrap()
} .update(cx, |pane, cx| pane.focus(cx));
if self.last_active_center_pane == Some(pane.downgrade()) { if self.last_active_center_pane == Some(pane.downgrade()) {
self.last_active_center_pane = None; self.last_active_center_pane = None;
} }
@ -3401,10 +3402,6 @@ impl Workspace {
// }); // });
} }
// todo!()
// #[cfg(any(test, feature = "test-support"))]
// pub fn test_new(project: ModelHandle<Project>, cx: &mut ViewContext<Self>) -> Self {
// use node_runtime::FakeNodeRuntime;
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
pub fn test_new(project: Model<Project>, cx: &mut ViewContext<Self>) -> Self { pub fn test_new(project: Model<Project>, cx: &mut ViewContext<Self>) -> Self {
use node_runtime::FakeNodeRuntime; use node_runtime::FakeNodeRuntime;
@ -3423,7 +3420,9 @@ impl Workspace {
initialize_workspace: |_, _, _, _| Task::ready(Ok(())), initialize_workspace: |_, _, _, _| Task::ready(Ok(())),
node_runtime: FakeNodeRuntime::new(), node_runtime: FakeNodeRuntime::new(),
}); });
Self::new(0, project, app_state, cx) let workspace = Self::new(0, project, app_state, cx);
workspace.active_pane.update(cx, |pane, cx| pane.focus(cx));
workspace
} }
// fn render_dock(&self, position: DockPosition, cx: &WindowContext) -> Option<AnyElement<Self>> { // fn render_dock(&self, position: DockPosition, cx: &WindowContext) -> Option<AnyElement<Self>> {
@ -3476,6 +3475,10 @@ impl Workspace {
div div
} }
pub fn current_modal<V: Modal + 'static>(&mut self, cx: &ViewContext<Self>) -> Option<View<V>> {
self.modal_layer.read(cx).current_modal()
}
pub fn toggle_modal<V: Modal, B>(&mut self, cx: &mut ViewContext<Self>, build: B) pub fn toggle_modal<V: Modal, B>(&mut self, cx: &mut ViewContext<Self>, build: B)
where where
B: FnOnce(&mut ViewContext<V>) -> V, B: FnOnce(&mut ViewContext<V>) -> V,
@ -3699,6 +3702,7 @@ impl Render for Workspace {
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element { fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let mut context = KeyContext::default(); let mut context = KeyContext::default();
context.add("Workspace"); context.add("Workspace");
let ui_font = ThemeSettings::get_global(cx).ui_font.family.clone();
self.add_workspace_actions_listeners(div()) self.add_workspace_actions_listeners(div())
.key_context(context) .key_context(context)
@ -3706,7 +3710,7 @@ impl Render for Workspace {
.size_full() .size_full()
.flex() .flex()
.flex_col() .flex_col()
.font("Zed Sans") .font(ui_font)
.gap_0() .gap_0()
.justify_start() .justify_start()
.items_start() .items_start()
@ -3732,7 +3736,15 @@ impl Render for Workspace {
.flex_row() .flex_row()
.flex_1() .flex_1()
.h_full() .h_full()
.child(div().flex().flex_1().child(self.left_dock.clone())) // Left Dock
.child(
div()
.flex()
.flex_none()
.overflow_hidden()
.child(self.left_dock.clone()),
)
// Panes
.child( .child(
div() div()
.flex() .flex()
@ -3749,7 +3761,14 @@ impl Render for Workspace {
)) ))
.child(div().flex().flex_1().child(self.bottom_dock.clone())), .child(div().flex().flex_1().child(self.bottom_dock.clone())),
) )
.child(div().flex().flex_1().child(self.right_dock.clone())), // Right Dock
.child(
div()
.flex()
.flex_none()
.overflow_hidden()
.child(self.right_dock.clone()),
),
), ),
) )
.child(self.status_bar.clone()) .child(self.status_bar.clone())

View file

@ -36,7 +36,7 @@ copilot = { package = "copilot2", path = "../copilot2" }
db = { package = "db2", path = "../db2" } db = { package = "db2", path = "../db2" }
editor = { package="editor2", path = "../editor2" } editor = { package="editor2", path = "../editor2" }
# feedback = { path = "../feedback" } # feedback = { path = "../feedback" }
# file_finder = { path = "../file_finder" } file_finder = { package="file_finder2", path = "../file_finder2" }
# search = { path = "../search" } # search = { path = "../search" }
fs = { package = "fs2", path = "../fs2" } fs = { package = "fs2", path = "../fs2" }
fsevent = { path = "../fsevent" } fsevent = { path = "../fsevent" }

View file

@ -190,7 +190,7 @@ fn main() {
// recent_projects::init(cx); // recent_projects::init(cx);
go_to_line::init(cx); go_to_line::init(cx);
// file_finder::init(cx); file_finder::init(cx);
// outline::init(cx); // outline::init(cx);
// project_symbols::init(cx); // project_symbols::init(cx);
project_panel::init(Assets, cx); project_panel::init(Assets, cx);

View file

@ -502,7 +502,6 @@ fn quit(_: &mut Workspace, _: &Quit, cx: &mut gpui::ViewContext<Workspace>) {
cx.update(|_, cx| { cx.update(|_, cx| {
cx.quit(); cx.quit();
})?; })?;
anyhow::Ok(()) anyhow::Ok(())
}) })
.detach_and_log_err(cx); .detach_and_log_err(cx);