mirror of
https://github.com/zed-industries/zed.git
synced 2026-09-10 20:05:26 +00:00
Reduce LLVM IR bloat one last time (#63882)
I think this is the final attempt of mine to do anything about the binary size, as those fixes become relatively intricate and I like them less and less. Each commit has its own improvement stats and mainly reduce generic-code duplication through shared non-generic helpers and `#[inline(never)]` boundaries. Total reduction stats: With sccache disabled, `cargo clean` and `cargo build --release -p zed`: Before: zed binary 396716472 bytes After: zed binary 359416504 bytes (-9.40%, -37.29MB) Release Notes: - N/A --------- Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
This commit is contained in:
parent
63d15474c7
commit
20fa2fa809
25 changed files with 399 additions and 218 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -7802,7 +7802,6 @@ dependencies = [
|
|||
"anyhow",
|
||||
"language_core",
|
||||
"rust-embed",
|
||||
"toml 0.8.23",
|
||||
"tree-sitter",
|
||||
"tree-sitter-bash",
|
||||
"tree-sitter-c",
|
||||
|
|
@ -14968,7 +14967,6 @@ dependencies = [
|
|||
"log",
|
||||
"parking_lot",
|
||||
"paths",
|
||||
"prost",
|
||||
"release_channel",
|
||||
"rpc",
|
||||
"schemars 1.0.4",
|
||||
|
|
|
|||
|
|
@ -1797,7 +1797,7 @@ impl ExtensionStore {
|
|||
let config = fs.load(&language_config_path).await.with_context(|| {
|
||||
format!("loading language config from {language_config_path:?}")
|
||||
})?;
|
||||
::toml::from_str::<LanguageConfig>(&config).map_err(anyhow::Error::from)
|
||||
LanguageConfig::from_toml(&config).map_err(anyhow::Error::from)
|
||||
}
|
||||
};
|
||||
let query_files = async {
|
||||
|
|
@ -2335,7 +2335,7 @@ async fn load_plugin_language(
|
|||
let config_path = language_path.join(LanguageConfig::FILE_NAME);
|
||||
async move {
|
||||
let contents = fs.load(&config_path).await?;
|
||||
toml::from_str::<LanguageConfig>(&contents).map_err(anyhow::Error::from)
|
||||
LanguageConfig::from_toml(&contents).map_err(anyhow::Error::from)
|
||||
}
|
||||
};
|
||||
let context_provider = {
|
||||
|
|
|
|||
|
|
@ -272,7 +272,7 @@ impl HeadlessExtensionStore {
|
|||
for language_path in &manifest.languages {
|
||||
let config_path = load_dir.join(language_path).join(LanguageConfig::FILE_NAME);
|
||||
let config = fs.load(&config_path).await?;
|
||||
let mut config = ::toml::from_str::<LanguageConfig>(&config)?;
|
||||
let mut config = LanguageConfig::from_toml(&config)?;
|
||||
config.grammar = None;
|
||||
languages.push((config.name.clone(), config));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -195,6 +195,11 @@ where
|
|||
}
|
||||
|
||||
let mut results = segment_results.concat();
|
||||
gpui_util::truncate_to_bottom_n_sorted_by(&mut results, max_results, &|a, b| b.cmp(a));
|
||||
rank_matches(&mut results, max_results);
|
||||
results
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
fn rank_matches(results: &mut Vec<StringMatch>, max_results: usize) {
|
||||
gpui_util::truncate_to_bottom_n_sorted_by(results, max_results, &|a, b| b.cmp(a));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@ pub trait ParentElement {
|
|||
where
|
||||
Self: Sized,
|
||||
{
|
||||
self.extend(std::iter::once(child.into_element().into_any()));
|
||||
self.extend(std::iter::once(child.into_any_element()));
|
||||
self
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -457,42 +457,17 @@ impl<V: View> Element for ViewElement<V> {
|
|||
) {
|
||||
if let Some(entity_id) = self.entity_id {
|
||||
// Stateful path.
|
||||
window.with_rendered_view(entity_id, |window| {
|
||||
let caching_disabled = window.is_inspector_picking(cx);
|
||||
if self.cached_style.is_some() && !caching_disabled {
|
||||
window.with_element_state::<ViewElementState, _>(
|
||||
global_id.unwrap(),
|
||||
|element_state, window| {
|
||||
let mut element_state = element_state.unwrap();
|
||||
|
||||
let paint_start = window.paint_index();
|
||||
|
||||
if let Some(element) = element {
|
||||
let refreshing = mem::replace(&mut window.refreshing, true);
|
||||
element.paint(window, cx);
|
||||
window.refreshing = refreshing;
|
||||
} else {
|
||||
window.reuse_paint(element_state.paint_range.clone());
|
||||
}
|
||||
|
||||
let paint_end = window.paint_index();
|
||||
element_state.paint_range = paint_start..paint_end;
|
||||
|
||||
((), element_state)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
element.as_mut().unwrap().paint(window, cx);
|
||||
}
|
||||
});
|
||||
paint_view(
|
||||
entity_id,
|
||||
self.cached_style.is_some(),
|
||||
global_id,
|
||||
element,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
} else {
|
||||
// Stateless path: just paint the element.
|
||||
window.with_id(
|
||||
ElementId::Name(std::any::type_name::<V>().into()),
|
||||
|window| {
|
||||
element.as_mut().unwrap().paint(window, cx);
|
||||
},
|
||||
);
|
||||
paint_component(std::any::type_name::<V>(), element, window, cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -505,3 +480,54 @@ impl Render for EmptyView {
|
|||
Empty
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
fn paint_view(
|
||||
entity_id: EntityId,
|
||||
cached: bool,
|
||||
global_id: Option<&GlobalElementId>,
|
||||
element: &mut Option<AnyElement>,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) {
|
||||
window.with_rendered_view(entity_id, |window| {
|
||||
let caching_disabled = window.is_inspector_picking(cx);
|
||||
if cached && !caching_disabled {
|
||||
window.with_element_state::<ViewElementState, _>(
|
||||
global_id.unwrap(),
|
||||
|element_state, window| {
|
||||
let mut element_state = element_state.unwrap();
|
||||
|
||||
let paint_start = window.paint_index();
|
||||
|
||||
if let Some(element) = element {
|
||||
let refreshing = mem::replace(&mut window.refreshing, true);
|
||||
element.paint(window, cx);
|
||||
window.refreshing = refreshing;
|
||||
} else {
|
||||
window.reuse_paint(element_state.paint_range.clone());
|
||||
}
|
||||
|
||||
let paint_end = window.paint_index();
|
||||
element_state.paint_range = paint_start..paint_end;
|
||||
|
||||
((), element_state)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
element.as_mut().unwrap().paint(window, cx);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
fn paint_component(
|
||||
name: &'static str,
|
||||
element: &mut Option<AnyElement>,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) {
|
||||
window.with_id(ElementId::Name(name.into()), |window| {
|
||||
element.as_mut().unwrap().paint(window, cx);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,12 @@ pub fn derive_into_element(input: TokenStream) -> TokenStream {
|
|||
fn into_element(self) -> Self::Element {
|
||||
gpui::ViewElement::new(self)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
#[inline(never)]
|
||||
fn into_any_element(self) -> gpui::AnyElement {
|
||||
gpui::Element::into_any(self.into_element())
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
use std::{
|
||||
env,
|
||||
ffi::OsStr,
|
||||
fmt,
|
||||
ops::AddAssign,
|
||||
panic::Location,
|
||||
pin::Pin,
|
||||
|
|
@ -251,7 +252,7 @@ where
|
|||
Err(error) => {
|
||||
log_error_with_caller(
|
||||
*Location::caller(),
|
||||
DebugAsDisplay(&error),
|
||||
format_args!("{:#}", DebugAsDisplay(&error)),
|
||||
log::Level::Error,
|
||||
);
|
||||
None
|
||||
|
|
@ -277,7 +278,7 @@ where
|
|||
match self {
|
||||
Ok(value) => Some(value),
|
||||
Err(error) => {
|
||||
log_error_with_caller(*Location::caller(), error, level);
|
||||
log_error_with_caller(*Location::caller(), format_args!("{error:#}"), level);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
@ -291,10 +292,12 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn log_error_with_caller<E>(caller: core::panic::Location<'_>, error: E, level: log::Level)
|
||||
where
|
||||
E: std::fmt::Display,
|
||||
{
|
||||
#[inline(never)]
|
||||
fn log_error_with_caller(
|
||||
caller: core::panic::Location<'_>,
|
||||
arguments: fmt::Arguments<'_>,
|
||||
level: log::Level,
|
||||
) {
|
||||
#[cfg(not(windows))]
|
||||
let file = caller.file();
|
||||
#[cfg(windows)]
|
||||
|
|
@ -316,7 +319,7 @@ where
|
|||
&log::Record::builder()
|
||||
.target(module_path.as_deref().unwrap_or(""))
|
||||
.module_path(file.as_deref())
|
||||
.args(format_args!("{:#}", error))
|
||||
.args(arguments)
|
||||
.file(Some(caller.file()))
|
||||
.line(Some(caller.line()))
|
||||
.level(level)
|
||||
|
|
@ -326,7 +329,11 @@ where
|
|||
|
||||
#[track_caller]
|
||||
pub fn log_err<E: std::fmt::Display>(error: &E) {
|
||||
log_error_with_caller(*Location::caller(), error, log::Level::Error);
|
||||
log_error_with_caller(
|
||||
*Location::caller(),
|
||||
format_args!("{error:#}"),
|
||||
log::Level::Error,
|
||||
);
|
||||
}
|
||||
|
||||
// Forces `{:?}` formatting through a `Display`-bounded logging helper so `anyhow::Error` emits a
|
||||
|
|
@ -452,7 +459,7 @@ where
|
|||
Poll::Ready(output) => Poll::Ready(match output {
|
||||
Ok(output) => Some(output),
|
||||
Err(error) => {
|
||||
log_error_with_caller(location, error, level);
|
||||
log_error_with_caller(location, format_args!("{error:#}"), level);
|
||||
None
|
||||
}
|
||||
}),
|
||||
|
|
@ -479,7 +486,11 @@ where
|
|||
Poll::Ready(output) => Poll::Ready(match output {
|
||||
Ok(output) => Some(output),
|
||||
Err(error) => {
|
||||
log_error_with_caller(location, DebugAsDisplay(&error), level);
|
||||
log_error_with_caller(
|
||||
location,
|
||||
format_args!("{:#}", DebugAsDisplay(&error)),
|
||||
level,
|
||||
);
|
||||
None
|
||||
}
|
||||
}),
|
||||
|
|
@ -605,3 +616,68 @@ where
|
|||
items.truncate(limit);
|
||||
items.sort_by(compare);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod logging_tests {
|
||||
use super::{ResultExt, TryFutureExt, TryFutureExtBacktrace};
|
||||
use log::{Level, Log, Metadata, Record};
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
future::ready,
|
||||
pin::pin,
|
||||
task::{Context, Poll, Waker},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn logging_preserves_diagnostics_and_callers() {
|
||||
log::set_logger(&TestLogger).expect("failed to install test logger");
|
||||
let error = anyhow::anyhow!("root failure")
|
||||
.context("inner context")
|
||||
.context("outer context");
|
||||
let display = "outer context: inner context: root failure";
|
||||
let debug = format!("{error:?}");
|
||||
let line = line!() + 1;
|
||||
assert_eq!(Err::<(), _>(&error).log_err(), None);
|
||||
assert_logged(line, display);
|
||||
let line = line!() + 1;
|
||||
assert_eq!(Err::<(), _>(&error).log_err_with_backtrace(), None);
|
||||
assert_logged(line, &debug);
|
||||
let mut context = Context::from_waker(Waker::noop());
|
||||
let line = line!() + 1;
|
||||
let mut future = pin!(ready(Err::<(), _>(&error)).log_err());
|
||||
assert_eq!(future.as_mut().poll(&mut context), Poll::Ready(None));
|
||||
assert_logged(line, display);
|
||||
let line = line!() + 1;
|
||||
let mut future = pin!(ready(Err::<(), _>(&error)).log_err_with_backtrace());
|
||||
assert_eq!(future.as_mut().poll(&mut context), Poll::Ready(None));
|
||||
assert_logged(line, &debug);
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static RECORDS: RefCell<Vec<(Option<u32>, String)>> = const { RefCell::new(Vec::new()) };
|
||||
}
|
||||
|
||||
struct TestLogger;
|
||||
|
||||
impl Log for TestLogger {
|
||||
fn enabled(&self, _: &Metadata<'_>) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn log(&self, record: &Record<'_>) {
|
||||
assert_eq!(record.target(), "gpui_util::lib");
|
||||
assert_eq!(record.module_path(), Some("crates/gpui_util/src/lib.rs"));
|
||||
assert_eq!(record.file(), Some(file!()));
|
||||
assert_eq!(record.level(), Level::Error);
|
||||
RECORDS.with_borrow_mut(|records| {
|
||||
records.push((record.line(), record.args().to_string()));
|
||||
});
|
||||
}
|
||||
|
||||
fn flush(&self) {}
|
||||
}
|
||||
|
||||
fn assert_logged(line: u32, message: &str) {
|
||||
assert_eq!(RECORDS.take(), [(Some(line), message.to_owned())]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ path = "src/grammars.rs"
|
|||
anyhow.workspace = true
|
||||
language_core.workspace = true
|
||||
rust-embed.workspace = true
|
||||
toml.workspace = true
|
||||
util.workspace = true
|
||||
|
||||
tree-sitter = { workspace = true, optional = true }
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ pub fn load_config(name: &str) -> LanguageConfig {
|
|||
)
|
||||
.unwrap();
|
||||
|
||||
let config: LanguageConfig = ::toml::from_str(&config_toml)
|
||||
let config = LanguageConfig::from_toml(&config_toml)
|
||||
.with_context(|| format!("failed to load config.toml for language {name:?}"))
|
||||
.unwrap();
|
||||
|
||||
|
|
|
|||
|
|
@ -18,8 +18,11 @@ pub use crate::html_element::*;
|
|||
pub use crate::markdown_writer::*;
|
||||
|
||||
/// Converts the provided HTML to Markdown.
|
||||
pub fn convert_html_to_markdown(html: impl Read, handlers: &mut [TagHandler]) -> Result<String> {
|
||||
let dom = parse_html(html).context("failed to parse HTML")?;
|
||||
pub fn convert_html_to_markdown(
|
||||
mut html: impl Read,
|
||||
handlers: &mut [TagHandler],
|
||||
) -> Result<String> {
|
||||
let dom = parse_html(&mut html).context("failed to parse HTML")?;
|
||||
|
||||
let markdown_writer = MarkdownWriter::new();
|
||||
let markdown = markdown_writer
|
||||
|
|
@ -29,7 +32,8 @@ pub fn convert_html_to_markdown(html: impl Read, handlers: &mut [TagHandler]) ->
|
|||
Ok(markdown)
|
||||
}
|
||||
|
||||
fn parse_html(mut html: impl Read) -> Result<RcDom> {
|
||||
#[inline(never)]
|
||||
fn parse_html(mut html: &mut dyn Read) -> Result<RcDom> {
|
||||
let parse_options = ParseOpts {
|
||||
tree_builder: TreeBuilderOpts {
|
||||
drop_doctype: true,
|
||||
|
|
|
|||
|
|
@ -150,9 +150,14 @@ pub struct LanguageConfig {
|
|||
impl LanguageConfig {
|
||||
pub const FILE_NAME: &str = "config.toml";
|
||||
|
||||
#[inline(never)]
|
||||
pub fn from_toml(config: &str) -> Result<Self, toml::de::Error> {
|
||||
toml::from_str::<Self>(config)
|
||||
}
|
||||
|
||||
pub fn load(config_path: impl AsRef<Path>) -> anyhow::Result<Self> {
|
||||
let config = std::fs::read_to_string(config_path.as_ref())?;
|
||||
toml::from_str(&config).map_err(Into::into)
|
||||
Self::from_toml(&config).map_err(anyhow::Error::from)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,14 @@
|
|||
mod html_minifier;
|
||||
pub(crate) mod html_parser;
|
||||
mod html_rendering;
|
||||
|
||||
use html5ever::{ParseOpts, parse_document, tendril::TendrilSink};
|
||||
use markup5ever_rcdom::RcDom;
|
||||
use std::io::{self, Read};
|
||||
|
||||
#[inline(never)]
|
||||
fn parse_html(mut reader: &mut dyn Read) -> io::Result<RcDom> {
|
||||
parse_document(RcDom::default(), ParseOpts::default())
|
||||
.from_utf8()
|
||||
.read_from(&mut reader)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use html5ever::{
|
||||
Attribute, ParseOpts, QualName, parse_document,
|
||||
tendril::{Tendril, TendrilSink, fmt::UTF8},
|
||||
Attribute, QualName,
|
||||
tendril::{Tendril, fmt::UTF8},
|
||||
};
|
||||
use markup5ever_rcdom::{Node, NodeData, RcDom};
|
||||
use markup5ever_rcdom::{Node, NodeData};
|
||||
use std::{cell::RefCell, io, rc::Rc, str};
|
||||
|
||||
#[derive(Default)]
|
||||
|
|
@ -38,10 +38,8 @@ where
|
|||
///
|
||||
/// Will return `Err` if unable to write to the output writer.
|
||||
#[inline]
|
||||
pub fn minify<R: io::Read>(&mut self, mut r: &mut R) -> io::Result<()> {
|
||||
let dom = parse_document(RcDom::default(), ParseOpts::default())
|
||||
.from_utf8()
|
||||
.read_from(&mut r)?;
|
||||
pub fn minify(&mut self, reader: &mut dyn io::Read) -> io::Result<()> {
|
||||
let dom = super::parse_html(reader)?;
|
||||
|
||||
if !self.options.omit_doctype {
|
||||
self.w.write_all(b"<!doctype html>")?;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
use std::{cell::RefCell, collections::HashMap, mem, ops::Range};
|
||||
|
||||
use gpui::{DefiniteLength, FontWeight, SharedString, TextAlign, px, relative};
|
||||
use html5ever::{
|
||||
Attribute, LocalName, ParseOpts, local_name, parse_document, tendril::TendrilSink,
|
||||
};
|
||||
use markup5ever_rcdom::{Node, NodeData, RcDom};
|
||||
use html5ever::{Attribute, LocalName, local_name};
|
||||
use markup5ever_rcdom::{Node, NodeData};
|
||||
use pulldown_cmark::{Alignment, HeadingLevel};
|
||||
use stacksafe::stacksafe;
|
||||
|
||||
|
|
@ -195,10 +193,7 @@ pub(crate) fn parse_html_block(
|
|||
) -> Option<ParsedHtmlBlock> {
|
||||
let bytes = cleanup_html(source);
|
||||
let mut cursor = std::io::Cursor::new(bytes);
|
||||
let dom = parse_document(RcDom::default(), ParseOpts::default())
|
||||
.from_utf8()
|
||||
.read_from(&mut cursor)
|
||||
.ok()?;
|
||||
let dom = super::parse_html(&mut cursor).ok()?;
|
||||
|
||||
let mut children = Vec::new();
|
||||
parse_html_node(
|
||||
|
|
|
|||
|
|
@ -820,7 +820,7 @@ pub async fn stream_response(
|
|||
if line == "[DONE]" || line.is_empty() {
|
||||
None
|
||||
} else {
|
||||
match serde_json::from_str::<StreamEvent>(line) {
|
||||
match decode_stream_event(line) {
|
||||
Ok(event) => Some(Ok(event)),
|
||||
Err(error) => {
|
||||
log::error!(
|
||||
|
|
@ -974,6 +974,11 @@ pub async fn stream_response(
|
|||
}
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
fn decode_stream_event(line: &str) -> serde_json::Result<StreamEvent> {
|
||||
serde_json::from_str(line)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
|||
|
|
@ -69,9 +69,7 @@ pub(crate) fn try_load_shape(
|
|||
return Ok(None);
|
||||
};
|
||||
|
||||
let shape = serde_json::from_str::<PickerConfig>(&shape)
|
||||
.context("Could not deserialize loaded picker shape from persistence")?
|
||||
.into_centered();
|
||||
let shape = parse_shape(&shape)?;
|
||||
Ok(Some(Shape::HorizontallyCentered(shape)))
|
||||
}
|
||||
|
||||
|
|
@ -90,6 +88,13 @@ pub(crate) fn load_last_preview_layout(
|
|||
parse_layout(&last_layout)
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
fn parse_shape(json: &str) -> anyhow::Result<Centered> {
|
||||
Ok(serde_json::from_str::<PickerConfig>(json)
|
||||
.context("Could not deserialize loaded picker shape from persistence")?
|
||||
.into_centered())
|
||||
}
|
||||
|
||||
fn shape_key(picker_delegate: &'static str, preview_layout: Option<preview::Layout>) -> String {
|
||||
format!("{picker_delegate}/{}", layout_as_str(preview_layout))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,23 @@ include!(concat!(env!("OUT_DIR"), "/zed.messages.rs"));
|
|||
pub const REMOTE_SERVER_PEER_ID: PeerId = PeerId { owner_id: 0, id: 0 };
|
||||
pub const REMOTE_SERVER_PROJECT_ID: u64 = 0;
|
||||
|
||||
impl Envelope {
|
||||
#[inline(never)]
|
||||
pub fn decode_from_slice(buffer: &[u8]) -> Result<Self, DecodeError> {
|
||||
Self::decode(buffer)
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn encode_to_buffer(&self, buffer: &mut Vec<u8>) -> Result<(), prost::EncodeError> {
|
||||
self.encode(buffer)
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn encoded_size(&self) -> usize {
|
||||
self.encoded_len()
|
||||
}
|
||||
}
|
||||
|
||||
messages!(
|
||||
(Ack, Foreground),
|
||||
(AckBufferOperation, Background),
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ gpui.workspace = true
|
|||
log.workspace = true
|
||||
parking_lot.workspace = true
|
||||
paths.workspace = true
|
||||
prost.workspace = true
|
||||
release_channel.workspace = true
|
||||
rpc = { workspace = true, features = ["gpui"] }
|
||||
schemars.workspace = true
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
use anyhow::Result;
|
||||
use futures::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
||||
use prost::Message as _;
|
||||
use rpc::proto::Envelope;
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
|
|
@ -20,7 +19,7 @@ pub async fn read_message_with_len<S: AsyncRead + Unpin>(
|
|||
) -> Result<Envelope> {
|
||||
buffer.resize(message_len as usize, 0);
|
||||
stream.read_exact(buffer).await?;
|
||||
Ok(Envelope::decode(buffer.as_slice())?)
|
||||
Ok(Envelope::decode_from_slice(buffer.as_slice())?)
|
||||
}
|
||||
|
||||
pub async fn read_message<S: AsyncRead + Unpin>(
|
||||
|
|
@ -40,13 +39,13 @@ pub async fn write_message<S: AsyncWrite + Unpin>(
|
|||
buffer: &mut Vec<u8>,
|
||||
message: Envelope,
|
||||
) -> Result<()> {
|
||||
let message_len = message.encoded_len() as u32;
|
||||
let message_len = message.encoded_size() as u32;
|
||||
stream
|
||||
.write_all(message_len.to_le_bytes().as_slice())
|
||||
.await?;
|
||||
buffer.clear();
|
||||
buffer.reserve(message_len as usize);
|
||||
message.encode(buffer)?;
|
||||
message.encode_to_buffer(buffer)?;
|
||||
stream.write_all(buffer).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,13 +47,13 @@ pub struct TerminalOutput {
|
|||
|
||||
/// Returns the default text style for the terminal output.
|
||||
pub fn text_style(window: &mut Window, cx: &App) -> TextStyle {
|
||||
let settings = ThemeSettings::get_global(cx).clone();
|
||||
let settings = ThemeSettings::get_global(cx);
|
||||
|
||||
let font_size = settings.buffer_font_size(cx).into();
|
||||
let font_family = settings.buffer_font.family;
|
||||
let font_features = settings.buffer_font.features;
|
||||
let font_family = settings.buffer_font.family.clone();
|
||||
let font_features = settings.buffer_font.features.clone();
|
||||
let font_weight = settings.buffer_font.weight;
|
||||
let font_fallbacks = settings.buffer_font.fallbacks;
|
||||
let font_fallbacks = settings.buffer_font.fallbacks.clone();
|
||||
|
||||
let theme = cx.theme();
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ pub use ::proto::*;
|
|||
|
||||
use async_tungstenite::tungstenite::Message as WebSocketMessage;
|
||||
use futures::{SinkExt as _, StreamExt as _};
|
||||
use proto::Message as _;
|
||||
use std::time::Instant;
|
||||
use std::{fmt::Debug, io};
|
||||
|
||||
|
|
@ -47,9 +46,9 @@ where
|
|||
|
||||
match message {
|
||||
Message::Envelope(message) => {
|
||||
self.encoding_buffer.reserve(message.encoded_len());
|
||||
self.encoding_buffer.reserve(message.encoded_size());
|
||||
message
|
||||
.encode(&mut self.encoding_buffer)
|
||||
.encode_to_buffer(&mut self.encoding_buffer)
|
||||
.map_err(io::Error::from)?;
|
||||
let buffer =
|
||||
zstd::stream::encode_all(self.encoding_buffer.as_slice(), COMPRESSION_LEVEL)
|
||||
|
|
@ -90,7 +89,7 @@ where
|
|||
zstd::zstd_safe::WriteBuf::as_slice(&*bytes),
|
||||
&mut self.encoding_buffer,
|
||||
)?;
|
||||
let envelope = Envelope::decode(self.encoding_buffer.as_slice())
|
||||
let envelope = Envelope::decode_from_slice(self.encoding_buffer.as_slice())
|
||||
.map_err(io::Error::from)?;
|
||||
|
||||
self.encoding_buffer.clear();
|
||||
|
|
|
|||
|
|
@ -253,125 +253,8 @@ impl Peer {
|
|||
}
|
||||
};
|
||||
|
||||
let response_channels = connection_state.response_channels.clone();
|
||||
let stream_response_channels = connection_state.stream_response_channels.clone();
|
||||
self.connections
|
||||
.write()
|
||||
.insert(connection_id, connection_state);
|
||||
|
||||
let incoming_rx = incoming_rx.filter_map(move |(incoming, received_at)| {
|
||||
let response_channels = response_channels.clone();
|
||||
let stream_response_channels = stream_response_channels.clone();
|
||||
async move {
|
||||
let message_id = incoming.id;
|
||||
tracing::trace!(?incoming, "incoming message future: start");
|
||||
let _end = util::defer(move || {
|
||||
tracing::trace!(%connection_id, message_id, "incoming message future: end");
|
||||
});
|
||||
|
||||
if let Some(responding_to) = incoming.responding_to {
|
||||
tracing::trace!(
|
||||
%connection_id,
|
||||
message_id,
|
||||
responding_to,
|
||||
"incoming response: received"
|
||||
);
|
||||
let response_channel =
|
||||
response_channels.lock().as_mut()?.remove(&responding_to);
|
||||
let terminal_stream_response = matches!(
|
||||
&incoming.payload,
|
||||
Some(proto::envelope::Payload::Error(_))
|
||||
| Some(proto::envelope::Payload::EndStream(_))
|
||||
);
|
||||
let stream_response_channel = if terminal_stream_response {
|
||||
stream_response_channels
|
||||
.lock()
|
||||
.as_mut()?
|
||||
.remove(&responding_to)
|
||||
} else {
|
||||
stream_response_channels
|
||||
.lock()
|
||||
.as_ref()?
|
||||
.get(&responding_to)
|
||||
.cloned()
|
||||
};
|
||||
|
||||
if let Some(tx) = response_channel {
|
||||
let requester_resumed = oneshot::channel();
|
||||
if let Err(error) = tx.send((incoming, received_at, requester_resumed.0)) {
|
||||
tracing::trace!(
|
||||
%connection_id,
|
||||
message_id,
|
||||
responding_to = responding_to,
|
||||
?error,
|
||||
"incoming response: request future dropped",
|
||||
);
|
||||
}
|
||||
|
||||
tracing::trace!(
|
||||
%connection_id,
|
||||
message_id,
|
||||
responding_to,
|
||||
"incoming response: waiting to resume requester"
|
||||
);
|
||||
let _ = requester_resumed.1.await;
|
||||
tracing::trace!(
|
||||
%connection_id,
|
||||
message_id,
|
||||
responding_to,
|
||||
"incoming response: requester resumed"
|
||||
);
|
||||
} else if let Some(tx) = stream_response_channel {
|
||||
let requester_resumed = oneshot::channel();
|
||||
if let Err(error) = tx.unbounded_send((Ok(incoming), requester_resumed.0)) {
|
||||
tracing::debug!(
|
||||
%connection_id,
|
||||
message_id,
|
||||
responding_to = responding_to,
|
||||
?error,
|
||||
"incoming stream response: request future dropped",
|
||||
);
|
||||
// The consumer has gone away, so drop the bookkeeping
|
||||
// for this stream rather than letting it accumulate
|
||||
// every subsequent message until a terminal frame.
|
||||
if let Some(channels) = stream_response_channels.lock().as_mut() {
|
||||
channels.remove(&responding_to);
|
||||
}
|
||||
} else {
|
||||
let _ = requester_resumed.1.await;
|
||||
}
|
||||
} else {
|
||||
let message_type = proto::build_typed_envelope(
|
||||
connection_id.into(),
|
||||
received_at,
|
||||
incoming,
|
||||
)
|
||||
.map(|p| p.payload_type_name());
|
||||
tracing::warn!(
|
||||
%connection_id,
|
||||
message_id,
|
||||
responding_to,
|
||||
message_type,
|
||||
"incoming response: unknown request"
|
||||
);
|
||||
}
|
||||
|
||||
None
|
||||
} else {
|
||||
tracing::trace!(%connection_id, message_id, "incoming message: received");
|
||||
proto::build_typed_envelope(connection_id.into(), received_at, incoming)
|
||||
.or_else(|| {
|
||||
tracing::error!(
|
||||
%connection_id,
|
||||
message_id,
|
||||
"unable to construct a typed envelope"
|
||||
);
|
||||
None
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
(connection_id, handle_io, incoming_rx.boxed())
|
||||
let incoming_rx = self.register_connection(connection_id, connection_state, incoming_rx);
|
||||
(connection_id, handle_io, incoming_rx)
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
|
|
@ -704,6 +587,134 @@ impl Peer {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
fn register_connection(
|
||||
&self,
|
||||
connection_id: ConnectionId,
|
||||
connection_state: ConnectionState,
|
||||
incoming_rx: mpsc::Receiver<(proto::Envelope, Instant)>,
|
||||
) -> BoxStream<'static, Box<dyn AnyTypedEnvelope>> {
|
||||
let response_channels = connection_state.response_channels.clone();
|
||||
let stream_response_channels = connection_state.stream_response_channels.clone();
|
||||
self.connections
|
||||
.write()
|
||||
.insert(connection_id, connection_state);
|
||||
|
||||
let incoming_rx = incoming_rx.filter_map(move |(incoming, received_at)| {
|
||||
let response_channels = response_channels.clone();
|
||||
let stream_response_channels = stream_response_channels.clone();
|
||||
async move {
|
||||
let message_id = incoming.id;
|
||||
tracing::trace!(?incoming, "incoming message future: start");
|
||||
let _end = util::defer(move || {
|
||||
tracing::trace!(%connection_id, message_id, "incoming message future: end");
|
||||
});
|
||||
|
||||
if let Some(responding_to) = incoming.responding_to {
|
||||
tracing::trace!(
|
||||
%connection_id,
|
||||
message_id,
|
||||
responding_to,
|
||||
"incoming response: received"
|
||||
);
|
||||
let response_channel =
|
||||
response_channels.lock().as_mut()?.remove(&responding_to);
|
||||
let terminal_stream_response = matches!(
|
||||
&incoming.payload,
|
||||
Some(proto::envelope::Payload::Error(_))
|
||||
| Some(proto::envelope::Payload::EndStream(_))
|
||||
);
|
||||
let stream_response_channel = if terminal_stream_response {
|
||||
stream_response_channels
|
||||
.lock()
|
||||
.as_mut()?
|
||||
.remove(&responding_to)
|
||||
} else {
|
||||
stream_response_channels
|
||||
.lock()
|
||||
.as_ref()?
|
||||
.get(&responding_to)
|
||||
.cloned()
|
||||
};
|
||||
|
||||
if let Some(tx) = response_channel {
|
||||
let requester_resumed = oneshot::channel();
|
||||
if let Err(error) = tx.send((incoming, received_at, requester_resumed.0)) {
|
||||
tracing::trace!(
|
||||
%connection_id,
|
||||
message_id,
|
||||
responding_to = responding_to,
|
||||
?error,
|
||||
"incoming response: request future dropped",
|
||||
);
|
||||
}
|
||||
|
||||
tracing::trace!(
|
||||
%connection_id,
|
||||
message_id,
|
||||
responding_to,
|
||||
"incoming response: waiting to resume requester"
|
||||
);
|
||||
let _ = requester_resumed.1.await;
|
||||
tracing::trace!(
|
||||
%connection_id,
|
||||
message_id,
|
||||
responding_to,
|
||||
"incoming response: requester resumed"
|
||||
);
|
||||
} else if let Some(tx) = stream_response_channel {
|
||||
let requester_resumed = oneshot::channel();
|
||||
if let Err(error) = tx.unbounded_send((Ok(incoming), requester_resumed.0)) {
|
||||
tracing::debug!(
|
||||
%connection_id,
|
||||
message_id,
|
||||
responding_to = responding_to,
|
||||
?error,
|
||||
"incoming stream response: request future dropped",
|
||||
);
|
||||
// The consumer has gone away, so drop the bookkeeping
|
||||
// for this stream rather than letting it accumulate
|
||||
// every subsequent message until a terminal frame.
|
||||
if let Some(channels) = stream_response_channels.lock().as_mut() {
|
||||
channels.remove(&responding_to);
|
||||
}
|
||||
} else {
|
||||
let _ = requester_resumed.1.await;
|
||||
}
|
||||
} else {
|
||||
let message_type = proto::build_typed_envelope(
|
||||
connection_id.into(),
|
||||
received_at,
|
||||
incoming,
|
||||
)
|
||||
.map(|p| p.payload_type_name());
|
||||
tracing::warn!(
|
||||
%connection_id,
|
||||
message_id,
|
||||
responding_to,
|
||||
message_type,
|
||||
"incoming response: unknown request"
|
||||
);
|
||||
}
|
||||
|
||||
None
|
||||
} else {
|
||||
tracing::trace!(%connection_id, message_id, "incoming message: received");
|
||||
proto::build_typed_envelope(connection_id.into(), received_at, incoming)
|
||||
.or_else(|| {
|
||||
tracing::error!(
|
||||
%connection_id,
|
||||
message_id,
|
||||
"unable to construct a typed envelope"
|
||||
);
|
||||
None
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
incoming_rx.boxed()
|
||||
}
|
||||
|
||||
fn connection_state(&self, connection_id: ConnectionId) -> Result<ConnectionState> {
|
||||
let connections = self.connections.read();
|
||||
let connection = connections
|
||||
|
|
|
|||
|
|
@ -1106,7 +1106,7 @@ impl TerminalElement {
|
|||
}
|
||||
|
||||
fn rem_size(&self, cx: &mut App) -> Option<Pixels> {
|
||||
let settings = ThemeSettings::get_global(cx).clone();
|
||||
let settings = ThemeSettings::get_global(cx);
|
||||
let buffer_font_size = settings.buffer_font_size(cx);
|
||||
let rem_size_scale = {
|
||||
// Our default UI font size is 14px on a 16px base scale.
|
||||
|
|
@ -1205,7 +1205,7 @@ impl Element for TerminalElement {
|
|||
cx,
|
||||
|_, _, hitbox, window, cx| {
|
||||
let hitbox = hitbox.unwrap();
|
||||
let settings = ThemeSettings::get_global(cx).clone();
|
||||
let settings = ThemeSettings::get_global(cx);
|
||||
|
||||
let buffer_font_size = settings.buffer_font_size(cx);
|
||||
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@ use std::{any::Any, fmt::Debug, ops::Not, time::Duration};
|
|||
use web_time::Instant;
|
||||
|
||||
use gpui::{
|
||||
Along, Anchor, App, AppContext as _, Axis as ScrollbarAxis, BorderStyle, Bounds, ContentMask,
|
||||
Context, Corners, CursorStyle, DispatchPhase, Div, Edges, Element, ElementId, Entity, EntityId,
|
||||
GlobalElementId, Hitbox, HitboxBehavior, Hsla, InteractiveElement, IntoElement, IsZero,
|
||||
LayoutId, ListState, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement,
|
||||
Pixels, Point, Position, Render, ScrollHandle, ScrollWheelEvent, Size, Stateful,
|
||||
StatefulInteractiveElement, Style, Styled, Task, UniformListDecoration,
|
||||
Along, Anchor, AnyElement, App, AppContext as _, Axis as ScrollbarAxis, BorderStyle, Bounds,
|
||||
ContentMask, Context, Corners, CursorStyle, DispatchPhase, Div, Edges, Element, ElementId,
|
||||
Entity, EntityId, GlobalElementId, Hitbox, HitboxBehavior, Hsla, InteractiveElement,
|
||||
IntoElement, IsZero, LayoutId, ListState, MouseButton, MouseDownEvent, MouseMoveEvent,
|
||||
MouseUpEvent, ParentElement, Pixels, Point, Position, Render, ScrollHandle, ScrollWheelEvent,
|
||||
Size, Stateful, StatefulInteractiveElement, Style, Styled, Task, UniformListDecoration,
|
||||
UniformListScrollHandle, Window, ease_in_out, prelude::FluentBuilder as _, px, quad, relative,
|
||||
size,
|
||||
};
|
||||
|
|
@ -237,7 +237,7 @@ impl<T: ScrollableHandle> UniformListDecoration for ScrollbarStateWrapper<T> {
|
|||
origin: -scroll_offset,
|
||||
state: self.0.clone(),
|
||||
}
|
||||
.into_any()
|
||||
.into_any_element()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -934,7 +934,7 @@ impl<T: ScrollableHandle> Render for ScrollbarState<T> {
|
|||
}
|
||||
}
|
||||
|
||||
struct ScrollbarElement<T: ScrollableHandle> {
|
||||
pub struct ScrollbarElement<T: ScrollableHandle> {
|
||||
origin: Point<Pixels>,
|
||||
state: Entity<ScrollbarState<T>>,
|
||||
}
|
||||
|
|
@ -954,6 +954,11 @@ impl ThumbState {
|
|||
}
|
||||
|
||||
impl ScrollableHandle for UniformListScrollHandle {
|
||||
#[inline(never)]
|
||||
fn into_scrollbar_element(element: ScrollbarElement<Self>) -> AnyElement {
|
||||
element.into_any()
|
||||
}
|
||||
|
||||
fn max_offset(&self) -> Point<Pixels> {
|
||||
self.0.borrow().base_handle.max_offset()
|
||||
}
|
||||
|
|
@ -972,6 +977,11 @@ impl ScrollableHandle for UniformListScrollHandle {
|
|||
}
|
||||
|
||||
impl ScrollableHandle for ListState {
|
||||
#[inline(never)]
|
||||
fn into_scrollbar_element(element: ScrollbarElement<Self>) -> AnyElement {
|
||||
element.into_any()
|
||||
}
|
||||
|
||||
fn max_offset(&self) -> Point<Pixels> {
|
||||
self.max_offset_for_scrollbar()
|
||||
}
|
||||
|
|
@ -998,6 +1008,11 @@ impl ScrollableHandle for ListState {
|
|||
}
|
||||
|
||||
impl ScrollableHandle for ScrollHandle {
|
||||
#[inline(never)]
|
||||
fn into_scrollbar_element(element: ScrollbarElement<Self>) -> AnyElement {
|
||||
element.into_any()
|
||||
}
|
||||
|
||||
fn max_offset(&self) -> Point<Pixels> {
|
||||
self.max_offset()
|
||||
}
|
||||
|
|
@ -1016,6 +1031,10 @@ impl ScrollableHandle for ScrollHandle {
|
|||
}
|
||||
|
||||
pub trait ScrollableHandle: 'static + Any + Sized + Clone {
|
||||
fn into_scrollbar_element(element: ScrollbarElement<Self>) -> AnyElement {
|
||||
element.into_any()
|
||||
}
|
||||
|
||||
fn max_offset(&self) -> Point<Pixels>;
|
||||
fn set_offset(&self, point: Point<Pixels>);
|
||||
fn offset(&self) -> Point<Pixels>;
|
||||
|
|
@ -1642,6 +1661,10 @@ impl<T: ScrollableHandle> IntoElement for ScrollbarElement<T> {
|
|||
fn into_element(self) -> Self::Element {
|
||||
self
|
||||
}
|
||||
|
||||
fn into_any_element(self) -> AnyElement {
|
||||
T::into_scrollbar_element(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue