Compare commits

..

1 Commits

Author SHA1 Message Date
Nate Butler
a6c071c363 WIP: Work on conflict colors
Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-05-15 19:51:25 +02:00
118 changed files with 1053 additions and 2286 deletions

View File

@@ -2,14 +2,16 @@
{
"label": "Debug Zed (CodeLLDB)",
"adapter": "CodeLLDB",
"program": "target/debug/zed",
"request": "launch"
"program": "$ZED_WORKTREE_ROOT/target/debug/zed",
"request": "launch",
"cwd": "$ZED_WORKTREE_ROOT"
},
{
"label": "Debug Zed (GDB)",
"adapter": "GDB",
"program": "target/debug/zed",
"program": "$ZED_WORKTREE_ROOT/target/debug/zed",
"request": "launch",
"cwd": "$ZED_WORKTREE_ROOT",
"initialize_args": {
"stopAtBeginningOfMainSubprogram": true
}

10
Cargo.lock generated
View File

@@ -2792,7 +2792,6 @@ dependencies = [
"anyhow",
"async-recursion 0.3.2",
"async-tungstenite",
"base64 0.22.1",
"chrono",
"clock",
"cocoa 0.26.0",
@@ -2804,7 +2803,6 @@ dependencies = [
"gpui_tokio",
"http_client",
"http_client_tls",
"httparse",
"log",
"parking_lot",
"paths",
@@ -2825,7 +2823,6 @@ dependencies = [
"time",
"tiny_http",
"tokio",
"tokio-native-tls",
"tokio-socks",
"url",
"util",
@@ -5068,7 +5065,6 @@ dependencies = [
"task",
"toml 0.8.20",
"util",
"wasi-preview1-component-adapter-provider",
"wasm-encoder 0.221.3",
"wasmparser 0.221.3",
"wit-component 0.221.3",
@@ -16215,12 +16211,6 @@ dependencies = [
"wit-bindgen-rt 0.39.0",
]
[[package]]
name = "wasi-preview1-component-adapter-provider"
version = "29.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcd9f21bbde82ba59e415a8725e6ad0d0d7e9e460b1a3ccbca5bdee952c1a324"
[[package]]
name = "wasite"
version = "0.1.0"

View File

@@ -594,7 +594,6 @@ url = "2.2"
urlencoding = "2.1.2"
uuid = { version = "1.1.2", features = ["v4", "v5", "v7", "serde"] }
walkdir = "2.3"
wasi-preview1-component-adapter-provider = "29"
wasm-encoder = "0.221"
wasmparser = "0.221"
wasmtime = { version = "29", default-features = false, features = [
@@ -788,9 +787,6 @@ let_underscore_future = "allow"
# running afoul of the borrow checker.
too_many_arguments = "allow"
# We often have large enum variants yet we rarely actually bother with splitting them up.
large_enum_variant = "allow"
[workspace.metadata.cargo-machete]
ignored = [
"bindgen",

View File

@@ -1,6 +1,6 @@
# syntax = docker/dockerfile:1.2
FROM rust:1.87-bookworm as builder
FROM rust:1.86-bookworm as builder
WORKDIR app
COPY . .

View File

@@ -766,7 +766,7 @@
"alt-ctrl-r": "project_panel::RevealInFileManager",
"ctrl-shift-enter": "project_panel::OpenWithSystem",
"shift-find": "project_panel::NewSearchInDirectory",
"ctrl-alt-shift-f": "project_panel::NewSearchInDirectory",
"ctrl-shift-f": "project_panel::NewSearchInDirectory",
"shift-down": "menu::SelectNext",
"shift-up": "menu::SelectPrevious",
"escape": "menu::Cancel"

View File

@@ -825,7 +825,7 @@
"alt-cmd-r": "project_panel::RevealInFileManager",
"ctrl-shift-enter": "project_panel::OpenWithSystem",
"cmd-alt-backspace": ["project_panel::Delete", { "skip_prompt": false }],
"cmd-alt-shift-f": "project_panel::NewSearchInDirectory",
"cmd-shift-f": "project_panel::NewSearchInDirectory",
"shift-down": "menu::SelectNext",
"shift-up": "menu::SelectPrevious",
"escape": "menu::Cancel"

View File

@@ -99,6 +99,9 @@
"version_control.added": "#27a657ff",
"version_control.modified": "#d3b020ff",
"version_control.deleted": "#e06c76ff",
"version_control.conflict_marker.ours": "#a1c1811a",
"version_control.conflict_marker.theirs": "#74ade81a",
"version_control.conflict_marker.border": "#d3b0201f",
"conflict": "#dec184ff",
"conflict.background": "#dec1841a",
"conflict.border": "#5d4c2fff",
@@ -478,6 +481,11 @@
"version_control.added": "#27a657ff",
"version_control.modified": "#d3b020ff",
"version_control.deleted": "#e06c76ff",
"version_control.conflict.ours_background": "#FF0000",
"version_control.conflict.ours_border": "#FF0000",
"version_control.conflict.theirs_background": "#e2e2faff",
"version_control.conflict.theirs_border": "#cbcdf6ff",
"version_control.conflict.divider_background": "#faf2e6ff",
"conflict": "#a48819ff",
"conflict.background": "#faf2e6ff",
"conflict.border": "#f4e7d1ff",

View File

@@ -185,14 +185,12 @@ pub(crate) fn default_markdown_style(window: &Window, cx: &App) -> MarkdownStyle
let ui_font_size = TextSize::Default.rems(cx);
let buffer_font_size = TextSize::Small.rems(cx);
let mut text_style = window.text_style();
let line_height = buffer_font_size * 1.75;
text_style.refine(&TextStyleRefinement {
font_family: Some(theme_settings.ui_font.family.clone()),
font_fallbacks: theme_settings.ui_font.fallbacks.clone(),
font_features: Some(theme_settings.ui_font.features.clone()),
font_size: Some(ui_font_size.into()),
line_height: Some(line_height.into()),
color: Some(cx.theme().colors().text),
..Default::default()
});
@@ -1722,11 +1720,10 @@ impl ActiveThread {
.on_action(cx.listener(Self::confirm_editing_message))
.capture_action(cx.listener(Self::paste))
.min_h_6()
.w_full()
.flex_grow()
.w_full()
.gap_2()
.child(state.context_strip.clone())
.child(div().pt(px(-3.)).px_neg_0p5().child(EditorElement::new(
.child(EditorElement::new(
&state.editor,
EditorStyle {
background: colors.editor_background,
@@ -1735,7 +1732,8 @@ impl ActiveThread {
syntax: cx.theme().syntax().clone(),
..Default::default()
},
)))
))
.child(state.context_strip.clone())
}
fn render_message(&self, ix: usize, window: &mut Window, cx: &mut Context<Self>) -> AnyElement {
@@ -1923,6 +1921,16 @@ impl ActiveThread {
v_flex()
.w_full()
.gap_1()
.when(!message_is_empty, |parent| {
parent.child(div().min_h_6().child(self.render_message_content(
message_id,
rendered_message,
has_tool_uses,
workspace.clone(),
window,
cx,
)))
})
.when(!added_context.is_empty(), |parent| {
parent.child(h_flex().flex_wrap().gap_1().children(
added_context.into_iter().map(|added_context| {
@@ -1941,16 +1949,6 @@ impl ActiveThread {
}),
))
})
.when(!message_is_empty, |parent| {
parent.child(div().pt_0p5().min_h_6().child(self.render_message_content(
message_id,
rendered_message,
has_tool_uses,
workspace.clone(),
window,
cx,
)))
})
.into_any_element()
}
});
@@ -1976,7 +1974,6 @@ impl ActiveThread {
h_flex()
.p_2p5()
.gap_1()
.items_end()
.children(message_content)
.when_some(editing_message_state, |this, state| {
let focus_handle = state.editor.focus_handle(cx).clone();
@@ -1990,7 +1987,6 @@ impl ActiveThread {
)
.shape(ui::IconButtonShape::Square)
.icon_color(Color::Error)
.icon_size(IconSize::Small)
.tooltip({
let focus_handle = focus_handle.clone();
move |window, cx| {
@@ -2008,12 +2004,11 @@ impl ActiveThread {
.child(
IconButton::new(
"confirm-edit-message",
IconName::Return,
IconName::Check,
)
.disabled(state.editor.read(cx).is_empty(cx))
.shape(ui::IconButtonShape::Square)
.icon_color(Color::Muted)
.icon_size(IconSize::Small)
.icon_color(Color::Success)
.tooltip({
let focus_handle = focus_handle.clone();
move |window, cx| {
@@ -2033,6 +2028,9 @@ impl ActiveThread {
)
}),
)
.when(editing_message_state.is_none(), |this| {
this.tooltip(Tooltip::text("Click To Edit"))
})
.on_click(cx.listener({
let message_segments = message.segments.clone();
move |this, _, window, cx| {
@@ -2073,16 +2071,6 @@ impl ActiveThread {
let panel_background = cx.theme().colors().panel_background;
let backdrop = div()
.id("backdrop")
.stop_mouse_events_except_scroll()
.absolute()
.inset_0()
.size_full()
.bg(panel_background)
.opacity(0.8)
.on_click(cx.listener(Self::handle_cancel_click));
v_flex()
.w_full()
.map(|parent| {
@@ -2252,7 +2240,15 @@ impl ActiveThread {
})
.when(after_editing_message, |parent| {
// Backdrop to dim out the whole thread below the editing user message
parent.relative().child(backdrop)
parent.relative().child(
div()
.stop_mouse_events_except_scroll()
.absolute()
.inset_0()
.size_full()
.bg(panel_background)
.opacity(0.8),
)
})
.into_any()
}
@@ -2367,7 +2363,6 @@ impl ActiveThread {
move |el, range, metadata, _, cx| {
let can_expand = metadata.line_count
>= MAX_UNCOLLAPSED_LINES_IN_CODE_BLOCK;
if !can_expand {
return el;
}
@@ -2375,7 +2370,6 @@ impl ActiveThread {
let is_expanded = active_thread
.read(cx)
.is_codeblock_expanded(message_id, range.start);
if is_expanded {
return el;
}
@@ -3398,14 +3392,14 @@ impl ActiveThread {
self.expanded_code_blocks
.get(&(message_id, ix))
.copied()
.unwrap_or(true)
.unwrap_or(false)
}
pub fn toggle_codeblock_expanded(&mut self, message_id: MessageId, ix: usize) {
let is_expanded = self
.expanded_code_blocks
.entry((message_id, ix))
.or_insert(true);
.or_insert(false);
*is_expanded = !*is_expanded;
}
}
@@ -3421,7 +3415,6 @@ impl Render for ActiveThread {
v_flex()
.size_full()
.relative()
.bg(cx.theme().colors().panel_background)
.on_mouse_move(cx.listener(|this, _, _, cx| {
this.show_scrollbar = true;
this.hide_scrollbar_later(cx);

View File

@@ -30,6 +30,7 @@ pub(crate) struct ConfigureContextServerModal {
context_server_store: Entity<ContextServerStore>,
}
#[allow(clippy::large_enum_variant)]
enum Configuration {
NotAvailable,
Required(ConfigurationRequiredState),

View File

@@ -2135,7 +2135,6 @@ impl AgentPanel {
v_flex()
.size_full()
.bg(cx.theme().colors().panel_background)
.when(recent_history.is_empty(), |this| {
let configuration_error_ref = &configuration_error;
this.child(

View File

@@ -84,12 +84,6 @@ impl ContextStrip {
}
}
/// Whether or not the context strip has items to display
pub fn has_context_items(&self, cx: &App) -> bool {
self.context_store.read(cx).context().next().is_some()
|| self.suggested_context(cx).is_some()
}
fn added_contexts(&self, cx: &App) -> Vec<AddedContext> {
if let Some(workspace) = self.workspace.upgrade() {
let project = workspace.read(cx).project().read(cx);
@@ -110,14 +104,14 @@ impl ContextStrip {
}
}
fn suggested_context(&self, cx: &App) -> Option<SuggestedContext> {
fn suggested_context(&self, cx: &Context<Self>) -> Option<SuggestedContext> {
match self.suggest_context_kind {
SuggestContextKind::File => self.suggested_file(cx),
SuggestContextKind::Thread => self.suggested_thread(cx),
}
}
fn suggested_file(&self, cx: &App) -> Option<SuggestedContext> {
fn suggested_file(&self, cx: &Context<Self>) -> Option<SuggestedContext> {
let workspace = self.workspace.upgrade()?;
let active_item = workspace.read(cx).active_item(cx)?;
@@ -144,7 +138,7 @@ impl ContextStrip {
})
}
fn suggested_thread(&self, cx: &App) -> Option<SuggestedContext> {
fn suggested_thread(&self, cx: &Context<Self>) -> Option<SuggestedContext> {
if !self.context_picker.read(cx).allow_threads() {
return None;
}

View File

@@ -451,7 +451,7 @@ impl<T: 'static> PromptEditor<T> {
editor.move_to_end(&Default::default(), window, cx)
});
}
} else if self.context_strip.read(cx).has_context_items(cx) {
} else {
self.context_strip.focus_handle(cx).focus(window);
}
}

View File

@@ -401,7 +401,7 @@ impl MessageEditor {
fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
if self.context_picker_menu_handle.is_deployed() {
cx.propagate();
} else if self.context_strip.read(cx).has_context_items(cx) {
} else {
self.context_strip.focus_handle(cx).focus(window);
}
}

View File

@@ -425,17 +425,16 @@ impl ToolUseState {
let content = match tool_result {
ToolResultContent::Text(text) => {
let text = if text.len() < tool_output_limit {
text
} else {
let truncated = truncate_lines_to_byte_limit(&text, tool_output_limit);
let truncated = truncate_lines_to_byte_limit(&text, tool_output_limit);
LanguageModelToolResultContent::Text(
format!(
"Tool result too long. The first {} bytes:\n\n{}",
truncated.len(),
truncated
)
};
LanguageModelToolResultContent::Text(text.into())
.into(),
)
}
ToolResultContent::Image(language_model_image) => {
if language_model_image.estimate_tokens() < tool_output_limit {

View File

@@ -163,10 +163,8 @@ impl AskPassSession {
#[cfg(unix)]
fn get_shell_safe_zed_path() -> anyhow::Result<String> {
let zed_path = std::env::current_exe()
.context("Failed to determine current executable path for use in askpass")?
.context("Failed to figure out current executable path for use in askpass")?
.to_string_lossy()
// see https://github.com/rust-lang/rust/issues/69343
.trim_end_matches(" (deleted)")
.to_string();
// NOTE: this was previously enabled, however, it caused errors when it shouldn't have

View File

@@ -692,7 +692,7 @@ impl JsonSchema for LanguageModelProviderSetting {
schemars::schema::SchemaObject {
enum_values: Some(vec![
"anthropic".into(),
"amazon-bedrock".into(),
"bedrock".into(),
"google".into(),
"lmstudio".into(),
"ollama".into(),

View File

@@ -15,7 +15,7 @@ use gpui::{AppContext, TestAppContext};
use indoc::{formatdoc, indoc};
use language_model::{
LanguageModelRegistry, LanguageModelRequestTool, LanguageModelToolResult,
LanguageModelToolResultContent, LanguageModelToolUse, LanguageModelToolUseId, SelectedModel,
LanguageModelToolResultContent, LanguageModelToolUse, LanguageModelToolUseId,
};
use project::Project;
use rand::prelude::*;
@@ -25,7 +25,6 @@ use std::{
cmp::Reverse,
fmt::{self, Display},
io::Write as _,
str::FromStr,
sync::mpsc,
};
use util::path;
@@ -1217,7 +1216,7 @@ fn report_progress(evaluated_count: usize, failed_count: usize, iterations: usiz
passed_count as f64 / evaluated_count as f64
};
print!(
"\r\x1b[KEvaluated {}/{} ({:.2}% passed)",
"\r\x1b[KEvaluated {}/{} ({:.2}%)",
evaluated_count,
iterations,
passed_ratio * 100.0
@@ -1256,21 +1255,13 @@ impl EditAgentTest {
fs.insert_tree("/root", json!({})).await;
let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await;
let agent_model = SelectedModel::from_str(
&std::env::var("ZED_AGENT_MODEL")
.unwrap_or("anthropic/claude-3-7-sonnet-latest".into()),
)
.unwrap();
let judge_model = SelectedModel::from_str(
&std::env::var("ZED_JUDGE_MODEL")
.unwrap_or("anthropic/claude-3-7-sonnet-latest".into()),
)
.unwrap();
let (agent_model, judge_model) = cx
.update(|cx| {
cx.spawn(async move |cx| {
let agent_model = Self::load_model(&agent_model, cx).await;
let judge_model = Self::load_model(&judge_model, cx).await;
let agent_model =
Self::load_model("anthropic", "claude-3-7-sonnet-latest", cx).await;
let judge_model =
Self::load_model("anthropic", "claude-3-7-sonnet-latest", cx).await;
(agent_model.unwrap(), judge_model.unwrap())
})
})
@@ -1285,17 +1276,15 @@ impl EditAgentTest {
}
async fn load_model(
selected_model: &SelectedModel,
provider: &str,
id: &str,
cx: &mut AsyncApp,
) -> Result<Arc<dyn LanguageModel>> {
let (provider, model) = cx.update(|cx| {
let models = LanguageModelRegistry::read_global(cx);
let model = models
.available_models(cx)
.find(|model| {
model.provider_id() == selected_model.provider
&& model.id() == selected_model.model
})
.find(|model| model.provider_id().0 == provider && model.id().0 == id)
.unwrap();
let provider = models.provider(&model.provider_id()).unwrap();
(provider, model)

View File

@@ -1249,7 +1249,7 @@ pub struct ActiveDiagnosticGroup {
}
#[derive(Debug, PartialEq, Eq)]
#[allow(clippy::large_enum_variant)]
pub(crate) enum ActiveDiagnostic {
None,
All,

View File

@@ -19,7 +19,6 @@ test-support = ["clock/test-support", "collections/test-support", "gpui/test-sup
anyhow.workspace = true
async-recursion = "0.3"
async-tungstenite = { workspace = true, features = ["tokio", "tokio-rustls-manual-roots"] }
base64.workspace = true
chrono = { workspace = true, features = ["serde"] }
clock.workspace = true
collections.workspace = true
@@ -30,7 +29,6 @@ gpui.workspace = true
gpui_tokio.workspace = true
http_client.workspace = true
http_client_tls.workspace = true
httparse = "1.10"
log.workspace = true
paths.workspace = true
parking_lot.workspace = true
@@ -49,7 +47,6 @@ text.workspace = true
thiserror.workspace = true
time.workspace = true
tiny_http = "0.8"
tokio-native-tls = "0.3"
tokio-socks = { version = "0.5.2", default-features = false, features = ["futures-io"] }
url.workspace = true
util.workspace = true

View File

@@ -1,7 +1,7 @@
#[cfg(any(test, feature = "test-support"))]
pub mod test;
mod proxy;
mod socks;
pub mod telemetry;
pub mod user;
pub mod zed_urls;
@@ -24,13 +24,13 @@ use gpui::{App, AsyncApp, Entity, Global, Task, WeakEntity, actions};
use http_client::{AsyncBody, HttpClient, HttpClientWithUrl};
use parking_lot::RwLock;
use postage::watch;
use proxy::connect_proxy_stream;
use rand::prelude::*;
use release_channel::{AppVersion, ReleaseChannel};
use rpc::proto::{AnyTypedEnvelope, EnvelopedMessage, PeerId, RequestMessage};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsSources};
use socks::connect_socks_proxy_stream;
use std::pin::Pin;
use std::{
any::TypeId,
@@ -1156,7 +1156,7 @@ impl Client {
let handle = cx.update(|cx| gpui_tokio::Tokio::handle(cx)).ok().unwrap();
let _guard = handle.enter();
match proxy {
Some(proxy) => connect_proxy_stream(&proxy, rpc_host).await?,
Some(proxy) => connect_socks_proxy_stream(&proxy, rpc_host).await?,
None => Box::new(TcpStream::connect(rpc_host).await?),
}
};

View File

@@ -1,66 +0,0 @@
//! client proxy
mod http_proxy;
mod socks_proxy;
use anyhow::{Context, Result, anyhow};
use http_client::Url;
use http_proxy::{HttpProxyType, connect_http_proxy_stream, parse_http_proxy};
use socks_proxy::{SocksVersion, connect_socks_proxy_stream, parse_socks_proxy};
pub(crate) async fn connect_proxy_stream(
proxy: &Url,
rpc_host: (&str, u16),
) -> Result<Box<dyn AsyncReadWrite>> {
let Some(((proxy_domain, proxy_port), proxy_type)) = parse_proxy_type(proxy) else {
// If parsing the proxy URL fails, we must avoid falling back to an insecure connection.
// SOCKS proxies are often used in contexts where security and privacy are critical,
// so any fallback could expose users to significant risks.
return Err(anyhow!("Parsing proxy url failed"));
};
// Connect to proxy and wrap protocol later
let stream = tokio::net::TcpStream::connect((proxy_domain.as_str(), proxy_port))
.await
.context("Failed to connect to proxy")?;
let proxy_stream = match proxy_type {
ProxyType::SocksProxy(proxy) => connect_socks_proxy_stream(stream, proxy, rpc_host).await?,
ProxyType::HttpProxy(proxy) => {
connect_http_proxy_stream(stream, proxy, rpc_host, &proxy_domain).await?
}
};
Ok(proxy_stream)
}
enum ProxyType<'t> {
SocksProxy(SocksVersion<'t>),
HttpProxy(HttpProxyType<'t>),
}
fn parse_proxy_type<'t>(proxy: &'t Url) -> Option<((String, u16), ProxyType<'t>)> {
let scheme = proxy.scheme();
let host = proxy.host()?.to_string();
let port = proxy.port_or_known_default()?;
let proxy_type = match scheme {
scheme if scheme.starts_with("socks") => {
Some(ProxyType::SocksProxy(parse_socks_proxy(scheme, proxy)))
}
scheme if scheme.starts_with("http") => {
Some(ProxyType::HttpProxy(parse_http_proxy(scheme, proxy)))
}
_ => None,
}?;
Some(((host, port), proxy_type))
}
pub(crate) trait AsyncReadWrite:
tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send + 'static
{
}
impl<T: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send + 'static> AsyncReadWrite
for T
{
}

View File

@@ -1,171 +0,0 @@
use anyhow::{Context, Result};
use base64::Engine;
use httparse::{EMPTY_HEADER, Response};
use tokio::{
io::{AsyncBufReadExt, AsyncWriteExt, BufStream},
net::TcpStream,
};
use tokio_native_tls::{TlsConnector, native_tls};
use url::Url;
use super::AsyncReadWrite;
pub(super) enum HttpProxyType<'t> {
HTTP(Option<HttpProxyAuthorization<'t>>),
HTTPS(Option<HttpProxyAuthorization<'t>>),
}
pub(super) struct HttpProxyAuthorization<'t> {
username: &'t str,
password: &'t str,
}
pub(super) fn parse_http_proxy<'t>(scheme: &str, proxy: &'t Url) -> HttpProxyType<'t> {
let auth = proxy.password().map(|password| HttpProxyAuthorization {
username: proxy.username(),
password,
});
if scheme.starts_with("https") {
HttpProxyType::HTTPS(auth)
} else {
HttpProxyType::HTTP(auth)
}
}
pub(crate) async fn connect_http_proxy_stream(
stream: TcpStream,
http_proxy: HttpProxyType<'_>,
rpc_host: (&str, u16),
proxy_domain: &str,
) -> Result<Box<dyn AsyncReadWrite>> {
match http_proxy {
HttpProxyType::HTTP(auth) => http_connect(stream, rpc_host, auth).await,
HttpProxyType::HTTPS(auth) => https_connect(stream, rpc_host, auth, proxy_domain).await,
}
.context("error connecting to http/https proxy")
}
async fn http_connect<T>(
stream: T,
target: (&str, u16),
auth: Option<HttpProxyAuthorization<'_>>,
) -> Result<Box<dyn AsyncReadWrite>>
where
T: AsyncReadWrite,
{
let mut stream = BufStream::new(stream);
let request = make_request(target, auth);
stream.write_all(request.as_bytes()).await?;
stream.flush().await?;
check_response(&mut stream).await?;
Ok(Box::new(stream))
}
async fn https_connect<T>(
stream: T,
target: (&str, u16),
auth: Option<HttpProxyAuthorization<'_>>,
proxy_domain: &str,
) -> Result<Box<dyn AsyncReadWrite>>
where
T: AsyncReadWrite,
{
let tls_connector = TlsConnector::from(native_tls::TlsConnector::new()?);
let stream = tls_connector.connect(proxy_domain, stream).await?;
http_connect(stream, target, auth).await
}
fn make_request(target: (&str, u16), auth: Option<HttpProxyAuthorization<'_>>) -> String {
let (host, port) = target;
let mut request = format!(
"CONNECT {host}:{port} HTTP/1.1\r\nHost: {host}:{port}\r\nProxy-Connection: Keep-Alive\r\n"
);
if let Some(HttpProxyAuthorization { username, password }) = auth {
let auth =
base64::prelude::BASE64_STANDARD.encode(format!("{username}:{password}").as_bytes());
let auth = format!("Proxy-Authorization: Basic {auth}\r\n");
request.push_str(&auth);
}
request.push_str("\r\n");
request
}
async fn check_response<T>(stream: &mut BufStream<T>) -> Result<()>
where
T: AsyncReadWrite,
{
let response = recv_response(stream).await?;
let mut dummy_headers = [EMPTY_HEADER; MAX_RESPONSE_HEADERS];
let mut parser = Response::new(&mut dummy_headers);
parser.parse(response.as_bytes())?;
match parser.code {
Some(code) => {
if code == 200 {
Ok(())
} else {
Err(anyhow::anyhow!(
"Proxy connection failed with HTTP code: {code}"
))
}
}
None => Err(anyhow::anyhow!(
"Proxy connection failed with no HTTP code: {}",
parser.reason.unwrap_or("Unknown reason")
)),
}
}
const MAX_RESPONSE_HEADER_LENGTH: usize = 4096;
const MAX_RESPONSE_HEADERS: usize = 16;
async fn recv_response<T>(stream: &mut BufStream<T>) -> Result<String>
where
T: AsyncReadWrite,
{
let mut response = String::new();
loop {
if stream.read_line(&mut response).await? == 0 {
return Err(anyhow::anyhow!("End of stream"));
}
if MAX_RESPONSE_HEADER_LENGTH < response.len() {
return Err(anyhow::anyhow!("Maximum response header length exceeded"));
}
if response.ends_with("\r\n\r\n") {
return Ok(response);
}
}
}
#[cfg(test)]
mod tests {
use url::Url;
use super::{HttpProxyAuthorization, HttpProxyType, parse_http_proxy};
#[test]
fn test_parse_http_proxy() {
let proxy = Url::parse("http://proxy.example.com:1080").unwrap();
let scheme = proxy.scheme();
let version = parse_http_proxy(scheme, &proxy);
assert!(matches!(version, HttpProxyType::HTTP(None)))
}
#[test]
fn test_parse_http_proxy_with_auth() {
let proxy = Url::parse("http://username:password@proxy.example.com:1080").unwrap();
let scheme = proxy.scheme();
let version = parse_http_proxy(scheme, &proxy);
assert!(matches!(
version,
HttpProxyType::HTTP(Some(HttpProxyAuthorization {
username: "username",
password: "password"
}))
))
}
}

View File

@@ -1,19 +1,15 @@
//! socks proxy
use anyhow::{Context, Result};
use tokio::net::TcpStream;
use anyhow::{Context, Result, anyhow};
use http_client::Url;
use tokio_socks::tcp::{Socks4Stream, Socks5Stream};
use url::Url;
use super::AsyncReadWrite;
/// Identification to a Socks V4 Proxy
pub(super) struct Socks4Identification<'a> {
struct Socks4Identification<'a> {
user_id: &'a str,
}
/// Authorization to a Socks V5 Proxy
pub(super) struct Socks5Authorization<'a> {
struct Socks5Authorization<'a> {
username: &'a str,
password: &'a str,
}
@@ -22,50 +18,45 @@ pub(super) struct Socks5Authorization<'a> {
///
/// V4 allows idenfication using a user_id
/// V5 allows authorization using a username and password
pub(super) enum SocksVersion<'a> {
enum SocksVersion<'a> {
V4(Option<Socks4Identification<'a>>),
V5(Option<Socks5Authorization<'a>>),
}
pub(super) fn parse_socks_proxy<'t>(scheme: &str, proxy: &'t Url) -> SocksVersion<'t> {
if scheme.starts_with("socks4") {
let identification = match proxy.username() {
"" => None,
username => Some(Socks4Identification { user_id: username }),
};
SocksVersion::V4(identification)
} else {
let authorization = proxy.password().map(|password| Socks5Authorization {
username: proxy.username(),
password,
});
SocksVersion::V5(authorization)
}
}
pub(super) async fn connect_socks_proxy_stream(
stream: TcpStream,
socks_version: SocksVersion<'_>,
pub(crate) async fn connect_socks_proxy_stream(
proxy: &Url,
rpc_host: (&str, u16),
) -> Result<Box<dyn AsyncReadWrite>> {
match socks_version {
let Some((socks_proxy, version)) = parse_socks_proxy(proxy) else {
// If parsing the proxy URL fails, we must avoid falling back to an insecure connection.
// SOCKS proxies are often used in contexts where security and privacy are critical,
// so any fallback could expose users to significant risks.
return Err(anyhow!("Parsing proxy url failed"));
};
// Connect to proxy and wrap protocol later
let stream = tokio::net::TcpStream::connect(socks_proxy)
.await
.context("Failed to connect to socks proxy")?;
let socks: Box<dyn AsyncReadWrite> = match version {
SocksVersion::V4(None) => {
let socks = Socks4Stream::connect_with_socket(stream, rpc_host)
.await
.context("error connecting to socks")?;
Ok(Box::new(socks))
Box::new(socks)
}
SocksVersion::V4(Some(Socks4Identification { user_id })) => {
let socks = Socks4Stream::connect_with_userid_and_socket(stream, rpc_host, user_id)
.await
.context("error connecting to socks")?;
Ok(Box::new(socks))
Box::new(socks)
}
SocksVersion::V5(None) => {
let socks = Socks5Stream::connect_with_socket(stream, rpc_host)
.await
.context("error connecting to socks")?;
Ok(Box::new(socks))
Box::new(socks)
}
SocksVersion::V5(Some(Socks5Authorization { username, password })) => {
let socks = Socks5Stream::connect_with_password_and_socket(
@@ -73,9 +64,44 @@ pub(super) async fn connect_socks_proxy_stream(
)
.await
.context("error connecting to socks")?;
Ok(Box::new(socks))
Box::new(socks)
}
}
};
Ok(socks)
}
fn parse_socks_proxy(proxy: &Url) -> Option<((String, u16), SocksVersion<'_>)> {
let scheme = proxy.scheme();
let socks_version = if scheme.starts_with("socks4") {
let identification = match proxy.username() {
"" => None,
username => Some(Socks4Identification { user_id: username }),
};
SocksVersion::V4(identification)
} else if scheme.starts_with("socks") {
let authorization = proxy.password().map(|password| Socks5Authorization {
username: proxy.username(),
password,
});
SocksVersion::V5(authorization)
} else {
return None;
};
let host = proxy.host()?.to_string();
let port = proxy.port_or_known_default()?;
Some(((host, port), socks_version))
}
pub(crate) trait AsyncReadWrite:
tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send + 'static
{
}
impl<T: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send + 'static> AsyncReadWrite
for T
{
}
#[cfg(test)]
@@ -87,18 +113,20 @@ mod tests {
#[test]
fn parse_socks4() {
let proxy = Url::parse("socks4://proxy.example.com:1080").unwrap();
let scheme = proxy.scheme();
let version = parse_socks_proxy(scheme, &proxy);
let ((host, port), version) = parse_socks_proxy(&proxy).unwrap();
assert_eq!(host, "proxy.example.com");
assert_eq!(port, 1080);
assert!(matches!(version, SocksVersion::V4(None)))
}
#[test]
fn parse_socks4_with_identification() {
let proxy = Url::parse("socks4://userid@proxy.example.com:1080").unwrap();
let scheme = proxy.scheme();
let version = parse_socks_proxy(scheme, &proxy);
let ((host, port), version) = parse_socks_proxy(&proxy).unwrap();
assert_eq!(host, "proxy.example.com");
assert_eq!(port, 1080);
assert!(matches!(
version,
SocksVersion::V4(Some(Socks4Identification { user_id: "userid" }))
@@ -108,18 +136,20 @@ mod tests {
#[test]
fn parse_socks5() {
let proxy = Url::parse("socks5://proxy.example.com:1080").unwrap();
let scheme = proxy.scheme();
let version = parse_socks_proxy(scheme, &proxy);
let ((host, port), version) = parse_socks_proxy(&proxy).unwrap();
assert_eq!(host, "proxy.example.com");
assert_eq!(port, 1080);
assert!(matches!(version, SocksVersion::V5(None)))
}
#[test]
fn parse_socks5_with_authorization() {
let proxy = Url::parse("socks5://username:password@proxy.example.com:1080").unwrap();
let scheme = proxy.scheme();
let version = parse_socks_proxy(scheme, &proxy);
let ((host, port), version) = parse_socks_proxy(&proxy).unwrap();
assert_eq!(host, "proxy.example.com");
assert_eq!(port, 1080);
assert!(matches!(
version,
SocksVersion::V5(Some(Socks5Authorization {
@@ -128,4 +158,19 @@ mod tests {
}))
))
}
/// If parsing the proxy URL fails, we must avoid falling back to an insecure connection.
/// SOCKS proxies are often used in contexts where security and privacy are critical,
/// so any fallback could expose users to significant risks.
#[tokio::test]
async fn fails_on_bad_proxy() {
// Should fail connecting because http is not a valid Socks proxy scheme
let proxy = Url::parse("http://localhost:2313").unwrap();
let result = connect_socks_proxy_stream(&proxy, ("test", 1080)).await;
match result {
Err(e) => assert_eq!(e.to_string(), "Parsing proxy url failed"),
Ok(_) => panic!("Connecting on bad proxy should fail"),
};
}
}

View File

@@ -543,7 +543,7 @@ pub struct MembershipUpdated {
/// The result of setting a member's role.
#[derive(Debug)]
#[allow(clippy::large_enum_variant)]
pub enum SetMemberRoleResult {
InviteUpdated(Channel),
MembershipUpdated(MembershipUpdated),

View File

@@ -36,7 +36,6 @@ use util::{ResultExt as _, maybe};
const VERSION: &str = env!("CARGO_PKG_VERSION");
const REVISION: Option<&'static str> = option_env!("GITHUB_SHA");
#[expect(clippy::result_large_err)]
#[tokio::main]
async fn main() -> Result<()> {
if let Err(error) = env::load_dotenv() {

View File

@@ -36,8 +36,8 @@ fn room_participants(room: &Entity<Room>, cx: &mut TestAppContext) -> RoomPartic
room.read_with(cx, |room, _| {
let mut remote = room
.remote_participants()
.values()
.map(|participant| participant.user.github_login.clone())
.iter()
.map(|(_, participant)| participant.user.github_login.clone())
.collect::<Vec<_>>();
let mut pending = room
.pending_participants()

View File

@@ -7,11 +7,11 @@ use crate::notifications::collab_notification::CollabNotification;
pub struct CollabNotificationStory;
impl Render for CollabNotificationStory {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
let window_container = |width, height| div().w(px(width)).h(px(height));
Story::container(cx)
.child(Story::title_for::<CollabNotification>(cx))
Story::container()
.child(Story::title_for::<CollabNotification>())
.child(
StorySection::new().child(StoryItem::new(
"Incoming Call Notification",

View File

@@ -21,8 +21,8 @@ use project::{
use ui::{
App, Clickable, Color, Context, Div, Icon, IconButton, IconName, Indicator, InteractiveElement,
IntoElement, Label, LabelCommon, LabelSize, ListItem, ParentElement, Render, RenderOnce,
Scrollbar, ScrollbarState, SharedString, StatefulInteractiveElement, Styled, Tooltip, Window,
div, h_flex, px, v_flex,
Scrollbar, ScrollbarState, SharedString, StatefulInteractiveElement, Styled, Window, div,
h_flex, px, v_flex,
};
use util::{ResultExt, maybe};
use workspace::Workspace;
@@ -259,11 +259,6 @@ impl LineBreakpoint {
dir, name, line
)))
.cursor_pointer()
.tooltip(Tooltip::text(if breakpoint.state.is_enabled() {
"Disable Breakpoint"
} else {
"Enable Breakpoint"
}))
.on_click({
let weak = weak.clone();
let path = path.clone();
@@ -295,9 +290,6 @@ impl LineBreakpoint {
)))
.start_slot(indicator)
.rounded()
.on_secondary_mouse_down(|_, _, cx| {
cx.stop_propagation();
})
.end_hover_slot(
IconButton::new(
SharedString::from(format!(
@@ -431,20 +423,12 @@ impl ExceptionBreakpoint {
self.id
)))
.rounded()
.on_secondary_mouse_down(|_, _, cx| {
cx.stop_propagation();
})
.start_slot(
div()
.id(SharedString::from(format!(
"exception-breakpoint-ui-item-{}-click-handler",
self.id
)))
.tooltip(Tooltip::text(if self.is_enabled {
"Disable Exception Breakpoint"
} else {
"Enable Exception Breakpoint"
}))
.on_click(move |_, _, cx| {
list.update(cx, |this, cx| {
this.session.update(cx, |this, cx| {

View File

@@ -129,9 +129,6 @@ impl ModuleList {
.w_full()
.group("")
.id(("module-list", ix))
.on_any_mouse_down(|_, _, cx| {
cx.stop_propagation();
})
.when(module.path.is_some(), |this| {
this.on_click({
let path = module

View File

@@ -39,6 +39,7 @@ pub struct StackFrameList {
_refresh_task: Task<()>,
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug, PartialEq, Eq)]
pub enum StackFrameEntry {
Normal(dap::StackFrame),
@@ -393,9 +394,6 @@ impl StackFrameList {
.when(is_selected_frame, |this| {
this.bg(cx.theme().colors().element_hover)
})
.on_any_mouse_down(|_, _, cx| {
cx.stop_propagation();
})
.on_click(cx.listener(move |this, _, window, cx| {
this.selected_ix = Some(ix);
this.activate_selected_entry(window, cx);
@@ -483,9 +481,6 @@ impl StackFrameList {
.when(is_selected, |this| {
this.bg(cx.theme().colors().element_hover)
})
.on_any_mouse_down(|_, _, cx| {
cx.stop_propagation();
})
.on_click(cx.listener(move |this, _, window, cx| {
this.selected_ix = Some(ix);
this.activate_selected_entry(window, cx);

View File

@@ -40,6 +40,7 @@ pub const MENU_ASIDE_X_PADDING: Pixels = px(16.);
pub const MENU_ASIDE_MIN_WIDTH: Pixels = px(260.);
pub const MENU_ASIDE_MAX_WIDTH: Pixels = px(500.);
#[allow(clippy::large_enum_variant)]
pub enum CodeContextMenu {
Completions(CompletionsMenu),
CodeActions(CodeActionsMenu),
@@ -927,6 +928,7 @@ impl CodeActionContents {
}
}
#[allow(clippy::large_enum_variant)]
#[derive(Clone)]
pub enum CodeActionsItem {
Task(TaskSourceKind, ResolvedTask),

View File

@@ -282,6 +282,7 @@ struct Transform {
block: Option<Block>,
}
#[allow(clippy::large_enum_variant)]
#[derive(Clone)]
pub enum Block {
Custom(Arc<CustomBlock>),

View File

@@ -107,9 +107,9 @@ pub use items::MAX_TAB_TITLE_LEN;
use itertools::Itertools;
use language::{
AutoindentMode, BracketMatch, BracketPair, Buffer, Capability, CharKind, CodeLabel,
CursorShape, DiagnosticEntry, DiffOptions, DocumentationConfig, EditPredictionsMode,
EditPreview, HighlightedText, IndentKind, IndentSize, Language, OffsetRangeExt, Point,
Selection, SelectionGoal, TextObject, TransactionId, TreeSitterOptions, WordsQuery,
CursorShape, DiagnosticEntry, DiffOptions, EditPredictionsMode, EditPreview, HighlightedText,
IndentKind, IndentSize, Language, OffsetRangeExt, Point, Selection, SelectionGoal, TextObject,
TransactionId, TreeSitterOptions, WordsQuery,
language_settings::{
self, InlayHintSettings, LspInsertMode, RewrapBehavior, WordsCompletionMode,
all_language_settings, language_settings,
@@ -1311,7 +1311,7 @@ pub struct ActiveDiagnosticGroup {
}
#[derive(Debug, PartialEq, Eq)]
#[allow(clippy::large_enum_variant)]
pub(crate) enum ActiveDiagnostic {
None,
All,
@@ -3912,7 +3912,7 @@ impl Editor {
pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
self.hide_mouse_cursor(&HideMouseCursorOrigin::TypingAction);
self.transact(window, cx, |this, window, cx| {
let (edits_with_flags, selection_info): (Vec<_>, Vec<_>) = {
let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
let selections = this.selections.all::<usize>(cx);
let multi_buffer = this.buffer.read(cx);
let buffer = multi_buffer.snapshot(cx);
@@ -3920,21 +3920,17 @@ impl Editor {
.iter()
.map(|selection| {
let start_point = selection.start.to_point(&buffer);
let mut existing_indent =
let mut indent =
buffer.indent_size_for_line(MultiBufferRow(start_point.row));
existing_indent.len = cmp::min(existing_indent.len, start_point.column);
indent.len = cmp::min(indent.len, start_point.column);
let start = selection.start;
let end = selection.end;
let selection_is_empty = start == end;
let language_scope = buffer.language_scope_at(start);
let (
comment_delimiter,
doc_delimiter,
insert_extra_newline,
indent_on_newline,
indent_on_extra_newline,
) = if let Some(language) = &language_scope {
let mut insert_extra_newline =
let (comment_delimiter, insert_extra_newline) = if let Some(language) =
&language_scope
{
let insert_extra_newline =
insert_extra_newline_brackets(&buffer, start..end, language)
|| insert_extra_newline_tree_sitter(&buffer, start..end);
@@ -3954,208 +3950,63 @@ impl Editor {
let (snapshot, range) =
buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
let num_of_whitespaces = snapshot
.chars_for_range(range.clone())
.take_while(|c| c.is_whitespace())
.count();
let mut index_of_first_non_whitespace = 0;
let comment_candidate = snapshot
.chars_for_range(range)
.skip(num_of_whitespaces)
.skip_while(|c| {
let should_skip = c.is_whitespace();
if should_skip {
index_of_first_non_whitespace += 1;
}
should_skip
})
.take(max_len_of_delimiter)
.collect::<String>();
let (delimiter, trimmed_len) =
delimiters.iter().find_map(|delimiter| {
let trimmed = delimiter.trim_end();
if comment_candidate.starts_with(trimmed) {
Some((delimiter, trimmed.len()))
} else {
None
}
})?;
let comment_prefix = delimiters.iter().find(|comment_prefix| {
comment_candidate.starts_with(comment_prefix.as_ref())
})?;
let cursor_is_placed_after_comment_marker =
num_of_whitespaces + trimmed_len <= start_point.column as usize;
index_of_first_non_whitespace + comment_prefix.len()
<= start_point.column as usize;
if cursor_is_placed_after_comment_marker {
Some(delimiter.clone())
Some(comment_prefix.clone())
} else {
None
}
});
let mut indent_on_newline = IndentSize::spaces(0);
let mut indent_on_extra_newline = IndentSize::spaces(0);
let doc_delimiter = maybe!({
if !selection_is_empty {
return None;
}
if !multi_buffer.language_settings(cx).extend_comment_on_newline {
return None;
}
let DocumentationConfig {
start: start_tag,
end: end_tag,
prefix: delimiter,
tab_size: len,
} = language.documentation()?;
let (snapshot, range) =
buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
let num_of_whitespaces = snapshot
.chars_for_range(range.clone())
.take_while(|c| c.is_whitespace())
.count();
let cursor_is_after_start_tag = {
let start_tag_len = start_tag.len();
let start_tag_line = snapshot
.chars_for_range(range.clone())
.skip(num_of_whitespaces)
.take(start_tag_len)
.collect::<String>();
if start_tag_line.starts_with(start_tag.as_ref()) {
num_of_whitespaces + start_tag_len
<= start_point.column as usize
} else {
false
}
};
let cursor_is_after_delimiter = {
let delimiter_trim = delimiter.trim_end();
let delimiter_line = snapshot
.chars_for_range(range.clone())
.skip(num_of_whitespaces)
.take(delimiter_trim.len())
.collect::<String>();
if delimiter_line.starts_with(delimiter_trim) {
num_of_whitespaces + delimiter_trim.len()
<= start_point.column as usize
} else {
false
}
};
let cursor_is_before_end_tag_if_exists = {
let num_of_whitespaces_rev = snapshot
.reversed_chars_for_range(range.clone())
.take_while(|c| c.is_whitespace())
.count();
let mut line_iter = snapshot
.reversed_chars_for_range(range)
.skip(num_of_whitespaces_rev);
let end_tag_exists = end_tag
.chars()
.rev()
.all(|char| line_iter.next() == Some(char));
if end_tag_exists {
let max_point = snapshot.line_len(start_point.row) as usize;
let ordering = (num_of_whitespaces_rev
+ end_tag.len()
+ start_point.column as usize)
.cmp(&max_point);
let cursor_is_before_end_tag =
ordering != Ordering::Greater;
if cursor_is_after_start_tag {
if cursor_is_before_end_tag {
insert_extra_newline = true;
}
let cursor_is_at_start_of_end_tag =
ordering == Ordering::Equal;
if cursor_is_at_start_of_end_tag {
indent_on_extra_newline.len = (*len).into();
}
}
cursor_is_before_end_tag
} else {
true
}
};
if (cursor_is_after_start_tag || cursor_is_after_delimiter)
&& cursor_is_before_end_tag_if_exists
{
if cursor_is_after_start_tag {
indent_on_newline.len = (*len).into();
}
Some(delimiter.clone())
} else {
None
}
});
(
comment_delimiter,
doc_delimiter,
insert_extra_newline,
indent_on_newline,
indent_on_extra_newline,
)
(comment_delimiter, insert_extra_newline)
} else {
(
None,
None,
false,
IndentSize::default(),
IndentSize::default(),
)
(None, false)
};
let prevent_auto_indent = doc_delimiter.is_some();
let delimiter = comment_delimiter.or(doc_delimiter);
let capacity_for_delimiter =
delimiter.as_deref().map(str::len).unwrap_or_default();
let mut new_text = String::with_capacity(
1 + capacity_for_delimiter
+ existing_indent.len as usize
+ indent_on_newline.len as usize
+ indent_on_extra_newline.len as usize,
);
let capacity_for_delimiter = comment_delimiter
.as_deref()
.map(str::len)
.unwrap_or_default();
let mut new_text =
String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
new_text.push('\n');
new_text.extend(existing_indent.chars());
new_text.extend(indent_on_newline.chars());
if let Some(delimiter) = &delimiter {
new_text.extend(indent.chars());
if let Some(delimiter) = &comment_delimiter {
new_text.push_str(delimiter);
}
if insert_extra_newline {
new_text.push('\n');
new_text.extend(existing_indent.chars());
new_text.extend(indent_on_extra_newline.chars());
new_text = new_text.repeat(2);
}
let anchor = buffer.anchor_after(end);
let new_selection = selection.map(|_| anchor);
(
((start..end, new_text), prevent_auto_indent),
(start..end, new_text),
(insert_extra_newline, new_selection),
)
})
.unzip()
};
let mut auto_indent_edits = Vec::new();
let mut edits = Vec::new();
for (edit, prevent_auto_indent) in edits_with_flags {
if prevent_auto_indent {
edits.push(edit);
} else {
auto_indent_edits.push(edit);
}
}
if !edits.is_empty() {
this.edit(edits, cx);
}
if !auto_indent_edits.is_empty() {
this.edit_with_autoindent(auto_indent_edits, cx);
}
this.edit_with_autoindent(edits, cx);
let buffer = this.buffer.read(cx).snapshot(cx);
let new_selections = selection_info
let new_selections = selection_fixup_info
.into_iter()
.map(|(extra_newline_inserted, new_selection)| {
let mut cursor = new_selection.end.to_point(&buffer);
@@ -18067,6 +17918,10 @@ impl Editor {
.and_then(|lines| lines.last().map(|line| line.range.start));
self.inline_value_cache.refresh_task = cx.spawn(async move |editor, cx| {
let snapshot = editor
.update(cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
.ok()?;
let inline_values = editor
.update(cx, |editor, cx| {
let Some(current_execution_position) = current_execution_position else {
@@ -18094,40 +17949,22 @@ impl Editor {
.context("refreshing debugger inlays")
.log_err()?;
let mut buffer_inline_values: HashMap<BufferId, Vec<InlayHint>> = HashMap::default();
for (buffer_id, inline_value) in inline_values
.into_iter()
.filter_map(|hint| Some((hint.position.buffer_id?, hint)))
{
buffer_inline_values
.entry(buffer_id)
.or_default()
.push(inline_value);
}
let (excerpt_id, buffer_id) = snapshot
.excerpts()
.next()
.map(|excerpt| (excerpt.0, excerpt.1.remote_id()))?;
editor
.update(cx, |editor, cx| {
let snapshot = editor.buffer.read(cx).snapshot(cx);
let mut new_inlays = Vec::default();
for (excerpt_id, buffer_snapshot, _) in snapshot.excerpts() {
let buffer_id = buffer_snapshot.remote_id();
buffer_inline_values
.get(&buffer_id)
.into_iter()
.flatten()
.for_each(|hint| {
let inlay = Inlay::debugger_hint(
post_inc(&mut editor.next_inlay_id),
Anchor::in_buffer(excerpt_id, buffer_id, hint.position),
hint.text(),
);
new_inlays.push(inlay);
});
}
let new_inlays = inline_values
.into_iter()
.map(|debugger_value| {
Inlay::debugger_hint(
post_inc(&mut editor.next_inlay_id),
Anchor::in_buffer(excerpt_id, buffer_id, debugger_value.position),
debugger_value.text(),
)
})
.collect::<Vec<_>>();
let mut inlay_ids = new_inlays.iter().map(|inlay| inlay.id).collect();
std::mem::swap(&mut editor.inline_value_cache.inlays, &mut inlay_ids);
@@ -20351,8 +20188,8 @@ impl EditorSnapshot {
let participant_indices = collaboration_hub.user_participant_indices(cx);
let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
let collaborators_by_replica_id = collaborators_by_peer_id
.values()
.map(|collaborator| (collaborator.replica_id, collaborator))
.iter()
.map(|(_, collaborator)| (collaborator.replica_id, collaborator))
.collect::<HashMap<_, _>>();
self.buffer_snapshot
.selections_in_range(range, false)

View File

@@ -2755,7 +2755,7 @@ async fn test_newline_comments(cx: &mut TestAppContext) {
let language = Arc::new(Language::new(
LanguageConfig {
line_comments: vec!["// ".into()],
line_comments: vec!["//".into()],
..LanguageConfig::default()
},
None,
@@ -2770,29 +2770,7 @@ async fn test_newline_comments(cx: &mut TestAppContext) {
cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
cx.assert_editor_state(indoc! {"
// Foo
// ˇ
"});
// Ensure that we add comment prefix when existing line contains space
cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
cx.assert_editor_state(
indoc! {"
// Foo
//s
// ˇ
"}
.replace("s", " ") // s is used as space placeholder to prevent format on save
.as_str(),
);
// Ensure that we add comment prefix when existing line does not contain space
cx.set_state(indoc! {"
// Foo
//ˇ
"});
cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
cx.assert_editor_state(indoc! {"
// Foo
//
// ˇ
"});
// Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
cx.set_state(indoc! {"
@@ -2819,177 +2797,6 @@ async fn test_newline_comments(cx: &mut TestAppContext) {
"});
}
#[gpui::test]
async fn test_newline_documentation_comments(cx: &mut TestAppContext) {
init_test(cx, |settings| {
settings.defaults.tab_size = NonZeroU32::new(4)
});
let language = Arc::new(Language::new(
LanguageConfig {
documentation: Some(language::DocumentationConfig {
start: "/**".into(),
end: "*/".into(),
prefix: "* ".into(),
tab_size: NonZeroU32::new(1).unwrap(),
}),
..LanguageConfig::default()
},
None,
));
{
let mut cx = EditorTestContext::new(cx).await;
cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
cx.set_state(indoc! {"
/**ˇ
"});
cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
cx.assert_editor_state(indoc! {"
/**
* ˇ
"});
// Ensure that if cursor is before the comment start,
// we do not actually insert a comment prefix.
cx.set_state(indoc! {"
ˇ/**
"});
cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
cx.assert_editor_state(indoc! {"
ˇ/**
"});
// Ensure that if cursor is between it doesn't add comment prefix.
cx.set_state(indoc! {"
/*ˇ*
"});
cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
cx.assert_editor_state(indoc! {"
/*
ˇ*
"});
// Ensure that if suffix exists on same line after cursor it adds new line.
cx.set_state(indoc! {"
/**ˇ*/
"});
cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
cx.assert_editor_state(indoc! {"
/**
* ˇ
*/
"});
// Ensure that if suffix exists on same line after cursor with space it adds new line.
cx.set_state(indoc! {"
/**ˇ */
"});
cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
cx.assert_editor_state(indoc! {"
/**
* ˇ
*/
"});
// Ensure that if suffix exists on same line after cursor with space it adds new line.
cx.set_state(indoc! {"
/** ˇ*/
"});
cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
cx.assert_editor_state(
indoc! {"
/**s
* ˇ
*/
"}
.replace("s", " ") // s is used as space placeholder to prevent format on save
.as_str(),
);
// Ensure that delimiter space is preserved when newline on already
// spaced delimiter.
cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
cx.assert_editor_state(
indoc! {"
/**s
*s
* ˇ
*/
"}
.replace("s", " ") // s is used as space placeholder to prevent format on save
.as_str(),
);
// Ensure that delimiter space is preserved when space is not
// on existing delimiter.
cx.set_state(indoc! {"
/**
*/
"});
cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
cx.assert_editor_state(indoc! {"
/**
*
* ˇ
*/
"});
// Ensure that if suffix exists on same line after cursor it
// doesn't add extra new line if prefix is not on same line.
cx.set_state(indoc! {"
/**
ˇ*/
"});
cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
cx.assert_editor_state(indoc! {"
/**
ˇ*/
"});
// Ensure that it detects suffix after existing prefix.
cx.set_state(indoc! {"
/**ˇ/
"});
cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
cx.assert_editor_state(indoc! {"
/**
ˇ/
"});
// Ensure that if suffix exists on same line before
// cursor it does not add comment prefix.
cx.set_state(indoc! {"
/** */ˇ
"});
cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
cx.assert_editor_state(indoc! {"
/** */
ˇ
"});
// Ensure that if suffix exists on same line before
// cursor it does not add comment prefix.
cx.set_state(indoc! {"
/**
*
*/ˇ
"});
cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
cx.assert_editor_state(indoc! {"
/**
*
*/
ˇ
"});
}
// Ensure that comment continuations can be disabled.
update_test_language_settings(cx, |settings| {
settings.defaults.extend_comment_on_newline = Some(false);
});
let mut cx = EditorTestContext::new(cx).await;
cx.set_state(indoc! {"
/**ˇ
"});
cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
cx.assert_editor_state(indoc! {"
/**
ˇ
"});
}
#[gpui::test]
fn test_insert_with_old_selections(cx: &mut TestAppContext) {
init_test(cx, |_| {});

View File

@@ -6393,6 +6393,7 @@ pub(crate) struct LineWithInvisibles {
font_size: Pixels,
}
#[allow(clippy::large_enum_variant)]
enum LineFragment {
Text(ShapedLine),
Element {

View File

@@ -33,7 +33,6 @@ serde_json.workspace = true
task.workspace = true
toml.workspace = true
util.workspace = true
wasi-preview1-component-adapter-provider.workspace = true
wasm-encoder.workspace = true
wasmparser.workspace = true
wit-component.workspace = true

View File

@@ -4,6 +4,7 @@ use crate::{
use anyhow::{Context as _, Result, anyhow, bail};
use async_compression::futures::bufread::GzipDecoder;
use async_tar::Archive;
use futures::AsyncReadExt;
use futures::io::BufReader;
use heck::ToSnakeCase;
use http_client::{self, AsyncBody, HttpClient};
@@ -14,7 +15,6 @@ use std::{
process::Stdio,
sync::Arc,
};
use wasi_preview1_component_adapter_provider::WASI_SNAPSHOT_PREVIEW1_REACTOR_ADAPTER;
use wasm_encoder::{ComponentSectionId, Encode as _, RawSection, Section as _};
use wasmparser::Parser;
use wit_component::ComponentEncoder;
@@ -26,6 +26,7 @@ use wit_component::ComponentEncoder;
/// Once Rust 1.78 is released, there will be a `wasm32-wasip2` target available, so we will
/// not need the adapter anymore.
const RUST_TARGET: &str = "wasm32-wasip1";
const WASI_ADAPTER_URL: &str = "https://github.com/bytecodealliance/wasmtime/releases/download/v18.0.2/wasi_snapshot_preview1.reactor.wasm";
/// Compiling Tree-sitter parsers from C to WASM requires Clang 17, and a WASM build of libc
/// and clang's runtime library. The `wasi-sdk` provides these binaries.
@@ -136,6 +137,7 @@ impl ExtensionBuilder {
options: CompileExtensionOptions,
) -> Result<(), anyhow::Error> {
self.install_rust_wasm_target_if_needed()?;
let adapter_bytes = self.install_wasi_preview1_adapter_if_needed().await?;
let cargo_toml_content = fs::read_to_string(extension_dir.join("Cargo.toml"))?;
let cargo_toml: CargoToml = toml::from_str(&cargo_toml_content)?;
@@ -184,10 +186,7 @@ impl ExtensionBuilder {
let mut encoder = ComponentEncoder::default()
.module(&wasm_bytes)?
.adapter(
"wasi_snapshot_preview1",
WASI_SNAPSHOT_PREVIEW1_REACTOR_ADAPTER,
)
.adapter("wasi_snapshot_preview1", &adapter_bytes)
.context("failed to load adapter module")?
.validate(true);
@@ -396,6 +395,38 @@ impl ExtensionBuilder {
Ok(())
}
async fn install_wasi_preview1_adapter_if_needed(&self) -> Result<Vec<u8>> {
let cache_path = self.cache_dir.join("wasi_snapshot_preview1.reactor.wasm");
if let Ok(content) = fs::read(&cache_path) {
if Parser::is_core_wasm(&content) {
return Ok(content);
}
}
fs::remove_file(&cache_path).ok();
log::info!(
"downloading wasi adapter module to {}",
cache_path.display()
);
let mut response = self
.http
.get(WASI_ADAPTER_URL, AsyncBody::default(), true)
.await?;
let mut content = Vec::new();
let mut body = BufReader::new(response.body_mut());
body.read_to_end(&mut content).await?;
fs::write(&cache_path, &content)
.with_context(|| format!("failed to save file {}", cache_path.display()))?;
if !Parser::is_core_wasm(&content) {
bail!("downloaded wasi adapter is invalid");
}
Ok(content)
}
async fn install_wasi_sdk_if_needed(&self) -> Result<PathBuf> {
let url = if let Some(asset_name) = WASI_SDK_ASSET_NAME {
format!("{WASI_SDK_URL}/{asset_name}")

View File

@@ -324,8 +324,7 @@ fn update_conflict_highlighting(
cx: &mut Context<Editor>,
) {
log::debug!("update conflict highlighting for {conflict:?}");
let theme = cx.theme().clone();
let colors = theme.colors();
let outer_start = buffer
.anchor_in_excerpt(excerpt_id, conflict.range.start)
.unwrap();
@@ -345,11 +344,9 @@ fn update_conflict_highlighting(
.anchor_in_excerpt(excerpt_id, conflict.theirs.end)
.unwrap();
let ours_background = colors.version_control_conflict_ours_background;
let ours_marker = colors.version_control_conflict_ours_marker_background;
let theirs_background = colors.version_control_conflict_theirs_background;
let theirs_marker = colors.version_control_conflict_theirs_marker_background;
let divider_background = colors.version_control_conflict_divider_background;
let ours_background = cx.theme().colors().version_control_conflict_marker_ours;
let theirs_background = cx.theme().colors().version_control_conflict_marker_theirs;
let divider_background = cx.theme().colors().version_control_conflict_marker_border;
let options = RowHighlightOptions {
include_gutter: false,
@@ -357,14 +354,14 @@ fn update_conflict_highlighting(
};
// Prevent diff hunk highlighting within the entire conflict region.
editor.highlight_rows::<ConflictsOuter>(
outer_start..outer_end,
divider_background,
editor.highlight_rows::<ConflictsOuter>(outer_start..outer_end, theirs_background, options, cx);
editor.highlight_rows::<ConflictsOurs>(our_start..our_end, ours_background, options, cx);
editor.highlight_rows::<ConflictsOursMarker>(
outer_start..our_start,
ours_background,
options,
cx,
);
editor.highlight_rows::<ConflictsOurs>(our_start..our_end, ours_background, options, cx);
editor.highlight_rows::<ConflictsOursMarker>(outer_start..our_start, ours_marker, options, cx);
editor.highlight_rows::<ConflictsTheirs>(
their_start..their_end,
theirs_background,
@@ -373,7 +370,7 @@ fn update_conflict_highlighting(
);
editor.highlight_rows::<ConflictsTheirsMarker>(
their_end..outer_end,
theirs_marker,
theirs_background,
options,
cx,
);

View File

@@ -257,10 +257,6 @@ path = "examples/image/image.rs"
name = "input"
path = "examples/input.rs"
[[example]]
name = "on_window_close_quit"
path = "examples/on_window_close_quit.rs"
[[example]]
name = "opacity"
path = "examples/opacity.rs"
@@ -281,10 +277,6 @@ path = "examples/shadow.rs"
name = "svg"
path = "examples/svg/svg.rs"
[[example]]
name = "text"
path = "examples/text.rs"
[[example]]
name = "text_wrapper"
path = "examples/text_wrapper.rs"
@@ -296,3 +288,7 @@ path = "examples/uniform_list.rs"
[[example]]
name = "window_shadow"
path = "examples/window_shadow.rs"
[[example]]
name = "on_window_close_quit"
path = "examples/on_window_close_quit.rs"

View File

@@ -1,15 +1,14 @@
use gpui::{
App, Application, Bounds, Context, SharedString, Window, WindowBounds, WindowOptions, button,
div, prelude::*, px, rgb, size,
App, Application, Bounds, Context, SharedString, Window, WindowBounds, WindowOptions, div,
prelude::*, px, rgb, size,
};
struct HelloWorld {
text: SharedString,
count: usize,
}
impl Render for HelloWorld {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
div()
.flex()
.flex_col()
@@ -24,14 +23,6 @@ impl Render for HelloWorld {
.text_xl()
.text_color(rgb(0xffffff))
.child(format!("Hello, {}!", &self.text))
.child(
button("counter-button")
.on_click(Box::new(cx.listener(move |this, _, _, cx| {
this.count += 1;
cx.notify();
})))
.child(div().child(format!("Count: {}", &self.count))),
)
.child(
div()
.flex()
@@ -106,7 +97,6 @@ fn main() {
|_, cx| {
cx.new(|_| HelloWorld {
text: "World".into(),
count: 0,
})
},
)

View File

@@ -1,333 +0,0 @@
use std::{
ops::{Deref, DerefMut},
sync::Arc,
};
use gpui::{
AbsoluteLength, App, Application, Context, DefiniteLength, ElementId, Global, Hsla, Menu,
SharedString, TextStyle, TitlebarOptions, Window, WindowBounds, WindowOptions, bounds,
colors::DefaultColors, div, point, prelude::*, px, relative, rgb, size,
};
use std::iter;
#[derive(Clone, Debug)]
pub struct TextContext {
font_size: f32,
line_height: f32,
type_scale: f32,
}
impl Default for TextContext {
fn default() -> Self {
TextContext {
font_size: 16.0,
line_height: 1.3,
type_scale: 1.33,
}
}
}
impl TextContext {
pub fn get_global(cx: &App) -> &Arc<TextContext> {
&cx.global::<GlobalTextContext>().0
}
}
#[derive(Clone, Debug)]
pub struct GlobalTextContext(pub Arc<TextContext>);
impl Deref for GlobalTextContext {
type Target = Arc<TextContext>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for GlobalTextContext {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl Global for GlobalTextContext {}
pub trait ActiveTextContext {
fn text_context(&self) -> &Arc<TextContext>;
}
impl ActiveTextContext for App {
fn text_context(&self) -> &Arc<TextContext> {
&self.global::<GlobalTextContext>().0
}
}
#[derive(Clone, PartialEq)]
pub struct SpecimenTheme {
pub bg: Hsla,
pub fg: Hsla,
}
impl Default for SpecimenTheme {
fn default() -> Self {
Self {
bg: gpui::white(),
fg: gpui::black(),
}
}
}
impl SpecimenTheme {
pub fn invert(&self) -> Self {
Self {
bg: self.fg,
fg: self.bg,
}
}
}
#[derive(Debug, Clone, PartialEq, IntoElement)]
struct Specimen {
id: ElementId,
scale: f32,
text_style: Option<TextStyle>,
string: SharedString,
invert: bool,
}
impl Specimen {
pub fn new(id: usize) -> Self {
let string = SharedString::new_static("The quick brown fox jumps over the lazy dog");
let id_string = format!("specimen-{}", id);
let id = ElementId::Name(id_string.into());
Self {
id,
scale: 1.0,
text_style: None,
string,
invert: false,
}
}
pub fn invert(mut self) -> Self {
self.invert = !self.invert;
self
}
pub fn scale(mut self, scale: f32) -> Self {
self.scale = scale;
self
}
}
impl RenderOnce for Specimen {
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
let rem_size = window.rem_size();
let scale = self.scale;
let global_style = cx.text_context();
let style_override = self.text_style;
let mut font_size = global_style.font_size;
let mut line_height = global_style.line_height;
if let Some(style_override) = style_override {
font_size = style_override.font_size.to_pixels(rem_size).0;
line_height = match style_override.line_height {
DefiniteLength::Absolute(absolute_len) => match absolute_len {
AbsoluteLength::Rems(absolute_len) => absolute_len.to_pixels(rem_size).0,
AbsoluteLength::Pixels(absolute_len) => absolute_len.0,
},
DefiniteLength::Fraction(value) => value,
};
}
let mut theme = SpecimenTheme::default();
if self.invert {
theme = theme.invert();
}
div()
.id(self.id)
.bg(theme.bg)
.text_color(theme.fg)
.text_size(px(font_size * scale))
.line_height(relative(line_height))
.p(px(10.0))
.child(self.string.clone())
}
}
#[derive(Debug, Clone, PartialEq, IntoElement)]
struct CharacterGrid {
scale: f32,
invert: bool,
text_style: Option<TextStyle>,
}
impl CharacterGrid {
pub fn new() -> Self {
Self {
scale: 1.0,
invert: false,
text_style: None,
}
}
pub fn scale(mut self, scale: f32) -> Self {
self.scale = scale;
self
}
}
impl RenderOnce for CharacterGrid {
fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
let mut theme = SpecimenTheme::default();
if self.invert {
theme = theme.invert();
}
let characters = vec![
"1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "A", "B", "C", "D", "E", "F", "G",
"H", "I", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y",
"Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "p", "q",
"r", "s", "t", "u", "v", "w", "x", "y", "z", "", "ſ", "ß", "ð", "Þ", "þ", "α", "β",
"Γ", "γ", "Δ", "δ", "η", "θ", "ι", "κ", "Λ", "λ", "μ", "ν", "ξ", "π", "τ", "υ", "φ",
"χ", "ψ", "", "а", "в", "Ж", "ж", "З", "з", "К", "к", "л", "м", "Н", "н", "Р", "р",
"У", "у", "ф", "ч", "ь", "ы", "Э", "э", "Я", "я", "ij", "öẋ", ".,", "⣝⣑", "~", "*",
"_", "^", "`", "'", "(", "{", "«", "#", "&", "@", "$", "¢", "%", "|", "?", "", "µ",
"", "<=", "!=", "==", "--", "++", "=>", "->",
];
let columns = 11;
let rows = characters.len().div_ceil(columns);
let grid_rows = (0..rows).map(|row_idx| {
let start_idx = row_idx * columns;
let end_idx = (start_idx + columns).min(characters.len());
div()
.w_full()
.flex()
.flex_row()
.children((start_idx..end_idx).map(|i| {
div()
.text_center()
.size(px(62.))
.bg(theme.bg)
.text_color(theme.fg)
.text_size(px(24.0))
.line_height(relative(1.0))
.child(characters[i])
}))
.when(end_idx - start_idx < columns, |d| {
d.children(
iter::repeat_with(|| div().flex_1()).take(columns - (end_idx - start_idx)),
)
})
});
div().p_4().gap_2().flex().flex_col().children(grid_rows)
}
}
struct TextExample {
next_id: usize,
}
impl TextExample {
fn next_id(&mut self) -> usize {
self.next_id += 1;
self.next_id
}
}
impl Render for TextExample {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let tcx = cx.text_context();
let colors = cx.default_colors().clone();
let type_scale = tcx.type_scale;
let step_down_2 = 1.0 / (type_scale * type_scale);
let step_down_1 = 1.0 / type_scale;
let base = 1.0;
let step_up_1 = base * type_scale;
let step_up_2 = step_up_1 * type_scale;
let step_up_3 = step_up_2 * type_scale;
let step_up_4 = step_up_3 * type_scale;
let step_up_5 = step_up_4 * type_scale;
let step_up_6 = step_up_5 * type_scale;
div()
.size_full()
.child(
div()
.id("text-example")
.overflow_y_scroll()
.overflow_x_hidden()
.bg(rgb(0xffffff))
.size_full()
.child(div().child(CharacterGrid::new().scale(base)))
.child(
div()
.child(Specimen::new(self.next_id()).scale(step_down_2))
.child(Specimen::new(self.next_id()).scale(step_down_2).invert())
.child(Specimen::new(self.next_id()).scale(step_down_1))
.child(Specimen::new(self.next_id()).scale(step_down_1).invert())
.child(Specimen::new(self.next_id()).scale(base))
.child(Specimen::new(self.next_id()).scale(base).invert())
.child(Specimen::new(self.next_id()).scale(step_up_1))
.child(Specimen::new(self.next_id()).scale(step_up_1).invert())
.child(Specimen::new(self.next_id()).scale(step_up_2))
.child(Specimen::new(self.next_id()).scale(step_up_2).invert())
.child(Specimen::new(self.next_id()).scale(step_up_3))
.child(Specimen::new(self.next_id()).scale(step_up_3).invert())
.child(Specimen::new(self.next_id()).scale(step_up_4))
.child(Specimen::new(self.next_id()).scale(step_up_4).invert())
.child(Specimen::new(self.next_id()).scale(step_up_5))
.child(Specimen::new(self.next_id()).scale(step_up_5).invert())
.child(Specimen::new(self.next_id()).scale(step_up_6))
.child(Specimen::new(self.next_id()).scale(step_up_6).invert()),
),
)
.child(div().w(px(240.)).h_full().bg(colors.container))
}
}
fn main() {
Application::new().run(|cx: &mut App| {
cx.set_menus(vec![Menu {
name: "GPUI Typography".into(),
items: vec![],
}]);
cx.init_colors();
cx.set_global(GlobalTextContext(Arc::new(TextContext::default())));
let window = cx
.open_window(
WindowOptions {
titlebar: Some(TitlebarOptions {
title: Some("GPUI Typography".into()),
..Default::default()
}),
window_bounds: Some(WindowBounds::Windowed(bounds(
point(px(0.0), px(0.0)),
size(px(920.), px(720.)),
))),
..Default::default()
},
|_window, cx| cx.new(|_cx| TextExample { next_id: 0 }),
)
.unwrap();
window
.update(cx, |_view, _window, cx| {
cx.activate(true);
})
.unwrap();
});
}

View File

@@ -38,9 +38,7 @@ use crate::{
PlatformDisplay, PlatformKeyboardLayout, Point, PromptBuilder, PromptHandle, PromptLevel,
Render, RenderImage, RenderablePromptHandle, Reservation, ScreenCaptureSource, SharedString,
SubscriberSet, Subscription, SvgRenderer, Task, TextSystem, Window, WindowAppearance,
WindowHandle, WindowId, WindowInvalidator,
colors::{Colors, GlobalColors},
current_platform, hash, init_app_menus,
WindowHandle, WindowId, WindowInvalidator, current_platform, hash, init_app_menus,
};
mod async_context;
@@ -1658,13 +1656,6 @@ impl App {
_ = window.drop_image(image);
}
}
/// Initializes gpui's default colors for the application.
///
/// These colors can be accessed through `cx.default_colors()`.
pub fn init_colors(&mut self) {
self.set_global(GlobalColors(Arc::new(Colors::default())));
}
}
impl AppContext for App {

View File

@@ -1,122 +0,0 @@
use crate::{App, Global, Rgba, Window, WindowAppearance, rgb};
use std::ops::Deref;
use std::sync::Arc;
/// The default set of colors for gpui.
///
/// These are used for styling base components, examples and more.
#[derive(Clone, Debug)]
pub struct Colors {
/// Text color
pub text: Rgba,
/// Selected text color
pub selected_text: Rgba,
/// Background color
pub background: Rgba,
/// Disabled color
pub disabled: Rgba,
/// Selected color
pub selected: Rgba,
/// Border color
pub border: Rgba,
/// Separator color
pub separator: Rgba,
/// Container color
pub container: Rgba,
}
impl Default for Colors {
fn default() -> Self {
Self::light()
}
}
impl Colors {
/// Returns the default colors for the given window appearance.
pub fn for_appearance(window: &Window) -> Self {
match window.appearance() {
WindowAppearance::Light | WindowAppearance::VibrantLight => Self::light(),
WindowAppearance::Dark | WindowAppearance::VibrantDark => Self::dark(),
}
}
/// Returns the default dark colors.
pub fn dark() -> Self {
Self {
text: rgb(0xffffff),
selected_text: rgb(0xffffff),
disabled: rgb(0x565656),
selected: rgb(0x2457ca),
background: rgb(0x222222),
border: rgb(0x000000),
separator: rgb(0xd9d9d9),
container: rgb(0x262626),
}
}
/// Returns the default light colors.
pub fn light() -> Self {
Self {
text: rgb(0x252525),
selected_text: rgb(0xffffff),
background: rgb(0xffffff),
disabled: rgb(0xb0b0b0),
selected: rgb(0x2a63d9),
border: rgb(0xd9d9d9),
separator: rgb(0xe6e6e6),
container: rgb(0xf4f5f5),
}
}
/// Get [Colors] from the global state
pub fn get_global(cx: &App) -> &Arc<Colors> {
&cx.global::<GlobalColors>().0
}
}
/// Get [Colors] from the global state
#[derive(Clone, Debug)]
pub struct GlobalColors(pub Arc<Colors>);
impl Deref for GlobalColors {
type Target = Arc<Colors>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Global for GlobalColors {}
/// Implement this trait to allow global [Color] access via `cx.default_colors()`.
pub trait DefaultColors {
/// Returns the default [`gpui::Colors`]
fn default_colors(&self) -> &Arc<Colors>;
}
impl DefaultColors for App {
fn default_colors(&self) -> &Arc<Colors> {
&self.global::<GlobalColors>().0
}
}
/// The appearance of the base GPUI colors, used to style GPUI elements
///
/// Varies based on the system's current [`WindowAppearance`].
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub enum DefaultAppearance {
/// Use the set of colors for light appearances.
#[default]
Light,
/// Use the set of colors for dark appearances.
Dark,
}
impl From<WindowAppearance> for DefaultAppearance {
fn from(appearance: WindowAppearance) -> Self {
match appearance {
WindowAppearance::Light | WindowAppearance::VibrantLight => Self::Light,
WindowAppearance::Dark | WindowAppearance::VibrantDark => Self::Dark,
}
}
}

View File

@@ -1,187 +0,0 @@
#![allow(missing_docs)]
use super::{FocusableElement, InteractiveElement, Interactivity, StatefulInteractiveElement};
use crate::{
AbsoluteLength, AnyElement, App, BorderStyle, ClickEvent, CornersRefinement, Edges,
EdgesRefinement, Element, ElementId, GlobalElementId, Hitbox, IntoElement, LayoutId,
ParentElement, SharedString, StyleRefinement, Styled, TextStyleRefinement, Window,
colors::Colors, px,
};
use refineable::Refineable;
use smallvec::SmallVec;
use taffy::FlexDirection;
use util::default;
pub fn button(id: impl Into<ElementId>) -> Button {
Button {
id: id.into(),
interactivity: Interactivity::default(),
children: SmallVec::new(),
}
}
pub struct Button {
id: ElementId,
interactivity: Interactivity,
children: SmallVec<[AnyElement; 2]>,
}
impl Element for Button {
type RequestLayoutState = ();
type PrepaintState = Option<Hitbox>;
fn id(&self) -> Option<ElementId> {
Some(self.id.clone())
}
fn request_layout(
&mut self,
global_id: Option<&GlobalElementId>,
window: &mut Window,
cx: &mut App,
) -> (LayoutId, Self::RequestLayoutState) {
// Get a LayoutId, an identifier Taffy uses to indicate a unique layout element
let layout_id =
self.interactivity
.request_layout(global_id, window, cx, |style, window, cx| {
let mut child_layout_ids = Vec::new();
for child in &mut self.children {
let child_layout_id = child.request_layout(window, cx);
child_layout_ids.push(child_layout_id);
}
window.request_layout(style, child_layout_ids, cx)
});
// Initialize the layout state
let layout_state = ();
(layout_id, layout_state)
}
fn prepaint(
&mut self,
global_id: Option<&crate::GlobalElementId>,
bounds: crate::Bounds<crate::Pixels>,
_request_layout: &mut Self::RequestLayoutState,
window: &mut Window,
cx: &mut App,
) -> Self::PrepaintState {
if let Some(handle) = self.interactivity.scroll_anchor.as_ref() {
*handle.last_origin.borrow_mut() = bounds.origin - window.element_offset();
}
let content_size = bounds.size;
// Prepaint children
for child in &mut self.children {
child.prepaint(window, cx);
}
self.interactivity.prepaint(
global_id,
bounds,
content_size,
window,
cx,
|_style, _scroll_offset, hitbox, _window, _cx| hitbox,
)
}
fn paint(
&mut self,
global_id: Option<&crate::GlobalElementId>,
bounds: crate::Bounds<crate::Pixels>,
_request_layout: &mut Self::RequestLayoutState,
hitbox: &mut Option<Hitbox>,
window: &mut Window,
cx: &mut App,
) {
let colors = Colors::for_appearance(window);
let text_style = self.text_style().clone();
let abs_px = |number: f32| AbsoluteLength::Pixels(px(number));
let mut text_style = if let Some(style) = text_style {
style.clone()
} else {
TextStyleRefinement::default()
};
let new_style = StyleRefinement {
background: Some(colors.container.into()),
text: Some(TextStyleRefinement {
color: Some(colors.text.into()),
font_size: Some(px(13.).into()),
..text_style
}),
border_color: Some(colors.border.into()),
border_style: Some(BorderStyle::Solid),
border_widths: EdgesRefinement {
top: Some(abs_px(1.)),
right: Some(abs_px(1.)),
bottom: Some(abs_px(1.)),
left: Some(abs_px(1.)),
},
corner_radii: CornersRefinement {
top_left: Some(abs_px(4.)),
top_right: Some(abs_px(4.)),
bottom_left: Some(abs_px(4.)),
bottom_right: Some(abs_px(4.)),
},
flex_direction: Some(FlexDirection::Row),
flex_grow: Some(0.),
..StyleRefinement::default()
};
let refined = self.style().refine(&new_style);
self.interactivity.paint(
global_id,
bounds,
hitbox.as_ref(),
window,
cx,
|style, window, cx| {
for child in &mut self.children {
child.paint(window, cx);
}
},
)
}
}
impl IntoElement for Button {
type Element = Self;
fn into_element(self) -> Self::Element {
self
}
}
impl Styled for Button {
fn style(&mut self) -> &mut StyleRefinement {
&mut self.interactivity.base_style
}
}
impl InteractiveElement for Button {
fn interactivity(&mut self) -> &mut Interactivity {
&mut self.interactivity
}
}
impl StatefulInteractiveElement for Button {}
impl FocusableElement for Button {}
impl ParentElement for Button {
fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
self.children.extend(elements)
}
}
impl Button {
pub fn on_click(
mut self,
callback: Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>,
) -> Self {
self.interactivity
.on_click(move |event, window, cx| callback(event, window, cx));
self
}
}

View File

@@ -0,0 +1,115 @@
use crate::{Hsla, Rgba, WindowAppearance, rgb};
/// The appearance of the base GPUI colors, used to style GPUI elements
///
/// Varies based on the system's current [`WindowAppearance`].
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub enum DefaultThemeAppearance {
/// Use the set of colors for light appearances.
#[default]
Light,
/// Use the set of colors for dark appearances.
Dark,
}
impl From<WindowAppearance> for DefaultThemeAppearance {
fn from(appearance: WindowAppearance) -> Self {
match appearance {
WindowAppearance::Light | WindowAppearance::VibrantLight => Self::Light,
WindowAppearance::Dark | WindowAppearance::VibrantDark => Self::Dark,
}
}
}
/// Returns the default colors for the given appearance.
pub fn colors(appearance: DefaultThemeAppearance) -> DefaultColors {
match appearance {
DefaultThemeAppearance::Light => DefaultColors::light(),
DefaultThemeAppearance::Dark => DefaultColors::dark(),
}
}
/// A collection of colors.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct DefaultColors {
text: Rgba,
selected_text: Rgba,
background: Rgba,
disabled: Rgba,
selected: Rgba,
border: Rgba,
separator: Rgba,
container: Rgba,
}
impl DefaultColors {
/// Returns the default dark colors.
pub fn dark() -> Self {
Self {
text: rgb(0xffffff),
selected_text: rgb(0xffffff),
disabled: rgb(0x565656),
selected: rgb(0x2457ca),
background: rgb(0x222222),
border: rgb(0x000000),
separator: rgb(0xd9d9d9),
container: rgb(0x262626),
}
}
/// Returns the default light colors.
pub fn light() -> Self {
Self {
text: rgb(0x252525),
selected_text: rgb(0xffffff),
background: rgb(0xffffff),
disabled: rgb(0xb0b0b0),
selected: rgb(0x2a63d9),
border: rgb(0xd9d9d9),
separator: rgb(0xe6e6e6),
container: rgb(0xf4f5f5),
}
}
}
/// A default GPUI color.
#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::EnumIter)]
pub enum DefaultColor {
/// Text color
Text,
/// Selected text color
SelectedText,
/// Background color
Background,
/// Disabled color
Disabled,
/// Selected color
Selected,
/// Border color
Border,
/// Separator color
Separator,
/// Container color
Container,
}
impl DefaultColor {
/// Returns the RGBA color for the given color type.
pub fn color(&self, colors: &DefaultColors) -> Rgba {
match self {
DefaultColor::Text => colors.text,
DefaultColor::SelectedText => colors.selected_text,
DefaultColor::Background => colors.background,
DefaultColor::Disabled => colors.disabled,
DefaultColor::Selected => colors.selected,
DefaultColor::Border => colors.border,
DefaultColor::Separator => colors.separator,
DefaultColor::Container => colors.container,
}
}
/// Returns the HSLA color for the given color type.
pub fn hsla(&self, colors: &DefaultColors) -> Hsla {
self.color(colors).into()
}
}

View File

@@ -2877,8 +2877,8 @@ where
/// Contrary to [ScrollHandle::scroll_to_item], an anchored element does not have to be an immediate child of the parent.
#[derive(Clone)]
pub struct ScrollAnchor {
pub(super) handle: ScrollHandle,
pub(super) last_origin: Rc<RefCell<Point<Pixels>>>,
handle: ScrollHandle,
last_origin: Rc<RefCell<Point<Pixels>>>,
}
impl ScrollAnchor {

View File

@@ -1,7 +1,7 @@
mod anchored;
mod animation;
mod button;
mod canvas;
mod common;
mod deferred;
mod div;
mod image_cache;
@@ -14,8 +14,8 @@ mod uniform_list;
pub use anchored::*;
pub use animation::*;
pub use button::*;
pub use canvas::*;
pub use common::*;
pub use deferred::*;
pub use div::*;
pub use image_cache::*;

View File

@@ -73,8 +73,6 @@ mod asset_cache;
mod assets;
mod bounds_tree;
mod color;
/// The default colors used by GPUI.
pub mod colors;
mod element;
mod elements;
mod executor;

View File

@@ -426,8 +426,8 @@ impl<P: LinuxClient + 'static> Platform for P {
fn app_path(&self) -> Result<PathBuf> {
// get the path of the executable of the current process
let app_path = env::current_exe()?;
return Ok(app_path);
let exe_path = env::current_exe()?;
Ok(exe_path)
}
fn set_menus(&self, menus: Vec<Menu>, _keymap: &Keymap) {

View File

@@ -983,7 +983,7 @@ impl Clipboard {
// format that the contents can be converted to
format_atoms[0..IMAGE_FORMAT_COUNT].copy_from_slice(&image_format_atoms);
format_atoms[IMAGE_FORMAT_COUNT..].copy_from_slice(&text_format_atoms);
debug_assert!(!format_atoms.contains(&atom_none));
debug_assert!(!format_atoms.iter().any(|&a| a == atom_none));
let result = self.inner.read(&format_atoms, selection)?;

View File

@@ -137,7 +137,10 @@ fn add_recent_folders(
let tasks: IObjectCollection =
CoCreateInstance(&EnumerableObjectCollection, None, CLSCTX_INPROC_SERVER)?;
for folder_path in entries.iter().filter(|path| !removed.contains(path)) {
for folder_path in entries
.iter()
.filter(|path| !is_item_in_array(path, removed))
{
let argument = HSTRING::from(
folder_path
.iter()
@@ -178,6 +181,11 @@ fn add_recent_folders(
}
}
#[inline]
fn is_item_in_array(item: &SmallVec<[PathBuf; 2]>, removed: &Vec<SmallVec<[PathBuf; 2]>>) -> bool {
removed.iter().any(|removed_item| removed_item == item)
}
fn create_shell_link(
argument: HSTRING,
description: HSTRING,

View File

@@ -293,35 +293,37 @@ fn handle_mouse_move_msg(
start_tracking_mouse(handle, &state_ptr, TME_LEAVE);
let mut lock = state_ptr.state.borrow_mut();
let Some(mut func) = lock.callbacks.input.take() else {
return Some(1);
};
let scale_factor = lock.scale_factor;
drop(lock);
let pressed_button = match MODIFIERKEYS_FLAGS(wparam.loword() as u32) {
flags if flags.contains(MK_LBUTTON) => Some(MouseButton::Left),
flags if flags.contains(MK_RBUTTON) => Some(MouseButton::Right),
flags if flags.contains(MK_MBUTTON) => Some(MouseButton::Middle),
flags if flags.contains(MK_XBUTTON1) => {
Some(MouseButton::Navigate(NavigationDirection::Back))
}
flags if flags.contains(MK_XBUTTON2) => {
Some(MouseButton::Navigate(NavigationDirection::Forward))
}
_ => None,
};
let x = lparam.signed_loword() as f32;
let y = lparam.signed_hiword() as f32;
let input = PlatformInput::MouseMove(MouseMoveEvent {
position: logical_point(x, y, scale_factor),
pressed_button,
modifiers: current_modifiers(),
});
let handled = !func(input).propagate;
state_ptr.state.borrow_mut().callbacks.input = Some(func);
if handled { Some(0) } else { Some(1) }
if let Some(mut callback) = lock.callbacks.input.take() {
let scale_factor = lock.scale_factor;
drop(lock);
let pressed_button = match MODIFIERKEYS_FLAGS(wparam.loword() as u32) {
flags if flags.contains(MK_LBUTTON) => Some(MouseButton::Left),
flags if flags.contains(MK_RBUTTON) => Some(MouseButton::Right),
flags if flags.contains(MK_MBUTTON) => Some(MouseButton::Middle),
flags if flags.contains(MK_XBUTTON1) => {
Some(MouseButton::Navigate(NavigationDirection::Back))
}
flags if flags.contains(MK_XBUTTON2) => {
Some(MouseButton::Navigate(NavigationDirection::Forward))
}
_ => None,
};
let x = lparam.signed_loword() as f32;
let y = lparam.signed_hiword() as f32;
let event = MouseMoveEvent {
position: logical_point(x, y, scale_factor),
pressed_button,
modifiers: current_modifiers(),
};
let result = if callback(PlatformInput::MouseMove(event)).default_prevented {
Some(0)
} else {
Some(1)
};
state_ptr.state.borrow_mut().callbacks.input = Some(callback);
return result;
}
Some(1)
}
fn handle_mouse_leave_msg(state_ptr: Rc<WindowsWindowStatePtr>) -> Option<isize> {
@@ -437,10 +439,14 @@ fn handle_keyup_msg(
};
drop(lock);
let handled = !func(input).propagate;
let result = if func(input).default_prevented {
Some(0)
} else {
Some(1)
};
state_ptr.state.borrow_mut().callbacks.input = Some(func);
if handled { Some(0) } else { Some(1) }
result
}
fn handle_char_msg(wparam: WPARAM, state_ptr: Rc<WindowsWindowStatePtr>) -> Option<isize> {
@@ -473,27 +479,32 @@ fn handle_mouse_down_msg(
) -> Option<isize> {
unsafe { SetCapture(handle) };
let mut lock = state_ptr.state.borrow_mut();
let Some(mut func) = lock.callbacks.input.take() else {
return Some(1);
};
let x = lparam.signed_loword();
let y = lparam.signed_hiword();
let physical_point = point(DevicePixels(x as i32), DevicePixels(y as i32));
let click_count = lock.click_state.update(button, physical_point);
let scale_factor = lock.scale_factor;
drop(lock);
if let Some(mut callback) = lock.callbacks.input.take() {
let x = lparam.signed_loword() as f32;
let y = lparam.signed_hiword() as f32;
let physical_point = point(DevicePixels(x as i32), DevicePixels(y as i32));
let click_count = lock.click_state.update(button, physical_point);
let scale_factor = lock.scale_factor;
drop(lock);
let input = PlatformInput::MouseDown(MouseDownEvent {
button,
position: logical_point(x as f32, y as f32, scale_factor),
modifiers: current_modifiers(),
click_count,
first_mouse: false,
});
let handled = !func(input).propagate;
state_ptr.state.borrow_mut().callbacks.input = Some(func);
let event = MouseDownEvent {
button,
position: logical_point(x, y, scale_factor),
modifiers: current_modifiers(),
click_count,
first_mouse: false,
};
let result = if callback(PlatformInput::MouseDown(event)).default_prevented {
Some(0)
} else {
Some(1)
};
state_ptr.state.borrow_mut().callbacks.input = Some(callback);
if handled { Some(0) } else { Some(1) }
result
} else {
Some(1)
}
}
fn handle_mouse_up_msg(
@@ -504,25 +515,30 @@ fn handle_mouse_up_msg(
) -> Option<isize> {
unsafe { ReleaseCapture().log_err() };
let mut lock = state_ptr.state.borrow_mut();
let Some(mut func) = lock.callbacks.input.take() else {
return Some(1);
};
let x = lparam.signed_loword() as f32;
let y = lparam.signed_hiword() as f32;
let click_count = lock.click_state.current_count;
let scale_factor = lock.scale_factor;
drop(lock);
if let Some(mut callback) = lock.callbacks.input.take() {
let x = lparam.signed_loword() as f32;
let y = lparam.signed_hiword() as f32;
let click_count = lock.click_state.current_count;
let scale_factor = lock.scale_factor;
drop(lock);
let input = PlatformInput::MouseUp(MouseUpEvent {
button,
position: logical_point(x, y, scale_factor),
modifiers: current_modifiers(),
click_count,
});
let handled = !func(input).propagate;
state_ptr.state.borrow_mut().callbacks.input = Some(func);
let event = MouseUpEvent {
button,
position: logical_point(x, y, scale_factor),
modifiers: current_modifiers(),
click_count,
};
let result = if callback(PlatformInput::MouseUp(event)).default_prevented {
Some(0)
} else {
Some(1)
};
state_ptr.state.borrow_mut().callbacks.input = Some(callback);
if handled { Some(0) } else { Some(1) }
result
} else {
Some(1)
}
}
fn handle_xbutton_msg(
@@ -548,42 +564,46 @@ fn handle_mouse_wheel_msg(
) -> Option<isize> {
let modifiers = current_modifiers();
let mut lock = state_ptr.state.borrow_mut();
let Some(mut func) = lock.callbacks.input.take() else {
return Some(1);
};
let scale_factor = lock.scale_factor;
let wheel_scroll_amount = match modifiers.shift {
true => lock.system_settings.mouse_wheel_settings.wheel_scroll_chars,
false => lock.system_settings.mouse_wheel_settings.wheel_scroll_lines,
};
drop(lock);
if let Some(mut callback) = lock.callbacks.input.take() {
let scale_factor = lock.scale_factor;
let wheel_scroll_amount = match modifiers.shift {
true => lock.system_settings.mouse_wheel_settings.wheel_scroll_chars,
false => lock.system_settings.mouse_wheel_settings.wheel_scroll_lines,
};
drop(lock);
let wheel_distance =
(wparam.signed_hiword() as f32 / WHEEL_DELTA as f32) * wheel_scroll_amount as f32;
let mut cursor_point = POINT {
x: lparam.signed_loword().into(),
y: lparam.signed_hiword().into(),
};
unsafe { ScreenToClient(handle, &mut cursor_point).ok().log_err() };
let event = ScrollWheelEvent {
position: logical_point(cursor_point.x as f32, cursor_point.y as f32, scale_factor),
delta: ScrollDelta::Lines(match modifiers.shift {
true => Point {
x: wheel_distance,
y: 0.0,
},
false => Point {
y: wheel_distance,
x: 0.0,
},
}),
modifiers: current_modifiers(),
touch_phase: TouchPhase::Moved,
};
let result = if callback(PlatformInput::ScrollWheel(event)).default_prevented {
Some(0)
} else {
Some(1)
};
state_ptr.state.borrow_mut().callbacks.input = Some(callback);
let wheel_distance =
(wparam.signed_hiword() as f32 / WHEEL_DELTA as f32) * wheel_scroll_amount as f32;
let mut cursor_point = POINT {
x: lparam.signed_loword().into(),
y: lparam.signed_hiword().into(),
};
unsafe { ScreenToClient(handle, &mut cursor_point).ok().log_err() };
let input = PlatformInput::ScrollWheel(ScrollWheelEvent {
position: logical_point(cursor_point.x as f32, cursor_point.y as f32, scale_factor),
delta: ScrollDelta::Lines(match modifiers.shift {
true => Point {
x: wheel_distance,
y: 0.0,
},
false => Point {
y: wheel_distance,
x: 0.0,
},
}),
modifiers,
touch_phase: TouchPhase::Moved,
});
let handled = !func(input).propagate;
state_ptr.state.borrow_mut().callbacks.input = Some(func);
if handled { Some(0) } else { Some(1) }
result
} else {
Some(1)
}
}
fn handle_mouse_horizontal_wheel_msg(
@@ -593,33 +613,37 @@ fn handle_mouse_horizontal_wheel_msg(
state_ptr: Rc<WindowsWindowStatePtr>,
) -> Option<isize> {
let mut lock = state_ptr.state.borrow_mut();
let Some(mut func) = lock.callbacks.input.take() else {
return Some(1);
};
let scale_factor = lock.scale_factor;
let wheel_scroll_chars = lock.system_settings.mouse_wheel_settings.wheel_scroll_chars;
drop(lock);
if let Some(mut callback) = lock.callbacks.input.take() {
let scale_factor = lock.scale_factor;
let wheel_scroll_chars = lock.system_settings.mouse_wheel_settings.wheel_scroll_chars;
drop(lock);
let wheel_distance =
(-wparam.signed_hiword() as f32 / WHEEL_DELTA as f32) * wheel_scroll_chars as f32;
let mut cursor_point = POINT {
x: lparam.signed_loword().into(),
y: lparam.signed_hiword().into(),
};
unsafe { ScreenToClient(handle, &mut cursor_point).ok().log_err() };
let event = ScrollWheelEvent {
position: logical_point(cursor_point.x as f32, cursor_point.y as f32, scale_factor),
delta: ScrollDelta::Lines(Point {
x: wheel_distance,
y: 0.0,
}),
modifiers: current_modifiers(),
touch_phase: TouchPhase::Moved,
};
let result = if callback(PlatformInput::ScrollWheel(event)).default_prevented {
Some(0)
} else {
Some(1)
};
state_ptr.state.borrow_mut().callbacks.input = Some(callback);
let wheel_distance =
(-wparam.signed_hiword() as f32 / WHEEL_DELTA as f32) * wheel_scroll_chars as f32;
let mut cursor_point = POINT {
x: lparam.signed_loword().into(),
y: lparam.signed_hiword().into(),
};
unsafe { ScreenToClient(handle, &mut cursor_point).ok().log_err() };
let event = PlatformInput::ScrollWheel(ScrollWheelEvent {
position: logical_point(cursor_point.x as f32, cursor_point.y as f32, scale_factor),
delta: ScrollDelta::Lines(Point {
x: wheel_distance,
y: 0.0,
}),
modifiers: current_modifiers(),
touch_phase: TouchPhase::Moved,
});
let handled = !func(event).propagate;
state_ptr.state.borrow_mut().callbacks.input = Some(func);
if handled { Some(0) } else { Some(1) }
result
} else {
Some(1)
}
}
fn retrieve_caret_position(state_ptr: &Rc<WindowsWindowStatePtr>) -> Option<POINT> {
@@ -784,10 +808,10 @@ fn handle_activate_msg(
.executor
.spawn(async move {
let mut lock = this.state.borrow_mut();
if let Some(mut func) = lock.callbacks.active_status_change.take() {
if let Some(mut cb) = lock.callbacks.active_status_change.take() {
drop(lock);
func(activated);
this.state.borrow_mut().callbacks.active_status_change = Some(func);
cb(activated);
this.state.borrow_mut().callbacks.active_status_change = Some(cb);
}
})
.detach();
@@ -854,7 +878,7 @@ fn handle_display_change_msg(handle: HWND, state_ptr: Rc<WindowsWindowStatePtr>)
// Because WM_DPICHANGED, WM_MOVE, WM_SIZE will come first, window reposition and resize
// are handled there.
// So we only care about if monitor is disconnected.
let previous_monitor = state_ptr.state.borrow().display;
let previous_monitor = state_ptr.as_ref().state.borrow().display;
if WindowsDisplay::is_connected(previous_monitor.handle) {
// we are fine, other display changed
return None;
@@ -872,7 +896,7 @@ fn handle_display_change_msg(handle: HWND, state_ptr: Rc<WindowsWindowStatePtr>)
return None;
}
let new_display = WindowsDisplay::new_with_handle(new_monitor);
state_ptr.state.borrow_mut().display = new_display;
state_ptr.as_ref().state.borrow_mut().display = new_display;
Some(0)
}
@@ -956,24 +980,30 @@ fn handle_nc_mouse_move_msg(
start_tracking_mouse(handle, &state_ptr, TME_LEAVE | TME_NONCLIENT);
let mut lock = state_ptr.state.borrow_mut();
let mut func = lock.callbacks.input.take()?;
let scale_factor = lock.scale_factor;
drop(lock);
if let Some(mut callback) = lock.callbacks.input.take() {
let scale_factor = lock.scale_factor;
drop(lock);
let mut cursor_point = POINT {
x: lparam.signed_loword().into(),
y: lparam.signed_hiword().into(),
};
unsafe { ScreenToClient(handle, &mut cursor_point).ok().log_err() };
let event = MouseMoveEvent {
position: logical_point(cursor_point.x as f32, cursor_point.y as f32, scale_factor),
pressed_button: None,
modifiers: current_modifiers(),
};
let result = if callback(PlatformInput::MouseMove(event)).default_prevented {
Some(0)
} else {
Some(1)
};
state_ptr.state.borrow_mut().callbacks.input = Some(callback);
let mut cursor_point = POINT {
x: lparam.signed_loword().into(),
y: lparam.signed_hiword().into(),
};
unsafe { ScreenToClient(handle, &mut cursor_point).ok().log_err() };
let input = PlatformInput::MouseMove(MouseMoveEvent {
position: logical_point(cursor_point.x as f32, cursor_point.y as f32, scale_factor),
pressed_button: None,
modifiers: current_modifiers(),
});
let handled = !func(input).propagate;
state_ptr.state.borrow_mut().callbacks.input = Some(func);
if handled { Some(0) } else { None }
result
} else {
None
}
}
fn handle_nc_mouse_down_msg(
@@ -988,7 +1018,7 @@ fn handle_nc_mouse_down_msg(
}
let mut lock = state_ptr.state.borrow_mut();
if let Some(mut func) = lock.callbacks.input.take() {
if let Some(mut callback) = lock.callbacks.input.take() {
let scale_factor = lock.scale_factor;
let mut cursor_point = POINT {
x: lparam.signed_loword().into(),
@@ -998,19 +1028,22 @@ fn handle_nc_mouse_down_msg(
let physical_point = point(DevicePixels(cursor_point.x), DevicePixels(cursor_point.y));
let click_count = lock.click_state.update(button, physical_point);
drop(lock);
let input = PlatformInput::MouseDown(MouseDownEvent {
let event = MouseDownEvent {
button,
position: logical_point(cursor_point.x as f32, cursor_point.y as f32, scale_factor),
modifiers: current_modifiers(),
click_count,
first_mouse: false,
});
let handled = !func(input).propagate;
state_ptr.state.borrow_mut().callbacks.input = Some(func);
};
let result = if callback(PlatformInput::MouseDown(event)).default_prevented {
Some(0)
} else {
None
};
state_ptr.state.borrow_mut().callbacks.input = Some(callback);
if handled {
return Some(0);
if result.is_some() {
return result;
}
} else {
drop(lock);
@@ -1042,26 +1075,28 @@ fn handle_nc_mouse_up_msg(
}
let mut lock = state_ptr.state.borrow_mut();
if let Some(mut func) = lock.callbacks.input.take() {
if let Some(mut callback) = lock.callbacks.input.take() {
let scale_factor = lock.scale_factor;
drop(lock);
let mut cursor_point = POINT {
x: lparam.signed_loword().into(),
y: lparam.signed_hiword().into(),
};
unsafe { ScreenToClient(handle, &mut cursor_point).ok().log_err() };
let input = PlatformInput::MouseUp(MouseUpEvent {
let event = MouseUpEvent {
button,
position: logical_point(cursor_point.x as f32, cursor_point.y as f32, scale_factor),
modifiers: current_modifiers(),
click_count: 1,
});
let handled = !func(input).propagate;
state_ptr.state.borrow_mut().callbacks.input = Some(func);
if handled {
return Some(0);
};
let result = if callback(PlatformInput::MouseUp(event)).default_prevented {
Some(0)
} else {
None
};
state_ptr.state.borrow_mut().callbacks.input = Some(callback);
if result.is_some() {
return result;
}
} else {
drop(lock);
@@ -1069,27 +1104,35 @@ fn handle_nc_mouse_up_msg(
let last_pressed = state_ptr.state.borrow_mut().nc_button_pressed.take();
if button == MouseButton::Left && last_pressed.is_some() {
let handled = match (wparam.0 as u32, last_pressed.unwrap()) {
(HTMINBUTTON, HTMINBUTTON) => {
unsafe { ShowWindowAsync(handle, SW_MINIMIZE).ok().log_err() };
true
}
(HTMAXBUTTON, HTMAXBUTTON) => {
if state_ptr.state.borrow().is_maximized() {
unsafe { ShowWindowAsync(handle, SW_NORMAL).ok().log_err() };
} else {
unsafe { ShowWindowAsync(handle, SW_MAXIMIZE).ok().log_err() };
let last_button = last_pressed.unwrap();
let mut handled = false;
match wparam.0 as u32 {
HTMINBUTTON => {
if last_button == HTMINBUTTON {
unsafe { ShowWindowAsync(handle, SW_MINIMIZE).ok().log_err() };
handled = true;
}
true
}
(HTCLOSE, HTCLOSE) => {
unsafe {
PostMessageW(Some(handle), WM_CLOSE, WPARAM::default(), LPARAM::default())
.log_err()
};
true
HTMAXBUTTON => {
if last_button == HTMAXBUTTON {
if state_ptr.state.borrow().is_maximized() {
unsafe { ShowWindowAsync(handle, SW_NORMAL).ok().log_err() };
} else {
unsafe { ShowWindowAsync(handle, SW_MAXIMIZE).ok().log_err() };
}
handled = true;
}
}
_ => false,
HTCLOSE => {
if last_button == HTCLOSE {
unsafe {
PostMessageW(Some(handle), WM_CLOSE, WPARAM::default(), LPARAM::default())
.log_err()
};
handled = true;
}
}
_ => {}
};
if handled {
return Some(0);

View File

@@ -755,10 +755,6 @@ pub struct LanguageConfig {
/// A list of preferred debuggers for this language.
#[serde(default)]
pub debuggers: IndexSet<SharedString>,
/// Whether to treat documentation comment of this language differently by
/// auto adding prefix on new line, adjusting the indenting , etc.
#[serde(default)]
pub documentation: Option<DocumentationConfig>,
}
#[derive(Clone, Debug, Serialize, Deserialize, Default, JsonSchema)]
@@ -809,19 +805,6 @@ pub struct JsxTagAutoCloseConfig {
pub erroneous_close_tag_name_node_name: Option<String>,
}
/// The configuration for documentation block for this language.
#[derive(Clone, Deserialize, JsonSchema)]
pub struct DocumentationConfig {
/// A start tag of documentation block.
pub start: Arc<str>,
/// A end tag of documentation block.
pub end: Arc<str>,
/// A character to add as a prefix when a new line is added to a documentation block.
pub prefix: Arc<str>,
/// A indent to add for prefix and end line upon new line.
pub tab_size: NonZeroU32,
}
/// Represents a language for the given range. Some languages (e.g. HTML)
/// interleave several languages together, thus a single buffer might actually contain
/// several nested scopes.
@@ -900,7 +883,6 @@ impl Default for LanguageConfig {
completion_query_characters: Default::default(),
debuggers: Default::default(),
significant_indentation: Default::default(),
documentation: None,
}
}
}
@@ -1820,14 +1802,6 @@ impl LanguageScope {
.unwrap_or(false)
}
/// Returns config to documentation block for this language.
///
/// Used for documentation styles that require a leading character on each line,
/// such as the asterisk in JSDoc, Javadoc, etc.
pub fn documentation(&self) -> Option<&DocumentationConfig> {
self.language.config.documentation.as_ref()
}
/// Returns a list of bracket pairs for a given language with an additional
/// piece of information about whether the particular bracket pair is currently active for a given language.
pub fn brackets(&self) -> impl Iterator<Item = (&BracketPair, bool)> {
@@ -1859,9 +1833,9 @@ impl LanguageScope {
pub fn language_allowed(&self, name: &LanguageServerName) -> bool {
let config = &self.language.config;
let opt_in_servers = &config.scope_opt_in_language_servers;
if opt_in_servers.contains(name) {
if opt_in_servers.iter().any(|o| *o == *name) {
if let Some(over) = self.config_override() {
over.opt_into_language_servers.contains(name)
over.opt_into_language_servers.iter().any(|o| *o == *name)
} else {
false
}

View File

@@ -4,7 +4,7 @@ use crate::{
};
use collections::BTreeMap;
use gpui::{App, Context, Entity, EventEmitter, Global, prelude::*};
use std::{str::FromStr, sync::Arc};
use std::sync::Arc;
use util::maybe;
pub fn init(cx: &mut App) {
@@ -27,36 +27,11 @@ pub struct LanguageModelRegistry {
inline_alternatives: Vec<Arc<dyn LanguageModel>>,
}
#[derive(Debug)]
pub struct SelectedModel {
pub provider: LanguageModelProviderId,
pub model: LanguageModelId,
}
impl FromStr for SelectedModel {
type Err = String;
/// Parse string identifiers like `provider_id/model_id` into a `SelectedModel`
fn from_str(id: &str) -> Result<SelectedModel, Self::Err> {
let parts: Vec<&str> = id.split('/').collect();
let [provider_id, model_id] = parts.as_slice() else {
return Err(format!(
"Invalid model identifier format: `{}`. Expected `provider_id/model_id`",
id
));
};
if provider_id.is_empty() || model_id.is_empty() {
return Err(format!("Provider and model ids can't be empty: `{}`", id));
}
Ok(SelectedModel {
provider: LanguageModelProviderId(provider_id.to_string().into()),
model: LanguageModelId(model_id.to_string().into()),
})
}
}
#[derive(Clone)]
pub struct ConfiguredModel {
pub provider: Arc<dyn LanguageModelProvider>,

View File

@@ -12,4 +12,3 @@ brackets = [
{ start = "/*", end = " */", close = true, newline = false, not_in = ["string", "comment"] },
]
debuggers = ["CodeLLDB", "GDB"]
documentation = { start = "/*", end = "*/", prefix = "* ", tab_size = 1 }

View File

@@ -12,4 +12,3 @@ brackets = [
{ start = "/*", end = " */", close = true, newline = false, not_in = ["string", "comment"] },
]
debuggers = ["CodeLLDB", "GDB"]
documentation = { start = "/*", end = "*/", prefix = "* ", tab_size = 1 }

View File

@@ -15,4 +15,3 @@ brackets = [
tab_size = 4
hard_tabs = true
debuggers = ["Delve"]
documentation = { start = "/*", end = "*/", prefix = "* ", tab_size = 1 }

View File

@@ -20,7 +20,6 @@ tab_size = 2
scope_opt_in_language_servers = ["tailwindcss-language-server", "emmet-language-server"]
prettier_parser_name = "babel"
debuggers = ["JavaScript"]
documentation = { start = "/**", end = "*/", prefix = "* ", tab_size = 1 }
[jsx_tag_auto_close]
open_tag_node_name = "jsx_opening_element"

View File

@@ -2,7 +2,6 @@ use anyhow::Context as _;
use gpui::{App, UpdateGlobal};
use json::json_task_context;
use node_runtime::NodeRuntime;
use python::PyprojectTomlManifestProvider;
use rust::CargoManifestProvider;
use rust_embed::RustEmbed;
use settings::SettingsStore;
@@ -303,13 +302,7 @@ pub fn init(languages: Arc<LanguageRegistry>, node: NodeRuntime, cx: &mut App) {
anyhow::Ok(())
})
.detach();
let manifest_providers: [Arc<dyn ManifestProvider>; 2] = [
Arc::from(CargoManifestProvider),
Arc::from(PyprojectTomlManifestProvider),
];
for provider in manifest_providers {
project::ManifestProviders::global(cx).register(provider);
}
project::ManifestProviders::global(cx).register(Arc::from(CargoManifestProvider));
}
#[derive(Default)]

View File

@@ -4,13 +4,13 @@ use async_trait::async_trait;
use collections::HashMap;
use gpui::{App, Task};
use gpui::{AsyncApp, SharedString};
use language::LanguageName;
use language::LanguageToolchainStore;
use language::Toolchain;
use language::ToolchainList;
use language::ToolchainLister;
use language::language_settings::language_settings;
use language::{ContextProvider, LspAdapter, LspAdapterDelegate};
use language::{LanguageName, ManifestName, ManifestProvider, ManifestQuery};
use lsp::LanguageServerBinary;
use lsp::LanguageServerName;
use node_runtime::NodeRuntime;
@@ -38,32 +38,6 @@ use std::{
use task::{TaskTemplate, TaskTemplates, VariableName};
use util::ResultExt;
pub(crate) struct PyprojectTomlManifestProvider;
impl ManifestProvider for PyprojectTomlManifestProvider {
fn name(&self) -> ManifestName {
SharedString::new_static("pyproject.toml").into()
}
fn search(
&self,
ManifestQuery {
path,
depth,
delegate,
}: ManifestQuery,
) -> Option<Arc<Path>> {
for path in path.ancestors().take(depth) {
let p = path.join("pyproject.toml");
if delegate.exists(&p, Some(false)) {
return Some(path.into());
}
}
None
}
}
const SERVER_PATH: &str = "node_modules/pyright/langserver.index.js";
const NODE_MODULE_RELATIVE_SERVER_PATH: &str = "pyright/langserver.index.js";
@@ -327,9 +301,6 @@ impl LspAdapter for PythonLspAdapter {
user_settings
})
}
fn manifest_name(&self) -> Option<ManifestName> {
Some(SharedString::new_static("pyproject.toml").into())
}
}
async fn get_cached_server_binary(
@@ -1171,9 +1142,6 @@ impl LspAdapter for PyLspAdapter {
user_settings
})
}
fn manifest_name(&self) -> Option<ManifestName> {
Some(SharedString::new_static("pyproject.toml").into())
}
}
#[cfg(test)]

View File

@@ -686,7 +686,6 @@ impl ContextProvider for RustContextProvider {
RUST_PACKAGE_TASK_VARIABLE.template_value(),
"--".into(),
"--nocapture".into(),
"--include-ignored".into(),
RUST_TEST_NAME_TASK_VARIABLE.template_value(),
],
tags: vec!["rust-test".to_owned()],
@@ -707,7 +706,6 @@ impl ContextProvider for RustContextProvider {
RUST_PACKAGE_TASK_VARIABLE.template_value(),
"--".into(),
"--nocapture".into(),
"--include-ignored".into(),
RUST_DOC_TEST_NAME_TASK_VARIABLE.template_value(),
],
tags: vec!["rust-doc-test".to_owned()],

View File

@@ -16,4 +16,3 @@ brackets = [
]
collapsed_placeholder = " /* ... */ "
debuggers = ["CodeLLDB", "GDB"]
documentation = { start = "/*", end = "*/", prefix = "* ", tab_size = 1 }

View File

@@ -18,7 +18,6 @@ scope_opt_in_language_servers = ["tailwindcss-language-server", "emmet-language-
prettier_parser_name = "typescript"
tab_size = 2
debuggers = ["JavaScript"]
documentation = { start = "/**", end = "*/", prefix = "* ", tab_size = 1 }
[jsx_tag_auto_close]
open_tag_node_name = "jsx_opening_element"

View File

@@ -18,7 +18,6 @@ word_characters = ["#", "$"]
prettier_parser_name = "typescript"
tab_size = 2
debuggers = ["JavaScript"]
documentation = { start = "/**", end = "*/", prefix = "* ", tab_size = 1 }
[overrides.string]
completion_query_characters = ["."]

View File

@@ -1620,7 +1620,7 @@ impl OutlinePanel {
.get(&external_file.buffer_id)
.into_iter()
.flat_map(|excerpts| {
excerpts.keys().map(|excerpt_id| {
excerpts.iter().map(|(excerpt_id, _)| {
CollapsedEntry::Excerpt(
external_file.buffer_id,
*excerpt_id,
@@ -1641,7 +1641,7 @@ impl OutlinePanel {
entries.extend(
self.excerpts.get(&file.buffer_id).into_iter().flat_map(
|excerpts| {
excerpts.keys().map(|excerpt_id| {
excerpts.iter().map(|(excerpt_id, _)| {
CollapsedEntry::Excerpt(file.buffer_id, *excerpt_id)
})
},

View File

@@ -64,6 +64,7 @@ pub enum DapStoreEvent {
RemoteHasInitialized,
}
#[allow(clippy::large_enum_variant)]
enum DapStoreMode {
Local(LocalDapStore),
Ssh(SshDapStore),

View File

@@ -3415,6 +3415,7 @@ pub struct RemoteLspStore {
upstream_project_id: u64,
}
#[allow(clippy::large_enum_variant)]
pub(crate) enum LspStoreMode {
Local(LocalLspStore), // ssh host and collab host
Remote(RemoteLspStore), // collab guest
@@ -8805,10 +8806,9 @@ impl LspStore {
})
});
let is_unnecessary = diagnostic
.tags
.as_ref()
.map_or(false, |tags| tags.contains(&DiagnosticTag::UNNECESSARY));
let is_unnecessary = diagnostic.tags.as_ref().map_or(false, |tags| {
tags.iter().any(|tag| *tag == DiagnosticTag::UNNECESSARY)
});
if is_supporting {
supporting_diagnostics.insert(

View File

@@ -98,7 +98,7 @@ impl<Label: Ord + Clone> RootPathTrie<Label> {
};
}
if !current.labels.is_empty() {
let _ = (callback)(&current.worktree_relative_path, &current.labels);
(callback)(&current.worktree_relative_path, &current.labels);
}
}

View File

@@ -3653,7 +3653,7 @@ impl Project {
let mut buffer_count = 0;
let mut limit_reached = false;
let query = Arc::new(query);
let chunks = matching_buffers_rx.ready_chunks(64);
let mut chunks = matching_buffers_rx.ready_chunks(64);
// Now that we know what paths match the query, we will load at most
// 64 buffers at a time to avoid overwhelming the main thread. For each
@@ -4392,7 +4392,7 @@ impl Project {
envelope: TypedEnvelope<proto::LanguageServerPromptRequest>,
mut cx: AsyncApp,
) -> Result<proto::LanguageServerPromptResponse> {
let (tx, rx) = smol::channel::bounded(1);
let (tx, mut rx) = smol::channel::bounded(1);
let actions: Vec<_> = envelope
.payload
.actions

View File

@@ -21,7 +21,7 @@ use crate::{
worktree_store::WorktreeStore,
};
// platform-dependent warning
#[allow(clippy::large_enum_variant)] // platform-dependent warning
pub enum TaskStore {
Functional(StoreState),
Noop,

View File

@@ -24,7 +24,7 @@ pub struct Terminals {
}
/// Terminals are opened either for the users shell, or to run a task.
#[allow(clippy::large_enum_variant)]
#[derive(Debug)]
pub enum TerminalKind {
/// Run a shell at the given path (or $HOME if None)

View File

@@ -876,7 +876,7 @@ impl WorktreeStore {
async fn filter_paths(
fs: &Arc<dyn Fs>,
input: Receiver<MatchingEntry>,
mut input: Receiver<MatchingEntry>,
query: &SearchQuery,
) -> Result<()> {
let mut input = pin!(input);

View File

@@ -2854,9 +2854,12 @@ impl ProjectPanel {
}
let precedes_new_entry = if let Some(new_entry_id) = new_entry_parent_id {
entry.id == new_entry_id || {
self.ancestors
.get(&entry.id)
.map_or(false, |entries| entries.ancestors.contains(&new_entry_id))
self.ancestors.get(&entry.id).map_or(false, |entries| {
entries
.ancestors
.iter()
.any(|entry_id| *entry_id == new_entry_id)
})
}
} else {
false
@@ -3366,7 +3369,10 @@ impl ProjectPanel {
.ancestors
.get(&entry.id)
.is_some_and(|auto_folded_dirs| {
auto_folded_dirs.ancestors.contains(&edit_state.entry_id)
auto_folded_dirs
.ancestors
.iter()
.any(|entry_id| *entry_id == edit_state.entry_id)
})
};

View File

@@ -925,7 +925,7 @@ impl SshRemoteClient {
if missed_heartbeats != 0 {
missed_heartbeats = 0;
let _ =this.update(cx, |this, mut cx| {
this.update(cx, |this, mut cx| {
this.handle_heartbeat_result(missed_heartbeats, &mut cx)
})?;
}

View File

@@ -170,6 +170,7 @@ pub fn run(
anyhow::Ok(())
}
#[allow(clippy::large_enum_variant)]
pub enum SessionSupport {
ActiveSession(Entity<Session>),
Inactive(KernelSpecification),

View File

@@ -20,6 +20,7 @@ pub struct MessageStream<S> {
encoding_buffer: Vec<u8>,
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug)]
pub enum Message {
Envelope(Envelope),

View File

@@ -1,5 +1,6 @@
use gpui::{
AnyElement, App, Div, SharedString, Window, colors::DefaultColors, div, prelude::*, px, rems,
AnyElement, App, DefaultColor, DefaultColors, Div, SharedString, Window, div, prelude::*, px,
rems,
};
use itertools::Itertools;
use smallvec::SmallVec;
@@ -7,7 +8,9 @@ use smallvec::SmallVec;
pub struct Story {}
impl Story {
pub fn container(cx: &App) -> gpui::Stateful<Div> {
pub fn container() -> gpui::Stateful<Div> {
let colors = DefaultColors::light();
div()
.id("story_container")
.overflow_y_scroll()
@@ -15,66 +18,84 @@ impl Story {
.min_h_full()
.flex()
.flex_col()
.text_color(cx.default_colors().text)
.bg(cx.default_colors().background)
.text_color(DefaultColor::Text.hsla(&colors))
.bg(DefaultColor::Background.hsla(&colors))
}
pub fn title(title: impl Into<SharedString>, cx: &App) -> impl Element {
pub fn title(title: impl Into<SharedString>) -> impl Element {
let colors = DefaultColors::light();
div()
.text_xs()
.text_color(cx.default_colors().text)
.text_color(DefaultColor::Text.hsla(&colors))
.child(title.into())
}
pub fn title_for<T>(cx: &App) -> impl Element {
Self::title(std::any::type_name::<T>(), cx)
pub fn title_for<T>() -> impl Element {
Self::title(std::any::type_name::<T>())
}
pub fn section(cx: &App) -> Div {
pub fn section() -> Div {
let colors = DefaultColors::light();
div()
.p_4()
.m_4()
.border_1()
.border_color(cx.default_colors().separator)
.border_color(DefaultColor::Separator.hsla(&colors))
}
pub fn section_title(cx: &App) -> Div {
div().text_lg().text_color(cx.default_colors().text)
pub fn section_title() -> Div {
let colors = DefaultColors::light();
div().text_lg().text_color(DefaultColor::Text.hsla(&colors))
}
pub fn group(cx: &App) -> Div {
div().my_2().bg(cx.default_colors().container)
pub fn group() -> Div {
let colors = DefaultColors::light();
div().my_2().bg(DefaultColor::Container.hsla(&colors))
}
pub fn code_block(code: impl Into<SharedString>, cx: &App) -> Div {
pub fn code_block(code: impl Into<SharedString>) -> Div {
let colors = DefaultColors::light();
div()
.size_full()
.p_2()
.max_w(rems(36.))
.bg(cx.default_colors().container)
.bg(DefaultColor::Container.hsla(&colors))
.rounded_sm()
.text_sm()
.text_color(cx.default_colors().text)
.text_color(DefaultColor::Text.hsla(&colors))
.overflow_hidden()
.child(code.into())
}
pub fn divider(cx: &App) -> Div {
div().my_2().h(px(1.)).bg(cx.default_colors().separator)
pub fn divider() -> Div {
let colors = DefaultColors::light();
div()
.my_2()
.h(px(1.))
.bg(DefaultColor::Separator.hsla(&colors))
}
pub fn description(description: impl Into<SharedString>, cx: &App) -> impl Element {
pub fn description(description: impl Into<SharedString>) -> impl Element {
let colors = DefaultColors::light();
div()
.text_sm()
.text_color(cx.default_colors().text)
.text_color(DefaultColor::Text.hsla(&colors))
.min_w_96()
.child(description.into())
}
pub fn label(label: impl Into<SharedString>, cx: &App) -> impl Element {
pub fn label(label: impl Into<SharedString>) -> impl Element {
let colors = DefaultColors::light();
div()
.text_xs()
.text_color(cx.default_colors().text)
.text_color(DefaultColor::Text.hsla(&colors))
.child(label.into())
}
@@ -114,8 +135,8 @@ impl StoryItem {
}
impl RenderOnce for StoryItem {
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
let colors = cx.default_colors();
fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
let colors = DefaultColors::light();
div()
.my_2()
@@ -127,20 +148,20 @@ impl RenderOnce for StoryItem {
.px_2()
.w_1_2()
.min_h_px()
.child(Story::label(self.label, cx))
.child(Story::label(self.label))
.child(
div()
.rounded_sm()
.bg(colors.background)
.bg(DefaultColor::Background.hsla(&colors))
.border_1()
.border_color(colors.border)
.border_color(DefaultColor::Border.hsla(&colors))
.py_1()
.px_2()
.overflow_hidden()
.child(self.item),
)
.when_some(self.description, |this, description| {
this.child(Story::description(description, cx))
this.child(Story::description(description))
}),
)
.child(
@@ -150,8 +171,8 @@ impl RenderOnce for StoryItem {
.w_1_2()
.min_h_px()
.when_some(self.usage, |this, usage| {
this.child(Story::label("Example Usage", cx))
.child(Story::code_block(usage, cx))
this.child(Story::label("Example Usage"))
.child(Story::code_block(usage))
}),
)
}
@@ -184,21 +205,21 @@ impl StorySection {
}
impl RenderOnce for StorySection {
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
let children: SmallVec<[AnyElement; 2]> = SmallVec::from_iter(Itertools::intersperse_with(
self.children.into_iter(),
|| Story::divider(cx).into_any_element(),
|| Story::divider().into_any_element(),
));
Story::section(cx)
Story::section()
// Section title
.py_2()
// Section description
.when_some(self.description.clone(), |section, description| {
section.child(Story::description(description, cx))
section.child(Story::description(description))
})
.child(div().flex().flex_col().gap_2().children(children))
.child(Story::divider(cx))
.child(Story::divider())
}
}

View File

@@ -1,5 +1,6 @@
mod auto_height_editor;
mod cursor;
mod default_colors;
mod focus;
mod kitchen_sink;
mod overflow_scroll;
@@ -11,6 +12,7 @@ mod with_rem_size;
pub use auto_height_editor::*;
pub use cursor::*;
pub use default_colors::*;
pub use focus::*;
pub use kitchen_sink::*;
pub use overflow_scroll::*;

View File

@@ -5,7 +5,7 @@ use ui::prelude::*;
pub struct CursorStory;
impl Render for CursorStory {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
let all_cursors: [(&str, Box<dyn Fn(Stateful<Div>) -> Stateful<Div>>); 19] = [
(
"cursor_default",
@@ -85,10 +85,10 @@ impl Render for CursorStory {
),
];
Story::container(cx)
Story::container()
.flex()
.gap_1()
.child(Story::title("cursor", cx))
.child(Story::title("cursor"))
.children(all_cursors.map(|(name, apply_cursor)| {
div().gap_1().flex().text_color(gpui::white()).child(
div()
@@ -102,7 +102,7 @@ impl Render for CursorStory {
.bg(gpui::red())
.active(|style| style.bg(gpui::green()))
.text_sm()
.child(Story::label(name, cx)),
.child(Story::label(name)),
)
}))
}

View File

@@ -0,0 +1,89 @@
use gpui::{
App, Context, DefaultColor, DefaultThemeAppearance, Entity, Hsla, Render, Window, colors, div,
prelude::*,
};
use story::Story;
use strum::IntoEnumIterator;
use ui::{ActiveTheme, h_flex};
pub struct DefaultColorsStory;
impl DefaultColorsStory {
pub fn model(cx: &mut App) -> Entity<Self> {
cx.new(|_| Self)
}
}
impl Render for DefaultColorsStory {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let appearances = [DefaultThemeAppearance::Light, DefaultThemeAppearance::Dark];
Story::container()
.child(Story::title("Default Colors"))
.children(appearances.iter().map(|&appearance| {
let colors = colors(appearance);
let color_types = DefaultColor::iter()
.map(|color| {
let name = format!("{:?}", color);
let rgba = color.hsla(&colors);
(name, rgba)
})
.collect::<Vec<_>>();
div()
.flex()
.flex_col()
.gap_4()
.p_4()
.child(Story::label(format!("{:?} Appearance", appearance)))
.children(color_types.iter().map(|(name, color)| {
let color: Hsla = *color;
div()
.flex()
.items_center()
.gap_2()
.child(
div()
.w_12()
.h_12()
.bg(color)
.border_1()
.border_color(cx.theme().colors().border),
)
.child(Story::label(format!("{}: {:?}", name, color.clone())))
}))
.child(
h_flex()
.gap_1()
.child(
h_flex()
.bg(DefaultColor::Background.hsla(&colors))
.h_8()
.p_2()
.text_sm()
.text_color(DefaultColor::Text.hsla(&colors))
.child("Default Text"),
)
.child(
h_flex()
.bg(DefaultColor::Container.hsla(&colors))
.h_8()
.p_2()
.text_sm()
.text_color(DefaultColor::Text.hsla(&colors))
.child("Text on Container"),
)
.child(
h_flex()
.bg(DefaultColor::Selected.hsla(&colors))
.h_8()
.p_2()
.text_sm()
.text_color(DefaultColor::SelectedText.hsla(&colors))
.child("Selected Text"),
),
)
}))
}
}

View File

@@ -1,12 +1,12 @@
use std::fmt::format;
use gpui::{
DefaultColor, DefaultThemeAppearance, Hsla, Render, colors, div, prelude::*, uniform_list,
colors, div, prelude::*, uniform_list, DefaultColor, DefaultThemeAppearance, Hsla, Render,
};
use story::Story;
use strum::IntoEnumIterator;
use ui::{
AbsoluteLength, ActiveTheme, Color, DefiniteLength, Label, LabelCommon, h_flex, px, v_flex,
h_flex, px, v_flex, AbsoluteLength, ActiveTheme, Color, DefiniteLength, Label, LabelCommon,
};
const LENGTH: usize = 100;
@@ -34,7 +34,7 @@ impl IndentGuidesStory {
impl Render for IndentGuidesStory {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
Story::container(cx)
Story::container()
.child(Story::title("Indent guides"))
.child(
v_flex().size_full().child(

View File

@@ -19,11 +19,11 @@ impl Render for KitchenSinkStory {
.map(|selector| selector.story(window, cx))
.collect::<Vec<_>>();
Story::container(cx)
Story::container()
.id("kitchen-sink")
.overflow_y_scroll()
.child(Story::title("Kitchen Sink", cx))
.child(Story::label("Components", cx))
.child(Story::title("Kitchen Sink"))
.child(Story::label("Components"))
.child(div().flex().flex_col().children(component_stories))
// Add a bit of space at the bottom of the kitchen sink so elements
// don't end up squished right up against the bottom of the screen.

View File

@@ -6,10 +6,10 @@ use ui::prelude::*;
pub struct OverflowScrollStory;
impl Render for OverflowScrollStory {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
Story::container(cx)
.child(Story::title("Overflow Scroll", cx))
.child(Story::label("`overflow_x_scroll`", cx))
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
Story::container()
.child(Story::title("Overflow Scroll"))
.child(Story::label("`overflow_x_scroll`"))
.child(
h_flex()
.id("overflow_x_scroll")
@@ -22,7 +22,7 @@ impl Render for OverflowScrollStory {
.child(SharedString::from(format!("Child {}", i + 1)))
})),
)
.child(Story::label("`overflow_y_scroll`", cx))
.child(Story::label("`overflow_y_scroll`"))
.child(
v_flex()
.w_full()

View File

@@ -14,9 +14,9 @@ impl TextStory {
}
impl Render for TextStory {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
Story::container(cx)
.child(Story::title("Text", cx))
fn render(&mut self, window: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
Story::container()
.child(Story::title("Text"))
.children(vec![
StorySection::new()
.child(

View File

@@ -6,8 +6,8 @@ use ui::prelude::*;
pub struct ViewportUnitsStory;
impl Render for ViewportUnitsStory {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
Story::container(cx).child(
fn render(&mut self, window: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
Story::container().child(
div()
.flex()
.flex_row()

View File

@@ -6,8 +6,8 @@ use ui::{prelude::*, utils::WithRemSize};
pub struct WithRemSizeStory;
impl Render for WithRemSizeStory {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
Story::container(cx).child(
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
Story::container().child(
Example::new(16., gpui::red())
.child(
Example::new(24., gpui::green())

View File

@@ -17,6 +17,7 @@ pub enum ComponentStory {
CollabNotification,
ContextMenu,
Cursor,
DefaultColors,
Focus,
IconButton,
Keybinding,
@@ -46,6 +47,7 @@ impl ComponentStory {
.into(),
Self::ContextMenu => cx.new(|_| ui::ContextMenuStory).into(),
Self::Cursor => cx.new(|_| crate::stories::CursorStory).into(),
Self::DefaultColors => DefaultColorsStory::model(cx).into(),
Self::Focus => FocusStory::model(window, cx).into(),
Self::IconButton => cx.new(|_| ui::IconButtonStory).into(),
Self::Keybinding => cx.new(|_| ui::KeybindingStory).into(),

View File

@@ -186,6 +186,7 @@ impl From<AttachRequest> for DebugRequest {
#[derive(Deserialize, Serialize, PartialEq, Eq, JsonSchema, Clone, Debug)]
#[serde(untagged)]
#[allow(clippy::large_enum_variant)]
pub enum BuildTaskDefinition {
ByName(SharedString),
Template {

View File

@@ -144,11 +144,9 @@ impl ThemeColors {
version_control_renamed: MODIFIED_COLOR,
version_control_conflict: orange().light().step_12(),
version_control_ignored: gray().light().step_12(),
version_control_conflict_ours_background: green().light().step_10().alpha(0.5),
version_control_conflict_theirs_background: blue().light().step_10().alpha(0.5),
version_control_conflict_ours_marker_background: green().light().step_10().alpha(0.7),
version_control_conflict_theirs_marker_background: blue().light().step_10().alpha(0.7),
version_control_conflict_divider_background: Hsla::default(),
version_control_conflict_marker_ours: green().light().step_10().alpha(0.5),
version_control_conflict_marker_theirs: blue().light().step_10().alpha(0.5),
version_control_conflict_marker_border: Hsla::default(),
}
}
@@ -265,11 +263,9 @@ impl ThemeColors {
version_control_renamed: MODIFIED_COLOR,
version_control_conflict: orange().dark().step_12(),
version_control_ignored: gray().dark().step_12(),
version_control_conflict_ours_background: green().dark().step_10().alpha(0.5),
version_control_conflict_theirs_background: blue().dark().step_10().alpha(0.5),
version_control_conflict_ours_marker_background: green().dark().step_10().alpha(0.7),
version_control_conflict_theirs_marker_background: blue().dark().step_10().alpha(0.7),
version_control_conflict_divider_background: Hsla::default(),
version_control_conflict_marker_ours: green().dark().step_10().alpha(0.5),
version_control_conflict_marker_theirs: blue().dark().step_10().alpha(0.5),
version_control_conflict_marker_border: Hsla::default(),
}
}
}

View File

@@ -207,23 +207,9 @@ pub(crate) fn zed_default_dark() -> Theme {
version_control_renamed: MODIFIED_COLOR,
version_control_conflict: crate::orange().light().step_12(),
version_control_ignored: crate::gray().light().step_12(),
version_control_conflict_ours_background: crate::green()
.light()
.step_12()
.alpha(0.5),
version_control_conflict_theirs_background: crate::blue()
.light()
.step_12()
.alpha(0.5),
version_control_conflict_ours_marker_background: crate::green()
.light()
.step_12()
.alpha(0.7),
version_control_conflict_theirs_marker_background: crate::blue()
.light()
.step_12()
.alpha(0.7),
version_control_conflict_divider_background: Hsla::default(),
version_control_conflict_marker_ours: crate::green().light().step_12().alpha(0.5),
version_control_conflict_marker_theirs: crate::blue().light().step_12().alpha(0.5),
version_control_conflict_marker_border: Hsla::default(),
},
status: StatusColors {
conflict: yellow,

View File

@@ -592,24 +592,16 @@ pub struct ThemeColorsContent {
pub version_control_ignored: Option<String>,
/// Background color for row highlights of "ours" regions in merge conflicts.
#[serde(rename = "version_control.conflict.ours_background")]
pub version_control_conflict_ours_background: Option<String>,
#[serde(rename = "version_control.conflict_marker.ours")]
pub version_control_conflict_marker_ours: Option<String>,
/// Background color for row highlights of "theirs" regions in merge conflicts.
#[serde(rename = "version_control.conflict.theirs_background")]
pub version_control_conflict_theirs_background: Option<String>,
/// Background color for row highlights of "ours" conflict markers in merge conflicts.
#[serde(rename = "version_control.conflict.ours_marker_background")]
pub version_control_conflict_ours_marker_background: Option<String>,
/// Background color for row highlights of "theirs" conflict markers in merge conflicts.
#[serde(rename = "version_control.conflict.theirs_marker_background")]
pub version_control_conflict_theirs_marker_background: Option<String>,
#[serde(rename = "version_control.conflict_marker.theirs")]
pub version_control_conflict_marker_theirs: Option<String>,
/// Background color for row highlights of the "ours"/"theirs" divider in merge conflicts.
#[serde(rename = "version_control.conflict.divider_background")]
pub version_control_conflict_divider_background: Option<String>,
#[serde(rename = "version_control.conflict_marker.border")]
pub version_control_conflict_marker_border: Option<String>,
}
impl ThemeColorsContent {
@@ -1067,24 +1059,16 @@ impl ThemeColorsContent {
.and_then(|color| try_parse_color(color).ok())
// Fall back to `conflict`, for backwards compatibility.
.or(status_colors.ignored),
version_control_conflict_ours_background: self
.version_control_conflict_ours_background
version_control_conflict_marker_ours: self
.version_control_conflict_marker_ours
.as_ref()
.and_then(|color| try_parse_color(color).ok()),
version_control_conflict_theirs_background: self
.version_control_conflict_theirs_background
version_control_conflict_marker_theirs: self
.version_control_conflict_marker_theirs
.as_ref()
.and_then(|color| try_parse_color(color).ok()),
version_control_conflict_ours_marker_background: self
.version_control_conflict_ours_marker_background
.as_ref()
.and_then(|color| try_parse_color(color).ok()),
version_control_conflict_theirs_marker_background: self
.version_control_conflict_theirs_marker_background
.as_ref()
.and_then(|color| try_parse_color(color).ok()),
version_control_conflict_divider_background: self
.version_control_conflict_divider_background
version_control_conflict_marker_border: self
.version_control_conflict_marker_border
.as_ref()
.and_then(|color| try_parse_color(color).ok()),
}

View File

@@ -265,12 +265,10 @@ pub struct ThemeColors {
pub version_control_ignored: Hsla,
/// Represents the "ours" region of a merge conflict.
pub version_control_conflict_ours_background: Hsla,
pub version_control_conflict_marker_ours: Hsla,
/// Represents the "theirs" region of a merge conflict.
pub version_control_conflict_theirs_background: Hsla,
pub version_control_conflict_ours_marker_background: Hsla,
pub version_control_conflict_theirs_marker_background: Hsla,
pub version_control_conflict_divider_background: Hsla,
pub version_control_conflict_marker_theirs: Hsla,
pub version_control_conflict_marker_border: Hsla,
}
#[derive(EnumIter, Debug, Clone, Copy, AsRefStr)]

View File

@@ -18,9 +18,9 @@ impl ApplicationMenuStory {
}
impl Render for ApplicationMenuStory {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
Story::container(cx)
.child(Story::title_for::<ApplicationMenu>(cx))
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
Story::container()
.child(Story::title_for::<ApplicationMenu>())
.child(StorySection::new().child(StoryItem::new(
"Application Menu",
h_flex().child(self.menu.clone()),

View File

@@ -26,8 +26,8 @@ fn build_menu(
pub struct ContextMenuStory;
impl Render for ContextMenuStory {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
Story::container(cx)
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
Story::container()
.on_action(|_: &PrintCurrentDate, _, _| {
println!("printing unix time!");
if let Ok(unix_time) = std::time::UNIX_EPOCH.elapsed() {

View File

@@ -7,9 +7,9 @@ use crate::prelude::*;
pub struct DisclosureStory;
impl Render for DisclosureStory {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
Story::container(cx)
.child(Story::title_for::<Disclosure>(cx))
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
Story::container()
.child(Story::title_for::<Disclosure>())
.child(Story::label("Toggled"))
.child(Disclosure::new("toggled", true))
.child(Story::label("Not Toggled"))

View File

@@ -7,7 +7,7 @@ use crate::{IconButtonShape, Tooltip, prelude::*};
pub struct IconButtonStory;
impl Render for IconButtonStory {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
let default_button = StoryItem::new(
"Default",
IconButton::new("default_icon_button", IconName::Hash),
@@ -113,8 +113,8 @@ impl Render for IconButtonStory {
selected_with_tooltip_button,
];
Story::container(cx)
.child(Story::title_for::<IconButton>(cx))
Story::container()
.child(Story::title_for::<IconButton>())
.child(StorySection::new().children(buttons))
.child(
StorySection::new().child(StoryItem::new(

View File

@@ -15,11 +15,11 @@ impl Render for KeybindingStory {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let all_modifier_permutations = ["ctrl", "alt", "cmd", "shift"].into_iter().permutations(2);
Story::container(cx)
.child(Story::title_for::<KeyBinding>(cx))
.child(Story::label("Single Key", cx))
Story::container()
.child(Story::title_for::<KeyBinding>())
.child(Story::label("Single Key"))
.child(KeyBinding::new(binding("Z"), cx))
.child(Story::label("Single Key with Modifier", cx))
.child(Story::label("Single Key with Modifier"))
.child(
div()
.flex()
@@ -29,7 +29,7 @@ impl Render for KeybindingStory {
.child(KeyBinding::new(binding("cmd-c"), cx))
.child(KeyBinding::new(binding("shift-c"), cx)),
)
.child(Story::label("Single Key with Modifier (Permuted)", cx))
.child(Story::label("Single Key with Modifier (Permuted)"))
.child(
div().flex().flex_col().children(
all_modifier_permutations
@@ -46,33 +46,33 @@ impl Render for KeybindingStory {
}),
),
)
.child(Story::label("Single Key with All Modifiers", cx))
.child(Story::label("Single Key with All Modifiers"))
.child(KeyBinding::new(binding("ctrl-alt-cmd-shift-z"), cx))
.child(Story::label("Chord", cx))
.child(Story::label("Chord"))
.child(KeyBinding::new(binding("a z"), cx))
.child(Story::label("Chord with Modifier", cx))
.child(Story::label("Chord with Modifier"))
.child(KeyBinding::new(binding("ctrl-a shift-z"), cx))
.child(KeyBinding::new(binding("fn-s"), cx))
.child(Story::label("Single Key with All Modifiers (Linux)", cx))
.child(Story::label("Single Key with All Modifiers (Linux)"))
.child(
KeyBinding::new(binding("ctrl-alt-cmd-shift-z"), cx)
.platform_style(PlatformStyle::Linux),
)
.child(Story::label("Chord (Linux)", cx))
.child(Story::label("Chord (Linux)"))
.child(KeyBinding::new(binding("a z"), cx).platform_style(PlatformStyle::Linux))
.child(Story::label("Chord with Modifier (Linux)", cx))
.child(Story::label("Chord with Modifier (Linux)"))
.child(
KeyBinding::new(binding("ctrl-a shift-z"), cx).platform_style(PlatformStyle::Linux),
)
.child(KeyBinding::new(binding("fn-s"), cx).platform_style(PlatformStyle::Linux))
.child(Story::label("Single Key with All Modifiers (Windows)", cx))
.child(Story::label("Single Key with All Modifiers (Windows)"))
.child(
KeyBinding::new(binding("ctrl-alt-cmd-shift-z"), cx)
.platform_style(PlatformStyle::Windows),
)
.child(Story::label("Chord (Windows)", cx))
.child(Story::label("Chord (Windows)"))
.child(KeyBinding::new(binding("a z"), cx).platform_style(PlatformStyle::Windows))
.child(Story::label("Chord with Modifier (Windows)", cx))
.child(Story::label("Chord with Modifier (Windows)"))
.child(
KeyBinding::new(binding("ctrl-a shift-z"), cx)
.platform_style(PlatformStyle::Windows),

View File

@@ -7,17 +7,17 @@ use crate::{ListHeader, ListSeparator, ListSubHeader, prelude::*};
pub struct ListStory;
impl Render for ListStory {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
Story::container(cx)
.child(Story::title_for::<List>(cx))
.child(Story::label("Default", cx))
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
Story::container()
.child(Story::title_for::<List>())
.child(Story::label("Default"))
.child(
List::new()
.child(ListItem::new("apple").child("Apple"))
.child(ListItem::new("banana").child("Banana"))
.child(ListItem::new("cherry").child("Cherry")),
)
.child(Story::label("With sections", cx))
.child(Story::label("With sections"))
.child(
List::new()
.header(ListHeader::new("Produce"))

View File

@@ -7,20 +7,20 @@ use crate::{IconName, ListHeader};
pub struct ListHeaderStory;
impl Render for ListHeaderStory {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
Story::container(cx)
.child(Story::title_for::<ListHeader>(cx))
.child(Story::label("Default", cx))
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
Story::container()
.child(Story::title_for::<ListHeader>())
.child(Story::label("Default"))
.child(ListHeader::new("Section 1"))
.child(Story::label("With left icon", cx))
.child(Story::label("With left icon"))
.child(ListHeader::new("Section 2").start_slot(Icon::new(IconName::Bell)))
.child(Story::label("With left icon and meta", cx))
.child(Story::label("With left icon and meta"))
.child(
ListHeader::new("Section 3")
.start_slot(Icon::new(IconName::BellOff))
.end_slot(IconButton::new("action_1", IconName::Bolt)),
)
.child(Story::label("With multiple meta", cx))
.child(Story::label("With multiple meta"))
.child(
ListHeader::new("Section 4")
.end_slot(IconButton::new("action_1", IconName::Bolt))

View File

@@ -10,12 +10,12 @@ pub struct ListItemStory;
impl Render for ListItemStory {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
Story::container(cx)
Story::container()
.bg(cx.theme().colors().background)
.child(Story::title_for::<ListItem>(cx))
.child(Story::label("Default", cx))
.child(Story::title_for::<ListItem>())
.child(Story::label("Default"))
.child(ListItem::new("hello_world").child("Hello, world!"))
.child(Story::label("Inset", cx))
.child(Story::label("Inset"))
.child(
ListItem::new("inset_list_item")
.inset(true)
@@ -31,7 +31,7 @@ impl Render for ListItemStory {
.color(Color::Muted),
),
)
.child(Story::label("With start slot icon", cx))
.child(Story::label("With start slot icon"))
.child(
ListItem::new("with start slot_icon")
.child("Hello, world!")
@@ -41,7 +41,7 @@ impl Render for ListItemStory {
.color(Color::Muted),
),
)
.child(Story::label("With start slot avatar", cx))
.child(Story::label("With start slot avatar"))
.child(
ListItem::new("with_start slot avatar")
.child("Hello, world!")
@@ -49,7 +49,7 @@ impl Render for ListItemStory {
"https://avatars.githubusercontent.com/u/1714999?v=4",
)),
)
.child(Story::label("With end slot", cx))
.child(Story::label("With end slot"))
.child(
ListItem::new("with_left_avatar")
.child("Hello, world!")
@@ -57,7 +57,7 @@ impl Render for ListItemStory {
"https://avatars.githubusercontent.com/u/1714999?v=4",
)),
)
.child(Story::label("With end hover slot", cx))
.child(Story::label("With end hover slot"))
.child(
ListItem::new("with_end_hover_slot")
.child("Hello, world!")
@@ -84,13 +84,13 @@ impl Render for ListItemStory {
"https://avatars.githubusercontent.com/u/1714999?v=4",
)),
)
.child(Story::label("With `on_click`", cx))
.child(Story::label("With `on_click`"))
.child(ListItem::new("with_on_click").child("Click me").on_click(
|_event, _window, _cx| {
println!("Clicked!");
},
))
.child(Story::label("With `on_secondary_mouse_down`", cx))
.child(Story::label("With `on_secondary_mouse_down`"))
.child(
ListItem::new("with_on_secondary_mouse_down")
.child("Right click me")
@@ -98,10 +98,7 @@ impl Render for ListItemStory {
println!("Right mouse down!");
}),
)
.child(Story::label(
"With overflowing content in the `end_slot`",
cx,
))
.child(Story::label("With overflowing content in the `end_slot`"))
.child(
ListItem::new("with_overflowing_content_in_end_slot")
.child("An excerpt")
@@ -109,7 +106,6 @@ impl Render for ListItemStory {
)
.child(Story::label(
"`inset` with overflowing content in the `end_slot`",
cx,
))
.child(
ListItem::new("inset_with_overflowing_content_in_end_slot")
@@ -119,7 +115,6 @@ impl Render for ListItemStory {
)
.child(Story::label(
"`inset` with overflowing content in `children` and `end_slot`",
cx,
))
.child(
ListItem::new("inset_with_overflowing_content_in_children_and_end_slot")

Some files were not shown because too many files have changed in this diff Show More