mod connection;mod diff;mod mention;mod terminal; use agent_settings::AgentSettings;use collections::HashSet;pub use connection::*;pub use diff::*;use language::language_settings::FormatOnSave;pub use mention::*;use project::lsp_store::{FormatTrigger, LspFormatTarget};use serde::{Deserialize, Serialize};use serde_json::to_string_pretty;use settings::Settings as _;use task::{Shell, ShellBuilder};pub use terminal::*; use action_log::{ActionLog, ActionLogTelemetry};use agent_client_protocol::{self as acp};use anyhow::{Context as _, Result, anyhow};use editor::Bias;use futures::{FutureExt, channel::oneshot, future::BoxFuture};use gpui::{AppContext, AsyncApp, Context, Entity, EventEmitter, SharedString, Task, WeakEntity};use itertools::Itertools;use language::{Anchor, Buffer, BufferSnapshot, LanguageRegistry, Point, ToPoint, text_diff};use markdown::Markdown;use project::{AgentLocation, Project, git_store::GitStoreCheckpoint};use std::collections::HashMap;use std::error::Error;use std::fmt::{Formatter, Write};use std::ops::Range;use std::process::ExitStatus;use std::rc::Rc;use std::time::{Duration, Instant};use std::{fmt::Display, mem, path::PathBuf, sync::Arc};use ui::App;use util::{ResultExt, get_default_system_shell_preferring_bash, paths::PathStyle};use uuid::Uuid; #[derive(Debug)]pub struct UserMessage { pub id: Option<UserMessageId>, pub content: ContentBlock, pub chunks: Vec<acp::ContentBlock>, pub checkpoint: Option<Checkpoint>, pub indented: bool,} #[derive(Debug)]pub struct Checkpoint { git_checkpoint: GitStoreCheckpoint, pub show: bool,} impl UserMessage { fn to_markdown(&self, cx: &App) -> String { let mut markdown = String::new(); if self .checkpoint .as_ref() .is_some_and(|checkpoint| checkpoint.show) { writeln!(markdown, "## User (checkpoint)").unwrap(); } else { writeln!(markdown, "## User").unwrap(); } writeln!(markdown).unwrap(); writeln!(markdown, "{}", self.content.to_markdown(cx)).unwrap(); writeln!(markdown).unwrap(); markdown }} #[derive(Debug, PartialEq)]pub struct AssistantMessage { pub chunks: Vec<AssistantMessageChunk>, pub indented: bool,} impl AssistantMessage { pub fn to_markdown(&self, cx: &App) -> String { format!( "## Assistant\n\n{}\n\n", self.chunks .iter() .map(|chunk| chunk.to_markdown(cx)) .join("\n\n") ) }} #[derive(Debug, PartialEq)]pub enum AssistantMessageChunk { Message { block: ContentBlock }, Thought { block: ContentBlock },} impl AssistantMessageChunk { pub fn from_str( chunk: &str, language_registry: &Arc<LanguageRegistry>, path_style: PathStyle, cx: &mut App, ) -> Self { Self::Message { block: ContentBlock::new(chunk.into(), language_registry, path_style, cx), } } fn to_markdown(&self, cx: &App) -> String { match self { Self::Message { block } => block.to_markdown(cx).to_string(), Self::Thought { block } => { format!("<thinking>\n{}\n</thinking>", block.to_markdown(cx)) } } }} #[derive(Debug)]pub enum AgentThreadEntry { UserMessage(UserMessage), AssistantMessage(AssistantMessage), ToolCall(ToolCall),} impl AgentThreadEntry { pub fn is_indented(&self) -> bool { match self { Self::UserMessage(message) => message.indented, Self::AssistantMessage(message) => message.indented, Self::ToolCall(_) => false, } } pub fn to_markdown(&self, cx: &App) -> String { match self { Self::UserMessage(message) => message.to_markdown(cx), Self::AssistantMessage(message) => message.to_markdown(cx), Self::ToolCall(tool_call) => tool_call.to_markdown(cx), } } pub fn user_message(&self) -> Option<&UserMessage> { if let AgentThreadEntry::UserMessage(message) = self { Some(message) } else { None } } pub fn diffs(&self) -> impl Iterator<Item = &Entity<Diff>> { if let AgentThreadEntry::ToolCall(call) = self { itertools::Either::Left(call.diffs()) } else { itertools::Either::Right(std::iter::empty()) } } pub fn terminals(&self) -> impl Iterator<Item = &Entity<Terminal>> { if let AgentThreadEntry::ToolCall(call) = self { itertools::Either::Left(call.terminals()) } else { itertools::Either::Right(std::iter::empty()) } } pub fn location(&self, ix: usize) -> Option<(acp::ToolCallLocation, AgentLocation)> { if let AgentThreadEntry::ToolCall(ToolCall { locations, resolved_locations, .. }) = self { Some(( locations.get(ix)?.clone(), resolved_locations.get(ix)?.clone()?, )) } else { None } }} #[derive(Debug)]pub struct ToolCall { pub id: acp::ToolCallId, pub label: Entity<Markdown>, pub kind: acp::ToolKind, pub content: Vec<ToolCallContent>, pub status: ToolCallStatus, pub locations: Vec<acp::ToolCallLocation>, pub resolved_locations: Vec<Option<AgentLocation>>, pub raw_input: Option<serde_json::Value>, pub raw_input_markdown: Option<Entity<Markdown>>, pub raw_output: Option<serde_json::Value>,} impl ToolCall { fn from_acp( tool_call: acp::ToolCall, status: ToolCallStatus, language_registry: Arc<LanguageRegistry>, path_style: PathStyle, terminals: &HashMap<acp::TerminalId, Entity<Terminal>>, cx: &mut App, ) -> Result<Self> { let title = if let Some((first_line, _)) = tool_call.title.split_once("\n") { first_line.to_owned() + "…" } else { tool_call.title }; let mut content = Vec::with_capacity(tool_call.content.len()); for item in tool_call.content { if let Some(item) = ToolCallContent::from_acp( item, language_registry.clone(), path_style, terminals, cx, )? { content.push(item); } } let raw_input_markdown = tool_call .raw_input .as_ref() .and_then(|input| markdown_for_raw_output(input, &language_registry, cx)); let result = Self { id: tool_call.tool_call_id, label: cx .new(|cx| Markdown::new(title.into(), Some(language_registry.clone()), None, cx)), kind: tool_call.kind, content, locations: tool_call.locations, resolved_locations: Vec::default(), status, raw_input: tool_call.raw_input, raw_input_markdown, raw_output: tool_call.raw_output, }; Ok(result) } fn update_fields( &mut self, fields: acp::ToolCallUpdateFields, language_registry: Arc<LanguageRegistry>, path_style: PathStyle, terminals: &HashMap<acp::TerminalId, Entity<Terminal>>, cx: &mut App, ) -> Result<()> { let acp::ToolCallUpdateFields { kind, status, title, content, locations, raw_input, raw_output, .. } = fields; if let Some(kind) = kind { self.kind = kind; } if let Some(status) = status { self.status = status.into(); } if let Some(title) = title { self.label.update(cx, |label, cx| { if let Some((first_line, _)) = title.split_once("\n") { label.replace(first_line.to_owned() + "…", cx) } else { label.replace(title, cx); } }); } if let Some(content) = content { let mut new_content_len = content.len(); let mut content = content.into_iter(); // Reuse existing content if we can for (old, new) in self.content.iter_mut().zip(content.by_ref()) { let valid_content = old.update_from_acp(new, language_registry.clone(), path_style, terminals, cx)?; if !valid_content { new_content_len -= 1; } } for new in content { if let Some(new) = ToolCallContent::from_acp( new, language_registry.clone(), path_style, terminals, cx, )? { self.content.push(new); } else { new_content_len -= 1; } } self.content.truncate(new_content_len); } if let Some(locations) = locations { self.locations = locations; } if let Some(raw_input) = raw_input { self.raw_input_markdown = markdown_for_raw_output(&raw_input, &language_registry, cx); self.raw_input = Some(raw_input); } if let Some(raw_output) = raw_output { if self.content.is_empty() && let Some(markdown) = markdown_for_raw_output(&raw_output, &language_registry, cx) { self.content .push(ToolCallContent::ContentBlock(ContentBlock::Markdown { markdown, })); } self.raw_output = Some(raw_output); } Ok(()) } pub fn diffs(&self) -> impl Iterator<Item = &Entity<Diff>> { self.content.iter().filter_map(|content| match content { ToolCallContent::Diff(diff) => Some(diff), ToolCallContent::ContentBlock(_) => None, ToolCallContent::Terminal(_) => None, }) } pub fn terminals(&self) -> impl Iterator<Item = &Entity<Terminal>> { self.content.iter().filter_map(|content| match content { ToolCallContent::Terminal(terminal) => Some(terminal), ToolCallContent::ContentBlock(_) => None, ToolCallContent::Diff(_) => None, }) } fn to_markdown(&self, cx: &App) -> String { let mut markdown = format!( "**Tool Call: {}**\nStatus: {}\n\n", self.label.read(cx).source(), self.status ); for content in &self.content { markdown.push_str(content.to_markdown(cx).as_str()); markdown.push_str("\n\n"); } markdown } async fn resolve_location( location: acp::ToolCallLocation, project: WeakEntity<Project>, cx: &mut AsyncApp, ) -> Option<ResolvedLocation> { let buffer = project .update(cx, |project, cx| { project .project_path_for_absolute_path(&location.path, cx) .map(|path| project.open_buffer(path, cx)) }) .ok()??; let buffer = buffer.await.log_err()?; let position = buffer .update(cx, |buffer, _| { let snapshot = buffer.snapshot(); if let Some(row) = location.line { let column = snapshot.indent_size_for_line(row).len; let point = snapshot.clip_point(Point::new(row, column), Bias::Left); snapshot.anchor_before(point) } else { Anchor::min_for_buffer(snapshot.remote_id()) } }) .ok()?; Some(ResolvedLocation { buffer, position }) } fn resolve_locations( &self, project: Entity<Project>, cx: &mut App, ) -> Task<Vec<Option<ResolvedLocation>>> { let locations = self.locations.clone(); project.update(cx, |_, cx| { cx.spawn(async move |project, cx| { let mut new_locations = Vec::new(); for location in locations { new_locations.push(Self::resolve_location(location, project.clone(), cx).await); } new_locations }) }) }} // Separate so we can hold a strong reference to the buffer// for saving on the thread#[derive(Clone, Debug, PartialEq, Eq)]struct ResolvedLocation { buffer: Entity<Buffer>, position: Anchor,} impl From<&ResolvedLocation> for AgentLocation { fn from(value: &ResolvedLocation) -> Self { Self { buffer: value.buffer.downgrade(), position: value.position, } }} #[derive(Debug)]pub enum ToolCallStatus { /// The tool call hasn't started running yet, but we start showing it to /// the user. Pending, /// The tool call is waiting for confirmation from the user. WaitingForConfirmation { options: Vec<acp::PermissionOption>, respond_tx: oneshot::Sender<acp::PermissionOptionId>, }, /// The tool call is currently running. InProgress, /// The tool call completed successfully. Completed, /// The tool call failed. Failed, /// The user rejected the tool call. Rejected, /// The user canceled generation so the tool call was canceled. Canceled,} impl From<acp::ToolCallStatus> for ToolCallStatus { fn from(status: acp::ToolCallStatus) -> Self { match status { acp::ToolCallStatus::Pending => Self::Pending, acp::ToolCallStatus::InProgress => Self::InProgress, acp::ToolCallStatus::Completed => Self::Completed, acp::ToolCallStatus::Failed => Self::Failed, _ => Self::Pending, } }} impl Display for ToolCallStatus { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!( f, "{}", match self { ToolCallStatus::Pending => "Pending", ToolCallStatus::WaitingForConfirmation { .. } => "Waiting for confirmation", ToolCallStatus::InProgress => "In Progress", ToolCallStatus::Completed => "Completed", ToolCallStatus::Failed => "Failed", ToolCallStatus::Rejected => "Rejected", ToolCallStatus::Canceled => "Canceled", } ) }} #[derive(Debug, PartialEq, Clone)]pub enum ContentBlock { Empty, Markdown { markdown: Entity<Markdown> }, ResourceLink { resource_link: acp::ResourceLink },} impl ContentBlock { pub fn new( block: acp::ContentBlock, language_registry: &Arc<LanguageRegistry>, path_style: PathStyle, cx: &mut App, ) -> Self { let mut this = Self::Empty; this.append(block, language_registry, path_style, cx); this } pub fn new_combined(Lines 1–500 of 4145. Load more
mod connection; mod diff; mod mention; mod terminal; use agent_settings::AgentSettings; use collections::HashSet; pub use connection::*; pub use diff::*; use language::language_settings::FormatOnSave; pub use mention::*; use project::lsp_store::{FormatTrigger, LspFormatTarget}; use serde::{Deserialize, Serialize}; use serde_json::to_string_pretty; use settings::Settings as _; use task::{Shell, ShellBuilder}; pub use terminal::*; use action_log::{ActionLog, ActionLogTelemetry}; use agent_client_protocol::{self as acp}; use anyhow::{Context as _, Result, anyhow}; use editor::Bias; use futures::{FutureExt, channel::oneshot, future::BoxFuture}; use gpui::{AppContext, AsyncApp, Context, Entity, EventEmitter, SharedString, Task, WeakEntity}; use itertools::Itertools; use language::{Anchor, Buffer, BufferSnapshot, LanguageRegistry, Point, ToPoint, text_diff}; use markdown::Markdown; use project::{AgentLocation, Project, git_store::GitStoreCheckpoint}; use std::collections::HashMap; use std::error::Error; use std::fmt::{Formatter, Write}; use std::ops::Range; use std::process::ExitStatus; use std::rc::Rc; use std::time::{Duration, Instant}; use std::{fmt::Display, mem, path::PathBuf, sync::Arc}; use ui::App; use util::{ResultExt, get_default_system_shell_preferring_bash, paths::PathStyle}; use uuid::Uuid; #[derive(Debug)] pub struct UserMessage { pub id: Option<UserMessageId>, pub content: ContentBlock, pub chunks: Vec<acp::ContentBlock>, pub checkpoint: Option<Checkpoint>, pub indented: bool, } #[derive(Debug)] pub struct Checkpoint { git_checkpoint: GitStoreCheckpoint, pub show: bool, } impl UserMessage { fn to_markdown(&self, cx: &App) -> String { let mut markdown = String::new(); if self .checkpoint .as_ref() .is_some_and(|checkpoint| checkpoint.show) { writeln!(markdown, "## User (checkpoint)").unwrap(); } else { writeln!(markdown, "## User").unwrap(); } writeln!(markdown).unwrap(); writeln!(markdown, "{}", self.content.to_markdown(cx)).unwrap(); writeln!(markdown).unwrap(); markdown } } #[derive(Debug, PartialEq)] pub struct AssistantMessage { pub chunks: Vec<AssistantMessageChunk>, pub indented: bool, } impl AssistantMessage { pub fn to_markdown(&self, cx: &App) -> String { format!( "## Assistant\n\n{}\n\n", self.chunks .iter() .map(|chunk| chunk.to_markdown(cx)) .join("\n\n") ) } } #[derive(Debug, PartialEq)] pub enum AssistantMessageChunk { Message { block: ContentBlock }, Thought { block: ContentBlock }, } impl AssistantMessageChunk { pub fn from_str( chunk: &str, language_registry: &Arc<LanguageRegistry>, path_style: PathStyle, cx: &mut App, ) -> Self { Self::Message { block: ContentBlock::new(chunk.into(), language_registry, path_style, cx), } } fn to_markdown(&self, cx: &App) -> String { match self { Self::Message { block } => block.to_markdown(cx).to_string(), Self::Thought { block } => { format!("<thinking>\n{}\n</thinking>", block.to_markdown(cx)) } } } } #[derive(Debug)] pub enum AgentThreadEntry { UserMessage(UserMessage), AssistantMessage(AssistantMessage), ToolCall(ToolCall), } impl AgentThreadEntry { pub fn is_indented(&self) -> bool { match self { Self::UserMessage(message) => message.indented, Self::AssistantMessage(message) => message.indented, Self::ToolCall(_) => false, } } pub fn to_markdown(&self, cx: &App) -> String { match self { Self::UserMessage(message) => message.to_markdown(cx), Self::AssistantMessage(message) => message.to_markdown(cx), Self::ToolCall(tool_call) => tool_call.to_markdown(cx), } } pub fn user_message(&self) -> Option<&UserMessage> { if let AgentThreadEntry::UserMessage(message) = self { Some(message) } else { None } } pub fn diffs(&self) -> impl Iterator<Item = &Entity<Diff>> { if let AgentThreadEntry::ToolCall(call) = self { itertools::Either::Left(call.diffs()) } else { itertools::Either::Right(std::iter::empty()) } } pub fn terminals(&self) -> impl Iterator<Item = &Entity<Terminal>> { if let AgentThreadEntry::ToolCall(call) = self { itertools::Either::Left(call.terminals()) } else { itertools::Either::Right(std::iter::empty()) } } pub fn location(&self, ix: usize) -> Option<(acp::ToolCallLocation, AgentLocation)> { if let AgentThreadEntry::ToolCall(ToolCall { locations, resolved_locations, .. }) = self { Some(( locations.get(ix)?.clone(), resolved_locations.get(ix)?.clone()?, )) } else { None } } } #[derive(Debug)] pub struct ToolCall { pub id: acp::ToolCallId, pub label: Entity<Markdown>, pub kind: acp::ToolKind, pub content: Vec<ToolCallContent>, pub status: ToolCallStatus, pub locations: Vec<acp::ToolCallLocation>, pub resolved_locations: Vec<Option<AgentLocation>>, pub raw_input: Option<serde_json::Value>, pub raw_input_markdown: Option<Entity<Markdown>>, pub raw_output: Option<serde_json::Value>, } impl ToolCall { fn from_acp( tool_call: acp::ToolCall, status: ToolCallStatus, language_registry: Arc<LanguageRegistry>, path_style: PathStyle, terminals: &HashMap<acp::TerminalId, Entity<Terminal>>, cx: &mut App, ) -> Result<Self> { let title = if let Some((first_line, _)) = tool_call.title.split_once("\n") { first_line.to_owned() + "…" } else { tool_call.title }; let mut content = Vec::with_capacity(tool_call.content.len()); for item in tool_call.content { if let Some(item) = ToolCallContent::from_acp( item, language_registry.clone(), path_style, terminals, cx, )? { content.push(item); } } let raw_input_markdown = tool_call .raw_input .as_ref() .and_then(|input| markdown_for_raw_output(input, &language_registry, cx)); let result = Self { id: tool_call.tool_call_id, label: cx .new(|cx| Markdown::new(title.into(), Some(language_registry.clone()), None, cx)), kind: tool_call.kind, content, locations: tool_call.locations, resolved_locations: Vec::default(), status, raw_input: tool_call.raw_input, raw_input_markdown, raw_output: tool_call.raw_output, }; Ok(result) } fn update_fields( &mut self, fields: acp::ToolCallUpdateFields, language_registry: Arc<LanguageRegistry>, path_style: PathStyle, terminals: &HashMap<acp::TerminalId, Entity<Terminal>>, cx: &mut App, ) -> Result<()> { let acp::ToolCallUpdateFields { kind, status, title, content, locations, raw_input, raw_output, .. } = fields; if let Some(kind) = kind { self.kind = kind; } if let Some(status) = status { self.status = status.into(); } if let Some(title) = title { self.label.update(cx, |label, cx| { if let Some((first_line, _)) = title.split_once("\n") { label.replace(first_line.to_owned() + "…", cx) } else { label.replace(title, cx); } }); } if let Some(content) = content { let mut new_content_len = content.len(); let mut content = content.into_iter(); // Reuse existing content if we can for (old, new) in self.content.iter_mut().zip(content.by_ref()) { let valid_content = old.update_from_acp(new, language_registry.clone(), path_style, terminals, cx)?; if !valid_content { new_content_len -= 1; } } for new in content { if let Some(new) = ToolCallContent::from_acp( new, language_registry.clone(), path_style, terminals, cx, )? { self.content.push(new); } else { new_content_len -= 1; } } self.content.truncate(new_content_len); } if let Some(locations) = locations { self.locations = locations; } if let Some(raw_input) = raw_input { self.raw_input_markdown = markdown_for_raw_output(&raw_input, &language_registry, cx); self.raw_input = Some(raw_input); } if let Some(raw_output) = raw_output { if self.content.is_empty() && let Some(markdown) = markdown_for_raw_output(&raw_output, &language_registry, cx) { self.content .push(ToolCallContent::ContentBlock(ContentBlock::Markdown { markdown, })); } self.raw_output = Some(raw_output); } Ok(()) } pub fn diffs(&self) -> impl Iterator<Item = &Entity<Diff>> { self.content.iter().filter_map(|content| match content { ToolCallContent::Diff(diff) => Some(diff), ToolCallContent::ContentBlock(_) => None, ToolCallContent::Terminal(_) => None, }) } pub fn terminals(&self) -> impl Iterator<Item = &Entity<Terminal>> { self.content.iter().filter_map(|content| match content { ToolCallContent::Terminal(terminal) => Some(terminal), ToolCallContent::ContentBlock(_) => None, ToolCallContent::Diff(_) => None, }) } fn to_markdown(&self, cx: &App) -> String { let mut markdown = format!( "**Tool Call: {}**\nStatus: {}\n\n", self.label.read(cx).source(), self.status ); for content in &self.content { markdown.push_str(content.to_markdown(cx).as_str()); markdown.push_str("\n\n"); } markdown } async fn resolve_location( location: acp::ToolCallLocation, project: WeakEntity<Project>, cx: &mut AsyncApp, ) -> Option<ResolvedLocation> { let buffer = project .update(cx, |project, cx| { project .project_path_for_absolute_path(&location.path, cx) .map(|path| project.open_buffer(path, cx)) }) .ok()??; let buffer = buffer.await.log_err()?; let position = buffer .update(cx, |buffer, _| { let snapshot = buffer.snapshot(); if let Some(row) = location.line { let column = snapshot.indent_size_for_line(row).len; let point = snapshot.clip_point(Point::new(row, column), Bias::Left); snapshot.anchor_before(point) } else { Anchor::min_for_buffer(snapshot.remote_id()) } }) .ok()?; Some(ResolvedLocation { buffer, position }) } fn resolve_locations( &self, project: Entity<Project>, cx: &mut App, ) -> Task<Vec<Option<ResolvedLocation>>> { let locations = self.locations.clone(); project.update(cx, |_, cx| { cx.spawn(async move |project, cx| { let mut new_locations = Vec::new(); for location in locations { new_locations.push(Self::resolve_location(location, project.clone(), cx).await); } new_locations }) }) } } // Separate so we can hold a strong reference to the buffer // for saving on the thread #[derive(Clone, Debug, PartialEq, Eq)] struct ResolvedLocation { buffer: Entity<Buffer>, position: Anchor, } impl From<&ResolvedLocation> for AgentLocation { fn from(value: &ResolvedLocation) -> Self { Self { buffer: value.buffer.downgrade(), position: value.position, } } } #[derive(Debug)] pub enum ToolCallStatus { /// The tool call hasn't started running yet, but we start showing it to /// the user. Pending, /// The tool call is waiting for confirmation from the user. WaitingForConfirmation { options: Vec<acp::PermissionOption>, respond_tx: oneshot::Sender<acp::PermissionOptionId>, }, /// The tool call is currently running. InProgress, /// The tool call completed successfully. Completed, /// The tool call failed. Failed, /// The user rejected the tool call. Rejected, /// The user canceled generation so the tool call was canceled. Canceled, } impl From<acp::ToolCallStatus> for ToolCallStatus { fn from(status: acp::ToolCallStatus) -> Self { match status { acp::ToolCallStatus::Pending => Self::Pending, acp::ToolCallStatus::InProgress => Self::InProgress, acp::ToolCallStatus::Completed => Self::Completed, acp::ToolCallStatus::Failed => Self::Failed, _ => Self::Pending, } } } impl Display for ToolCallStatus { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!( f, "{}", match self { ToolCallStatus::Pending => "Pending", ToolCallStatus::WaitingForConfirmation { .. } => "Waiting for confirmation", ToolCallStatus::InProgress => "In Progress", ToolCallStatus::Completed => "Completed", ToolCallStatus::Failed => "Failed", ToolCallStatus::Rejected => "Rejected", ToolCallStatus::Canceled => "Canceled", } ) } } #[derive(Debug, PartialEq, Clone)] pub enum ContentBlock { Empty, Markdown { markdown: Entity<Markdown> }, ResourceLink { resource_link: acp::ResourceLink }, } impl ContentBlock { pub fn new( block: acp::ContentBlock, language_registry: &Arc<LanguageRegistry>, path_style: PathStyle, cx: &mut App, ) -> Self { let mut this = Self::Empty; this.append(block, language_registry, path_style, cx); this } pub fn new_combined(