Compare commits

...

2 Commits

Author SHA1 Message Date
Piotr Osiewicz
8f44ff7d31 Cargo fmt 2025-03-31 15:31:27 +02:00
Piotr Osiewicz
e85ec8ffb7 chore: Prepare for Rust 2024
This is just a result of `cargo fix --edition`, without actually bumping the edition just yet.
2025-03-31 15:29:44 +02:00
320 changed files with 11821 additions and 10396 deletions

24
Cargo.lock generated
View File

@@ -1910,6 +1910,24 @@ name = "bindgen"
version = "0.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f"
dependencies = [
"bitflags 2.9.0",
"cexpr",
"clang-sys",
"itertools 0.12.1",
"proc-macro2",
"quote",
"regex",
"rustc-hash 1.1.0",
"shlex",
"syn 2.0.100",
]
[[package]]
name = "bindgen"
version = "0.71.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3"
dependencies = [
"bitflags 2.9.0",
"cexpr",
@@ -1920,7 +1938,7 @@ dependencies = [
"proc-macro2",
"quote",
"regex",
"rustc-hash 1.1.0",
"rustc-hash 2.1.1",
"shlex",
"syn 2.0.100",
]
@@ -5848,7 +5866,7 @@ dependencies = [
"ashpd",
"async-task",
"backtrace",
"bindgen 0.70.1",
"bindgen 0.71.1",
"blade-graphics",
"blade-macros",
"blade-util",
@@ -8220,7 +8238,7 @@ name = "media"
version = "0.1.0"
dependencies = [
"anyhow",
"bindgen 0.70.1",
"bindgen 0.71.1",
"core-foundation 0.10.0",
"core-video",
"ctor",

View File

@@ -190,7 +190,7 @@ impl ActivityIndicator {
fn pending_language_server_work<'a>(
&self,
cx: &'a App,
) -> impl Iterator<Item = PendingWork<'a>> {
) -> impl Iterator<Item = PendingWork<'a>> + use<'a> {
self.project
.read(cx)
.language_server_statuses(cx)

View File

@@ -89,22 +89,25 @@ impl AskPassSession {
buffer.clear();
}
let prompt = String::from_utf8_lossy(&buffer);
if let Some(password) = delegate
match delegate
.ask_password(prompt.to_string())
.await
.context("failed to get askpass password")
.log_err()
{
stream.write_all(password.as_bytes()).await.log_err();
} else {
if let Some(kill_tx) = kill_tx.take() {
kill_tx.send(()).log_err();
Some(password) => {
stream.write_all(password.as_bytes()).await.log_err();
}
_ => {
if let Some(kill_tx) = kill_tx.take() {
kill_tx.send(()).log_err();
}
// note: we expect the caller to drop this task when it's done.
// We need to keep the stream open until the caller is done to avoid
// spurious errors from ssh.
std::future::pending::<()>().await;
drop(stream);
}
// note: we expect the caller to drop this task when it's done.
// We need to keep the stream open until the caller is done to avoid
// spurious errors from ssh.
std::future::pending::<()>().await;
drop(stream);
}
}
drop(temp_dir)

View File

@@ -741,20 +741,29 @@ impl AssistantPanel {
}
});
if let Some(context_editor) = context_editor {
Some(InlineAssistTarget::Editor(context_editor, false))
} else if let Some(workspace_editor) = workspace
.active_item(cx)
.and_then(|item| item.act_as::<Editor>(cx))
{
Some(InlineAssistTarget::Editor(workspace_editor, true))
} else if let Some(terminal_view) = workspace
.active_item(cx)
.and_then(|item| item.act_as::<TerminalView>(cx))
{
Some(InlineAssistTarget::Terminal(terminal_view))
} else {
None
match context_editor {
Some(context_editor) => Some(InlineAssistTarget::Editor(context_editor, false)),
_ => {
match workspace
.active_item(cx)
.and_then(|item| item.act_as::<Editor>(cx))
{
Some(workspace_editor) => {
Some(InlineAssistTarget::Editor(workspace_editor, true))
}
_ => {
match workspace
.active_item(cx)
.and_then(|item| item.act_as::<TerminalView>(cx))
{
Some(terminal_view) => {
Some(InlineAssistTarget::Terminal(terminal_view))
}
_ => None,
}
}
}
}
}
}

View File

@@ -250,33 +250,35 @@ impl InlineAssistant {
selection.end.column = snapshot
.buffer_snapshot
.line_len(MultiBufferRow(selection.end.row));
} else if let Some(fold) =
snapshot.crease_for_buffer_row(MultiBufferRow(selection.end.row))
{
selection.start = fold.range().start;
selection.end = fold.range().end;
if MultiBufferRow(selection.end.row) < snapshot.buffer_snapshot.max_row() {
let chars = snapshot
.buffer_snapshot
.chars_at(Point::new(selection.end.row + 1, 0));
for c in chars {
if c == '\n' {
break;
}
if c.is_whitespace() {
continue;
}
if snapshot
.language_at(selection.end)
.is_some_and(|language| language.config().brackets.is_closing_brace(c))
{
selection.end.row += 1;
selection.end.column = snapshot
} else {
match snapshot.crease_for_buffer_row(MultiBufferRow(selection.end.row)) {
Some(fold) => {
selection.start = fold.range().start;
selection.end = fold.range().end;
if MultiBufferRow(selection.end.row) < snapshot.buffer_snapshot.max_row() {
let chars = snapshot
.buffer_snapshot
.line_len(MultiBufferRow(selection.end.row));
.chars_at(Point::new(selection.end.row + 1, 0));
for c in chars {
if c == '\n' {
break;
}
if c.is_whitespace() {
continue;
}
if snapshot.language_at(selection.end).is_some_and(|language| {
language.config().brackets.is_closing_brace(c)
}) {
selection.end.row += 1;
selection.end.column = snapshot
.buffer_snapshot
.line_len(MultiBufferRow(selection.end.row));
}
}
}
}
_ => {}
}
}
@@ -1031,24 +1033,27 @@ impl InlineAssistant {
let mut scroll_target_top;
let mut scroll_target_bottom;
if let Some(decorations) = assist.decorations.as_ref() {
scroll_target_top = editor
.row_for_block(decorations.prompt_block_id, cx)
.unwrap()
.0 as f32;
scroll_target_bottom = editor
.row_for_block(decorations.end_block_id, cx)
.unwrap()
.0 as f32;
} else {
let snapshot = editor.snapshot(window, cx);
let start_row = assist
.range
.start
.to_display_point(&snapshot.display_snapshot)
.row();
scroll_target_top = start_row.0 as f32;
scroll_target_bottom = scroll_target_top + 1.;
match assist.decorations.as_ref() {
Some(decorations) => {
scroll_target_top = editor
.row_for_block(decorations.prompt_block_id, cx)
.unwrap()
.0 as f32;
scroll_target_bottom = editor
.row_for_block(decorations.end_block_id, cx)
.unwrap()
.0 as f32;
}
_ => {
let snapshot = editor.snapshot(window, cx);
let start_row = assist
.range
.start
.to_display_point(&snapshot.display_snapshot)
.row();
scroll_target_top = start_row.0 as f32;
scroll_target_bottom = scroll_target_top + 1.;
}
}
scroll_target_top -= editor.vertical_scroll_margin() as f32;
scroll_target_bottom += editor.vertical_scroll_margin() as f32;
@@ -1093,10 +1098,11 @@ impl InlineAssistant {
}
pub fn start_assist(&mut self, assist_id: InlineAssistId, window: &mut Window, cx: &mut App) {
let assist = if let Some(assist) = self.assists.get_mut(&assist_id) {
assist
} else {
return;
let assist = match self.assists.get_mut(&assist_id) {
Some(assist) => assist,
_ => {
return;
}
};
let assist_group_id = assist.group_id;
@@ -1128,10 +1134,11 @@ impl InlineAssistant {
}
pub fn stop_assist(&mut self, assist_id: InlineAssistId, cx: &mut App) {
let assist = if let Some(assist) = self.assists.get_mut(&assist_id) {
assist
} else {
return;
let assist = match self.assists.get_mut(&assist_id) {
Some(assist) => assist,
_ => {
return;
}
};
assist.codegen.update(cx, |codegen, cx| codegen.stop(cx));
@@ -2184,7 +2191,7 @@ impl PromptEditor {
.into_any_element()
}
fn render_token_count(&self, cx: &mut Context<Self>) -> Option<impl IntoElement> {
fn render_token_count(&self, cx: &mut Context<Self>) -> Option<impl IntoElement + use<>> {
let model = LanguageModelRegistry::read_global(cx).active_model()?;
let token_counts = self.token_counts?;
let max_token_count = model.max_token_count();
@@ -2212,40 +2219,43 @@ impl PromptEditor {
.size(LabelSize::Small)
.color(Color::Muted),
);
if let Some(workspace) = self.workspace.clone() {
token_count = token_count
.tooltip(move |window, cx| {
Tooltip::with_meta(
format!(
"Tokens Used ({} from the Assistant Panel)",
humanize_token_count(token_counts.assistant_panel)
),
None,
"Click to open the Assistant Panel",
window,
cx,
)
})
.cursor_pointer()
.on_mouse_down(gpui::MouseButton::Left, |_, _, cx| cx.stop_propagation())
.on_click(move |_, window, cx| {
cx.stop_propagation();
workspace
.update(cx, |workspace, cx| {
workspace.focus_panel::<AssistantPanel>(window, cx)
})
.ok();
});
} else {
token_count = token_count
.cursor_default()
.tooltip(Tooltip::text("Tokens used"));
match self.workspace.clone() {
Some(workspace) => {
token_count = token_count
.tooltip(move |window, cx| {
Tooltip::with_meta(
format!(
"Tokens Used ({} from the Assistant Panel)",
humanize_token_count(token_counts.assistant_panel)
),
None,
"Click to open the Assistant Panel",
window,
cx,
)
})
.cursor_pointer()
.on_mouse_down(gpui::MouseButton::Left, |_, _, cx| cx.stop_propagation())
.on_click(move |_, window, cx| {
cx.stop_propagation();
workspace
.update(cx, |workspace, cx| {
workspace.focus_panel::<AssistantPanel>(window, cx)
})
.ok();
});
}
_ => {
token_count = token_count
.cursor_default()
.tooltip(Tooltip::text("Tokens used"));
}
}
Some(token_count)
}
fn render_prompt_editor(&self, cx: &mut Context<Self>) -> impl IntoElement {
fn render_prompt_editor(&self, cx: &mut Context<Self>) -> impl IntoElement + use<> {
let settings = ThemeSettings::get_global(cx);
let text_style = TextStyle {
color: if self.editor.read(cx).read_only(cx) {
@@ -2271,7 +2281,7 @@ impl PromptEditor {
)
}
fn render_rate_limit_notice(&self, cx: &mut Context<Self>) -> impl IntoElement {
fn render_rate_limit_notice(&self, cx: &mut Context<Self>) -> impl IntoElement + use<> {
Popover::new().child(
v_flex()
.occlude()
@@ -2430,10 +2440,11 @@ impl InlineAssist {
InlineAssistant::update_global(cx, |this, cx| match event {
CodegenEvent::Undone => this.finish_assist(assist_id, false, window, cx),
CodegenEvent::Finished => {
let assist = if let Some(assist) = this.assists.get(&assist_id) {
assist
} else {
return;
let assist = match this.assists.get(&assist_id) {
Some(assist) => assist,
_ => {
return;
}
};
if let CodegenStatus::Error(error) = codegen.read(cx).status(cx) {
@@ -2865,27 +2876,28 @@ impl CodegenAlternative {
assistant_panel_context: Option<LanguageModelRequest>,
cx: &App,
) -> BoxFuture<'static, Result<TokenCounts>> {
if let Some(model) = LanguageModelRegistry::read_global(cx).active_model() {
let request = self.build_request(user_prompt, assistant_panel_context.clone(), cx);
match request {
Ok(request) => {
let total_count = model.count_tokens(request.clone(), cx);
let assistant_panel_count = assistant_panel_context
.map(|context| model.count_tokens(context, cx))
.unwrap_or_else(|| future::ready(Ok(0)).boxed());
match LanguageModelRegistry::read_global(cx).active_model() {
Some(model) => {
let request = self.build_request(user_prompt, assistant_panel_context.clone(), cx);
match request {
Ok(request) => {
let total_count = model.count_tokens(request.clone(), cx);
let assistant_panel_count = assistant_panel_context
.map(|context| model.count_tokens(context, cx))
.unwrap_or_else(|| future::ready(Ok(0)).boxed());
async move {
Ok(TokenCounts {
total: total_count.await?,
assistant_panel: assistant_panel_count.await?,
})
async move {
Ok(TokenCounts {
total: total_count.await?,
assistant_panel: assistant_panel_count.await?,
})
}
.boxed()
}
.boxed()
Err(error) => futures::future::ready(Err(error)).boxed(),
}
Err(error) => futures::future::ready(Err(error)).boxed(),
}
} else {
future::ready(Err(anyhow!("no active model"))).boxed()
_ => future::ready(Err(anyhow!("no active model"))).boxed(),
}
}
@@ -3221,10 +3233,13 @@ impl CodegenAlternative {
.update(cx, |this, cx| {
this.message_id = message_id;
this.last_equal_ranges.clear();
if let Err(error) = result {
this.status = CodegenStatus::Error(error);
} else {
this.status = CodegenStatus::Done;
match result {
Err(error) => {
this.status = CodegenStatus::Error(error);
}
_ => {
this.status = CodegenStatus::Done;
}
}
this.elapsed_time = Some(elapsed_time);
this.completion = Some(completion.lock().clone());

View File

@@ -184,10 +184,11 @@ impl TerminalInlineAssistant {
}
fn start_assist(&mut self, assist_id: TerminalInlineAssistId, cx: &mut App) {
let assist = if let Some(assist) = self.assists.get_mut(&assist_id) {
assist
} else {
return;
let assist = match self.assists.get_mut(&assist_id) {
Some(assist) => assist,
_ => {
return;
}
};
let Some(user_prompt) = assist
@@ -222,10 +223,11 @@ impl TerminalInlineAssistant {
}
fn stop_assist(&mut self, assist_id: TerminalInlineAssistId, cx: &mut App) {
let assist = if let Some(assist) = self.assists.get_mut(&assist_id) {
assist
} else {
return;
let assist = match self.assists.get_mut(&assist_id) {
Some(assist) => assist,
_ => {
return;
}
};
assist.codegen.update(cx, |codegen, cx| codegen.stop(cx));
@@ -436,10 +438,11 @@ impl TerminalInlineAssist {
window.subscribe(&codegen, cx, move |codegen, event, window, cx| {
TerminalInlineAssistant::update_global(cx, |this, cx| match event {
CodegenEvent::Finished => {
let assist = if let Some(assist) = this.assists.get(&assist_id) {
assist
} else {
return;
let assist = match this.assists.get(&assist_id) {
Some(assist) => assist,
_ => {
return;
}
};
if let CodegenStatus::Error(error) = &codegen.read(cx).status {
@@ -664,8 +667,8 @@ impl Render for PromptEditor {
},
gpui::Corner::TopRight,
))
.children(
if let CodegenStatus::Error(error) = &self.codegen.read(cx).status {
.children(match &self.codegen.read(cx).status {
CodegenStatus::Error(error) => {
let error_message = SharedString::from(error.to_string());
Some(
div()
@@ -677,10 +680,9 @@ impl Render for PromptEditor {
.color(Color::Error),
),
)
} else {
None
},
),
}
_ => None,
}),
)
.child(div().flex_1().child(self.render_prompt_editor(cx)))
.child(
@@ -979,7 +981,7 @@ impl PromptEditor {
}
}
fn render_token_count(&self, cx: &mut Context<Self>) -> Option<impl IntoElement> {
fn render_token_count(&self, cx: &mut Context<Self>) -> Option<impl IntoElement + use<>> {
let model = LanguageModelRegistry::read_global(cx).active_model()?;
let token_count = self.token_count?;
let max_token_count = model.max_token_count();
@@ -1007,37 +1009,40 @@ impl PromptEditor {
.size(LabelSize::Small)
.color(Color::Muted),
);
if let Some(workspace) = self.workspace.clone() {
token_count = token_count
.tooltip(|window, cx| {
Tooltip::with_meta(
"Tokens Used by Inline Assistant",
None,
"Click to Open Assistant Panel",
window,
cx,
)
})
.cursor_pointer()
.on_mouse_down(gpui::MouseButton::Left, |_, _, cx| cx.stop_propagation())
.on_click(move |_, window, cx| {
cx.stop_propagation();
workspace
.update(cx, |workspace, cx| {
workspace.focus_panel::<AssistantPanel>(window, cx)
})
.ok();
});
} else {
token_count = token_count
.cursor_default()
.tooltip(Tooltip::text("Tokens Used by Inline Assistant"));
match self.workspace.clone() {
Some(workspace) => {
token_count = token_count
.tooltip(|window, cx| {
Tooltip::with_meta(
"Tokens Used by Inline Assistant",
None,
"Click to Open Assistant Panel",
window,
cx,
)
})
.cursor_pointer()
.on_mouse_down(gpui::MouseButton::Left, |_, _, cx| cx.stop_propagation())
.on_click(move |_, window, cx| {
cx.stop_propagation();
workspace
.update(cx, |workspace, cx| {
workspace.focus_panel::<AssistantPanel>(window, cx)
})
.ok();
});
}
_ => {
token_count = token_count
.cursor_default()
.tooltip(Tooltip::text("Tokens Used by Inline Assistant"));
}
}
Some(token_count)
}
fn render_prompt_editor(&self, cx: &mut Context<Self>) -> impl IntoElement {
fn render_prompt_editor(&self, cx: &mut Context<Self>) -> impl IntoElement + use<> {
let settings = ThemeSettings::get_global(cx);
let text_style = TextStyle {
color: if self.editor.read(cx).read_only(cx) {
@@ -1217,10 +1222,13 @@ impl Codegen {
let result = generate.await;
this.update(cx, |this, cx| {
if let Err(error) = result {
this.status = CodegenStatus::Error(error);
} else {
this.status = CodegenStatus::Done;
match result {
Err(error) => {
this.status = CodegenStatus::Error(error);
}
_ => {
this.status = CodegenStatus::Done;
}
}
cx.emit(CodegenEvent::Finished);
cx.notify();

View File

@@ -77,34 +77,44 @@ impl RenderedMessage {
}
fn append_thinking(&mut self, text: &String, window: &Window, cx: &mut App) {
if let Some(RenderedMessageSegment::Thinking {
content,
scroll_handle,
}) = self.segments.last_mut()
{
content.update(cx, |markdown, cx| {
markdown.append(text, cx);
});
scroll_handle.scroll_to_bottom();
} else {
self.segments.push(RenderedMessageSegment::Thinking {
content: render_markdown(text.into(), self.language_registry.clone(), window, cx),
scroll_handle: ScrollHandle::default(),
});
match self.segments.last_mut() {
Some(RenderedMessageSegment::Thinking {
content,
scroll_handle,
}) => {
content.update(cx, |markdown, cx| {
markdown.append(text, cx);
});
scroll_handle.scroll_to_bottom();
}
_ => {
self.segments.push(RenderedMessageSegment::Thinking {
content: render_markdown(
text.into(),
self.language_registry.clone(),
window,
cx,
),
scroll_handle: ScrollHandle::default(),
});
}
}
}
fn append_text(&mut self, text: &String, window: &Window, cx: &mut App) {
if let Some(RenderedMessageSegment::Text(markdown)) = self.segments.last_mut() {
markdown.update(cx, |markdown, cx| markdown.append(text, cx));
} else {
self.segments
.push(RenderedMessageSegment::Text(render_markdown(
SharedString::from(text),
self.language_registry.clone(),
window,
cx,
)));
match self.segments.last_mut() {
Some(RenderedMessageSegment::Text(markdown)) => {
markdown.update(cx, |markdown, cx| markdown.append(text, cx));
}
_ => {
self.segments
.push(RenderedMessageSegment::Text(render_markdown(
SharedString::from(text),
self.language_registry.clone(),
window,
cx,
)));
}
}
}
@@ -929,21 +939,18 @@ impl ActiveThread {
let message_content =
v_flex()
.gap_1p5()
.child(
if let Some(edit_message_editor) = edit_message_editor.clone() {
div()
.key_context("EditMessageEditor")
.on_action(cx.listener(Self::cancel_editing_message))
.on_action(cx.listener(Self::confirm_editing_message))
.min_h_6()
.child(edit_message_editor)
} else {
div()
.min_h_6()
.text_ui(cx)
.child(self.render_message_content(message_id, rendered_message, cx))
},
)
.child(match edit_message_editor.clone() {
Some(edit_message_editor) => div()
.key_context("EditMessageEditor")
.on_action(cx.listener(Self::cancel_editing_message))
.on_action(cx.listener(Self::confirm_editing_message))
.min_h_6()
.child(edit_message_editor),
_ => div()
.min_h_6()
.text_ui(cx)
.child(self.render_message_content(message_id, rendered_message, cx)),
})
.when_some(context, |parent, context| {
if !context.is_empty() {
parent.child(h_flex().flex_wrap().gap_1().children(
@@ -1204,7 +1211,7 @@ impl ActiveThread {
message_id: MessageId,
rendered_message: &RenderedMessage,
cx: &Context<Self>,
) -> impl IntoElement {
) -> impl IntoElement + use<> {
let pending_thinking_segment_index = rendered_message
.segments
.iter()
@@ -1259,7 +1266,7 @@ impl ActiveThread {
scroll_handle: &ScrollHandle,
pending: bool,
cx: &Context<Self>,
) -> impl IntoElement {
) -> impl IntoElement + use<> {
let is_open = self
.expanded_thinking_segments
.get(&(message_id, ix))
@@ -1412,7 +1419,11 @@ impl ActiveThread {
)
}
fn render_tool_use(&self, tool_use: ToolUse, cx: &mut Context<Self>) -> impl IntoElement {
fn render_tool_use(
&self,
tool_use: ToolUse,
cx: &mut Context<Self>,
) -> impl IntoElement + use<> {
let is_open = self
.expanded_tool_uses
.get(&tool_use.id)
@@ -1822,7 +1833,7 @@ impl ActiveThread {
fn render_confirmations<'a>(
&'a mut self,
cx: &'a mut Context<Self>,
) -> impl Iterator<Item = AnyElement> + 'a {
) -> impl Iterator<Item = AnyElement> + 'a + use<'a> {
let thread = self.thread.read(cx);
thread

View File

@@ -105,7 +105,7 @@ impl AssistantConfiguration {
&mut self,
provider: &Arc<dyn LanguageModelProvider>,
cx: &mut Context<Self>,
) -> impl IntoElement {
) -> impl IntoElement + use<> {
let provider_id = provider.id().0.clone();
let provider_name = provider.name().0.clone();
let configuration_view = self
@@ -167,7 +167,10 @@ impl AssistantConfiguration {
)
}
fn render_context_servers_section(&mut self, cx: &mut Context<Self>) -> impl IntoElement {
fn render_context_servers_section(
&mut self,
cx: &mut Context<Self>,
) -> impl IntoElement + use<> {
let context_servers = self.context_server_manager.read(cx).all_servers().clone();
let tools_by_source = self.tools.tools_by_source(cx);
let empty = Vec::new();

View File

@@ -318,7 +318,7 @@ impl ManageProfilesModal {
mode: ChooseProfileMode,
window: &mut Window,
cx: &mut Context<Self>,
) -> impl IntoElement {
) -> impl IntoElement + use<> {
Navigable::new(
div()
.track_focus(&self.focus_handle(cx))
@@ -418,7 +418,7 @@ impl ManageProfilesModal {
mode: NewProfileMode,
_window: &mut Window,
cx: &mut Context<Self>,
) -> impl IntoElement {
) -> impl IntoElement + use<> {
let settings = AssistantSettings::get_global(cx);
let base_profile_name = mode.base_profile_id.as_ref().map(|base_profile_id| {
@@ -448,7 +448,7 @@ impl ManageProfilesModal {
mode: ViewProfileMode,
window: &mut Window,
cx: &mut Context<Self>,
) -> impl IntoElement {
) -> impl IntoElement + use<> {
let settings = AssistantSettings::get_global(cx);
let profile_name = settings

View File

@@ -48,16 +48,17 @@ impl AssistantDiff {
.items_of_type::<AssistantDiff>(cx)
.find(|diff| diff.read(cx).thread == thread)
})?;
if let Some(existing_diff) = existing_diff {
workspace.update(cx, |workspace, cx| {
match existing_diff {
Some(existing_diff) => workspace.update(cx, |workspace, cx| {
workspace.activate_item(&existing_diff, true, true, window, cx);
})
} else {
let assistant_diff =
cx.new(|cx| AssistantDiff::new(thread.clone(), workspace.clone(), window, cx));
workspace.update(cx, |workspace, cx| {
workspace.add_item_to_center(Box::new(assistant_diff), window, cx);
})
}),
_ => {
let assistant_diff =
cx.new(|cx| AssistantDiff::new(thread.clone(), workspace.clone(), window, cx));
workspace.update(cx, |workspace, cx| {
workspace.add_item_to_center(Box::new(assistant_diff), window, cx);
})
}
}
}

View File

@@ -584,20 +584,14 @@ impl Focusable for AssistantPanel {
match self.active_view {
ActiveView::Thread => self.message_editor.focus_handle(cx),
ActiveView::History => self.history.focus_handle(cx),
ActiveView::PromptEditor => {
if let Some(context_editor) = self.context_editor.as_ref() {
context_editor.focus_handle(cx)
} else {
cx.focus_handle()
}
}
ActiveView::Configuration => {
if let Some(configuration) = self.configuration.as_ref() {
configuration.focus_handle(cx)
} else {
cx.focus_handle()
}
}
ActiveView::PromptEditor => match self.context_editor.as_ref() {
Some(context_editor) => context_editor.focus_handle(cx),
_ => cx.focus_handle(),
},
ActiveView::Configuration => match self.configuration.as_ref() {
Some(configuration) => configuration.focus_handle(cx),
_ => cx.focus_handle(),
},
}
}
}
@@ -683,7 +677,11 @@ impl Panel for AssistantPanel {
}
impl AssistantPanel {
fn render_toolbar(&self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
fn render_toolbar(
&self,
_window: &mut Window,
cx: &mut Context<Self>,
) -> impl IntoElement + use<> {
let thread = self.thread.read(cx);
let focus_handle = self.focus_handle(cx);
@@ -825,7 +823,7 @@ impl AssistantPanel {
&self,
window: &mut Window,
cx: &mut Context<Self>,
) -> impl IntoElement {
) -> impl IntoElement + use<> {
let recent_history = self
.history_store
.update(cx, |this, cx| this.recent_entries(6, cx));

View File

@@ -676,10 +676,13 @@ impl CodegenAlternative {
.update(cx, |this, cx| {
this.message_id = message_id;
this.last_equal_ranges.clear();
if let Err(error) = result {
this.status = CodegenStatus::Error(error);
} else {
this.status = CodegenStatus::Done;
match result {
Err(error) => {
this.status = CodegenStatus::Error(error);
}
_ => {
this.status = CodegenStatus::Done;
}
}
this.elapsed_time = Some(elapsed_time);
this.completion = Some(completion.lock().clone());

View File

@@ -581,15 +581,14 @@ impl CompletionProvider for ContextPickerCompletionProvider {
let line_start = Point::new(position.row, 0);
let offset_to_line = buffer.point_to_offset(line_start);
let mut lines = buffer.text_for_range(line_start..position).lines();
if let Some(line) = lines.next() {
MentionCompletion::try_parse(line, offset_to_line)
match lines.next() {
Some(line) => MentionCompletion::try_parse(line, offset_to_line)
.map(|completion| {
completion.source_range.start <= offset_to_line + position.column as usize
&& completion.source_range.end >= offset_to_line + position.column as usize
})
.unwrap_or(false)
} else {
false
.unwrap_or(false),
_ => false,
}
}

View File

@@ -360,12 +360,15 @@ impl ContextStore {
remove_if_exists: bool,
cx: &mut Context<Self>,
) {
if let Some(context_id) = self.includes_thread(&thread.read(cx).id()) {
if remove_if_exists {
self.remove_context(context_id);
match self.includes_thread(&thread.read(cx).id()) {
Some(context_id) => {
if remove_if_exists {
self.remove_context(context_id);
}
}
_ => {
self.insert_thread(thread, cx);
}
} else {
self.insert_thread(thread, cx);
}
}
@@ -687,7 +690,7 @@ pub fn refresh_context_store_text(
context_store: Entity<ContextStore>,
changed_buffers: &HashSet<Entity<Buffer>>,
cx: &App,
) -> impl Future<Output = Vec<ContextId>> {
) -> impl Future<Output = Vec<ContextId>> + use<> {
let mut tasks = Vec::new();
for context in &context_store.read(cx).context {
@@ -756,8 +759,8 @@ fn refresh_file_text(
) -> Option<Task<()>> {
let id = file_context.id;
let task = refresh_context_buffer(&file_context.context_buffer, cx);
if let Some(task) = task {
Some(cx.spawn(async move |cx| {
match task {
Some(task) => Some(cx.spawn(async move |cx| {
let context_buffer = task.await;
context_store
.update(cx, |context_store, _| {
@@ -765,9 +768,8 @@ fn refresh_file_text(
context_store.replace_context(AssistantContext::File(new_file_context));
})
.ok();
}))
} else {
None
})),
_ => None,
}
}
@@ -780,14 +782,15 @@ fn refresh_directory_text(
let futures = directory_context
.context_buffers
.iter()
.map(|context_buffer| {
if let Some(refresh_task) = refresh_context_buffer(context_buffer, cx) {
stale = true;
future::Either::Left(refresh_task)
} else {
future::Either::Right(future::ready((*context_buffer).clone()))
}
})
.map(
|context_buffer| match refresh_context_buffer(context_buffer, cx) {
Some(refresh_task) => {
stale = true;
future::Either::Left(refresh_task)
}
_ => future::Either::Right(future::ready((*context_buffer).clone())),
},
)
.collect::<Vec<_>>();
if !stale {
@@ -816,8 +819,8 @@ fn refresh_symbol_text(
) -> Option<Task<()>> {
let id = symbol_context.id;
let task = refresh_context_symbol(&symbol_context.context_symbol, cx);
if let Some(task) = task {
Some(cx.spawn(async move |cx| {
match task {
Some(task) => Some(cx.spawn(async move |cx| {
let context_symbol = task.await;
context_store
.update(cx, |context_store, _| {
@@ -825,9 +828,8 @@ fn refresh_symbol_text(
context_store.replace_context(AssistantContext::Symbol(new_symbol_context));
})
.ok();
}))
} else {
None
})),
_ => None,
}
}
@@ -855,7 +857,7 @@ fn refresh_thread_text(
fn refresh_context_buffer(
context_buffer: &ContextBuffer,
cx: &App,
) -> Option<impl Future<Output = ContextBuffer>> {
) -> Option<impl Future<Output = ContextBuffer> + use<>> {
let buffer = context_buffer.buffer.read(cx);
let path = buffer_path_log_err(buffer)?;
if buffer.version.changed_since(&context_buffer.version) {
@@ -875,7 +877,7 @@ fn refresh_context_buffer(
fn refresh_context_symbol(
context_symbol: &ContextSymbol,
cx: &App,
) -> Option<impl Future<Output = ContextSymbol>> {
) -> Option<impl Future<Output = ContextSymbol> + use<>> {
let buffer = context_symbol.buffer.read(cx);
let path = buffer_path_log_err(buffer)?;
let project_path = buffer.project_path(cx)?;

View File

@@ -341,33 +341,35 @@ impl InlineAssistant {
selection.end.column = snapshot
.buffer_snapshot
.line_len(MultiBufferRow(selection.end.row));
} else if let Some(fold) =
snapshot.crease_for_buffer_row(MultiBufferRow(selection.end.row))
{
selection.start = fold.range().start;
selection.end = fold.range().end;
if MultiBufferRow(selection.end.row) < snapshot.buffer_snapshot.max_row() {
let chars = snapshot
.buffer_snapshot
.chars_at(Point::new(selection.end.row + 1, 0));
for c in chars {
if c == '\n' {
break;
}
if c.is_whitespace() {
continue;
}
if snapshot
.language_at(selection.end)
.is_some_and(|language| language.config().brackets.is_closing_brace(c))
{
selection.end.row += 1;
selection.end.column = snapshot
} else {
match snapshot.crease_for_buffer_row(MultiBufferRow(selection.end.row)) {
Some(fold) => {
selection.start = fold.range().start;
selection.end = fold.range().end;
if MultiBufferRow(selection.end.row) < snapshot.buffer_snapshot.max_row() {
let chars = snapshot
.buffer_snapshot
.line_len(MultiBufferRow(selection.end.row));
.chars_at(Point::new(selection.end.row + 1, 0));
for c in chars {
if c == '\n' {
break;
}
if c.is_whitespace() {
continue;
}
if snapshot.language_at(selection.end).is_some_and(|language| {
language.config().brackets.is_closing_brace(c)
}) {
selection.end.row += 1;
selection.end.column = snapshot
.buffer_snapshot
.line_len(MultiBufferRow(selection.end.row));
}
}
}
}
_ => {}
}
}
@@ -1129,24 +1131,27 @@ impl InlineAssistant {
let mut scroll_target_top;
let mut scroll_target_bottom;
if let Some(decorations) = assist.decorations.as_ref() {
scroll_target_top = editor
.row_for_block(decorations.prompt_block_id, cx)
.unwrap()
.0 as f32;
scroll_target_bottom = editor
.row_for_block(decorations.end_block_id, cx)
.unwrap()
.0 as f32;
} else {
let snapshot = editor.snapshot(window, cx);
let start_row = assist
.range
.start
.to_display_point(&snapshot.display_snapshot)
.row();
scroll_target_top = start_row.0 as f32;
scroll_target_bottom = scroll_target_top + 1.;
match assist.decorations.as_ref() {
Some(decorations) => {
scroll_target_top = editor
.row_for_block(decorations.prompt_block_id, cx)
.unwrap()
.0 as f32;
scroll_target_bottom = editor
.row_for_block(decorations.end_block_id, cx)
.unwrap()
.0 as f32;
}
_ => {
let snapshot = editor.snapshot(window, cx);
let start_row = assist
.range
.start
.to_display_point(&snapshot.display_snapshot)
.row();
scroll_target_top = start_row.0 as f32;
scroll_target_bottom = scroll_target_top + 1.;
}
}
scroll_target_top -= editor.vertical_scroll_margin() as f32;
scroll_target_bottom += editor.vertical_scroll_margin() as f32;
@@ -1191,10 +1196,11 @@ impl InlineAssistant {
}
pub fn start_assist(&mut self, assist_id: InlineAssistId, window: &mut Window, cx: &mut App) {
let assist = if let Some(assist) = self.assists.get_mut(&assist_id) {
assist
} else {
return;
let assist = match self.assists.get_mut(&assist_id) {
Some(assist) => assist,
_ => {
return;
}
};
let assist_group_id = assist.group_id;
@@ -1222,10 +1228,11 @@ impl InlineAssistant {
}
pub fn stop_assist(&mut self, assist_id: InlineAssistId, cx: &mut App) {
let assist = if let Some(assist) = self.assists.get_mut(&assist_id) {
assist
} else {
return;
let assist = match self.assists.get_mut(&assist_id) {
Some(assist) => assist,
_ => {
return;
}
};
assist.codegen.update(cx, |codegen, cx| codegen.stop(cx));
@@ -1449,20 +1456,27 @@ impl InlineAssistant {
}
});
if let Some(context_editor) = context_editor {
Some(InlineAssistTarget::Editor(context_editor))
} else if let Some(workspace_editor) = workspace
.active_item(cx)
.and_then(|item| item.act_as::<Editor>(cx))
{
Some(InlineAssistTarget::Editor(workspace_editor))
} else if let Some(terminal_view) = workspace
.active_item(cx)
.and_then(|item| item.act_as::<TerminalView>(cx))
{
Some(InlineAssistTarget::Terminal(terminal_view))
} else {
None
match context_editor {
Some(context_editor) => Some(InlineAssistTarget::Editor(context_editor)),
_ => {
match workspace
.active_item(cx)
.and_then(|item| item.act_as::<Editor>(cx))
{
Some(workspace_editor) => Some(InlineAssistTarget::Editor(workspace_editor)),
_ => {
match workspace
.active_item(cx)
.and_then(|item| item.act_as::<TerminalView>(cx))
{
Some(terminal_view) => {
Some(InlineAssistTarget::Terminal(terminal_view))
}
_ => None,
}
}
}
}
}
}
}
@@ -1654,10 +1668,11 @@ impl InlineAssist {
InlineAssistant::update_global(cx, |this, cx| match event {
CodegenEvent::Undone => this.finish_assist(assist_id, false, window, cx),
CodegenEvent::Finished => {
let assist = if let Some(assist) = this.assists.get(&assist_id) {
assist
} else {
return;
let assist = match this.assists.get(&assist_id) {
Some(assist) => assist,
_ => {
return;
}
};
if let CodegenStatus::Error(error) = codegen.read(cx).status(cx) {

View File

@@ -677,7 +677,7 @@ impl<T: 'static> PromptEditor<T> {
.into_any_element()
}
fn render_rate_limit_notice(&self, cx: &mut Context<Self>) -> impl IntoElement {
fn render_rate_limit_notice(&self, cx: &mut Context<Self>) -> impl IntoElement + use<T> {
Popover::new().child(
v_flex()
.occlude()

View File

@@ -117,10 +117,13 @@ impl TerminalCodegen {
let result = generate.await;
this.update(cx, |this, cx| {
if let Err(error) = result {
this.status = CodegenStatus::Error(error);
} else {
this.status = CodegenStatus::Done;
match result {
Err(error) => {
this.status = CodegenStatus::Error(error);
}
_ => {
this.status = CodegenStatus::Done;
}
}
cx.emit(CodegenEvent::Finished);
cx.notify();

View File

@@ -164,10 +164,11 @@ impl TerminalInlineAssistant {
}
fn start_assist(&mut self, assist_id: TerminalInlineAssistId, cx: &mut App) {
let assist = if let Some(assist) = self.assists.get_mut(&assist_id) {
assist
} else {
return;
let assist = match self.assists.get_mut(&assist_id) {
Some(assist) => assist,
_ => {
return;
}
};
let Some(user_prompt) = assist
@@ -202,10 +203,11 @@ impl TerminalInlineAssistant {
}
fn stop_assist(&mut self, assist_id: TerminalInlineAssistId, cx: &mut App) {
let assist = if let Some(assist) = self.assists.get_mut(&assist_id) {
assist
} else {
return;
let assist = match self.assists.get_mut(&assist_id) {
Some(assist) => assist,
_ => {
return;
}
};
assist.codegen.update(cx, |codegen, cx| codegen.stop(cx));
@@ -402,10 +404,11 @@ impl TerminalInlineAssist {
window.subscribe(&codegen, cx, move |codegen, event, window, cx| {
TerminalInlineAssistant::update_global(cx, |this, cx| match event {
CodegenEvent::Finished => {
let assist = if let Some(assist) = this.assists.get(&assist_id) {
assist
} else {
return;
let assist = match this.assists.get(&assist_id) {
Some(assist) => assist,
_ => {
return;
}
};
if let CodegenStatus::Error(error) = &codegen.read(cx).status {

View File

@@ -379,14 +379,17 @@ impl Thread {
cx.spawn(async move |this, cx| {
let result = restore.await;
this.update(cx, |this, cx| {
if let Err(err) = result.as_ref() {
this.last_restore_checkpoint = Some(LastRestoreCheckpoint::Error {
message_id: checkpoint.message_id,
error: err.to_string(),
});
} else {
this.truncate(checkpoint.message_id, cx);
this.last_restore_checkpoint = None;
match result.as_ref() {
Err(err) => {
this.last_restore_checkpoint = Some(LastRestoreCheckpoint::Error {
message_id: checkpoint.message_id,
error: err.to_string(),
});
}
_ => {
this.truncate(checkpoint.message_id, cx);
this.last_restore_checkpoint = None;
}
}
this.pending_checkpoint = None;
cx.emit(ThreadEvent::CheckpointChanged);
@@ -721,8 +724,8 @@ impl Thread {
})
.next();
if let Some((rel_rules_path, abs_rules_path)) = selected_rules_file {
cx.spawn(async move |_| {
match selected_rules_file {
Some((rel_rules_path, abs_rules_path)) => cx.spawn(async move |_| {
let rules_file_result = maybe!(async move {
let abs_rules_path = abs_rules_path?;
let text = fs.load(&abs_rules_path).await.with_context(|| {
@@ -751,16 +754,15 @@ impl Thread {
rules_file,
};
(worktree_info, rules_file_error)
})
} else {
Task::ready((
}),
_ => Task::ready((
WorktreeInfoForSystemPrompt {
root_name,
abs_path,
rules_file: None,
},
None,
))
)),
}
}
@@ -1186,7 +1188,7 @@ impl Thread {
pub fn use_pending_tools(
&mut self,
cx: &mut Context<Self>,
) -> impl IntoIterator<Item = PendingToolUse> {
) -> impl IntoIterator<Item = PendingToolUse> + use<> {
let request = self.to_completion_request(RequestKind::Chat, cx);
let messages = Arc::new(request.messages);
let pending_tool_uses = self
@@ -1198,37 +1200,43 @@ impl Thread {
.collect::<Vec<_>>();
for tool_use in pending_tool_uses.iter() {
if let Some(tool) = self.tools.tool(&tool_use.name, cx) {
if tool.needs_confirmation()
&& !AssistantSettings::get_global(cx).always_allow_tool_actions
{
self.tool_use.confirm_tool_use(
tool_use.id.clone(),
tool_use.ui_text.clone(),
tool_use.input.clone(),
messages.clone(),
tool,
);
cx.emit(ThreadEvent::ToolConfirmationNeeded);
} else {
self.run_tool(
tool_use.id.clone(),
tool_use.ui_text.clone(),
tool_use.input.clone(),
&messages,
tool,
cx,
);
match self.tools.tool(&tool_use.name, cx) {
Some(tool) => {
if tool.needs_confirmation()
&& !AssistantSettings::get_global(cx).always_allow_tool_actions
{
self.tool_use.confirm_tool_use(
tool_use.id.clone(),
tool_use.ui_text.clone(),
tool_use.input.clone(),
messages.clone(),
tool,
);
cx.emit(ThreadEvent::ToolConfirmationNeeded);
} else {
self.run_tool(
tool_use.id.clone(),
tool_use.ui_text.clone(),
tool_use.input.clone(),
&messages,
tool,
cx,
);
}
}
} else if let Some(tool) = self.tools.tool(&tool_use.name, cx) {
self.run_tool(
tool_use.id.clone(),
tool_use.ui_text.clone(),
tool_use.input.clone(),
&messages,
tool,
cx,
);
_ => match self.tools.tool(&tool_use.name, cx) {
Some(tool) => {
self.run_tool(
tool_use.id.clone(),
tool_use.ui_text.clone(),
tool_use.input.clone(),
&messages,
tool,
cx,
);
}
_ => {}
},
}
}

View File

@@ -166,8 +166,8 @@ impl ToolUseState {
};
}
if let Some(pending_tool_use) = self.pending_tool_uses_by_id.get(&tool_use.id) {
match pending_tool_use.status {
match self.pending_tool_uses_by_id.get(&tool_use.id) {
Some(pending_tool_use) => match pending_tool_use.status {
PendingToolUseStatus::Idle => ToolUseStatus::Pending,
PendingToolUseStatus::NeedsConfirmation { .. } => {
ToolUseStatus::NeedsConfirmation
@@ -176,17 +176,14 @@ impl ToolUseState {
PendingToolUseStatus::Error(ref err) => {
ToolUseStatus::Error(err.clone().into())
}
}
} else {
ToolUseStatus::Pending
},
_ => ToolUseStatus::Pending,
}
})();
let (icon, needs_confirmation) = if let Some(tool) = self.tools.tool(&tool_use.name, cx)
{
(tool.icon(), tool.needs_confirmation())
} else {
(IconName::Cog, false)
let (icon, needs_confirmation) = match self.tools.tool(&tool_use.name, cx) {
Some(tool) => (tool.icon(), tool.needs_confirmation()),
_ => (IconName::Cog, false),
};
tool_uses.push(ToolUse {
@@ -209,10 +206,9 @@ impl ToolUseState {
input: &serde_json::Value,
cx: &App,
) -> SharedString {
if let Some(tool) = self.tools.tool(tool_name, cx) {
tool.ui_text(input).into()
} else {
format!("Unknown tool {tool_name:?}").into()
match self.tools.tool(tool_name, cx) {
Some(tool) => tool.ui_text(input).into(),
_ => format!("Unknown tool {tool_name:?}").into(),
}
}

View File

@@ -1877,15 +1877,18 @@ impl AssistantContext {
if let Some(mut pending_patch) = pending_patch {
let patch_start = pending_patch.range.start.to_offset(buffer);
if let Some(message) = self.message_for_offset(patch_start, cx) {
if message.anchor_range.end == text::Anchor::MAX {
pending_patch.range.end = text::Anchor::MAX;
} else {
let message_end = buffer.anchor_after(message.offset_range.end - 1);
pending_patch.range.end = message_end;
match self.message_for_offset(patch_start, cx) {
Some(message) => {
if message.anchor_range.end == text::Anchor::MAX {
pending_patch.range.end = text::Anchor::MAX;
} else {
let message_end = buffer.anchor_after(message.offset_range.end - 1);
pending_patch.range.end = message_end;
}
}
_ => {
pending_patch.range.end = text::Anchor::MAX;
}
} else {
pending_patch.range.end = text::Anchor::MAX;
}
new_patches.push(pending_patch);
@@ -2453,7 +2456,7 @@ impl AssistantContext {
let result = stream_completion.await;
this.update(cx, |this, cx| {
let error_message = if let Some(error) = result.as_ref().err() {
let error_message = match result.as_ref().err() { Some(error) => {
if error.is::<PaymentRequiredError>() {
cx.emit(ContextEvent::ShowPaymentRequiredError);
this.update_metadata(assistant_message_id, cx, |metadata| {
@@ -2481,12 +2484,12 @@ impl AssistantContext {
});
Some(error_message)
}
} else {
} _ => {
this.update_metadata(assistant_message_id, cx, |metadata| {
metadata.status = MessageStatus::Done;
});
None
};
}};
let language_name = this
.buffer
@@ -2631,15 +2634,16 @@ impl AssistantContext {
}
pub fn cancel_last_assist(&mut self, cx: &mut Context<Self>) -> bool {
if let Some(pending_completion) = self.pending_completions.pop() {
self.update_metadata(pending_completion.assistant_message_id, cx, |metadata| {
if metadata.status == MessageStatus::Pending {
metadata.status = MessageStatus::Canceled;
}
});
true
} else {
false
match self.pending_completions.pop() {
Some(pending_completion) => {
self.update_metadata(pending_completion.assistant_message_id, cx, |metadata| {
if metadata.status == MessageStatus::Pending {
metadata.status = MessageStatus::Canceled;
}
});
true
}
_ => false,
}
}
@@ -2799,121 +2803,123 @@ impl AssistantContext {
) -> (Option<MessageAnchor>, Option<MessageAnchor>) {
let start_message = self.message_for_offset(range.start, cx);
let end_message = self.message_for_offset(range.end, cx);
if let Some((start_message, end_message)) = start_message.zip(end_message) {
// Prevent splitting when range spans multiple messages.
if start_message.id != end_message.id {
return (None, None);
}
let message = start_message;
let role = message.role;
let mut edited_buffer = false;
let mut suffix_start = None;
// TODO: why did this start panicking?
if range.start > message.offset_range.start
&& range.end < message.offset_range.end.saturating_sub(1)
{
if self.buffer.read(cx).chars_at(range.end).next() == Some('\n') {
suffix_start = Some(range.end + 1);
} else if self.buffer.read(cx).reversed_chars_at(range.end).next() == Some('\n') {
suffix_start = Some(range.end);
match start_message.zip(end_message) {
Some((start_message, end_message)) => {
// Prevent splitting when range spans multiple messages.
if start_message.id != end_message.id {
return (None, None);
}
}
let version = self.version.clone();
let suffix = if let Some(suffix_start) = suffix_start {
MessageAnchor {
id: MessageId(self.next_timestamp()),
start: self.buffer.read(cx).anchor_before(suffix_start),
}
} else {
self.buffer.update(cx, |buffer, cx| {
buffer.edit([(range.end..range.end, "\n")], None, cx);
});
edited_buffer = true;
MessageAnchor {
id: MessageId(self.next_timestamp()),
start: self.buffer.read(cx).anchor_before(range.end + 1),
}
};
let message = start_message;
let role = message.role;
let mut edited_buffer = false;
let suffix_metadata = MessageMetadata {
role,
status: MessageStatus::Done,
timestamp: suffix.id.0,
cache: None,
};
self.insert_message(suffix.clone(), suffix_metadata.clone(), cx);
self.push_op(
ContextOperation::InsertMessage {
anchor: suffix.clone(),
metadata: suffix_metadata,
version,
},
cx,
);
let mut suffix_start = None;
let new_messages =
if range.start == range.end || range.start == message.offset_range.start {
(None, Some(suffix))
} else {
let mut prefix_end = None;
if range.start > message.offset_range.start
&& range.end < message.offset_range.end - 1
// TODO: why did this start panicking?
if range.start > message.offset_range.start
&& range.end < message.offset_range.end.saturating_sub(1)
{
if self.buffer.read(cx).chars_at(range.end).next() == Some('\n') {
suffix_start = Some(range.end + 1);
} else if self.buffer.read(cx).reversed_chars_at(range.end).next() == Some('\n')
{
if self.buffer.read(cx).chars_at(range.start).next() == Some('\n') {
prefix_end = Some(range.start + 1);
} else if self.buffer.read(cx).reversed_chars_at(range.start).next()
== Some('\n')
{
prefix_end = Some(range.start);
}
suffix_start = Some(range.end);
}
}
let version = self.version.clone();
let selection = if let Some(prefix_end) = prefix_end {
MessageAnchor {
id: MessageId(self.next_timestamp()),
start: self.buffer.read(cx).anchor_before(prefix_end),
}
} else {
self.buffer.update(cx, |buffer, cx| {
buffer.edit([(range.start..range.start, "\n")], None, cx)
});
edited_buffer = true;
MessageAnchor {
id: MessageId(self.next_timestamp()),
start: self.buffer.read(cx).anchor_before(range.end + 1),
}
};
let selection_metadata = MessageMetadata {
role,
status: MessageStatus::Done,
timestamp: selection.id.0,
cache: None,
};
self.insert_message(selection.clone(), selection_metadata.clone(), cx);
self.push_op(
ContextOperation::InsertMessage {
anchor: selection.clone(),
metadata: selection_metadata,
version,
},
cx,
);
(Some(selection), Some(suffix))
let version = self.version.clone();
let suffix = if let Some(suffix_start) = suffix_start {
MessageAnchor {
id: MessageId(self.next_timestamp()),
start: self.buffer.read(cx).anchor_before(suffix_start),
}
} else {
self.buffer.update(cx, |buffer, cx| {
buffer.edit([(range.end..range.end, "\n")], None, cx);
});
edited_buffer = true;
MessageAnchor {
id: MessageId(self.next_timestamp()),
start: self.buffer.read(cx).anchor_before(range.end + 1),
}
};
if !edited_buffer {
cx.emit(ContextEvent::MessagesEdited);
let suffix_metadata = MessageMetadata {
role,
status: MessageStatus::Done,
timestamp: suffix.id.0,
cache: None,
};
self.insert_message(suffix.clone(), suffix_metadata.clone(), cx);
self.push_op(
ContextOperation::InsertMessage {
anchor: suffix.clone(),
metadata: suffix_metadata,
version,
},
cx,
);
let new_messages =
if range.start == range.end || range.start == message.offset_range.start {
(None, Some(suffix))
} else {
let mut prefix_end = None;
if range.start > message.offset_range.start
&& range.end < message.offset_range.end - 1
{
if self.buffer.read(cx).chars_at(range.start).next() == Some('\n') {
prefix_end = Some(range.start + 1);
} else if self.buffer.read(cx).reversed_chars_at(range.start).next()
== Some('\n')
{
prefix_end = Some(range.start);
}
}
let version = self.version.clone();
let selection = if let Some(prefix_end) = prefix_end {
MessageAnchor {
id: MessageId(self.next_timestamp()),
start: self.buffer.read(cx).anchor_before(prefix_end),
}
} else {
self.buffer.update(cx, |buffer, cx| {
buffer.edit([(range.start..range.start, "\n")], None, cx)
});
edited_buffer = true;
MessageAnchor {
id: MessageId(self.next_timestamp()),
start: self.buffer.read(cx).anchor_before(range.end + 1),
}
};
let selection_metadata = MessageMetadata {
role,
status: MessageStatus::Done,
timestamp: selection.id.0,
cache: None,
};
self.insert_message(selection.clone(), selection_metadata.clone(), cx);
self.push_op(
ContextOperation::InsertMessage {
anchor: selection.clone(),
metadata: selection_metadata,
version,
},
cx,
);
(Some(selection), Some(suffix))
};
if !edited_buffer {
cx.emit(ContextEvent::MessagesEdited);
}
new_messages
}
new_messages
} else {
(None, None)
_ => (None, None),
}
}

View File

@@ -403,23 +403,31 @@ impl ContextEditor {
if request_type == RequestType::SuggestEdits && !self.context.read(cx).contains_files(cx) {
self.last_error = Some(AssistError::FileRequired);
cx.notify();
} else if let Some(user_message) = self
.context
.update(cx, |context, cx| context.assist(request_type, cx))
{
let new_selection = {
let cursor = user_message
.start
.to_offset(self.context.read(cx).buffer().read(cx));
cursor..cursor
};
self.editor.update(cx, |editor, cx| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
selections.select_ranges([new_selection])
});
});
// Avoid scrolling to the new cursor position so the assistant's output is stable.
cx.defer_in(window, |this, _, _| this.scroll_position = None);
} else {
match self
.context
.update(cx, |context, cx| context.assist(request_type, cx))
{
Some(user_message) => {
let new_selection = {
let cursor = user_message
.start
.to_offset(self.context.read(cx).buffer().read(cx));
cursor..cursor
};
self.editor.update(cx, |editor, cx| {
editor.change_selections(
Some(Autoscroll::fit()),
window,
cx,
|selections| selections.select_ranges([new_selection]),
);
});
// Avoid scrolling to the new cursor position so the assistant's output is stable.
cx.defer_in(window, |this, _, _| this.scroll_position = None);
}
_ => {}
}
}
cx.notify();
@@ -817,62 +825,68 @@ impl ContextEditor {
}
self.editor.update(cx, |editor, cx| {
if let Some(invoked_slash_command) =
self.context.read(cx).invoked_slash_command(&command_id)
{
if let InvokedSlashCommandStatus::Finished = invoked_slash_command.status {
let buffer = editor.buffer().read(cx).snapshot(cx);
let (&excerpt_id, _buffer_id, _buffer_snapshot) =
buffer.as_singleton().unwrap();
match self.context.read(cx).invoked_slash_command(&command_id) {
Some(invoked_slash_command) => match invoked_slash_command.status {
InvokedSlashCommandStatus::Finished => {
let buffer = editor.buffer().read(cx).snapshot(cx);
let (&excerpt_id, _buffer_id, _buffer_snapshot) =
buffer.as_singleton().unwrap();
let start = buffer
.anchor_in_excerpt(excerpt_id, invoked_slash_command.range.start)
.unwrap();
let end = buffer
.anchor_in_excerpt(excerpt_id, invoked_slash_command.range.end)
.unwrap();
editor.remove_folds_with_type(
&[start..end],
TypeId::of::<PendingSlashCommand>(),
false,
cx,
);
let start = buffer
.anchor_in_excerpt(excerpt_id, invoked_slash_command.range.start)
.unwrap();
let end = buffer
.anchor_in_excerpt(excerpt_id, invoked_slash_command.range.end)
.unwrap();
editor.remove_folds_with_type(
&[start..end],
TypeId::of::<PendingSlashCommand>(),
false,
cx,
);
editor.remove_creases(
HashSet::from_iter(
self.invoked_slash_command_creases.remove(&command_id),
),
cx,
);
}
_ => {
if let hash_map::Entry::Vacant(entry) =
self.invoked_slash_command_creases.entry(command_id)
{
let buffer = editor.buffer().read(cx).snapshot(cx);
let (&excerpt_id, _buffer_id, _buffer_snapshot) =
buffer.as_singleton().unwrap();
let context = self.context.downgrade();
let crease_start = buffer
.anchor_in_excerpt(excerpt_id, invoked_slash_command.range.start)
.unwrap();
let crease_end = buffer
.anchor_in_excerpt(excerpt_id, invoked_slash_command.range.end)
.unwrap();
let crease = Crease::inline(
crease_start..crease_end,
invoked_slash_command_fold_placeholder(command_id, context),
fold_toggle("invoked-slash-command"),
|_row, _folded, _window, _cx| Empty.into_any(),
);
let crease_ids = editor.insert_creases([crease.clone()], cx);
editor.fold_creases(vec![crease], false, window, cx);
entry.insert(crease_ids[0]);
} else {
cx.notify()
}
}
},
_ => {
editor.remove_creases(
HashSet::from_iter(self.invoked_slash_command_creases.remove(&command_id)),
cx,
);
} else if let hash_map::Entry::Vacant(entry) =
self.invoked_slash_command_creases.entry(command_id)
{
let buffer = editor.buffer().read(cx).snapshot(cx);
let (&excerpt_id, _buffer_id, _buffer_snapshot) =
buffer.as_singleton().unwrap();
let context = self.context.downgrade();
let crease_start = buffer
.anchor_in_excerpt(excerpt_id, invoked_slash_command.range.start)
.unwrap();
let crease_end = buffer
.anchor_in_excerpt(excerpt_id, invoked_slash_command.range.end)
.unwrap();
let crease = Crease::inline(
crease_start..crease_end,
invoked_slash_command_fold_placeholder(command_id, context),
fold_toggle("invoked-slash-command"),
|_row, _folded, _window, _cx| Empty.into_any(),
);
let crease_ids = editor.insert_creases([crease.clone()], cx);
editor.fold_creases(vec![crease], false, window, cx);
entry.insert(crease_ids[0]);
} else {
cx.notify()
cx.notify();
}
} else {
editor.remove_creases(
HashSet::from_iter(self.invoked_slash_command_creases.remove(&command_id)),
cx,
);
cx.notify();
};
});
}
@@ -958,34 +972,37 @@ impl ContextEditor {
);
let should_refold;
if let Some(state) = self.patches.get_mut(&range) {
if let Some(editor_state) = &state.editor {
if editor_state.opened_patch != patch {
state.update_task = Some({
let this = this.clone();
cx.spawn_in(window, async move |_, cx| {
Self::update_patch_editor(this.clone(), patch, cx)
.await
.log_err();
})
});
match self.patches.get_mut(&range) {
Some(state) => {
if let Some(editor_state) = &state.editor {
if editor_state.opened_patch != patch {
state.update_task = Some({
let this = this.clone();
cx.spawn_in(window, async move |_, cx| {
Self::update_patch_editor(this.clone(), patch, cx)
.await
.log_err();
})
});
}
}
should_refold = snapshot
.intersects_fold(patch_start.to_offset(&snapshot.buffer_snapshot));
}
_ => {
let crease_id = editor.insert_creases([crease.clone()], cx)[0];
self.patches.insert(
range.clone(),
PatchViewState {
crease_id,
editor: None,
update_task: None,
},
);
should_refold =
snapshot.intersects_fold(patch_start.to_offset(&snapshot.buffer_snapshot));
} else {
let crease_id = editor.insert_creases([crease.clone()], cx)[0];
self.patches.insert(
range.clone(),
PatchViewState {
crease_id,
editor: None,
update_task: None,
},
);
should_refold = true;
should_refold = true;
}
}
if should_refold {
@@ -1175,16 +1192,20 @@ impl ContextEditor {
}
}
if let Some(editor) = editor {
self.workspace
.update(cx, |workspace, cx| {
workspace.activate_item(&editor, true, false, window, cx);
})
.ok();
} else {
patch_state.update_task = Some(cx.spawn_in(window, async move |this, cx| {
Self::open_patch_editor(this, new_patch, cx).await.log_err();
}));
match editor {
Some(editor) => {
self.workspace
.update(cx, |workspace, cx| {
workspace.activate_item(&editor, true, false, window, cx);
})
.ok();
}
_ => {
patch_state.update_task =
Some(cx.spawn_in(window, async move |this, cx| {
Self::open_patch_editor(this, new_patch, cx).await.log_err();
}));
}
}
}
}
@@ -1282,16 +1303,19 @@ impl ContextEditor {
.collect();
if let Some(state) = &mut patch_state.editor {
if let Some(editor) = state.editor.upgrade() {
editor.update(cx, |editor, cx| {
editor.set_title(patch.title.clone(), cx);
editor.reset_locations(locations, window, cx);
resolved_patch.apply(editor, cx);
});
match state.editor.upgrade() {
Some(editor) => {
editor.update(cx, |editor, cx| {
editor.set_title(patch.title.clone(), cx);
editor.reset_locations(locations, window, cx);
resolved_patch.apply(editor, cx);
});
state.opened_patch = patch;
} else {
patch_state.editor.take();
state.opened_patch = patch;
}
_ => {
patch_state.editor.take();
}
}
}
patch_state.update_task.take();
@@ -1573,25 +1597,28 @@ impl ContextEditor {
let mut new_blocks = vec![];
let mut block_index_to_message = vec![];
for message in self.context.read(cx).messages(cx) {
if let Some(_) = blocks_to_remove.remove(&message.id) {
// This is an old message that we might modify.
let Some((meta, block_id)) = old_blocks.get_mut(&message.id) else {
debug_assert!(
false,
"old_blocks should contain a message_id we've just removed."
);
continue;
};
// Should we modify it?
let message_meta = MessageMetadata::from(&message);
if meta != &message_meta {
blocks_to_replace.insert(*block_id, render_block(message_meta.clone()));
*meta = message_meta;
match blocks_to_remove.remove(&message.id) {
Some(_) => {
// This is an old message that we might modify.
let Some((meta, block_id)) = old_blocks.get_mut(&message.id) else {
debug_assert!(
false,
"old_blocks should contain a message_id we've just removed."
);
continue;
};
// Should we modify it?
let message_meta = MessageMetadata::from(&message);
if meta != &message_meta {
blocks_to_replace.insert(*block_id, render_block(message_meta.clone()));
*meta = message_meta;
}
}
_ => {
// This is a new message.
new_blocks.push(create_block_properties(&message));
block_index_to_message.push((message.id, MessageMetadata::from(&message)));
}
} else {
// This is a new message.
new_blocks.push(create_block_properties(&message));
block_index_to_message.push((message.id, MessageMetadata::from(&message)));
}
}
editor.replace_blocks(blocks_to_replace, None, cx);
@@ -2321,54 +2348,67 @@ impl ContextEditor {
)
.into_any_element(),
)
} else if let Some(configuration_error) = configuration_error(cx) {
let label = match configuration_error {
ConfigurationError::NoProvider => "No LLM provider selected.",
ConfigurationError::ProviderNotAuthenticated => "LLM provider is not configured.",
ConfigurationError::ProviderPendingTermsAcceptance(_) => {
"LLM provider requires accepting the Terms of Service."
}
};
Some(
h_flex()
.px_3()
.py_2()
.border_b_1()
.border_color(cx.theme().colors().border_variant)
.bg(cx.theme().colors().editor_background)
.justify_between()
.child(
h_flex()
.gap_3()
.child(
Icon::new(IconName::Warning)
.size(IconSize::Small)
.color(Color::Warning),
)
.child(Label::new(label)),
)
.child(
Button::new("open-configuration", "Configure Providers")
.size(ButtonSize::Compact)
.icon(Some(IconName::SlidersVertical))
.icon_size(IconSize::Small)
.icon_position(IconPosition::Start)
.style(ButtonStyle::Filled)
.on_click({
let focus_handle = self.focus_handle(cx).clone();
move |_event, window, cx| {
focus_handle.dispatch_action(&ShowConfiguration, window, cx);
}
}),
)
.into_any_element(),
)
} else {
None
match configuration_error(cx) {
Some(configuration_error) => {
let label = match configuration_error {
ConfigurationError::NoProvider => "No LLM provider selected.",
ConfigurationError::ProviderNotAuthenticated => {
"LLM provider is not configured."
}
ConfigurationError::ProviderPendingTermsAcceptance(_) => {
"LLM provider requires accepting the Terms of Service."
}
};
Some(
h_flex()
.px_3()
.py_2()
.border_b_1()
.border_color(cx.theme().colors().border_variant)
.bg(cx.theme().colors().editor_background)
.justify_between()
.child(
h_flex()
.gap_3()
.child(
Icon::new(IconName::Warning)
.size(IconSize::Small)
.color(Color::Warning),
)
.child(Label::new(label)),
)
.child(
Button::new("open-configuration", "Configure Providers")
.size(ButtonSize::Compact)
.icon(Some(IconName::SlidersVertical))
.icon_size(IconSize::Small)
.icon_position(IconPosition::Start)
.style(ButtonStyle::Filled)
.on_click({
let focus_handle = self.focus_handle(cx).clone();
move |_event, window, cx| {
focus_handle.dispatch_action(
&ShowConfiguration,
window,
cx,
);
}
}),
)
.into_any_element(),
)
}
_ => None,
}
}
}
fn render_send_button(&self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
fn render_send_button(
&self,
window: &mut Window,
cx: &mut Context<Self>,
) -> impl IntoElement + use<> {
let focus_handle = self.focus_handle(cx).clone();
let (style, tooltip) = match token_state(&self.context, cx) {
@@ -2427,7 +2467,11 @@ impl ContextEditor {
})
}
fn render_edit_button(&self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
fn render_edit_button(
&self,
window: &mut Window,
cx: &mut Context<Self>,
) -> impl IntoElement + use<> {
let focus_handle = self.focus_handle(cx).clone();
let (style, tooltip) = match token_state(&self.context, cx) {
@@ -2480,7 +2524,7 @@ impl ContextEditor {
})
}
fn render_inject_context_menu(&self, cx: &mut Context<Self>) -> impl IntoElement {
fn render_inject_context_menu(&self, cx: &mut Context<Self>) -> impl IntoElement + use<> {
slash_command_picker::SlashCommandSelector::new(
self.slash_commands.clone(),
cx.entity().downgrade(),
@@ -2499,7 +2543,7 @@ impl ContextEditor {
)
}
fn render_language_model_selector(&self, cx: &mut Context<Self>) -> impl IntoElement {
fn render_language_model_selector(&self, cx: &mut Context<Self>) -> impl IntoElement + use<> {
let active_model = LanguageModelRegistry::read_global(cx).active_model();
let focus_handle = self.editor().focus_handle(cx).clone();
let model_name = match active_model {
@@ -3283,12 +3327,9 @@ impl FollowableItem for ContextEditor {
Some(proto::view::Variant::ContextEditor(
proto::view::ContextEditor {
context_id: context.id().to_proto(),
editor: if let Some(proto::view::Variant::Editor(proto)) =
self.editor.read(cx).to_state_proto(window, cx)
{
Some(proto)
} else {
None
editor: match self.editor.read(cx).to_state_proto(window, cx) {
Some(proto::view::Variant::Editor(proto)) => Some(proto),
_ => None,
},
},
))
@@ -3413,7 +3454,7 @@ impl ContextEditorToolbarItem {
pub fn render_remaining_tokens(
context_editor: &Entity<ContextEditor>,
cx: &App,
) -> Option<impl IntoElement> {
) -> Option<impl IntoElement + use<>> {
let context = &context_editor.read(cx).context;
let (token_count_color, token_count, max_token_count, tooltip) = match token_state(context, cx)?

View File

@@ -299,13 +299,12 @@ impl ContextStore {
}
if is_shared {
self.contexts.retain_mut(|context| {
if let Some(strong_context) = context.upgrade() {
self.contexts.retain_mut(|context| match context.upgrade() {
Some(strong_context) => {
*context = ContextHandle::Strong(strong_context);
true
} else {
false
}
_ => false,
});
let remote_id = self.project.read(cx).remote_id().unwrap();
self.client_subscription = self
@@ -336,8 +335,8 @@ impl ContextStore {
self.synchronize_contexts(cx);
}
project::Event::DisconnectedFromHost => {
self.contexts.retain_mut(|context| {
if let Some(strong_context) = context.upgrade() {
self.contexts.retain_mut(|context| match context.upgrade() {
Some(strong_context) => {
*context = ContextHandle::Weak(context.downgrade());
strong_context.update(cx, |context, cx| {
if context.replica_id() != ReplicaId::default() {
@@ -345,9 +344,8 @@ impl ContextStore {
}
});
true
} else {
false
}
_ => false,
});
self.host_contexts.clear();
cx.notify();
@@ -422,12 +420,13 @@ impl ContextStore {
.await?;
context.update(cx, |context, cx| context.apply_ops(operations, cx))?;
this.update(cx, |this, cx| {
if let Some(existing_context) = this.loaded_context_for_id(&context_id, cx) {
existing_context
} else {
this.register_context(&context, cx);
this.synchronize_contexts(cx);
context
match this.loaded_context_for_id(&context_id, cx) {
Some(existing_context) => existing_context,
_ => {
this.register_context(&context, cx);
this.synchronize_contexts(cx);
context
}
}
})
})
@@ -471,11 +470,12 @@ impl ContextStore {
)
})?;
this.update(cx, |this, cx| {
if let Some(existing_context) = this.loaded_context_for_path(&path, cx) {
existing_context
} else {
this.register_context(&context, cx);
context
match this.loaded_context_for_path(&path, cx) {
Some(existing_context) => existing_context,
_ => {
this.register_context(&context, cx);
context
}
}
})
})
@@ -591,12 +591,13 @@ impl ContextStore {
.await?;
context.update(cx, |context, cx| context.apply_ops(operations, cx))?;
this.update(cx, |this, cx| {
if let Some(existing_context) = this.loaded_context_for_id(&context_id, cx) {
existing_context
} else {
this.register_context(&context, cx);
this.synchronize_contexts(cx);
context
match this.loaded_context_for_id(&context_id, cx) {
Some(existing_context) => existing_context,
_ => {
this.register_context(&context, cx);
this.synchronize_contexts(cx);
context
}
}
})
})

View File

@@ -151,29 +151,27 @@ impl SlashCommandCompletionProvider {
let mut flag = self.cancel_flag.lock();
flag.store(true, SeqCst);
*flag = new_cancel_flag.clone();
if let Some(command) = self.slash_commands.command(command_name, cx) {
let completions = command.complete_argument(
arguments,
new_cancel_flag.clone(),
self.workspace.clone(),
window,
cx,
);
let command_name: Arc<str> = command_name.into();
let editor = self.editor.clone();
let workspace = self.workspace.clone();
let arguments = arguments.to_vec();
cx.background_spawn(async move {
Ok(Some(
completions
.await?
.into_iter()
.map(|new_argument| {
let confirm =
editor
.clone()
.zip(workspace.clone())
.map(|(editor, workspace)| {
match self.slash_commands.command(command_name, cx) {
Some(command) => {
let completions = command.complete_argument(
arguments,
new_cancel_flag.clone(),
self.workspace.clone(),
window,
cx,
);
let command_name: Arc<str> = command_name.into();
let editor = self.editor.clone();
let workspace = self.workspace.clone();
let arguments = arguments.to_vec();
cx.background_spawn(async move {
Ok(Some(
completions
.await?
.into_iter()
.map(|new_argument| {
let confirm = editor.clone().zip(workspace.clone()).map(
|(editor, workspace)| {
Arc::new({
let mut completed_arguments = arguments.clone();
if new_argument.replace_previous_arguments {
@@ -210,32 +208,33 @@ impl SlashCommandCompletionProvider {
}
}
}) as Arc<_>
});
},
);
let mut new_text = new_argument.new_text.clone();
if new_argument.after_completion == AfterCompletion::Continue {
new_text.push(' ');
}
let mut new_text = new_argument.new_text.clone();
if new_argument.after_completion == AfterCompletion::Continue {
new_text.push(' ');
}
project::Completion {
old_range: if new_argument.replace_previous_arguments {
argument_range.clone()
} else {
last_argument_range.clone()
},
label: new_argument.label,
icon_path: None,
new_text,
documentation: None,
confirm,
source: CompletionSource::Custom,
}
})
.collect(),
))
})
} else {
Task::ready(Ok(Some(Vec::new())))
project::Completion {
old_range: if new_argument.replace_previous_arguments {
argument_range.clone()
} else {
last_argument_range.clone()
},
label: new_argument.label,
icon_path: None,
new_text,
documentation: None,
confirm,
source: CompletionSource::Custom,
}
})
.collect(),
))
})
}
_ => Task::ready(Ok(Some(Vec::new()))),
}
}
}
@@ -333,10 +332,9 @@ impl CompletionProvider for SlashCommandCompletionProvider {
let position = position.to_point(buffer);
let line_start = Point::new(position.row, 0);
let mut lines = buffer.text_for_range(line_start..position).lines();
if let Some(line) = lines.next() {
SlashCommandLine::parse(line).is_some()
} else {
false
match lines.next() {
Some(line) => SlashCommandLine::parse(line).is_some(),
_ => false,
}
}

View File

@@ -105,8 +105,8 @@ impl JsonSchema for AssistantSettingsContent {
VersionedAssistantSettingsContent::schema_name()
}
fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> Schema {
VersionedAssistantSettingsContent::json_schema(gen)
fn json_schema(r#gen: &mut schemars::r#gen::SchemaGenerator) -> Schema {
VersionedAssistantSettingsContent::json_schema(r#gen)
}
fn is_referenceable() -> bool {
@@ -416,7 +416,7 @@ pub struct LanguageModelSelection {
pub model: String,
}
fn providers_schema(_: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
fn providers_schema(_: &mut schemars::r#gen::SchemaGenerator) -> schemars::schema::Schema {
schemars::schema::SchemaObject {
enum_values: Some(vec![
"anthropic".into(),

View File

@@ -88,8 +88,8 @@ impl SlashCommand for ContextServerSlashCommand {
let server_id = self.server_id.clone();
let prompt_name = self.prompt.name.clone();
if let Some(server) = self.server_manager.read(cx).get_server(&server_id) {
cx.foreground_executor().spawn(async move {
match self.server_manager.read(cx).get_server(&server_id) {
Some(server) => cx.foreground_executor().spawn(async move {
let Some(protocol) = server.client() else {
return Err(anyhow!("Context server not initialized"));
};
@@ -118,9 +118,8 @@ impl SlashCommand for ContextServerSlashCommand {
})
.collect();
Ok(completions)
})
} else {
Task::ready(Err(anyhow!("Context server not found")))
}),
_ => Task::ready(Err(anyhow!("Context server not found"))),
}
}
@@ -143,56 +142,57 @@ impl SlashCommand for ContextServerSlashCommand {
};
let manager = self.server_manager.read(cx);
if let Some(server) = manager.get_server(&server_id) {
cx.foreground_executor().spawn(async move {
let Some(protocol) = server.client() else {
return Err(anyhow!("Context server not initialized"));
};
let result = protocol.run_prompt(&prompt_name, prompt_args).await?;
match manager.get_server(&server_id) {
Some(server) => {
cx.foreground_executor().spawn(async move {
let Some(protocol) = server.client() else {
return Err(anyhow!("Context server not initialized"));
};
let result = protocol.run_prompt(&prompt_name, prompt_args).await?;
// Check that there are only user roles
if result
.messages
.iter()
.any(|msg| !matches!(msg.role, context_server::types::Role::User))
{
return Err(anyhow!(
"Prompt contains non-user roles, which is not supported"
));
}
// Check that there are only user roles
if result
.messages
.iter()
.any(|msg| !matches!(msg.role, context_server::types::Role::User))
{
return Err(anyhow!(
"Prompt contains non-user roles, which is not supported"
));
}
// Extract text from user messages into a single prompt string
let mut prompt = result
.messages
.into_iter()
.filter_map(|msg| match msg.content {
context_server::types::MessageContent::Text { text, .. } => Some(text),
_ => None,
})
.collect::<Vec<String>>()
.join("\n\n");
// Extract text from user messages into a single prompt string
let mut prompt = result
.messages
.into_iter()
.filter_map(|msg| match msg.content {
context_server::types::MessageContent::Text { text, .. } => Some(text),
_ => None,
})
.collect::<Vec<String>>()
.join("\n\n");
// We must normalize the line endings here, since servers might return CR characters.
LineEnding::normalize(&mut prompt);
// We must normalize the line endings here, since servers might return CR characters.
LineEnding::normalize(&mut prompt);
Ok(SlashCommandOutput {
sections: vec![SlashCommandOutputSection {
range: 0..(prompt.len()),
icon: IconName::ZedAssistant,
label: SharedString::from(
result
.description
.unwrap_or(format!("Result from {}", prompt_name)),
),
metadata: None,
}],
text: prompt,
run_commands_in_text: false,
}
.to_event_stream())
})
} else {
Task::ready(Err(anyhow!("Context server not found")))
Ok(SlashCommandOutput {
sections: vec![SlashCommandOutputSection {
range: 0..(prompt.len()),
icon: IconName::ZedAssistant,
label: SharedString::from(
result
.description
.unwrap_or(format!("Result from {}", prompt_name)),
),
metadata: None,
}],
text: prompt,
run_commands_in_text: false,
}
.to_event_stream())
})
}
_ => Task::ready(Err(anyhow!("Context server not found"))),
}
}
}

View File

@@ -229,19 +229,20 @@ fn collect_diagnostics(
options: Options,
cx: &mut App,
) -> Task<Result<Option<SlashCommandOutput>>> {
let error_source = if let Some(path_matcher) = &options.path_matcher {
debug_assert_eq!(path_matcher.sources().len(), 1);
Some(path_matcher.sources().first().cloned().unwrap_or_default())
} else {
None
let error_source = match &options.path_matcher {
Some(path_matcher) => {
debug_assert_eq!(path_matcher.sources().len(), 1);
Some(path_matcher.sources().first().cloned().unwrap_or_default())
}
_ => None,
};
let glob_is_exact_file_match = if let Some(path) = options
let glob_is_exact_file_match = match options
.path_matcher
.as_ref()
.and_then(|pm| pm.sources().first())
{
PathBuf::try_from(path)
Some(path) => PathBuf::try_from(path)
.ok()
.and_then(|path| {
project.read(cx).worktrees(cx).find_map(|worktree| {
@@ -251,9 +252,8 @@ fn collect_diagnostics(
worktree.absolutize(&relative_path).ok()
})
})
.is_some()
} else {
false
.is_some(),
_ => false,
};
let project_handle = project.downgrade();

View File

@@ -221,7 +221,7 @@ fn collect_files(
project: Entity<Project>,
glob_inputs: &[String],
cx: &mut App,
) -> impl Stream<Item = Result<SlashCommandEvent>> {
) -> impl Stream<Item = Result<SlashCommandEvent>> + use<> {
let Ok(matchers) = glob_inputs
.into_iter()
.map(|glob_input| {
@@ -285,22 +285,25 @@ fn collect_files(
if entry.is_dir() {
// Auto-fold directories that contain no files
let mut child_entries = snapshot.child_entries(&entry.path);
if let Some(child) = child_entries.next() {
if child_entries.next().is_none() && child.kind.is_dir() {
if is_top_level_directory {
is_top_level_directory = false;
folded_directory_names_stack.push(
path_including_worktree_name.to_string_lossy().to_string(),
);
} else {
folded_directory_names_stack.push(filename.to_string());
match child_entries.next() {
Some(child) => {
if child_entries.next().is_none() && child.kind.is_dir() {
if is_top_level_directory {
is_top_level_directory = false;
folded_directory_names_stack.push(
path_including_worktree_name.to_string_lossy().to_string(),
);
} else {
folded_directory_names_stack.push(filename.to_string());
}
continue;
}
}
_ => {
// Skip empty directories
folded_directory_names_stack.clear();
continue;
}
} else {
// Skip empty directories
folded_directory_names_stack.clear();
continue;
}
let prefix_paths = folded_directory_names_stack.drain(..).as_slice().join("/");
if prefix_paths.is_empty() {

View File

@@ -218,12 +218,9 @@ async fn project_symbols(
for symbol in symbols
.iter()
.filter(|symbol| {
if let Some(regex) = &regex {
regex.is_match(&symbol.name)
} else {
true
}
.filter(|symbol| match &regex {
Some(regex) => regex.is_match(&symbol.name),
_ => true,
})
.skip(offset as usize)
// Take 1 more than RESULTS_PER_PAGE so we can tell if there are more results.
@@ -316,13 +313,12 @@ async fn render_outline(
.collect();
let lang_name = lang.name();
if let Some(lsp_adapter) = registry.lsp_adapters(&lang_name).first().cloned() {
lsp_adapter
match registry.lsp_adapters(&lang_name).first().cloned() {
Some(lsp_adapter) => lsp_adapter
.labels_for_symbols(&entries_for_labels, lang)
.await
.ok()
} else {
None
.ok(),
_ => None,
}
}
None => None,

View File

@@ -63,16 +63,16 @@ impl Tool for DiagnosticsTool {
}
fn ui_text(&self, input: &serde_json::Value) -> String {
if let Some(path) = serde_json::from_value::<DiagnosticsToolInput>(input.clone())
match serde_json::from_value::<DiagnosticsToolInput>(input.clone())
.ok()
.and_then(|input| match input.path {
Some(path) if !path.is_empty() => Some(MarkdownString::inline_code(&path)),
_ => None,
})
{
format!("Check diagnostics for {path}")
} else {
"Check project diagnostics".to_string()
}) {
Some(path) => {
format!("Check diagnostics for {path}")
}
_ => "Check project diagnostics".to_string(),
}
}

View File

@@ -188,7 +188,7 @@ impl Tool for FindReplaceFileTool {
})
.await;
if let Some(diff) = result {
match result { Some(diff) => {
let edit_ids = buffer.update(cx, |buffer, cx| {
buffer.finalize_last_transaction();
buffer.apply_diff(diff, false, cx);
@@ -205,7 +205,7 @@ impl Tool for FindReplaceFileTool {
})?.await?;
Ok(format!("Edited {}", input.path.display()))
} else {
} _ => {
let err = buffer.read_with(cx, |buffer, _cx| {
let file_exists = buffer
.file()
@@ -224,7 +224,7 @@ impl Tool for FindReplaceFileTool {
})?;
Err(err)
}
}}
})
}
}

View File

@@ -35,7 +35,7 @@ impl SoundRegistry {
cx.set_global(GlobalSoundRegistry(SoundRegistry::new(source)));
}
pub fn get(&self, name: &str) -> Result<impl Source<Item = f32>> {
pub fn get(&self, name: &str) -> Result<impl Source<Item = f32> + use<>> {
if let Some(wav) = self.cache.lock().get(name) {
return Ok(wav.clone());
}

View File

@@ -201,16 +201,19 @@ pub fn check(_: &Check, window: &mut Window, cx: &mut App) {
return;
}
if let Some(updater) = AutoUpdater::get(cx) {
updater.update(cx, |updater, cx| updater.poll(cx));
} else {
drop(window.prompt(
gpui::PromptLevel::Info,
"Could not check for updates",
Some("Auto-updates disabled for non-bundled app."),
&["Ok"],
cx,
));
match AutoUpdater::get(cx) {
Some(updater) => {
updater.update(cx, |updater, cx| updater.poll(cx));
}
_ => {
drop(window.prompt(
gpui::PromptLevel::Info,
"Could not check for updates",
Some("Auto-updates disabled for non-bundled app."),
&["Ok"],
cx,
));
}
}
}

View File

@@ -110,8 +110,8 @@ impl Render for Breadcrumbs {
}
}
})
.tooltip(move |window, cx| {
if let Some(editor) = editor.upgrade() {
.tooltip(move |window, cx| match editor.upgrade() {
Some(editor) => {
let focus_handle = editor.read(cx).focus_handle(cx);
Tooltip::for_action_in(
"Show Symbol Outline",
@@ -120,14 +120,13 @@ impl Render for Breadcrumbs {
window,
cx,
)
} else {
Tooltip::for_action(
"Show Symbol Outline",
&zed_actions::outline::ToggleOutline,
window,
cx,
)
}
_ => Tooltip::for_action(
"Show Symbol Outline",
&zed_actions::outline::ToggleOutline,
window,
cx,
),
}),
),
None => element

View File

@@ -608,55 +608,58 @@ fn compute_hunks(
) -> SumTree<InternalDiffHunk> {
let mut tree = SumTree::new(&buffer);
if let Some((diff_base, diff_base_rope)) = diff_base {
let buffer_text = buffer.as_rope().to_string();
match diff_base {
Some((diff_base, diff_base_rope)) => {
let buffer_text = buffer.as_rope().to_string();
let mut options = GitOptions::default();
options.context_lines(0);
let patch = GitPatch::from_buffers(
diff_base.as_bytes(),
None,
buffer_text.as_bytes(),
None,
Some(&mut options),
)
.log_err();
let mut options = GitOptions::default();
options.context_lines(0);
let patch = GitPatch::from_buffers(
diff_base.as_bytes(),
None,
buffer_text.as_bytes(),
None,
Some(&mut options),
)
.log_err();
// A common case in Zed is that the empty buffer is represented as just a newline,
// but if we just compute a naive diff you get a "preserved" line in the middle,
// which is a bit odd.
if buffer_text == "\n" && diff_base.ends_with("\n") && diff_base.len() > 1 {
// A common case in Zed is that the empty buffer is represented as just a newline,
// but if we just compute a naive diff you get a "preserved" line in the middle,
// which is a bit odd.
if buffer_text == "\n" && diff_base.ends_with("\n") && diff_base.len() > 1 {
tree.push(
InternalDiffHunk {
buffer_range: buffer.anchor_before(0)..buffer.anchor_before(0),
diff_base_byte_range: 0..diff_base.len() - 1,
},
&buffer,
);
return tree;
}
if let Some(patch) = patch {
let mut divergence = 0;
for hunk_index in 0..patch.num_hunks() {
let hunk = process_patch_hunk(
&patch,
hunk_index,
&diff_base_rope,
&buffer,
&mut divergence,
);
tree.push(hunk, &buffer);
}
}
}
_ => {
tree.push(
InternalDiffHunk {
buffer_range: buffer.anchor_before(0)..buffer.anchor_before(0),
diff_base_byte_range: 0..diff_base.len() - 1,
buffer_range: Anchor::MIN..Anchor::MAX,
diff_base_byte_range: 0..0,
},
&buffer,
);
return tree;
}
if let Some(patch) = patch {
let mut divergence = 0;
for hunk_index in 0..patch.num_hunks() {
let hunk = process_patch_hunk(
&patch,
hunk_index,
&diff_base_rope,
&buffer,
&mut divergence,
);
tree.push(hunk, &buffer);
}
}
} else {
tree.push(
InternalDiffHunk {
buffer_range: Anchor::MIN..Anchor::MAX,
diff_base_byte_range: 0..0,
},
&buffer,
);
}
tree
@@ -776,7 +779,7 @@ impl BufferDiff {
language: Option<Arc<Language>>,
language_registry: Option<Arc<LanguageRegistry>>,
cx: &mut App,
) -> impl Future<Output = BufferDiffInner> {
) -> impl Future<Output = BufferDiffInner> + use<> {
let base_text_pair;
let base_text_exists;
let base_text_snapshot;
@@ -818,7 +821,7 @@ impl BufferDiff {
base_text: Option<Arc<String>>,
base_text_snapshot: language::BufferSnapshot,
cx: &App,
) -> impl Future<Output = BufferDiffInner> {
) -> impl Future<Output = BufferDiffInner> + use<> {
let base_text_exists = base_text.is_some();
let base_text_pair = base_text.map(|text| (text, base_text_snapshot.as_rope().clone()));
cx.background_spawn(async move {
@@ -2071,7 +2074,7 @@ mod tests {
)
});
let working_copy = working_copy.read_with(cx, |working_copy, _| working_copy.snapshot());
let mut index_text = if rng.gen() {
let mut index_text = if rng.r#gen() {
Rope::from(head_text.as_str())
} else {
working_copy.as_rope().clone()

View File

@@ -179,23 +179,21 @@ impl ActiveCall {
return Task::ready(Ok(()));
}
let room = if let Some(room) = self.room().cloned() {
Some(Task::ready(Ok(room)).shared())
} else {
self.pending_room_creation.clone()
let room = match self.room().cloned() {
Some(room) => Some(Task::ready(Ok(room)).shared()),
_ => self.pending_room_creation.clone(),
};
let invite = if let Some(room) = room {
cx.spawn(async move |_, cx| {
let invite = match room {
Some(room) => cx.spawn(async move |_, cx| {
let room = room.await.map_err(|err| anyhow!("{:?}", err))?;
let initial_project_id = if let Some(initial_project) = initial_project {
Some(
let initial_project_id = match initial_project {
Some(initial_project) => Some(
room.update(cx, |room, cx| room.share_project(initial_project, cx))?
.await?,
)
} else {
None
),
_ => None,
};
room.update(cx, move |room, cx| {
@@ -204,41 +202,42 @@ impl ActiveCall {
.await?;
anyhow::Ok(())
})
} else {
let client = self.client.clone();
let user_store = self.user_store.clone();
let room = cx
.spawn(async move |this, cx| {
let create_room = async {
let room = cx
.update(|cx| {
Room::create(
called_user_id,
initial_project,
client,
user_store,
cx,
)
})?
.await?;
}),
_ => {
let client = self.client.clone();
let user_store = self.user_store.clone();
let room = cx
.spawn(async move |this, cx| {
let create_room = async {
let room = cx
.update(|cx| {
Room::create(
called_user_id,
initial_project,
client,
user_store,
cx,
)
})?
.await?;
this.update(cx, |this, cx| this.set_room(Some(room.clone()), cx))?
.await?;
this.update(cx, |this, cx| this.set_room(Some(room.clone()), cx))?
.await?;
anyhow::Ok(room)
};
anyhow::Ok(room)
};
let room = create_room.await;
this.update(cx, |this, _| this.pending_room_creation = None)?;
room.map_err(Arc::new)
let room = create_room.await;
this.update(cx, |this, _| this.pending_room_creation = None)?;
room.map_err(Arc::new)
})
.shared();
self.pending_room_creation = Some(room.clone());
cx.background_spawn(async move {
room.await.map_err(|err| anyhow!("{:?}", err))?;
anyhow::Ok(())
})
.shared();
self.pending_room_creation = Some(room.clone());
cx.background_spawn(async move {
room.await.map_err(|err| anyhow!("{:?}", err))?;
anyhow::Ok(())
})
}
};
cx.spawn(async move |this, cx| {
@@ -292,10 +291,11 @@ impl ActiveCall {
return Task::ready(Err(anyhow!("cannot join while on another call")));
}
let call = if let Some(call) = self.incoming_call.0.borrow_mut().take() {
call
} else {
return Task::ready(Err(anyhow!("no incoming call")));
let call = match self.incoming_call.0.borrow_mut().take() {
Some(call) => call,
_ => {
return Task::ready(Err(anyhow!("no incoming call")));
}
};
if self.pending_room_creation.is_some() {
@@ -373,11 +373,12 @@ impl ActiveCall {
Audio::end_call(cx);
let channel_id = self.channel_id(cx);
if let Some((room, _)) = self.room.take() {
cx.emit(Event::RoomLeft { channel_id });
room.update(cx, |room, cx| room.leave(cx))
} else {
Task::ready(Ok(()))
match self.room.take() {
Some((room, _)) => {
cx.emit(Event::RoomLeft { channel_id });
room.update(cx, |room, cx| room.leave(cx))
}
_ => Task::ready(Ok(())),
}
}
@@ -386,11 +387,12 @@ impl ActiveCall {
project: Entity<Project>,
cx: &mut Context<Self>,
) -> Task<Result<u64>> {
if let Some((room, _)) = self.room.as_ref() {
self.report_call_event("Project Shared", cx);
room.update(cx, |room, cx| room.share_project(project, cx))
} else {
Task::ready(Err(anyhow!("no active call")))
match self.room.as_ref() {
Some((room, _)) => {
self.report_call_event("Project Shared", cx);
room.update(cx, |room, cx| room.share_project(project, cx))
}
_ => Task::ready(Err(anyhow!("no active call"))),
}
}
@@ -399,11 +401,12 @@ impl ActiveCall {
project: Entity<Project>,
cx: &mut Context<Self>,
) -> Result<()> {
if let Some((room, _)) = self.room.as_ref() {
self.report_call_event("Project Unshared", cx);
room.update(cx, |room, cx| room.unshare_project(project, cx))
} else {
Err(anyhow!("no active call"))
match self.room.as_ref() {
Some((room, _)) => {
self.report_call_event("Project Unshared", cx);
room.update(cx, |room, cx| room.unshare_project(project, cx))
}
_ => Err(anyhow!("no active call")),
}
}
@@ -430,33 +433,36 @@ impl ActiveCall {
Task::ready(Ok(()))
} else {
cx.notify();
if let Some(room) = room {
if room.read(cx).status().is_offline() {
match room {
Some(room) => {
if room.read(cx).status().is_offline() {
self.room = None;
Task::ready(Ok(()))
} else {
let subscriptions = vec![
cx.observe(&room, |this, room, cx| {
if room.read(cx).status().is_offline() {
this.set_room(None, cx).detach_and_log_err(cx);
}
cx.notify();
}),
cx.subscribe(&room, |_, _, event, cx| cx.emit(event.clone())),
];
self.room = Some((room.clone(), subscriptions));
let location = self
.location
.as_ref()
.and_then(|location| location.upgrade());
let channel_id = room.read(cx).channel_id();
cx.emit(Event::RoomJoined { channel_id });
room.update(cx, |room, cx| room.set_location(location.as_ref(), cx))
}
}
_ => {
self.room = None;
Task::ready(Ok(()))
} else {
let subscriptions = vec![
cx.observe(&room, |this, room, cx| {
if room.read(cx).status().is_offline() {
this.set_room(None, cx).detach_and_log_err(cx);
}
cx.notify();
}),
cx.subscribe(&room, |_, _, event, cx| cx.emit(event.clone())),
];
self.room = Some((room.clone(), subscriptions));
let location = self
.location
.as_ref()
.and_then(|location| location.upgrade());
let channel_id = room.read(cx).channel_id();
cx.emit(Event::RoomJoined { channel_id });
room.update(cx, |room, cx| room.set_location(location.as_ref(), cx))
}
} else {
self.room = None;
Task::ready(Ok(()))
}
}
}

View File

@@ -96,10 +96,11 @@ impl Room {
}
pub fn is_connected(&self, _: &App) -> bool {
if let Some(live_kit) = self.live_kit.as_ref() {
live_kit.room.connection_state() == livekit::ConnectionState::Connected
} else {
false
match self.live_kit.as_ref() {
Some(live_kit) => {
live_kit.room.connection_state() == livekit::ConnectionState::Connected
}
_ => false,
}
}
@@ -181,15 +182,16 @@ impl Room {
room
})?;
let initial_project_id = if let Some(initial_project) = initial_project {
let initial_project_id = room
.update(cx, |room, cx| {
room.share_project(initial_project.clone(), cx)
})?
.await?;
Some(initial_project_id)
} else {
None
let initial_project_id = match initial_project {
Some(initial_project) => {
let initial_project_id = room
.update(cx, |room, cx| {
room.share_project(initial_project.clone(), cx)
})?
.await?;
Some(initial_project_id)
}
_ => None,
};
let did_join = room
@@ -243,7 +245,7 @@ impl Room {
}
}
fn app_will_quit(&mut self, cx: &mut Context<Self>) -> impl Future<Output = ()> {
fn app_will_quit(&mut self, cx: &mut Context<Self>) -> impl Future<Output = ()> + use<> {
let task = if self.status.is_online() {
let leave = self.leave_internal(cx);
Some(cx.background_spawn(async move {
@@ -665,7 +667,7 @@ impl Room {
Ok(())
}
pub fn room_update_completed(&mut self) -> impl Future<Output = ()> {
pub fn room_update_completed(&mut self) -> impl Future<Output = ()> + use<> {
let mut done_rx = self.room_update_completed_rx.clone();
async move {
while let Some(result) = done_rx.next().await {
@@ -728,14 +730,14 @@ impl Room {
}
}
this.joined_projects.retain(|project| {
if let Some(project) = project.upgrade() {
project.update(cx, |project, cx| project.set_role(role, cx));
true
} else {
false
}
});
this.joined_projects
.retain(|project| match project.upgrade() {
Some(project) => {
project.update(cx, |project, cx| project.set_role(role, cx));
true
}
_ => false,
});
}
} else {
this.local_participant.projects.clear();
@@ -778,20 +780,18 @@ impl Room {
}
for unshared_project_id in old_projects.difference(&new_projects) {
this.joined_projects.retain(|project| {
if let Some(project) = project.upgrade() {
project.update(cx, |project, cx| {
this.joined_projects
.retain(|project| match project.upgrade() {
Some(project) => project.update(cx, |project, cx| {
if project.remote_id() == Some(*unshared_project_id) {
project.disconnected_from_host(cx);
false
} else {
true
}
})
} else {
false
}
});
}),
_ => false,
});
cx.emit(Event::RemoteProjectUnshared {
project_id: *unshared_project_id,
});
@@ -800,56 +800,57 @@ impl Room {
let role = participant.role();
let location = ParticipantLocation::from_proto(participant.location)
.unwrap_or(ParticipantLocation::External);
if let Some(remote_participant) =
this.remote_participants.get_mut(&participant.user_id)
{
remote_participant.peer_id = peer_id;
remote_participant.projects = participant.projects;
remote_participant.participant_index = participant_index;
if location != remote_participant.location
|| role != remote_participant.role
{
remote_participant.location = location;
remote_participant.role = role;
cx.emit(Event::ParticipantLocationChanged {
participant_id: peer_id,
});
}
} else {
this.remote_participants.insert(
participant.user_id,
RemoteParticipant {
user: user.clone(),
participant_index,
peer_id,
projects: participant.projects,
location,
role,
muted: true,
speaking: false,
video_tracks: Default::default(),
audio_tracks: Default::default(),
},
);
Audio::play_sound(Sound::Joined, cx);
if let Some(livekit_participants) = &livekit_participants {
if let Some(livekit_participant) = livekit_participants
.get(&ParticipantIdentity(user.id.to_string()))
match this.remote_participants.get_mut(&participant.user_id) {
Some(remote_participant) => {
remote_participant.peer_id = peer_id;
remote_participant.projects = participant.projects;
remote_participant.participant_index = participant_index;
if location != remote_participant.location
|| role != remote_participant.role
{
for publication in
livekit_participant.track_publications().into_values()
remote_participant.location = location;
remote_participant.role = role;
cx.emit(Event::ParticipantLocationChanged {
participant_id: peer_id,
});
}
}
_ => {
this.remote_participants.insert(
participant.user_id,
RemoteParticipant {
user: user.clone(),
participant_index,
peer_id,
projects: participant.projects,
location,
role,
muted: true,
speaking: false,
video_tracks: Default::default(),
audio_tracks: Default::default(),
},
);
Audio::play_sound(Sound::Joined, cx);
if let Some(livekit_participants) = &livekit_participants {
if let Some(livekit_participant) = livekit_participants
.get(&ParticipantIdentity(user.id.to_string()))
{
if let Some(track) = publication.track() {
this.livekit_room_updated(
RoomEvent::TrackSubscribed {
track,
publication,
participant: livekit_participant.clone(),
},
cx,
)
.warn_on_err();
for publication in
livekit_participant.track_publications().into_values()
{
if let Some(track) = publication.track() {
this.livekit_room_updated(
RoomEvent::TrackSubscribed {
track,
publication,
participant: livekit_participant.clone(),
},
cx,
)
.warn_on_err();
}
}
}
}
@@ -1141,13 +1142,11 @@ impl Room {
Project::in_room(id, client, user_store, language_registry, fs, cx.clone()).await?;
this.update(cx, |this, cx| {
this.joined_projects.retain(|project| {
if let Some(project) = project.upgrade() {
!project.read(cx).is_disconnected(cx)
} else {
false
}
});
this.joined_projects
.retain(|project| match project.upgrade() {
Some(project) => !project.read(cx).is_disconnected(cx),
_ => false,
});
this.joined_projects.insert(project.downgrade());
})?;
Ok(project)
@@ -1309,13 +1308,16 @@ impl Room {
return Task::ready(Err(anyhow!("room is offline")));
}
let (room, publish_id) = if let Some(live_kit) = self.live_kit.as_mut() {
let publish_id = post_inc(&mut live_kit.next_publish_id);
live_kit.microphone_track = LocalTrack::Pending { publish_id };
cx.notify();
(live_kit.room.clone(), publish_id)
} else {
return Task::ready(Err(anyhow!("live-kit was not initialized")));
let (room, publish_id) = match self.live_kit.as_mut() {
Some(live_kit) => {
let publish_id = post_inc(&mut live_kit.next_publish_id);
live_kit.microphone_track = LocalTrack::Pending { publish_id };
cx.notify();
(live_kit.room.clone(), publish_id)
}
_ => {
return Task::ready(Err(anyhow!("live-kit was not initialized")));
}
};
cx.spawn(async move |this, cx| {
@@ -1326,13 +1328,11 @@ impl Room {
.as_mut()
.ok_or_else(|| anyhow!("live-kit was not initialized"))?;
let canceled = if let LocalTrack::Pending {
publish_id: cur_publish_id,
} = &live_kit.microphone_track
{
*cur_publish_id != publish_id
} else {
true
let canceled = match &live_kit.microphone_track {
LocalTrack::Pending {
publish_id: cur_publish_id,
} => *cur_publish_id != publish_id,
_ => true,
};
match publication {
@@ -1376,13 +1376,16 @@ impl Room {
return Task::ready(Err(anyhow!("screen was already shared")));
}
let (participant, publish_id) = if let Some(live_kit) = self.live_kit.as_mut() {
let publish_id = post_inc(&mut live_kit.next_publish_id);
live_kit.screen_track = LocalTrack::Pending { publish_id };
cx.notify();
(live_kit.room.local_participant(), publish_id)
} else {
return Task::ready(Err(anyhow!("live-kit was not initialized")));
let (participant, publish_id) = match self.live_kit.as_mut() {
Some(live_kit) => {
let publish_id = post_inc(&mut live_kit.next_publish_id);
live_kit.screen_track = LocalTrack::Pending { publish_id };
cx.notify();
(live_kit.room.local_participant(), publish_id)
}
_ => {
return Task::ready(Err(anyhow!("live-kit was not initialized")));
}
};
let sources = cx.screen_capture_sources();
@@ -1399,13 +1402,11 @@ impl Room {
.as_mut()
.ok_or_else(|| anyhow!("live-kit was not initialized"))?;
let canceled = if let LocalTrack::Pending {
publish_id: cur_publish_id,
} = &live_kit.screen_track
{
*cur_publish_id != publish_id
} else {
true
let canceled = match &live_kit.screen_track {
LocalTrack::Pending {
publish_id: cur_publish_id,
} => *cur_publish_id != publish_id,
_ => true,
};
match publication {

View File

@@ -183,7 +183,7 @@ impl ChannelChat {
let channel_id = self.channel_id;
let pending_id = ChannelMessageId::Pending(post_inc(&mut self.next_pending_message_id));
let nonce = self.rng.gen();
let nonce = self.rng.r#gen();
self.insert_messages(
SumTree::from_item(
ChannelMessage {
@@ -257,7 +257,7 @@ impl ChannelChat {
cx,
);
let nonce: u128 = self.rng.gen();
let nonce: u128 = self.rng.r#gen();
let request = self.rpc.request(proto::UpdateChannelMessage {
channel_id: self.channel_id.0,

View File

@@ -329,17 +329,16 @@ impl ChannelStore {
.request(proto::GetChannelMessagesById { message_ids }),
)
};
cx.spawn(async move |this, cx| {
if let Some(request) = request {
cx.spawn(async move |this, cx| match request {
Some(request) => {
let response = request.await?;
let this = this
.upgrade()
.ok_or_else(|| anyhow!("channel store dropped"))?;
let user_store = this.update(cx, |this, _| this.user_store.clone())?;
ChannelMessage::from_proto_vec(response.messages, &user_store, cx).await
} else {
Ok(Vec::new())
}
_ => Ok(Vec::new()),
})
}
@@ -465,14 +464,15 @@ impl ChannelStore {
let task = loop {
match get_map(self).entry(channel_id) {
hash_map::Entry::Occupied(e) => match e.get() {
OpenEntityHandle::Open(entity) => {
if let Some(entity) = entity.upgrade() {
OpenEntityHandle::Open(entity) => match entity.upgrade() {
Some(entity) => {
break Task::ready(Ok(entity)).shared();
} else {
}
_ => {
get_map(self).remove(&channel_id);
continue;
}
}
},
OpenEntityHandle::Loading(task) => {
break task.clone();
}
@@ -824,7 +824,10 @@ impl ChannelStore {
})
}
pub fn remove_channel(&self, channel_id: ChannelId) -> impl Future<Output = Result<()>> {
pub fn remove_channel(
&self,
channel_id: ChannelId,
) -> impl Future<Output = Result<()>> + use<> {
let client = self.client.clone();
async move {
client

View File

@@ -228,13 +228,16 @@ fn main() -> Result<()> {
paths.push(file.path().to_string_lossy().to_string());
let (file, _) = file.keep()?;
stdin_tmp_file = Some(file);
} else if let Some(file) = anonymous_fd(path) {
let tmp_file = NamedTempFile::new()?;
paths.push(tmp_file.path().to_string_lossy().to_string());
let (tmp_file, _) = tmp_file.keep()?;
anonymous_fd_tmp_files.push((file, tmp_file));
} else {
paths.push(parse_path_with_position(path)?)
match anonymous_fd(path) {
Some(file) => {
let tmp_file = NamedTempFile::new()?;
paths.push(tmp_file.path().to_string_lossy().to_string());
let (tmp_file, _) = tmp_file.keep()?;
anonymous_fd_tmp_files.push((file, tmp_file));
}
_ => paths.push(parse_path_with_position(path)?),
}
}
}
@@ -775,7 +778,7 @@ mod mac_os {
}
impl Detect {
pub fn detect(path: Option<&Path>) -> anyhow::Result<impl InstalledApp> {
pub fn detect(path: Option<&Path>) -> anyhow::Result<impl InstalledApp + use<>> {
let bundle_path = if let Some(bundle_path) = path {
bundle_path
.canonicalize()

View File

@@ -618,10 +618,9 @@ impl Client {
}
pub fn peer_id(&self) -> Option<PeerId> {
if let Status::Connected { peer_id, .. } = &*self.status().borrow() {
Some(*peer_id)
} else {
None
match &*self.status().borrow() {
Status::Connected { peer_id, .. } => Some(*peer_id),
_ => None,
}
}
@@ -1024,7 +1023,7 @@ impl Client {
&self,
http: Arc<HttpClientWithUrl>,
release_channel: Option<ReleaseChannel>,
) -> impl Future<Output = Result<url::Url>> {
) -> impl Future<Output = Result<url::Url>> + use<> {
#[cfg(any(test, feature = "test-support"))]
let url_override = self.rpc_url.read().clone();
@@ -1429,10 +1428,9 @@ impl Client {
}
fn connection_id(&self) -> Result<ConnectionId> {
if let Status::Connected { connection_id, .. } = *self.status().borrow() {
Ok(connection_id)
} else {
Err(anyhow!("not connected"))
match *self.status().borrow() {
Status::Connected { connection_id, .. } => Ok(connection_id),
_ => Err(anyhow!("not connected")),
}
}
@@ -1444,7 +1442,7 @@ impl Client {
pub fn request<T: RequestMessage>(
&self,
request: T,
) -> impl Future<Output = Result<T::Response>> {
) -> impl Future<Output = Result<T::Response>> + use<T> {
self.request_envelope(request)
.map_ok(|envelope| envelope.payload)
}
@@ -1452,7 +1450,8 @@ impl Client {
pub fn request_stream<T: RequestMessage>(
&self,
request: T,
) -> impl Future<Output = Result<impl Stream<Item = Result<T::Response>>>> {
) -> impl Future<Output = Result<impl Stream<Item = Result<T::Response>> + use<T>>> + use<T>
{
let client_id = self.id.load(Ordering::SeqCst);
log::debug!(
"rpc request start. client_id:{}. name:{}",
@@ -1476,7 +1475,7 @@ impl Client {
pub fn request_envelope<T: RequestMessage>(
&self,
request: T,
) -> impl Future<Output = Result<TypedEnvelope<T::Response>>> {
) -> impl Future<Output = Result<TypedEnvelope<T::Response>>> + use<T> {
let client_id = self.id();
log::debug!(
"rpc request start. client_id:{}. name:{}",
@@ -1501,7 +1500,7 @@ impl Client {
&self,
envelope: proto::Envelope,
request_type: &'static str,
) -> impl Future<Output = Result<proto::Envelope>> {
) -> impl Future<Output = Result<proto::Envelope>> + use<> {
let client_id = self.id();
log::debug!(
"rpc request start. client_id:{}. name:{}",
@@ -1528,44 +1527,47 @@ impl Client {
let type_name = message.payload_type_name();
let original_sender_id = message.original_sender_id();
if let Some(future) = ProtoMessageHandlerSet::handle_message(
match ProtoMessageHandlerSet::handle_message(
&self.handler_set,
message,
self.clone().into(),
cx.clone(),
) {
let client_id = self.id();
log::debug!(
"rpc message received. client_id:{}, sender_id:{:?}, type:{}",
client_id,
original_sender_id,
type_name
);
cx.spawn(async move |_| match future.await {
Ok(()) => {
log::debug!(
"rpc message handled. client_id:{}, sender_id:{:?}, type:{}",
client_id,
original_sender_id,
type_name
);
}
Err(error) => {
log::error!(
Some(future) => {
let client_id = self.id();
log::debug!(
"rpc message received. client_id:{}, sender_id:{:?}, type:{}",
client_id,
original_sender_id,
type_name
);
cx.spawn(async move |_| match future.await {
Ok(()) => {
log::debug!(
"rpc message handled. client_id:{}, sender_id:{:?}, type:{}",
client_id,
original_sender_id,
type_name
);
}
Err(error) => {
log::error!(
"error handling message. client_id:{}, sender_id:{:?}, type:{}, error:{:?}",
client_id,
original_sender_id,
type_name,
error
);
}
})
.detach();
} else {
log::info!("unhandled message {}", type_name);
self.peer
.respond_with_unhandled_message(sender_id.into(), request_id, type_name)
.log_err();
}
})
.detach();
}
_ => {
log::info!("unhandled message {}", type_name);
self.peer
.respond_with_unhandled_message(sender_id.into(), request_id, type_name)
.log_err();
}
}
}

View File

@@ -273,7 +273,7 @@ impl Telemetry {
}
#[cfg(any(test, feature = "test-support"))]
fn shutdown_telemetry(self: &Arc<Self>) -> impl Future<Output = ()> {
fn shutdown_telemetry(self: &Arc<Self>) -> impl Future<Output = ()> + use<> {
Task::ready(())
}

View File

@@ -171,11 +171,13 @@ impl UserStore {
_maintain_contacts: cx.spawn(async move |this, cx| {
let _subscriptions = rpc_subscriptions;
while let Some(message) = update_contacts_rx.next().await {
if let Ok(task) = this.update(cx, |this, cx| this.update_contacts(message, cx))
{
task.log_err().await;
} else {
break;
match this.update(cx, |this, cx| this.update_contacts(message, cx)) {
Ok(task) => {
task.log_err().await;
}
_ => {
break;
}
}
}
}),
@@ -191,12 +193,13 @@ impl UserStore {
match status {
Status::Connected { .. } => {
if let Some(user_id) = client.user_id() {
let fetch_user = if let Ok(fetch_user) =
this.update(cx, |this, cx| this.get_user(user_id, cx).log_err())
let fetch_user = match this
.update(cx, |this, cx| this.get_user(user_id, cx).log_err())
{
fetch_user
} else {
break;
Ok(fetch_user) => fetch_user,
_ => {
break;
}
};
let fetch_private_user_info =
client.request(proto::GetPrivateUserInfo {}).log_err();
@@ -581,7 +584,7 @@ impl UserStore {
})
}
pub fn clear_contacts(&self) -> impl Future<Output = ()> {
pub fn clear_contacts(&self) -> impl Future<Output = ()> + use<> {
let (tx, mut rx) = postage::barrier::channel();
self.update_contacts_tx
.unbounded_send(UpdateContacts::Clear(tx))
@@ -591,7 +594,7 @@ impl UserStore {
}
}
pub fn contact_updates_done(&self) -> impl Future<Output = ()> {
pub fn contact_updates_done(&self) -> impl Future<Output = ()> + use<> {
let (tx, mut rx) = postage::barrier::channel();
self.update_contacts_tx
.unbounded_send(UpdateContacts::Wait(tx))
@@ -703,8 +706,8 @@ impl UserStore {
};
let client = self.client.clone();
cx.spawn(async move |this, cx| {
if let Some(client) = client.upgrade() {
cx.spawn(async move |this, cx| match client.upgrade() {
Some(client) => {
let response = client
.request(proto::AcceptTermsOfService {})
.await
@@ -714,9 +717,8 @@ impl UserStore {
this.set_current_user_accepted_tos_at(Some(response.accepted_tos_at));
cx.emit(Event::PrivateUserInfoUpdated);
})
} else {
Err(anyhow!("client not found"))
}
_ => Err(anyhow!("client not found")),
})
}
@@ -732,15 +734,14 @@ impl UserStore {
cx: &Context<Self>,
) -> Task<Result<Vec<Arc<User>>>> {
let client = self.client.clone();
cx.spawn(async move |this, cx| {
if let Some(rpc) = client.upgrade() {
cx.spawn(async move |this, cx| match client.upgrade() {
Some(rpc) => {
let response = rpc.request(request).await.context("error loading users")?;
let users = response.users;
this.update(cx, |this, _| this.insert(users))
} else {
Ok(Vec::new())
}
_ => Ok(Vec::new()),
})
}

View File

@@ -219,13 +219,16 @@ async fn create_access_token(
let mut impersonated_user_id = None;
if let Some(impersonate) = params.impersonate {
if user.admin {
if let Some(impersonated_user) = app.db.get_user_by_github_login(&impersonate).await? {
impersonated_user_id = Some(impersonated_user.id);
} else {
return Err(Error::http(
StatusCode::UNPROCESSABLE_ENTITY,
format!("user {impersonate} does not exist"),
));
match app.db.get_user_by_github_login(&impersonate).await? {
Some(impersonated_user) => {
impersonated_user_id = Some(impersonated_user.id);
}
_ => {
return Err(Error::http(
StatusCode::UNPROCESSABLE_ENTITY,
format!("user {impersonate} does not exist"),
));
}
}
} else {
return Err(Error::http(

View File

@@ -103,8 +103,8 @@ async fn update_billing_preferences(
let max_monthly_llm_usage_spending_in_cents =
body.max_monthly_llm_usage_spending_in_cents.max(0);
let billing_preferences =
if let Some(_billing_preferences) = app.db.get_billing_preferences(user.id).await? {
let billing_preferences = match app.db.get_billing_preferences(user.id).await? {
Some(_billing_preferences) => {
app.db
.update_billing_preferences(
user.id,
@@ -115,7 +115,8 @@ async fn update_billing_preferences(
},
)
.await?
} else {
}
_ => {
app.db
.create_billing_preferences(
user.id,
@@ -124,7 +125,8 @@ async fn update_billing_preferences(
},
)
.await?
};
}
};
SnowflakeRow::new(
"Spend Limit Updated",
@@ -624,28 +626,31 @@ async fn handle_customer_event(
return Ok(());
};
if let Some(existing_customer) = app
match app
.db
.get_billing_customer_by_stripe_customer_id(&customer.id)
.await?
{
app.db
.update_billing_customer(
existing_customer.id,
&UpdateBillingCustomerParams {
// For now we just leave the information as-is, as it is not
// likely to change.
..Default::default()
},
)
.await?;
} else {
app.db
.create_billing_customer(&CreateBillingCustomerParams {
user_id: user.id,
stripe_customer_id: customer.id.to_string(),
})
.await?;
Some(existing_customer) => {
app.db
.update_billing_customer(
existing_customer.id,
&UpdateBillingCustomerParams {
// For now we just leave the information as-is, as it is not
// likely to change.
..Default::default()
},
)
.await?;
}
_ => {
app.db
.create_billing_customer(&CreateBillingCustomerParams {
user_id: user.id,
stripe_customer_id: customer.id.to_string(),
})
.await?;
}
}
Ok(())
@@ -689,72 +694,75 @@ async fn handle_customer_subscription_event(
.await?;
}
if let Some(existing_subscription) = app
match app
.db
.get_billing_subscription_by_stripe_subscription_id(&subscription.id)
.await?
{
app.db
.update_billing_subscription(
existing_subscription.id,
&UpdateBillingSubscriptionParams {
billing_customer_id: ActiveValue::set(billing_customer.id),
stripe_subscription_id: ActiveValue::set(subscription.id.to_string()),
stripe_subscription_status: ActiveValue::set(subscription.status.into()),
stripe_cancel_at: ActiveValue::set(
subscription
.cancel_at
.and_then(|cancel_at| DateTime::from_timestamp(cancel_at, 0))
.map(|time| time.naive_utc()),
),
stripe_cancellation_reason: ActiveValue::set(
subscription
.cancellation_details
.and_then(|details| details.reason)
.map(|reason| reason.into()),
),
},
)
.await?;
} else {
// If the user already has an active billing subscription, ignore the
// event and return an `Ok` to signal that it was processed
// successfully.
//
// There is the possibility that this could cause us to not create a
// subscription in the following scenario:
//
// 1. User has an active subscription A
// 2. User cancels subscription A
// 3. User creates a new subscription B
// 4. We process the new subscription B before the cancellation of subscription A
// 5. User ends up with no subscriptions
//
// In theory this situation shouldn't arise as we try to process the events in the order they occur.
if app
.db
.has_active_billing_subscription(billing_customer.user_id)
.await?
{
log::info!(
Some(existing_subscription) => {
app.db
.update_billing_subscription(
existing_subscription.id,
&UpdateBillingSubscriptionParams {
billing_customer_id: ActiveValue::set(billing_customer.id),
stripe_subscription_id: ActiveValue::set(subscription.id.to_string()),
stripe_subscription_status: ActiveValue::set(subscription.status.into()),
stripe_cancel_at: ActiveValue::set(
subscription
.cancel_at
.and_then(|cancel_at| DateTime::from_timestamp(cancel_at, 0))
.map(|time| time.naive_utc()),
),
stripe_cancellation_reason: ActiveValue::set(
subscription
.cancellation_details
.and_then(|details| details.reason)
.map(|reason| reason.into()),
),
},
)
.await?;
}
_ => {
// If the user already has an active billing subscription, ignore the
// event and return an `Ok` to signal that it was processed
// successfully.
//
// There is the possibility that this could cause us to not create a
// subscription in the following scenario:
//
// 1. User has an active subscription A
// 2. User cancels subscription A
// 3. User creates a new subscription B
// 4. We process the new subscription B before the cancellation of subscription A
// 5. User ends up with no subscriptions
//
// In theory this situation shouldn't arise as we try to process the events in the order they occur.
if app
.db
.has_active_billing_subscription(billing_customer.user_id)
.await?
{
log::info!(
"user {user_id} already has an active subscription, skipping creation of subscription {subscription_id}",
user_id = billing_customer.user_id,
subscription_id = subscription.id
);
return Ok(());
}
return Ok(());
}
app.db
.create_billing_subscription(&CreateBillingSubscriptionParams {
billing_customer_id: billing_customer.id,
stripe_subscription_id: subscription.id.to_string(),
stripe_subscription_status: subscription.status.into(),
stripe_cancellation_reason: subscription
.cancellation_details
.and_then(|details| details.reason)
.map(|reason| reason.into()),
})
.await?;
app.db
.create_billing_subscription(&CreateBillingSubscriptionParams {
billing_customer_id: billing_customer.id,
stripe_subscription_id: subscription.id.to_string(),
stripe_subscription_status: subscription.status.into(),
stripe_cancellation_reason: subscription
.cancellation_details
.and_then(|details| details.reason)
.map(|reason| reason.into()),
})
.await?;
}
}
// When the user's subscription changes, we want to refresh their LLM tokens

View File

@@ -47,10 +47,15 @@ impl IpsFile {
Some(panic_message) => format!("Panic `{}`", panic_message),
None => "Crash `Abort trap: 6` (possible panic)".into(),
}
} else if let Some(msg) = &self.body.exception.message {
format!("Exception `{}`", msg)
} else {
format!("Crash `{}`", self.body.termination.indicator)
match &self.body.exception.message {
Some(msg) => {
format!("Exception `{}`", msg)
}
_ => {
format!("Crash `{}`", self.body.termination.indicator)
}
}
};
if let Some(thread) = self.faulting_thread() {
if let Some(queue) = thread.queue.as_ref() {
@@ -81,10 +86,13 @@ impl IpsFile {
return None;
}
Some(format!("{:#}", rustc_demangle::demangle(name)))
} else if let Some(image) = self.body.used_images.get(frame.image_index) {
Some(image.name.clone().unwrap_or("<unknown-image>".into()))
} else {
Some("<unknown>".into())
match self.body.used_images.get(frame.image_index) {
Some(image) => {
Some(image.name.clone().unwrap_or("<unknown-image>".into()))
}
_ => Some("<unknown>".into()),
}
}
})
.collect::<Vec<_>>();

View File

@@ -105,47 +105,52 @@ impl Database {
let mut accept_invite_result = None;
if role.is_none() {
if let Some(invitation) = self
match self
.pending_invite_for_channel(&channel, user_id, &tx)
.await?
{
// note, this may be a parent channel
role = Some(invitation.role);
channel_member::Entity::update(channel_member::ActiveModel {
accepted: ActiveValue::Set(true),
..invitation.into_active_model()
})
.exec(&*tx)
.await?;
Some(invitation) => {
// note, this may be a parent channel
role = Some(invitation.role);
channel_member::Entity::update(channel_member::ActiveModel {
accepted: ActiveValue::Set(true),
..invitation.into_active_model()
})
.exec(&*tx)
.await?;
accept_invite_result = Some(
self.calculate_membership_updated(&channel, user_id, &tx)
.await?,
);
accept_invite_result = Some(
self.calculate_membership_updated(&channel, user_id, &tx)
.await?,
);
debug_assert!(
self.channel_role_for_user(&channel, user_id, &tx).await? == role
);
} else if channel.visibility == ChannelVisibility::Public {
role = Some(ChannelRole::Guest);
channel_member::Entity::insert(channel_member::ActiveModel {
id: ActiveValue::NotSet,
channel_id: ActiveValue::Set(channel.root_id()),
user_id: ActiveValue::Set(user_id),
accepted: ActiveValue::Set(true),
role: ActiveValue::Set(ChannelRole::Guest),
})
.exec(&*tx)
.await?;
debug_assert!(
self.channel_role_for_user(&channel, user_id, &tx).await? == role
);
}
_ => {
if channel.visibility == ChannelVisibility::Public {
role = Some(ChannelRole::Guest);
channel_member::Entity::insert(channel_member::ActiveModel {
id: ActiveValue::NotSet,
channel_id: ActiveValue::Set(channel.root_id()),
user_id: ActiveValue::Set(user_id),
accepted: ActiveValue::Set(true),
role: ActiveValue::Set(ChannelRole::Guest),
})
.exec(&*tx)
.await?;
accept_invite_result = Some(
self.calculate_membership_updated(&channel, user_id, &tx)
.await?,
);
accept_invite_result = Some(
self.calculate_membership_updated(&channel, user_id, &tx)
.await?,
);
debug_assert!(
self.channel_role_for_user(&channel, user_id, &tx).await? == role
);
debug_assert!(
self.channel_role_for_user(&channel, user_id, &tx).await? == role
);
}
}
}
}

View File

@@ -101,12 +101,15 @@ impl Database {
}
if let Some(wasm_api_version) = version.wasm_api_version.as_ref() {
if let Some(version) = SemanticVersion::from_str(wasm_api_version).log_err() {
if !constraints.wasm_api_versions.contains(&version) {
match SemanticVersion::from_str(wasm_api_version).log_err() {
Some(version) => {
if !constraints.wasm_api_versions.contains(&version) {
continue;
}
}
_ => {
continue;
}
} else {
continue;
}
}
}

View File

@@ -192,28 +192,29 @@ impl Database {
response: Option<bool>,
tx: &DatabaseTransaction,
) -> Result<Option<(UserId, proto::Notification)>> {
if let Some(id) = self
match self
.find_notification(recipient_id, notification, tx)
.await?
{
let row = notification::Entity::update(notification::ActiveModel {
id: ActiveValue::Unchanged(id),
recipient_id: ActiveValue::Unchanged(recipient_id),
is_read: ActiveValue::Set(true),
response: if let Some(response) = response {
ActiveValue::Set(Some(response))
} else {
ActiveValue::NotSet
},
..Default::default()
})
.exec(tx)
.await?;
Ok(model_to_proto(self, row)
.map(|notification| (recipient_id, notification))
.ok())
} else {
Ok(None)
Some(id) => {
let row = notification::Entity::update(notification::ActiveModel {
id: ActiveValue::Unchanged(id),
recipient_id: ActiveValue::Unchanged(recipient_id),
is_read: ActiveValue::Set(true),
response: if let Some(response) = response {
ActiveValue::Set(Some(response))
} else {
ActiveValue::NotSet
},
..Default::default()
})
.exec(tx)
.await?;
Ok(model_to_proto(self, row)
.map(|notification| (recipient_id, notification))
.ok())
}
_ => Ok(None),
}
}

View File

@@ -132,50 +132,54 @@ impl Database {
initial_channel_id: Option<ChannelId>,
tx: &DatabaseTransaction,
) -> Result<User> {
if let Some(existing_user) = self
match self
.get_user_by_github_user_id_or_github_login(github_user_id, github_login, tx)
.await?
{
let mut existing_user = existing_user.into_active_model();
existing_user.github_login = ActiveValue::set(github_login.into());
existing_user.github_user_created_at = ActiveValue::set(Some(github_user_created_at));
Some(existing_user) => {
let mut existing_user = existing_user.into_active_model();
existing_user.github_login = ActiveValue::set(github_login.into());
existing_user.github_user_created_at =
ActiveValue::set(Some(github_user_created_at));
if let Some(github_email) = github_email {
existing_user.email_address = ActiveValue::set(Some(github_email.into()));
if let Some(github_email) = github_email {
existing_user.email_address = ActiveValue::set(Some(github_email.into()));
}
if let Some(github_name) = github_name {
existing_user.name = ActiveValue::set(Some(github_name.into()));
}
Ok(existing_user.update(tx).await?)
}
if let Some(github_name) = github_name {
existing_user.name = ActiveValue::set(Some(github_name.into()));
}
Ok(existing_user.update(tx).await?)
} else {
let user = user::Entity::insert(user::ActiveModel {
email_address: ActiveValue::set(github_email.map(|email| email.into())),
name: ActiveValue::set(github_name.map(|name| name.into())),
github_login: ActiveValue::set(github_login.into()),
github_user_id: ActiveValue::set(github_user_id),
github_user_created_at: ActiveValue::set(Some(github_user_created_at)),
admin: ActiveValue::set(false),
invite_count: ActiveValue::set(0),
invite_code: ActiveValue::set(None),
metrics_id: ActiveValue::set(Uuid::new_v4()),
..Default::default()
})
.exec_with_returning(tx)
.await?;
if let Some(channel_id) = initial_channel_id {
channel_member::Entity::insert(channel_member::ActiveModel {
id: ActiveValue::NotSet,
channel_id: ActiveValue::Set(channel_id),
user_id: ActiveValue::Set(user.id),
accepted: ActiveValue::Set(true),
role: ActiveValue::Set(ChannelRole::Guest),
_ => {
let user = user::Entity::insert(user::ActiveModel {
email_address: ActiveValue::set(github_email.map(|email| email.into())),
name: ActiveValue::set(github_name.map(|name| name.into())),
github_login: ActiveValue::set(github_login.into()),
github_user_id: ActiveValue::set(github_user_id),
github_user_created_at: ActiveValue::set(Some(github_user_created_at)),
admin: ActiveValue::set(false),
invite_count: ActiveValue::set(0),
invite_code: ActiveValue::set(None),
metrics_id: ActiveValue::set(Uuid::new_v4()),
..Default::default()
})
.exec(tx)
.exec_with_returning(tx)
.await?;
if let Some(channel_id) = initial_channel_id {
channel_member::Entity::insert(channel_member::ActiveModel {
id: ActiveValue::NotSet,
channel_id: ActiveValue::Set(channel_id),
user_id: ActiveValue::Set(user.id),
accepted: ActiveValue::Set(true),
role: ActiveValue::Set(ChannelRole::Guest),
})
.exec(tx)
.await?;
}
Ok(user)
}
Ok(user)
}
}

View File

@@ -74,7 +74,7 @@ impl TestDb {
let mut rng = StdRng::from_entropy();
let url = format!(
"postgres://postgres@localhost/zed-test-{}",
rng.gen::<u128>()
rng.r#gen::<u128>()
);
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_io()

View File

@@ -101,10 +101,11 @@ async fn test_channel_buffers(db: &Arc<Database>) {
);
buffer_b.apply_ops(buffer_response_b.operations.into_iter().map(|operation| {
let operation = proto::deserialize_operation(operation).unwrap();
if let language::Operation::Buffer(operation) = operation {
operation
} else {
unreachable!()
match operation {
language::Operation::Buffer(operation) => operation,
_ => {
unreachable!()
}
}
}));

View File

@@ -20,7 +20,8 @@ pub fn get_dotenv_vars(current_dir: impl AsRef<Path>) -> Result<Vec<(String, Str
pub fn load_dotenv() -> Result<()> {
for (key, value) in get_dotenv_vars("./crates/collab")? {
std::env::set_var(key, value);
// TODO: Audit that the environment access only happens in single-threaded code.
unsafe { std::env::set_var(key, value) };
}
Ok(())
}

View File

@@ -26,7 +26,7 @@ impl Executor {
}
}
pub fn sleep(&self, duration: Duration) -> impl Future<Output = ()> {
pub fn sleep(&self, duration: Duration) -> impl Future<Output = ()> + use<> {
let this = self.clone();
async move {
match this {

View File

@@ -26,7 +26,7 @@ impl TestLlmDb {
let mut rng = StdRng::from_entropy();
let url = format!(
"postgres://postgres@localhost/zed-llm-test-{}",
rng.gen::<u128>()
rng.r#gen::<u128>()
);
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_io()

View File

@@ -716,7 +716,7 @@ impl Server {
system_id: Option<String>,
send_connection_id: Option<oneshot::Sender<ConnectionId>>,
executor: Executor,
) -> impl Future<Output = ()> {
) -> impl Future<Output = ()> + use<> {
let this = self.clone();
let span = info_span!("handle connection", %address,
connection_id=field::Empty,
@@ -810,7 +810,7 @@ impl Server {
_ = foreground_message_handlers.next() => {}
next_message = next_message => {
let (permit, message) = next_message;
if let Some(message) = message {
match message { Some(message) => {
let type_name = message.payload_type_name();
// note: we copy all the fields from the parent span so we can query them in the logs.
// (https://github.com/tokio-rs/tracing/issues/2670).
@@ -821,7 +821,7 @@ impl Server {
);
principal.update_span(&span);
let span_enter = span.enter();
if let Some(handler) = this.handlers.get(&message.payload_type_id()) {
match this.handlers.get(&message.payload_type_id()) { Some(handler) => {
let is_background = message.is_background();
let handle_message = (handler)(message, session.clone());
drop(span_enter);
@@ -835,13 +835,13 @@ impl Server {
} else {
foreground_message_handlers.push(handle_message);
}
} else {
} _ => {
tracing::error!("no message handler");
}
} else {
}}
} _ => {
tracing::info!("connection closed");
break;
}
}}
}
}
}
@@ -1313,9 +1313,8 @@ async fn join_room(
.trace_err();
}
let live_kit_connection_info = if let Some(live_kit) = session.app_state.livekit_client.as_ref()
{
live_kit
let live_kit_connection_info = match session.app_state.livekit_client.as_ref() {
Some(live_kit) => live_kit
.room_token(
&joined_room.room.livekit_room,
&session.user_id().to_string(),
@@ -1325,9 +1324,8 @@ async fn join_room(
server_url: live_kit.url().into(),
token,
can_publish: true,
})
} else {
None
}),
_ => None,
};
response.send(proto::JoinRoomResponse {
@@ -4393,23 +4391,26 @@ async fn leave_room_for_session(session: &Session, connection_id: ConnectionId)
let room;
let channel;
if let Some(mut left_room) = session.db().await.leave_room(connection_id).await? {
contacts_to_update.insert(session.user_id());
match session.db().await.leave_room(connection_id).await? {
Some(mut left_room) => {
contacts_to_update.insert(session.user_id());
for project in left_room.left_projects.values() {
project_left(project, session);
for project in left_room.left_projects.values() {
project_left(project, session);
}
room_id = RoomId::from_proto(left_room.room.id);
canceled_calls_to_user_ids = mem::take(&mut left_room.canceled_calls_to_user_ids);
livekit_room = mem::take(&mut left_room.room.livekit_room);
delete_livekit_room = left_room.deleted;
room = mem::take(&mut left_room.room);
channel = mem::take(&mut left_room.channel);
room_updated(&room, &session.peer);
}
_ => {
return Ok(());
}
room_id = RoomId::from_proto(left_room.room.id);
canceled_calls_to_user_ids = mem::take(&mut left_room.canceled_calls_to_user_ids);
livekit_room = mem::take(&mut left_room.room.livekit_room);
delete_livekit_room = left_room.deleted;
room = mem::take(&mut left_room.room);
channel = mem::take(&mut left_room.channel);
room_updated(&room, &session.peer);
} else {
return Ok(());
}
if let Some(channel) = channel {

View File

@@ -130,74 +130,76 @@ impl StripeBilling {
}
let mut state = self.state.write().await;
let meter = if let Some(meter) = state.meters_by_event_name.get(meter_event_name) {
meter.clone()
} else {
let meter = StripeMeter::create(
&self.client,
StripeCreateMeterParams {
default_aggregation: DefaultAggregation { formula: "sum" },
display_name: price_description.to_string(),
event_name: meter_event_name,
},
)
.await?;
state
.meters_by_event_name
.insert(meter_event_name.to_string(), meter.clone());
meter
let meter = match state.meters_by_event_name.get(meter_event_name) {
Some(meter) => meter.clone(),
_ => {
let meter = StripeMeter::create(
&self.client,
StripeCreateMeterParams {
default_aggregation: DefaultAggregation { formula: "sum" },
display_name: price_description.to_string(),
event_name: meter_event_name,
},
)
.await?;
state
.meters_by_event_name
.insert(meter_event_name.to_string(), meter.clone());
meter
}
};
let price_id = if let Some(price_id) = state.price_ids_by_meter_id.get(&meter.id) {
price_id.clone()
} else {
let price = stripe::Price::create(
&self.client,
stripe::CreatePrice {
active: Some(true),
billing_scheme: Some(stripe::PriceBillingScheme::PerUnit),
currency: stripe::Currency::USD,
currency_options: None,
custom_unit_amount: None,
expand: &[],
lookup_key: None,
metadata: None,
nickname: None,
product: None,
product_data: Some(stripe::CreatePriceProductData {
id: None,
let price_id = match state.price_ids_by_meter_id.get(&meter.id) {
Some(price_id) => price_id.clone(),
_ => {
let price = stripe::Price::create(
&self.client,
stripe::CreatePrice {
active: Some(true),
billing_scheme: Some(stripe::PriceBillingScheme::PerUnit),
currency: stripe::Currency::USD,
currency_options: None,
custom_unit_amount: None,
expand: &[],
lookup_key: None,
metadata: None,
name: price_description.to_string(),
statement_descriptor: None,
tax_code: None,
unit_label: None,
}),
recurring: Some(stripe::CreatePriceRecurring {
aggregate_usage: None,
interval: stripe::CreatePriceRecurringInterval::Month,
interval_count: None,
trial_period_days: None,
usage_type: Some(stripe::CreatePriceRecurringUsageType::Metered),
meter: Some(meter.id.clone()),
}),
tax_behavior: None,
tiers: None,
tiers_mode: None,
transfer_lookup_key: None,
transform_quantity: None,
unit_amount: None,
unit_amount_decimal: Some(&format!(
"{:.12}",
price_per_million_tokens.0 as f64 / 1_000_000f64
)),
},
)
.await?;
state
.price_ids_by_meter_id
.insert(meter.id, price.id.clone());
price.id
nickname: None,
product: None,
product_data: Some(stripe::CreatePriceProductData {
id: None,
active: Some(true),
metadata: None,
name: price_description.to_string(),
statement_descriptor: None,
tax_code: None,
unit_label: None,
}),
recurring: Some(stripe::CreatePriceRecurring {
aggregate_usage: None,
interval: stripe::CreatePriceRecurringInterval::Month,
interval_count: None,
trial_period_days: None,
usage_type: Some(stripe::CreatePriceRecurringUsageType::Metered),
meter: Some(meter.id.clone()),
}),
tax_behavior: None,
tiers: None,
tiers_mode: None,
transfer_lookup_key: None,
transform_quantity: None,
unit_amount: None,
unit_amount_decimal: Some(&format!(
"{:.12}",
price_per_million_tokens.0 as f64 / 1_000_000f64
)),
},
)
.await?;
state
.price_ids_by_meter_id
.insert(meter.id, price.id.clone());
price.id
}
};
Ok(StripeBillingPrice {

View File

@@ -5602,7 +5602,7 @@ async fn test_open_buffer_while_getting_definition_pointing_to_it(
let definitions;
let buffer_b2;
if rng.gen() {
if rng.r#gen() {
definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
(buffer_b2, _) = project_b
.update(cx_b, |p, cx| {

View File

@@ -216,45 +216,47 @@ impl RandomizedTest for ProjectCollaborationTest {
// Open a new project
0..=70 => {
// Open a remote project
if let Some(room) = call.read_with(cx, |call, _| call.room().cloned()) {
let existing_dev_server_project_ids = cx.read(|cx| {
client
.dev_server_projects()
.iter()
.map(|p| p.read(cx).remote_id().unwrap())
.collect::<Vec<_>>()
});
let new_dev_server_projects = room.read_with(cx, |room, _| {
room.remote_participants()
.values()
.flat_map(|participant| {
participant.projects.iter().filter_map(|project| {
if existing_dev_server_project_ids.contains(&project.id)
{
None
} else {
Some((
UserId::from_proto(participant.user.id),
project.worktree_root_names[0].clone(),
))
}
match call.read_with(cx, |call, _| call.room().cloned()) {
Some(room) => {
let existing_dev_server_project_ids = cx.read(|cx| {
client
.dev_server_projects()
.iter()
.map(|p| p.read(cx).remote_id().unwrap())
.collect::<Vec<_>>()
});
let new_dev_server_projects = room.read_with(cx, |room, _| {
room.remote_participants()
.values()
.flat_map(|participant| {
participant.projects.iter().filter_map(|project| {
if existing_dev_server_project_ids
.contains(&project.id)
{
None
} else {
Some((
UserId::from_proto(participant.user.id),
project.worktree_root_names[0].clone(),
))
}
})
})
})
.collect::<Vec<_>>()
});
if !new_dev_server_projects.is_empty() {
let (host_id, first_root_name) =
new_dev_server_projects.choose(rng).unwrap().clone();
break ClientOperation::OpenRemoteProject {
host_id,
first_root_name,
};
.collect::<Vec<_>>()
});
if !new_dev_server_projects.is_empty() {
let (host_id, first_root_name) =
new_dev_server_projects.choose(rng).unwrap().clone();
break ClientOperation::OpenRemoteProject {
host_id,
first_root_name,
};
}
}
_ => {
let first_root_name = plan.next_root_dir_name();
break ClientOperation::OpenLocalProject { first_root_name };
}
}
// Open a local project
else {
let first_root_name = plan.next_root_dir_name();
break ClientOperation::OpenLocalProject { first_root_name };
}
}
@@ -279,7 +281,7 @@ impl RandomizedTest for ProjectCollaborationTest {
let project_root_name = root_name_for_project(&project, cx);
let mut paths = client.fs().paths(false);
paths.remove(0);
let new_root_path = if paths.is_empty() || rng.gen() {
let new_root_path = if paths.is_empty() || rng.r#gen() {
Path::new(path!("/")).join(plan.next_root_dir_name())
} else {
paths.choose(rng).unwrap().clone()
@@ -309,7 +311,7 @@ impl RandomizedTest for ProjectCollaborationTest {
.choose(rng)
});
let Some(worktree) = worktree else { continue };
let is_dir = rng.gen::<bool>();
let is_dir = rng.r#gen::<bool>();
let mut full_path =
worktree.read_with(cx, |w, _| PathBuf::from(w.root_name()));
full_path.push(gen_file_name(rng));
@@ -387,7 +389,7 @@ impl RandomizedTest for ProjectCollaborationTest {
language::Bias::Left,
)
});
let detach = rng.gen();
let detach = rng.r#gen();
break ClientOperation::RequestLspDataInBuffer {
project_root_name,
full_path,
@@ -460,7 +462,7 @@ impl RandomizedTest for ProjectCollaborationTest {
// Create or update a file or directory
96.. => {
let is_dir = rng.gen::<bool>();
let is_dir = rng.r#gen::<bool>();
let content;
let mut path;
let dir_paths = client.fs().directories(false);
@@ -1270,12 +1272,14 @@ impl RandomizedTest for ProjectCollaborationTest {
Some((client.user_id().unwrap(), project, cx))
});
let (host_user_id, host_project, host_cx) =
if let Some((host_user_id, host_project, host_cx)) = host_project {
let (host_user_id, host_project, host_cx) = match host_project {
Some((host_user_id, host_project, host_cx)) => {
(host_user_id, host_project, host_cx)
} else {
}
_ => {
continue;
};
}
};
for guest_buffer in guest_buffers {
let buffer_id =

View File

@@ -175,26 +175,26 @@ impl TestServer {
let clock = Arc::new(FakeSystemClock::new());
let http = FakeHttpClient::with_404_response();
let user_id = if let Ok(Some(user)) = self.app_state.db.get_user_by_github_login(name).await
{
user.id
} else {
let github_user_id = self.next_github_user_id;
self.next_github_user_id += 1;
self.app_state
.db
.create_user(
&format!("{name}@example.com"),
None,
false,
NewUserParams {
github_login: name.into(),
github_user_id,
},
)
.await
.expect("creating user failed")
.user_id
let user_id = match self.app_state.db.get_user_by_github_login(name).await {
Ok(Some(user)) => user.id,
_ => {
let github_user_id = self.next_github_user_id;
self.next_github_user_id += 1;
self.app_state
.db
.create_user(
&format!("{name}@example.com"),
None,
false,
NewUserParams {
github_login: name.into(),
github_user_id,
},
)
.await
.expect("creating user failed")
.user_id
}
};
let client_name = name.to_string();
let mut client = cx.update(|cx| Client::new(clock, http.clone(), cx));
@@ -660,7 +660,7 @@ impl TestClient {
pub fn buffers_for_project<'a>(
&'a self,
project: &Entity<Project>,
) -> impl DerefMut<Target = HashSet<Entity<language::Buffer>>> + 'a {
) -> impl DerefMut<Target = HashSet<Entity<language::Buffer>>> + 'a + use<'a> {
RefMut::map(self.state.borrow_mut(), |state| {
state.buffers.entry(project.clone()).or_default()
})

View File

@@ -556,12 +556,9 @@ impl FollowableItem for ChannelView {
Some(proto::view::Variant::ChannelView(
proto::view::ChannelView {
channel_id: channel_buffer.channel_id.0,
editor: if let Some(proto::view::Variant::Editor(proto)) =
self.editor.read(cx).to_state_proto(window, cx)
{
Some(proto)
} else {
None
editor: match self.editor.read(cx).to_state_proto(window, cx) {
Some(proto::view::Variant::Editor(proto)) => Some(proto),
_ => None,
},
},
))

View File

@@ -102,14 +102,11 @@ impl ChatPanel {
0,
gpui::ListAlignment::Bottom,
px(1000.),
move |ix, window, cx| {
if let Some(entity) = entity.upgrade() {
entity.update(cx, |this: &mut Self, cx| {
this.render_message(ix, window, cx).into_any_element()
})
} else {
div().into_any()
}
move |ix, window, cx| match entity.upgrade() {
Some(entity) => entity.update(cx, |this: &mut Self, cx| {
this.render_message(ix, window, cx).into_any_element()
}),
_ => div().into_any(),
},
);
@@ -200,15 +197,14 @@ impl ChatPanel {
cx: AsyncWindowContext,
) -> Task<Result<Entity<Self>>> {
cx.spawn(async move |cx| {
let serialized_panel = if let Some(panel) = cx
let serialized_panel = match cx
.background_spawn(async move { KEY_VALUE_STORE.read_kvp(CHAT_PANEL_KEY) })
.await
.log_err()
.flatten()
{
Some(serde_json::from_str::<SerializedChatPanel>(&panel)?)
} else {
None
Some(panel) => Some(serde_json::from_str::<SerializedChatPanel>(&panel)?),
_ => None,
};
workspace.update_in(cx, |workspace, window, cx| {
@@ -314,7 +310,7 @@ impl ChatPanel {
message_id: Option<ChannelMessageId>,
reply_to_message: &Option<ChannelMessage>,
cx: &mut Context<Self>,
) -> impl IntoElement {
) -> impl IntoElement + use<> {
let reply_to_message = match reply_to_message {
None => {
return div().child(
@@ -393,7 +389,7 @@ impl ChatPanel {
ix: usize,
window: &mut Window,
cx: &mut Context<Self>,
) -> impl IntoElement {
) -> impl IntoElement + use<> {
let active_chat = &self.active_chat.as_ref().unwrap().0;
let (message, is_continuation_from_previous, is_admin) =
active_chat.update(cx, |active_chat, cx| {
@@ -812,22 +808,30 @@ impl ChatPanel {
.message_editor
.update(cx, |editor, cx| editor.take_message(window, cx));
if let Some(id) = self.message_editor.read(cx).edit_message_id() {
self.message_editor.update(cx, |editor, _| {
editor.clear_edit_message_id();
});
match self.message_editor.read(cx).edit_message_id() {
Some(id) => {
self.message_editor.update(cx, |editor, _| {
editor.clear_edit_message_id();
});
if let Some(task) = chat
.update(cx, |chat, cx| chat.update_message(id, message, cx))
.log_err()
{
task.detach();
if let Some(task) = chat
.update(cx, |chat, cx| chat.update_message(id, message, cx))
.log_err()
{
task.detach();
}
}
_ => {
match chat
.update(cx, |chat, cx| chat.send_message(message, cx))
.log_err()
{
Some(task) => {
task.detach();
}
_ => {}
}
}
} else if let Some(task) = chat
.update(cx, |chat, cx| chat.send_message(message, cx))
.log_err()
{
task.detach();
}
}
}

View File

@@ -244,12 +244,11 @@ impl CollabPanel {
0,
gpui::ListAlignment::Top,
px(1000.),
move |ix, window, cx| {
if let Some(entity) = entity.upgrade() {
move |ix, window, cx| match entity.upgrade() {
Some(entity) => {
entity.update(cx, |this, cx| this.render_list_entry(ix, window, cx))
} else {
div().into_any()
}
_ => div().into_any(),
},
);
@@ -878,7 +877,7 @@ impl CollabPanel {
is_selected: bool,
window: &mut Window,
cx: &mut Context<Self>,
) -> impl IntoElement {
) -> impl IntoElement + use<> {
let project_name: SharedString = if worktree_root_names.is_empty() {
"untitled".to_string()
} else {
@@ -919,7 +918,7 @@ impl CollabPanel {
is_selected: bool,
window: &mut Window,
cx: &mut Context<Self>,
) -> impl IntoElement {
) -> impl IntoElement + use<> {
let id = peer_id.map_or(usize::MAX, |id| id.as_u64() as usize);
ListItem::new(("screen", id))
@@ -960,7 +959,7 @@ impl CollabPanel {
is_selected: bool,
window: &mut Window,
cx: &mut Context<Self>,
) -> impl IntoElement {
) -> impl IntoElement + use<> {
let channel_store = self.channel_store.read(cx);
let has_channel_buffer_changed = channel_store.has_channel_buffer_changed(channel_id);
ListItem::new("channel-notes")
@@ -993,7 +992,7 @@ impl CollabPanel {
is_selected: bool,
window: &mut Window,
cx: &mut Context<Self>,
) -> impl IntoElement {
) -> impl IntoElement + use<> {
let channel_store = self.channel_store.read(cx);
let has_messages_notification = channel_store.has_new_messages(channel_id);
ListItem::new("channel-chat")
@@ -2278,7 +2277,7 @@ impl CollabPanel {
&self,
editor: &Entity<Editor>,
cx: &mut Context<Self>,
) -> impl IntoElement {
) -> impl IntoElement + use<> {
let settings = ThemeSettings::get_global(cx);
let text_style = TextStyle {
color: if editor.read(cx).read_only(cx) {
@@ -2312,7 +2311,7 @@ impl CollabPanel {
is_selected: bool,
is_collapsed: bool,
cx: &mut Context<Self>,
) -> impl IntoElement {
) -> impl IntoElement + use<> {
let mut channel_link = None;
let mut channel_tooltip_text = None;
let mut channel_icon = None;
@@ -2411,7 +2410,7 @@ impl CollabPanel {
calling: bool,
is_selected: bool,
cx: &mut Context<Self>,
) -> impl IntoElement {
) -> impl IntoElement + use<> {
let online = contact.online;
let busy = contact.busy || calling;
let github_login = SharedString::from(contact.user.github_login.clone());
@@ -2492,7 +2491,7 @@ impl CollabPanel {
is_incoming: bool,
is_selected: bool,
cx: &mut Context<Self>,
) -> impl IntoElement {
) -> impl IntoElement + use<> {
let github_login = SharedString::from(user.github_login.clone());
let user_id = user.id;
let is_response_pending = self.user_store.read(cx).is_contact_request_pending(user);
@@ -2605,7 +2604,7 @@ impl CollabPanel {
is_selected: bool,
ix: usize,
cx: &mut Context<Self>,
) -> impl IntoElement {
) -> impl IntoElement + use<> {
let channel_id = channel.id;
let is_active = maybe!({
@@ -2803,7 +2802,7 @@ impl CollabPanel {
depth: usize,
_window: &mut Window,
_cx: &mut Context<Self>,
) -> impl IntoElement {
) -> impl IntoElement + use<> {
let item = ListItem::new("channel-editor")
.inset(false)
// Add one level of depth for the disclosure arrow.
@@ -2832,7 +2831,7 @@ fn render_tree_branch(
overdraw: bool,
window: &mut Window,
cx: &mut App,
) -> impl IntoElement {
) -> impl IntoElement + use<> {
let rem_size = window.rem_size();
let line_height = window.text_style().line_height_in_pixels(rem_size);
let width = rem_size * 1.5;

View File

@@ -421,20 +421,17 @@ impl PickerDelegate for ChannelModalDelegate {
el.child(IconButton::new("ellipsis", IconName::Ellipsis))
})
.when(is_me, |el| el.child(Label::new("You").color(Color::Muted)))
.children(
if let (Some((menu, _)), true) = (&self.context_menu, selected) {
Some(
deferred(
anchored()
.anchor(gpui::Corner::TopRight)
.child(menu.clone()),
)
.with_priority(1),
.children(match (&self.context_menu, selected) {
(Some((menu, _)), true) => Some(
deferred(
anchored()
.anchor(gpui::Corner::TopRight)
.child(menu.clone()),
)
} else {
None
},
),
.with_priority(1),
),
_ => None,
}),
Mode::InviteMembers => match request_status {
Some(proto::channel_member::Kind::Invitee) => {
slot.children(Some(Label::new("Invited")))

View File

@@ -182,15 +182,14 @@ impl NotificationPanel {
cx: AsyncWindowContext,
) -> Task<Result<Entity<Self>>> {
cx.spawn(async move |cx| {
let serialized_panel = if let Some(panel) = cx
let serialized_panel = match cx
.background_spawn(async move { KEY_VALUE_STORE.read_kvp(NOTIFICATION_PANEL_KEY) })
.await
.log_err()
.flatten()
{
Some(serde_json::from_str::<SerializedNotificationPanel>(&panel)?)
} else {
None
Some(panel) => Some(serde_json::from_str::<SerializedNotificationPanel>(&panel)?),
_ => None,
};
workspace.update_in(cx, |workspace, window, cx| {
@@ -494,14 +493,15 @@ impl NotificationPanel {
if let Notification::ChannelMessageMention { channel_id, .. } = &notification {
if let Some(workspace) = self.workspace.upgrade() {
return if let Some(panel) = workspace.read(cx).panel::<ChatPanel>(cx) {
let panel = panel.read(cx);
panel.is_scrolled_to_bottom()
&& panel
.active_chat()
.map_or(false, |chat| chat.read(cx).channel_id.0 == *channel_id)
} else {
false
return match workspace.read(cx).panel::<ChatPanel>(cx) {
Some(panel) => {
let panel = panel.read(cx);
panel.is_scrolled_to_bottom()
&& panel
.active_chat()
.map_or(false, |chat| chat.read(cx).channel_id.0 == *channel_id)
}
_ => false,
};
}
}

View File

@@ -133,10 +133,9 @@ impl CommandPaletteInterceptor {
/// Intercepts the given query from the command palette.
pub fn intercept(&self, query: &str, cx: &App) -> Vec<CommandInterceptResult> {
if let Some(handler) = self.0.as_ref() {
(handler)(query, cx)
} else {
Vec::new()
match self.0.as_ref() {
Some(handler) => (handler)(query, cx),
_ => Vec::new(),
}
}

View File

@@ -158,7 +158,7 @@ pub fn components() -> AllComponents {
let data = COMPONENT_DATA.read();
let mut all_components = AllComponents::new();
for (ref scope, name, description) in &data.components {
for (scope, name, description) in &data.components {
let preview = data.previews.get(name).cloned();
let component_name = SharedString::new_static(name);
let id = ComponentId(name);

View File

@@ -244,7 +244,7 @@ impl ComponentPreview {
ix: usize,
entry: &PreviewEntry,
cx: &Context<Self>,
) -> impl IntoElement {
) -> impl IntoElement + use<> {
match entry {
PreviewEntry::Component(component_metadata) => {
let id = component_metadata.id();
@@ -318,7 +318,7 @@ impl ComponentPreview {
title: SharedString,
_window: &Window,
_cx: &App,
) -> impl IntoElement {
) -> impl IntoElement + use<> {
h_flex()
.w_full()
.h_10()
@@ -332,7 +332,7 @@ impl ComponentPreview {
component: &ComponentMetadata,
window: &mut Window,
cx: &mut App,
) -> impl IntoElement {
) -> impl IntoElement + use<> {
let name = component.name();
let scope = component.scope();
@@ -379,7 +379,7 @@ impl ComponentPreview {
.into_any_element()
}
fn render_all_components(&self) -> impl IntoElement {
fn render_all_components(&self) -> impl IntoElement + use<> {
v_flex()
.id("component-list")
.px_8()
@@ -397,7 +397,7 @@ impl ComponentPreview {
component_id: &ComponentId,
window: &mut Window,
cx: &mut Context<Self>,
) -> impl IntoElement {
) -> impl IntoElement + use<> {
let component = self.component_map.get(&component_id);
if let Some(component) = component {

View File

@@ -62,10 +62,9 @@ pub struct Client {
pub struct ContextServerId(pub Arc<str>);
fn is_null_value<T: Serialize>(value: &T) -> bool {
if let Ok(Value::Null) = serde_json::to_value(value) {
true
} else {
false
match serde_json::to_value(value) {
Ok(Value::Null) => true,
_ => false,
}
}
@@ -232,10 +231,17 @@ impl Client {
handler(Ok(message.to_string()));
}
}
} else if let Ok(notification) = serde_json::from_str::<AnyNotification>(&message) {
let mut notification_handlers = notification_handlers.lock();
if let Some(handler) = notification_handlers.get_mut(notification.method.as_str()) {
handler(notification.params.unwrap_or(Value::Null), cx.clone());
} else {
match serde_json::from_str::<AnyNotification>(&message) {
Ok(notification) => {
let mut notification_handlers = notification_handlers.lock();
if let Some(handler) =
notification_handlers.get_mut(notification.method.as_str())
{
handler(notification.params.unwrap_or(Value::Null), cx.clone());
}
}
_ => {}
}
}
}

View File

@@ -77,47 +77,47 @@ impl Tool for ContextServerTool {
_action_log: Entity<ActionLog>,
cx: &mut App,
) -> Task<Result<String>> {
if let Some(server) = self.server_manager.read(cx).get_server(&self.server_id) {
let tool_name = self.tool.name.clone();
let server_clone = server.clone();
let input_clone = input.clone();
match self.server_manager.read(cx).get_server(&self.server_id) {
Some(server) => {
let tool_name = self.tool.name.clone();
let server_clone = server.clone();
let input_clone = input.clone();
cx.spawn(async move |_cx| {
let Some(protocol) = server_clone.client() else {
bail!("Context server not initialized");
};
cx.spawn(async move |_cx| {
let Some(protocol) = server_clone.client() else {
bail!("Context server not initialized");
};
let arguments = if let serde_json::Value::Object(map) = input_clone {
Some(map.into_iter().collect())
} else {
None
};
let arguments = match input_clone {
serde_json::Value::Object(map) => Some(map.into_iter().collect()),
_ => None,
};
log::trace!(
"Running tool: {} with arguments: {:?}",
tool_name,
arguments
);
let response = protocol.run_tool(tool_name, arguments).await?;
log::trace!(
"Running tool: {} with arguments: {:?}",
tool_name,
arguments
);
let response = protocol.run_tool(tool_name, arguments).await?;
let mut result = String::new();
for content in response.content {
match content {
types::ToolResponseContent::Text { text } => {
result.push_str(&text);
}
types::ToolResponseContent::Image { .. } => {
log::warn!("Ignoring image content from tool response");
}
types::ToolResponseContent::Resource { .. } => {
log::warn!("Ignoring resource content from tool response");
let mut result = String::new();
for content in response.content {
match content {
types::ToolResponseContent::Text { text } => {
result.push_str(&text);
}
types::ToolResponseContent::Image { .. } => {
log::warn!("Ignoring image content from tool response");
}
types::ToolResponseContent::Resource { .. } => {
log::warn!("Ignoring resource content from tool response");
}
}
}
}
Ok(result)
})
} else {
Task::ready(Err(anyhow!("Context server not found")))
Ok(result)
})
}
_ => Task::ready(Err(anyhow!("Context server not found"))),
}
}
}

View File

@@ -2,7 +2,7 @@ use std::sync::Arc;
use collections::HashMap;
use gpui::App;
use schemars::gen::SchemaGenerator;
use schemars::r#gen::SchemaGenerator;
use schemars::schema::{InstanceType, Schema, SchemaObject};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

View File

@@ -348,7 +348,10 @@ impl Copilot {
this
}
fn shutdown_language_server(&mut self, _cx: &mut Context<Self>) -> impl Future<Output = ()> {
fn shutdown_language_server(
&mut self,
_cx: &mut Context<Self>,
) -> impl Future<Output = ()> + use<> {
let shutdown = match mem::replace(&mut self.server, CopilotServer::Disabled) {
CopilotServer::Running(server) => Some(Box::pin(async move { server.lsp.shutdown() })),
_ => None,
@@ -553,24 +556,27 @@ impl Copilot {
}
pub fn sign_in(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
if let CopilotServer::Running(server) = &mut self.server {
let task = match &server.sign_in_status {
SignInStatus::Authorized { .. } => Task::ready(Ok(())).shared(),
SignInStatus::SigningIn { task, .. } => {
cx.notify();
task.clone()
}
SignInStatus::SignedOut { .. } | SignInStatus::Unauthorized { .. } => {
let lsp = server.lsp.clone();
let task = cx
.spawn(async move |this, cx| {
let sign_in = async {
let sign_in = lsp
.request::<request::SignInInitiate>(
request::SignInInitiateParams {},
)
.await?;
match sign_in {
match &mut self.server {
CopilotServer::Running(server) => {
let task =
match &server.sign_in_status {
SignInStatus::Authorized { .. } => Task::ready(Ok(())).shared(),
SignInStatus::SigningIn { task, .. } => {
cx.notify();
task.clone()
}
SignInStatus::SignedOut { .. } | SignInStatus::Unauthorized { .. } => {
let lsp = server.lsp.clone();
let task = cx
.spawn(async move |this, cx| {
let sign_in =
async {
let sign_in = lsp
.request::<request::SignInInitiate>(
request::SignInInitiateParams {},
)
.await?;
match sign_in {
request::SignInInitiateResult::AlreadySignedIn { user } => {
Ok(request::SignInStatus::Ok { user: Some(user) })
}
@@ -601,38 +607,40 @@ impl Copilot {
Ok(response)
}
}
};
let sign_in = sign_in.await;
this.update(cx, |this, cx| match sign_in {
Ok(status) => {
this.update_sign_in_status(status, cx);
Ok(())
}
Err(error) => {
this.update_sign_in_status(
request::SignInStatus::NotSignedIn,
cx,
);
Err(Arc::new(error))
}
})?
})
.shared();
server.sign_in_status = SignInStatus::SigningIn {
prompt: None,
task: task.clone(),
};
let sign_in = sign_in.await;
this.update(cx, |this, cx| match sign_in {
Ok(status) => {
this.update_sign_in_status(status, cx);
Ok(())
}
Err(error) => {
this.update_sign_in_status(
request::SignInStatus::NotSignedIn,
cx,
);
Err(Arc::new(error))
}
})?
})
.shared();
server.sign_in_status = SignInStatus::SigningIn {
prompt: None,
task: task.clone(),
cx.notify();
task
}
};
cx.notify();
task
}
};
cx.background_spawn(task.map_err(|err| anyhow!("{:?}", err)))
} else {
// If we're downloading, wait until download is finished
// If we're in a stuck state, display to the user
Task::ready(Err(anyhow!("copilot hasn't started yet")))
cx.background_spawn(task.map_err(|err| anyhow!("{:?}", err)))
}
_ => {
// If we're downloading, wait until download is finished
// If we're in a stuck state, display to the user
Task::ready(Err(anyhow!("copilot hasn't started yet")))
}
}
}
@@ -680,10 +688,9 @@ impl Copilot {
}
pub fn language_server(&self) -> Option<&Arc<LanguageServer>> {
if let CopilotServer::Running(server) = &self.server {
Some(&server.lsp)
} else {
None
match &self.server {
CopilotServer::Running(server) => Some(&server.lsp),
_ => None,
}
}

View File

@@ -1078,7 +1078,7 @@ mod tests {
cx: &mut EditorLspTestContext,
marked_string: &str,
completions: Vec<&'static str>,
) -> impl Future<Output = ()> {
) -> impl Future<Output = ()> + use<> {
let complete_from_marker: TextRangeMarker = '|'.into();
let replace_range_marker: TextRangeMarker = ('<', '>').into();
let (_, mut marked_ranges) = marked_text_ranges_by(

View File

@@ -139,7 +139,10 @@ impl CopilotCodeVerification {
cx.notify();
}
fn render_device_code(data: &PromptUserDeviceFlow, cx: &mut Context<Self>) -> impl IntoElement {
fn render_device_code(
data: &PromptUserDeviceFlow,
cx: &mut Context<Self>,
) -> impl IntoElement + use<> {
let copied = cx
.read_from_clipboard()
.map(|item| item.text().as_ref() == Some(&data.user_code))
@@ -172,7 +175,7 @@ impl CopilotCodeVerification {
data: &PromptUserDeviceFlow,
cx: &mut Context<Self>,
) -> impl Element {
) -> impl Element + use<> {
let connect_button_label = if connect_clicked {
"Waiting for connection..."
} else {
@@ -213,7 +216,7 @@ impl CopilotCodeVerification {
)
}
fn render_enabled_modal(cx: &mut Context<Self>) -> impl Element {
fn render_enabled_modal(cx: &mut Context<Self>) -> impl Element + use<> {
v_flex()
.gap_2()
.child(Headline::new("Copilot Enabled!").size(HeadlineSize::Large))
@@ -227,7 +230,7 @@ impl CopilotCodeVerification {
)
}
fn render_unauthorized_modal(cx: &mut Context<Self>) -> impl Element {
fn render_unauthorized_modal(cx: &mut Context<Self>) -> impl Element + use<> {
v_flex()
.child(Headline::new("You must have an active GitHub Copilot subscription.").size(HeadlineSize::Large))
@@ -246,7 +249,7 @@ impl CopilotCodeVerification {
)
}
fn render_loading(window: &mut Window, _: &mut Context<Self>) -> impl Element {
fn render_loading(window: &mut Window, _: &mut Context<Self>) -> impl Element + use<> {
let loading_icon = svg()
.size_8()
.path(IconName::ArrowCircle.path())

View File

@@ -222,13 +222,12 @@ impl TransportDelegate {
}
pub(crate) async fn send_message(&self, message: Message) -> Result<()> {
if let Some(server_tx) = self.server_tx.lock().await.as_ref() {
server_tx
match self.server_tx.lock().await.as_ref() {
Some(server_tx) => server_tx
.send(message)
.await
.map_err(|e| anyhow!("Failed to send message: {}", e))
} else {
Err(anyhow!("Server tx already dropped"))
.map_err(|e| anyhow!("Failed to send message: {}", e)),
_ => Err(anyhow!("Server tx already dropped")),
}
}
@@ -343,12 +342,15 @@ impl TransportDelegate {
match message {
Ok(Message::Response(res)) => {
if let Some(tx) = pending_requests.lock().await.remove(&res.request_seq) {
if let Err(e) = tx.send(Self::process_response(res)) {
log::trace!("Did not send response `{:?}` for a cancelled", e);
match pending_requests.lock().await.remove(&res.request_seq) {
Some(tx) => {
if let Err(e) = tx.send(Self::process_response(res)) {
log::trace!("Did not send response `{:?}` for a cancelled", e);
}
}
_ => {
client_tx.send(Message::Response(res)).await?;
}
} else {
client_tx.send(Message::Response(res)).await?;
};
}
Ok(message) => {
@@ -816,22 +818,25 @@ impl FakeTransport {
.unwrap();
writer.flush().await.unwrap();
} else {
if let Some(handle) = request_handlers
match request_handlers
.lock()
.await
.get_mut(request.command.as_str())
{
handle(
request.seq,
request.arguments.unwrap_or(json!({})),
stdout_writer.clone(),
)
.await;
} else {
log::error!(
"No request handler for {}",
request.command
);
Some(handle) => {
handle(
request.seq,
request.arguments.unwrap_or(json!({})),
stdout_writer.clone(),
)
.await;
}
_ => {
log::error!(
"No request handler for {}",
request.command
);
}
}
}
}
@@ -850,14 +855,20 @@ impl FakeTransport {
writer.flush().await.unwrap();
}
Message::Response(response) => {
if let Some(handle) = response_handlers
match response_handlers
.lock()
.await
.get(response.command.as_str())
{
handle(response);
} else {
log::error!("No response handler for {}", response.command);
Some(handle) => {
handle(response);
}
_ => {
log::error!(
"No response handler for {}",
response.command
);
}
}
}
}

View File

@@ -94,17 +94,16 @@ impl DebugAdapter for PythonDebugAdapter {
)
.await;
let python_path = if let Some(toolchain) = toolchain {
Some(toolchain.path.to_string())
} else {
BINARY_NAMES
let python_path = match toolchain {
Some(toolchain) => Some(toolchain.path.to_string()),
_ => BINARY_NAMES
.iter()
.filter_map(|cmd| {
delegate
.which(OsStr::new(cmd))
.map(|path| path.to_string_lossy().to_string())
})
.find(|_| true)
.find(|_| true),
};
Ok(DebugAdapterBinary {

View File

@@ -112,7 +112,7 @@ pub async fn open_test_db<M: Migrator>(db_name: &str) -> ThreadSafeConnection<M>
/// Implements a basic DB wrapper for a given domain
#[macro_export]
macro_rules! define_connection {
(pub static ref $id:ident: $t:ident<()> = $migrations:expr; $($global:ident)?) => {
(pub static ref $id:ident: $t:ident<()> = $migrations:expr_2021; $($global:ident)?) => {
pub struct $t($crate::sqlez::thread_safe_connection::ThreadSafeConnection<$t>);
impl ::std::ops::Deref for $t {
@@ -149,7 +149,7 @@ macro_rules! define_connection {
$t($crate::smol::block_on($crate::open_db(db_dir, scope)))
});
};
(pub static ref $id:ident: $t:ident<$($d:ty),+> = $migrations:expr; $($global:ident)?) => {
(pub static ref $id:ident: $t:ident<$($d:ty),+> = $migrations:expr_2021; $($global:ident)?) => {
pub struct $t($crate::sqlez::thread_safe_connection::ThreadSafeConnection<( $($d),+, $t )>);
impl ::std::ops::Deref for $t {

View File

@@ -284,7 +284,7 @@ impl Render for InertState {
}
impl InertState {
fn render_editor(editor: &Entity<Editor>, cx: &Context<Self>) -> impl IntoElement {
fn render_editor(editor: &Entity<Editor>, cx: &Context<Self>) -> impl IntoElement + use<> {
let settings = ThemeSettings::get_global(cx);
let text_style = TextStyle {
color: cx.theme().colors().text,

View File

@@ -153,7 +153,7 @@ impl Console {
});
}
fn render_console(&self, cx: &Context<Self>) -> impl IntoElement {
fn render_console(&self, cx: &Context<Self>) -> impl IntoElement + use<> {
let settings = ThemeSettings::get_global(cx);
let text_style = TextStyle {
color: if self.console.read(cx).read_only(cx) {
@@ -180,7 +180,7 @@ impl Console {
)
}
fn render_query_bar(&self, cx: &Context<Self>) -> impl IntoElement {
fn render_query_bar(&self, cx: &Context<Self>) -> impl IntoElement + use<> {
let settings = ThemeSettings::get_global(cx);
let text_style = TextStyle {
color: if self.console.read(cx).read_only(cx) {

View File

@@ -807,14 +807,16 @@ impl VariableList {
)
.when(!dap.value.is_empty(), |this| {
this.child(div().w_full().id(variable.item_value_id()).map(|this| {
if let Some((_, editor)) = self
match self
.edited_path
.as_ref()
.filter(|(path, _)| path == &variable.path)
{
this.child(div().size_full().px_2().child(editor.clone()))
} else {
this.text_color(cx.theme().colors().text_muted)
Some((_, editor)) => {
this.child(div().size_full().px_2().child(editor.clone()))
}
_ => this
.text_color(cx.theme().colors().text_muted)
.when(
!self.disabled
&& self
@@ -853,7 +855,7 @@ impl VariableList {
.when_some(variable_color, |this, color| {
this.color(Color::from(color))
}),
)
),
}
}))
}),

View File

@@ -32,12 +32,9 @@ impl StartingState {
let _notify_parent = cx.spawn(async move |this, cx| {
let entity = task.await;
this.update(cx, |_, cx| {
if let Ok(entity) = entity {
cx.emit(StartingEvent::Finished(entity))
} else {
cx.emit(StartingEvent::Failed)
}
this.update(cx, |_, cx| match entity {
Ok(entity) => cx.emit(StartingEvent::Finished(entity)),
_ => cx.emit(StartingEvent::Failed),
})
.ok();
});

View File

@@ -305,29 +305,32 @@ impl ProjectDiagnosticsEditor {
window: &mut Window,
cx: &mut Context<Workspace>,
) {
if let Some(existing) = workspace.item_of_type::<ProjectDiagnosticsEditor>(cx) {
let is_active = workspace
.active_item(cx)
.is_some_and(|item| item.item_id() == existing.item_id());
workspace.activate_item(&existing, true, !is_active, window, cx);
} else {
let workspace_handle = cx.entity().downgrade();
match workspace.item_of_type::<ProjectDiagnosticsEditor>(cx) {
Some(existing) => {
let is_active = workspace
.active_item(cx)
.is_some_and(|item| item.item_id() == existing.item_id());
workspace.activate_item(&existing, true, !is_active, window, cx);
}
_ => {
let workspace_handle = cx.entity().downgrade();
let include_warnings = match cx.try_global::<IncludeWarnings>() {
Some(include_warnings) => include_warnings.0,
None => ProjectSettings::get_global(cx).diagnostics.include_warnings,
};
let include_warnings = match cx.try_global::<IncludeWarnings>() {
Some(include_warnings) => include_warnings.0,
None => ProjectSettings::get_global(cx).diagnostics.include_warnings,
};
let diagnostics = cx.new(|cx| {
ProjectDiagnosticsEditor::new(
workspace.project().clone(),
include_warnings,
workspace_handle,
window,
cx,
)
});
workspace.add_item_to_active_pane(Box::new(diagnostics), None, true, window, cx);
let diagnostics = cx.new(|cx| {
ProjectDiagnosticsEditor::new(
workspace.project().clone(),
include_warnings,
workspace_handle,
window,
cx,
)
});
workspace.add_item_to_active_pane(Box::new(diagnostics), None, true, window, cx);
}
}
}
@@ -472,129 +475,143 @@ impl ProjectDiagnosticsEditor {
}
}
if let Some((language_server_id, group)) = to_insert {
let mut group_state = DiagnosticGroupState {
language_server_id,
primary_diagnostic: group.entries[group.primary_ix].clone(),
primary_excerpt_ix: 0,
excerpts: Default::default(),
blocks: Default::default(),
block_count: 0,
};
let mut pending_range: Option<(Range<Point>, Range<Point>, usize)> = None;
let mut is_first_excerpt_for_group = true;
for (ix, entry) in group.entries.iter().map(Some).chain([None]).enumerate() {
let resolved_entry = entry.map(|e| e.resolve::<Point>(&snapshot));
let expanded_range = if let Some(entry) = &resolved_entry {
Some(
context_range_for_entry(
entry.range.clone(),
context,
snapshot.clone(),
(**cx).clone(),
)
.await,
)
} else {
None
match to_insert {
Some((language_server_id, group)) => {
let mut group_state = DiagnosticGroupState {
language_server_id,
primary_diagnostic: group.entries[group.primary_ix].clone(),
primary_excerpt_ix: 0,
excerpts: Default::default(),
blocks: Default::default(),
block_count: 0,
};
if let Some((range, context_range, start_ix)) = &mut pending_range {
if let Some(expanded_range) = expanded_range.clone() {
// If the entries are overlapping or next to each-other, merge them into one excerpt.
if context_range.end.row + 1 >= expanded_range.start.row {
context_range.end = context_range.end.max(expanded_range.end);
continue;
}
}
let excerpt_id = excerpts.update(cx, |excerpts, cx| {
excerpts
.insert_excerpts_after(
prev_excerpt_id,
buffer.clone(),
[ExcerptRange {
context: context_range.clone(),
primary: Some(range.clone()),
}],
cx,
let mut pending_range: Option<(Range<Point>, Range<Point>, usize)> = None;
let mut is_first_excerpt_for_group = true;
for (ix, entry) in group.entries.iter().map(Some).chain([None]).enumerate()
{
let resolved_entry = entry.map(|e| e.resolve::<Point>(&snapshot));
let expanded_range = match &resolved_entry {
Some(entry) => Some(
context_range_for_entry(
entry.range.clone(),
context,
snapshot.clone(),
(**cx).clone(),
)
.pop()
.unwrap()
})?;
prev_excerpt_id = excerpt_id;
first_excerpt_id.get_or_insert(prev_excerpt_id);
group_state.excerpts.push(excerpt_id);
let header_position = (excerpt_id, language::Anchor::MIN);
if is_first_excerpt_for_group {
is_first_excerpt_for_group = false;
let mut primary =
group.entries[group.primary_ix].diagnostic.clone();
primary.message =
primary.message.split('\n').next().unwrap().to_string();
group_state.block_count += 1;
blocks_to_add.push(BlockProperties {
placement: BlockPlacement::Above(header_position),
height: 2,
style: BlockStyle::Sticky,
render: diagnostic_header_renderer(primary),
priority: 0,
});
}
for entry in &group.entries[*start_ix..ix] {
let mut diagnostic = entry.diagnostic.clone();
if diagnostic.is_primary {
group_state.primary_excerpt_ix = group_state.excerpts.len() - 1;
diagnostic.message =
entry.diagnostic.message.split('\n').skip(1).collect();
.await,
),
_ => None,
};
if let Some((range, context_range, start_ix)) = &mut pending_range {
if let Some(expanded_range) = expanded_range.clone() {
// If the entries are overlapping or next to each-other, merge them into one excerpt.
if context_range.end.row + 1 >= expanded_range.start.row {
context_range.end =
context_range.end.max(expanded_range.end);
continue;
}
}
if !diagnostic.message.is_empty() {
let excerpt_id = excerpts.update(cx, |excerpts, cx| {
excerpts
.insert_excerpts_after(
prev_excerpt_id,
buffer.clone(),
[ExcerptRange {
context: context_range.clone(),
primary: Some(range.clone()),
}],
cx,
)
.pop()
.unwrap()
})?;
prev_excerpt_id = excerpt_id;
first_excerpt_id.get_or_insert(prev_excerpt_id);
group_state.excerpts.push(excerpt_id);
let header_position = (excerpt_id, language::Anchor::MIN);
if is_first_excerpt_for_group {
is_first_excerpt_for_group = false;
let mut primary =
group.entries[group.primary_ix].diagnostic.clone();
primary.message =
primary.message.split('\n').next().unwrap().to_string();
group_state.block_count += 1;
blocks_to_add.push(BlockProperties {
placement: BlockPlacement::Below((
excerpt_id,
entry.range.start,
)),
height: diagnostic.message.matches('\n').count() as u32 + 1,
style: BlockStyle::Fixed,
render: diagnostic_block_renderer(diagnostic, None, true),
placement: BlockPlacement::Above(header_position),
height: 2,
style: BlockStyle::Sticky,
render: diagnostic_header_renderer(primary),
priority: 0,
});
}
for entry in &group.entries[*start_ix..ix] {
let mut diagnostic = entry.diagnostic.clone();
if diagnostic.is_primary {
group_state.primary_excerpt_ix =
group_state.excerpts.len() - 1;
diagnostic.message =
entry.diagnostic.message.split('\n').skip(1).collect();
}
if !diagnostic.message.is_empty() {
group_state.block_count += 1;
blocks_to_add.push(BlockProperties {
placement: BlockPlacement::Below((
excerpt_id,
entry.range.start,
)),
height: diagnostic.message.matches('\n').count() as u32
+ 1,
style: BlockStyle::Fixed,
render: diagnostic_block_renderer(
diagnostic, None, true,
),
priority: 0,
});
}
}
pending_range.take();
}
pending_range.take();
if let Some(entry) = resolved_entry.as_ref() {
let range = entry.range.clone();
pending_range = Some((range, expanded_range.unwrap(), ix));
}
}
if let Some(entry) = resolved_entry.as_ref() {
let range = entry.range.clone();
pending_range = Some((range, expanded_range.unwrap(), ix));
}
this.update(cx, |this, _| {
new_group_ixs.push(this.path_states[path_ix].diagnostic_groups.len());
this.path_states[path_ix]
.diagnostic_groups
.push(group_state);
})?;
}
_ => match to_remove {
Some((_, group_state)) => {
excerpts.update(cx, |excerpts, cx| {
excerpts.remove_excerpts(group_state.excerpts.iter().copied(), cx)
})?;
blocks_to_remove.extend(group_state.blocks.iter().copied());
}
_ => match to_keep {
Some((_, group_state)) => {
prev_excerpt_id = *group_state.excerpts.last().unwrap();
first_excerpt_id.get_or_insert(prev_excerpt_id);
this.update(cx, |this, _| {
new_group_ixs.push(this.path_states[path_ix].diagnostic_groups.len());
this.path_states[path_ix]
.diagnostic_groups
.push(group_state);
})?;
} else if let Some((_, group_state)) = to_remove {
excerpts.update(cx, |excerpts, cx| {
excerpts.remove_excerpts(group_state.excerpts.iter().copied(), cx)
})?;
blocks_to_remove.extend(group_state.blocks.iter().copied());
} else if let Some((_, group_state)) = to_keep {
prev_excerpt_id = *group_state.excerpts.last().unwrap();
first_excerpt_id.get_or_insert(prev_excerpt_id);
this.update(cx, |this, _| {
this.path_states[path_ix]
.diagnostic_groups
.push(group_state)
})?;
this.update(cx, |this, _| {
this.path_states[path_ix]
.diagnostic_groups
.push(group_state)
})?;
}
_ => {}
},
},
}
}

View File

@@ -62,26 +62,27 @@ impl Render for DiagnosticIndicator {
.child(Label::new(warning_count.to_string()).size(LabelSize::Small)),
};
let status = if let Some(diagnostic) = &self.current_diagnostic {
let message = diagnostic.message.split('\n').next().unwrap().to_string();
Some(
Button::new("diagnostic_message", message)
.label_size(LabelSize::Small)
.tooltip(|window, cx| {
Tooltip::for_action(
"Next Diagnostic",
&editor::actions::GoToDiagnostic,
window,
cx,
)
})
.on_click(cx.listener(|this, _, window, cx| {
this.go_to_next_diagnostic(window, cx);
}))
.into_any_element(),
)
} else {
None
let status = match &self.current_diagnostic {
Some(diagnostic) => {
let message = diagnostic.message.split('\n').next().unwrap().to_string();
Some(
Button::new("diagnostic_message", message)
.label_size(LabelSize::Small)
.tooltip(|window, cx| {
Tooltip::for_action(
"Next Diagnostic",
&editor::actions::GoToDiagnostic,
window,
cx,
)
})
.on_click(cx.listener(|this, _, window, cx| {
this.go_to_next_diagnostic(window, cx);
}))
.into_any_element(),
)
}
_ => None,
};
h_flex()
@@ -187,14 +188,17 @@ impl StatusItemView for DiagnosticIndicator {
window: &mut Window,
cx: &mut Context<Self>,
) {
if let Some(editor) = active_pane_item.and_then(|item| item.downcast::<Editor>()) {
self.active_editor = Some(editor.downgrade());
self._observe_active_editor = Some(cx.observe_in(&editor, window, Self::update));
self.update(editor, window, cx);
} else {
self.active_editor = None;
self.current_diagnostic = None;
self._observe_active_editor = None;
match active_pane_item.and_then(|item| item.downcast::<Editor>()) {
Some(editor) => {
self.active_editor = Some(editor.downgrade());
self._observe_active_editor = Some(cx.observe_in(&editor, window, Self::update));
self.update(editor, window, cx);
}
_ => {
self.active_editor = None;
self.current_diagnostic = None;
self._observe_active_editor = None;
}
}
cx.notify();
}

View File

@@ -83,11 +83,12 @@ impl ToolbarItemView for ToolbarControls {
_: &mut Context<Self>,
) -> ToolbarItemLocation {
if let Some(pane_item) = active_pane_item.as_ref() {
if let Some(editor) = pane_item.downcast::<ProjectDiagnosticsEditor>() {
self.editor = Some(editor.downgrade());
ToolbarItemLocation::PrimaryRight
} else {
ToolbarItemLocation::Hidden
match pane_item.downcast::<ProjectDiagnosticsEditor>() {
Some(editor) => {
self.editor = Some(editor.downgrade());
ToolbarItemLocation::PrimaryRight
}
_ => ToolbarItemLocation::Hidden,
}
} else {
ToolbarItemLocation::Hidden

View File

@@ -21,10 +21,13 @@ fn main() -> Result<()> {
let preprocessor =
ZedDocsPreprocessor::new().context("Failed to create ZedDocsPreprocessor")?;
if let Some(sub_args) = matches.subcommand_matches("supports") {
handle_supports(&preprocessor, sub_args);
} else {
handle_preprocessing(&preprocessor)?;
match matches.subcommand_matches("supports") {
Some(sub_args) => {
handle_supports(&preprocessor, sub_args);
}
_ => {
handle_preprocessing(&preprocessor)?;
}
}
Ok(())

View File

@@ -735,12 +735,12 @@ impl CompletionsMenu {
let completion = &completions[mat.candidate_id];
let sort_key = completion.sort_key();
let sort_text =
if let CompletionSource::Lsp { lsp_completion, .. } = &completion.source {
let sort_text = match &completion.source {
CompletionSource::Lsp { lsp_completion, .. } => {
lsp_completion.sort_text.as_deref()
} else {
None
};
}
_ => None,
};
let score = Reverse(OrderedFloat(mat.score));
if mat.score >= 0.2 {

View File

@@ -49,7 +49,7 @@ impl<'a> CommitAvatar<'a> {
&'a self,
window: &mut Window,
cx: &mut Context<CommitTooltip>,
) -> Option<impl IntoElement> {
) -> Option<impl IntoElement + use<>> {
let remote = self
.commit
.message

View File

@@ -226,26 +226,22 @@ impl DisplayMap {
.wrap_map
.update(cx, |map, cx| map.sync(snapshot, edits, cx));
let mut block_map = self.block_map.write(snapshot, edits);
let blocks = creases.into_iter().filter_map(|crease| {
if let Crease::Block {
let blocks = creases.into_iter().filter_map(|crease| match crease {
Crease::Block {
range,
block_height,
render_block,
block_style,
block_priority,
..
} = crease
{
Some((
range,
render_block,
block_height,
block_style,
block_priority,
))
} else {
None
}
} => Some((
range,
render_block,
block_height,
block_style,
block_priority,
)),
_ => None,
});
block_map.insert(
blocks
@@ -954,10 +950,9 @@ impl DisplaySnapshot {
for chunk in self.highlighted_chunks(range, false, editor_style) {
line.push_str(chunk.text);
let text_style = if let Some(style) = chunk.style {
Cow::Owned(editor_style.text.clone().highlight(style))
} else {
Cow::Borrowed(&editor_style.text)
let text_style = match chunk.style {
Some(style) => Cow::Owned(editor_style.text.clone().highlight(style)),
_ => Cow::Borrowed(&editor_style.text),
};
runs.push(text_style.to_run(chunk.text.len()))
@@ -1186,11 +1181,11 @@ impl DisplaySnapshot {
pub fn crease_for_buffer_row(&self, buffer_row: MultiBufferRow) -> Option<Crease<Point>> {
let start = MultiBufferPoint::new(buffer_row.0, self.buffer_snapshot.line_len(buffer_row));
if let Some(crease) = self
match self
.crease_snapshot
.query_row(buffer_row, &self.buffer_snapshot)
{
match crease {
Some(crease) => match crease {
Crease::Inline {
range,
placeholder,
@@ -1219,52 +1214,55 @@ impl DisplaySnapshot {
block_priority: *block_priority,
render_toggle: render_toggle.clone(),
}),
}
} else if self.starts_indent(MultiBufferRow(start.row))
&& !self.is_line_folded(MultiBufferRow(start.row))
{
let start_line_indent = self.line_indent_for_buffer_row(buffer_row);
let max_point = self.buffer_snapshot.max_point();
let mut end = None;
for row in (buffer_row.0 + 1)..=max_point.row {
let line_indent = self.line_indent_for_buffer_row(MultiBufferRow(row));
if !line_indent.is_line_blank()
&& line_indent.raw_len() <= start_line_indent.raw_len()
},
_ => {
if self.starts_indent(MultiBufferRow(start.row))
&& !self.is_line_folded(MultiBufferRow(start.row))
{
let prev_row = row - 1;
end = Some(Point::new(
prev_row,
self.buffer_snapshot.line_len(MultiBufferRow(prev_row)),
));
break;
let start_line_indent = self.line_indent_for_buffer_row(buffer_row);
let max_point = self.buffer_snapshot.max_point();
let mut end = None;
for row in (buffer_row.0 + 1)..=max_point.row {
let line_indent = self.line_indent_for_buffer_row(MultiBufferRow(row));
if !line_indent.is_line_blank()
&& line_indent.raw_len() <= start_line_indent.raw_len()
{
let prev_row = row - 1;
end = Some(Point::new(
prev_row,
self.buffer_snapshot.line_len(MultiBufferRow(prev_row)),
));
break;
}
}
let mut row_before_line_breaks = end.unwrap_or(max_point);
while row_before_line_breaks.row > start.row
&& self
.buffer_snapshot
.is_line_blank(MultiBufferRow(row_before_line_breaks.row))
{
row_before_line_breaks.row -= 1;
}
row_before_line_breaks = Point::new(
row_before_line_breaks.row,
self.buffer_snapshot
.line_len(MultiBufferRow(row_before_line_breaks.row)),
);
Some(Crease::Inline {
range: start..row_before_line_breaks,
placeholder: self.fold_placeholder.clone(),
render_toggle: None,
render_trailer: None,
metadata: None,
})
} else {
None
}
}
let mut row_before_line_breaks = end.unwrap_or(max_point);
while row_before_line_breaks.row > start.row
&& self
.buffer_snapshot
.is_line_blank(MultiBufferRow(row_before_line_breaks.row))
{
row_before_line_breaks.row -= 1;
}
row_before_line_breaks = Point::new(
row_before_line_breaks.row,
self.buffer_snapshot
.line_len(MultiBufferRow(row_before_line_breaks.row)),
);
Some(Crease::Inline {
range: start..row_before_line_breaks,
placeholder: self.fold_placeholder.clone(),
render_toggle: None,
render_trailer: None,
metadata: None,
})
} else {
None
}
}
@@ -1480,7 +1478,7 @@ pub mod tests {
});
let buffer = cx.update(|cx| {
if rng.gen() {
if rng.r#gen() {
let len = rng.gen_range(0..10);
let text = util::RandomCharIter::new(&mut rng)
.take(len)
@@ -1542,7 +1540,7 @@ pub mod tests {
}
30..=44 => {
map.update(cx, |map, cx| {
if rng.gen() || blocks.is_empty() {
if rng.r#gen() || blocks.is_empty() {
let buffer = map.snapshot(cx).buffer_snapshot;
let block_properties = (0..rng.gen_range(1..=1))
.map(|_| {
@@ -1552,7 +1550,7 @@ pub mod tests {
Bias::Left,
));
let placement = if rng.gen() {
let placement = if rng.r#gen() {
BlockPlacement::Above(position)
} else {
BlockPlacement::Below(position)
@@ -1596,7 +1594,7 @@ pub mod tests {
});
}
if rng.gen() && fold_count > 0 {
if rng.r#gen() && fold_count > 0 {
log::info!("unfolding ranges: {:?}", ranges);
map.update(cx, |map, cx| {
map.unfold_intersecting(ranges, true, cx);

View File

@@ -1351,11 +1351,14 @@ impl BlockSnapshot {
{
break;
}
if let Some(block) = &transform.block {
cursor.next(&());
return Some((start_row, block));
} else {
cursor.next(&());
match &transform.block {
Some(block) => {
cursor.next(&());
return Some((start_row, block));
}
_ => {
cursor.next(&());
}
}
}
None
@@ -1405,12 +1408,17 @@ impl BlockSnapshot {
cursor.seek(&wrap_row, Bias::Left, &());
while let Some(transform) = cursor.item() {
if let Some(block) = transform.block.as_ref() {
if block.id() == block_id {
return Some(block.clone());
match transform.block.as_ref() {
Some(block) => {
if block.id() == block_id {
return Some(block.clone());
}
}
_ => {
if *cursor.start() > wrap_row {
break;
}
}
} else if *cursor.start() > wrap_row {
break;
}
cursor.next(&());
@@ -1482,18 +1490,23 @@ impl BlockSnapshot {
pub(super) fn line_len(&self, row: BlockRow) -> u32 {
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
cursor.seek(&BlockRow(row.0), Bias::Right, &());
if let Some(transform) = cursor.item() {
let (output_start, input_start) = cursor.start();
let overshoot = row.0 - output_start.0;
if transform.block.is_some() {
0
} else {
self.wrap_snapshot.line_len(input_start.0 + overshoot)
match cursor.item() {
Some(transform) => {
let (output_start, input_start) = cursor.start();
let overshoot = row.0 - output_start.0;
if transform.block.is_some() {
0
} else {
self.wrap_snapshot.line_len(input_start.0 + overshoot)
}
}
_ => {
if row.0 == 0 {
0
} else {
panic!("row out of range");
}
}
} else if row.0 == 0 {
0
} else {
panic!("row out of range");
}
}
@@ -1536,52 +1549,57 @@ impl BlockSnapshot {
let mut reversed = false;
loop {
if let Some(transform) = cursor.item() {
let (output_start_row, input_start_row) = cursor.start();
let (output_end_row, input_end_row) = cursor.end(&());
let output_start = Point::new(output_start_row.0, 0);
let input_start = Point::new(input_start_row.0, 0);
let input_end = Point::new(input_end_row.0, 0);
match cursor.item() {
Some(transform) => {
let (output_start_row, input_start_row) = cursor.start();
let (output_end_row, input_end_row) = cursor.end(&());
let output_start = Point::new(output_start_row.0, 0);
let input_start = Point::new(input_start_row.0, 0);
let input_end = Point::new(input_end_row.0, 0);
match transform.block.as_ref() {
Some(block) => {
if block.is_replacement() {
if ((bias == Bias::Left || search_left) && output_start <= point.0)
|| (!search_left && output_start >= point.0)
{
return BlockPoint(output_start);
match transform.block.as_ref() {
Some(block) => {
if block.is_replacement() {
if ((bias == Bias::Left || search_left) && output_start <= point.0)
|| (!search_left && output_start >= point.0)
{
return BlockPoint(output_start);
}
}
}
None => {
let input_point = if point.row >= output_end_row.0 {
let line_len = self.wrap_snapshot.line_len(input_end_row.0 - 1);
self.wrap_snapshot
.clip_point(WrapPoint::new(input_end_row.0 - 1, line_len), bias)
} else {
let output_overshoot = point.0.saturating_sub(output_start);
self.wrap_snapshot
.clip_point(WrapPoint(input_start + output_overshoot), bias)
};
if (input_start..input_end).contains(&input_point.0) {
let input_overshoot = input_point.0.saturating_sub(input_start);
return BlockPoint(output_start + input_overshoot);
}
}
}
None => {
let input_point = if point.row >= output_end_row.0 {
let line_len = self.wrap_snapshot.line_len(input_end_row.0 - 1);
self.wrap_snapshot
.clip_point(WrapPoint::new(input_end_row.0 - 1, line_len), bias)
} else {
let output_overshoot = point.0.saturating_sub(output_start);
self.wrap_snapshot
.clip_point(WrapPoint(input_start + output_overshoot), bias)
};
if (input_start..input_end).contains(&input_point.0) {
let input_overshoot = input_point.0.saturating_sub(input_start);
return BlockPoint(output_start + input_overshoot);
}
if search_left {
cursor.prev(&());
} else {
cursor.next(&());
}
}
if search_left {
cursor.prev(&());
} else {
cursor.next(&());
_ => {
if reversed {
return self.max_point();
} else {
reversed = true;
search_left = !search_left;
cursor.seek(&BlockRow(point.row), Bias::Right, &());
}
}
} else if reversed {
return self.max_point();
} else {
reversed = true;
search_left = !search_left;
cursor.seek(&BlockRow(point.row), Bias::Right, &());
}
}
}
@@ -1589,26 +1607,27 @@ impl BlockSnapshot {
pub fn to_block_point(&self, wrap_point: WrapPoint) -> BlockPoint {
let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>(&());
cursor.seek(&WrapRow(wrap_point.row()), Bias::Right, &());
if let Some(transform) = cursor.item() {
if transform.block.is_some() {
BlockPoint::new(cursor.start().1 .0, 0)
} else {
let (input_start_row, output_start_row) = cursor.start();
let input_start = Point::new(input_start_row.0, 0);
let output_start = Point::new(output_start_row.0, 0);
let input_overshoot = wrap_point.0 - input_start;
BlockPoint(output_start + input_overshoot)
match cursor.item() {
Some(transform) => {
if transform.block.is_some() {
BlockPoint::new(cursor.start().1 .0, 0)
} else {
let (input_start_row, output_start_row) = cursor.start();
let input_start = Point::new(input_start_row.0, 0);
let output_start = Point::new(output_start_row.0, 0);
let input_overshoot = wrap_point.0 - input_start;
BlockPoint(output_start + input_overshoot)
}
}
} else {
self.max_point()
_ => self.max_point(),
}
}
pub fn to_wrap_point(&self, block_point: BlockPoint, bias: Bias) -> WrapPoint {
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
cursor.seek(&BlockRow(block_point.row), Bias::Right, &());
if let Some(transform) = cursor.item() {
match transform.block.as_ref() {
match cursor.item() {
Some(transform) => match transform.block.as_ref() {
Some(block) => {
if block.place_below() {
let wrap_row = cursor.start().1 .0 - 1;
@@ -1627,9 +1646,8 @@ impl BlockSnapshot {
let wrap_row = cursor.start().1 .0 + overshoot;
WrapPoint::new(wrap_row, block_point.column)
}
}
} else {
self.wrap_snapshot.max_point()
},
_ => self.wrap_snapshot.max_point(),
}
}
}
@@ -1702,20 +1720,23 @@ impl<'a> Iterator for BlockChunks<'a> {
}
if self.input_chunk.text.is_empty() {
if let Some(input_chunk) = self.input_chunks.next() {
self.input_chunk = input_chunk;
} else {
if self.output_row < self.max_output_row {
self.output_row += 1;
self.advance();
if self.transforms.item().is_some() {
return Some(Chunk {
text: "\n",
..Default::default()
});
}
match self.input_chunks.next() {
Some(input_chunk) => {
self.input_chunk = input_chunk;
}
_ => {
if self.output_row < self.max_output_row {
self.output_row += 1;
self.advance();
if self.transforms.item().is_some() {
return Some(Chunk {
text: "\n",
..Default::default()
});
}
}
return None;
}
return None;
}
}
@@ -1783,18 +1804,19 @@ impl Iterator for BlockRows<'_> {
}
let transform = self.transforms.item()?;
if let Some(block) = transform.block.as_ref() {
if block.is_replacement() && self.transforms.start().0 == self.output_row {
if matches!(block, Block::FoldedBuffer { .. }) {
Some(RowInfo::default())
match transform.block.as_ref() {
Some(block) => {
if block.is_replacement() && self.transforms.start().0 == self.output_row {
if matches!(block, Block::FoldedBuffer { .. }) {
Some(RowInfo::default())
} else {
Some(self.input_rows.next().unwrap())
}
} else {
Some(self.input_rows.next().unwrap())
Some(RowInfo::default())
}
} else {
Some(RowInfo::default())
}
} else {
Some(self.input_rows.next().unwrap())
_ => Some(self.input_rows.next().unwrap()),
}
}
}
@@ -2913,7 +2935,7 @@ mod tests {
log::info!("Wrap width: {:?}", wrap_width);
log::info!("Excerpt Header Height: {:?}", excerpt_header_height);
let is_singleton = rng.gen();
let is_singleton = rng.r#gen();
let buffer = if is_singleton {
let len = rng.gen_range(0..10);
let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();

View File

@@ -602,21 +602,24 @@ impl FoldSnapshot {
if let Some(transform) = cursor.item() {
let start_in_transform = range.start.0 - cursor.start().0 .0;
let end_in_transform = cmp::min(range.end, cursor.end(&()).0).0 - cursor.start().0 .0;
if let Some(placeholder) = transform.placeholder.as_ref() {
summary = TextSummary::from(
&placeholder.text
[start_in_transform.column as usize..end_in_transform.column as usize],
);
} else {
let inlay_start = self
.inlay_snapshot
.to_offset(InlayPoint(cursor.start().1 .0 + start_in_transform));
let inlay_end = self
.inlay_snapshot
.to_offset(InlayPoint(cursor.start().1 .0 + end_in_transform));
summary = self
.inlay_snapshot
.text_summary_for_range(inlay_start..inlay_end);
match transform.placeholder.as_ref() {
Some(placeholder) => {
summary = TextSummary::from(
&placeholder.text
[start_in_transform.column as usize..end_in_transform.column as usize],
);
}
_ => {
let inlay_start = self
.inlay_snapshot
.to_offset(InlayPoint(cursor.start().1 .0 + start_in_transform));
let inlay_end = self
.inlay_snapshot
.to_offset(InlayPoint(cursor.start().1 .0 + end_in_transform));
summary = self
.inlay_snapshot
.text_summary_for_range(inlay_start..inlay_end);
}
}
}
@@ -627,17 +630,21 @@ impl FoldSnapshot {
.output;
if let Some(transform) = cursor.item() {
let end_in_transform = range.end.0 - cursor.start().0 .0;
if let Some(placeholder) = transform.placeholder.as_ref() {
summary +=
TextSummary::from(&placeholder.text[..end_in_transform.column as usize]);
} else {
let inlay_start = self.inlay_snapshot.to_offset(cursor.start().1);
let inlay_end = self
.inlay_snapshot
.to_offset(InlayPoint(cursor.start().1 .0 + end_in_transform));
summary += self
.inlay_snapshot
.text_summary_for_range(inlay_start..inlay_end);
match transform.placeholder.as_ref() {
Some(placeholder) => {
summary += TextSummary::from(
&placeholder.text[..end_in_transform.column as usize],
);
}
_ => {
let inlay_start = self.inlay_snapshot.to_offset(cursor.start().1);
let inlay_end = self
.inlay_snapshot
.to_offset(InlayPoint(cursor.start().1 .0 + end_in_transform));
summary += self
.inlay_snapshot
.text_summary_for_range(inlay_start..inlay_end);
}
}
}
}
@@ -822,22 +829,23 @@ impl FoldSnapshot {
pub fn clip_point(&self, point: FoldPoint, bias: Bias) -> FoldPoint {
let mut cursor = self.transforms.cursor::<(FoldPoint, InlayPoint)>(&());
cursor.seek(&point, Bias::Right, &());
if let Some(transform) = cursor.item() {
let transform_start = cursor.start().0 .0;
if transform.placeholder.is_some() {
if point.0 == transform_start || matches!(bias, Bias::Left) {
FoldPoint(transform_start)
match cursor.item() {
Some(transform) => {
let transform_start = cursor.start().0 .0;
if transform.placeholder.is_some() {
if point.0 == transform_start || matches!(bias, Bias::Left) {
FoldPoint(transform_start)
} else {
FoldPoint(cursor.end(&()).0 .0)
}
} else {
FoldPoint(cursor.end(&()).0 .0)
let overshoot = InlayPoint(point.0 - transform_start);
let inlay_point = cursor.start().1 + overshoot;
let clipped_inlay_point = self.inlay_snapshot.clip_point(inlay_point, bias);
FoldPoint(cursor.start().0 .0 + (clipped_inlay_point - cursor.start().1).0)
}
} else {
let overshoot = InlayPoint(point.0 - transform_start);
let inlay_point = cursor.start().1 + overshoot;
let clipped_inlay_point = self.inlay_snapshot.clip_point(inlay_point, bias);
FoldPoint(cursor.start().0 .0 + (clipped_inlay_point - cursor.start().1).0)
}
} else {
FoldPoint(self.transforms.summary().output.lines)
_ => FoldPoint(self.transforms.summary().output.lines),
}
}
}
@@ -1616,7 +1624,7 @@ mod tests {
let len = rng.gen_range(0..10);
let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
let buffer = if rng.gen() {
let buffer = if rng.r#gen() {
MultiBuffer::build_simple(&text, cx)
} else {
MultiBuffer::build_random(&mut rng, cx)
@@ -1962,7 +1970,7 @@ mod tests {
let start = buffer.clip_offset(rng.gen_range(0..=end), Left);
to_unfold.push(start..end);
}
let inclusive = rng.gen();
let inclusive = rng.r#gen();
log::info!("unfolding {:?} (inclusive: {})", to_unfold, inclusive);
let (mut writer, snapshot, edits) = self.write(inlay_snapshot, vec![]);
snapshot_edits.push((snapshot, edits));

View File

@@ -610,9 +610,9 @@ impl InlayMap {
let mut to_insert = Vec::new();
let snapshot = &mut self.snapshot;
for i in 0..rng.gen_range(1..=5) {
if self.inlays.is_empty() || rng.gen() {
if self.inlays.is_empty() || rng.r#gen() {
let position = snapshot.buffer.random_byte_range(0, rng).start;
let bias = if rng.gen() { Bias::Left } else { Bias::Right };
let bias = if rng.r#gen() { Bias::Left } else { Bias::Right };
let len = if rng.gen_bool(0.01) {
0
} else {
@@ -809,33 +809,39 @@ impl InlaySnapshot {
match cursor.item() {
Some(Transform::Isomorphic(transform)) => {
if cursor.start().0 == point {
if let Some(Transform::Inlay(inlay)) = cursor.prev_item() {
if inlay.position.bias() == Bias::Left {
return point;
} else if bias == Bias::Left {
cursor.prev(&());
} else if transform.first_line_chars == 0 {
point.0 += Point::new(1, 0);
} else {
point.0 += Point::new(0, 1);
match cursor.prev_item() {
Some(Transform::Inlay(inlay)) => {
if inlay.position.bias() == Bias::Left {
return point;
} else if bias == Bias::Left {
cursor.prev(&());
} else if transform.first_line_chars == 0 {
point.0 += Point::new(1, 0);
} else {
point.0 += Point::new(0, 1);
}
}
_ => {
return point;
}
} else {
return point;
}
} else if cursor.end(&()).0 == point {
if let Some(Transform::Inlay(inlay)) = cursor.next_item() {
if inlay.position.bias() == Bias::Right {
return point;
} else if bias == Bias::Right {
cursor.next(&());
} else if point.0.column == 0 {
point.0.row -= 1;
point.0.column = self.line_len(point.0.row);
} else {
point.0.column -= 1;
match cursor.next_item() {
Some(Transform::Inlay(inlay)) => {
if inlay.position.bias() == Bias::Right {
return point;
} else if bias == Bias::Right {
cursor.next(&());
} else if point.0.column == 0 {
point.0.row -= 1;
point.0.column = self.line_len(point.0.row);
} else {
point.0.column -= 1;
}
}
_ => {
return point;
}
} else {
return point;
}
} else {
let overshoot = point.0 - cursor.start().0 .0;
@@ -1500,7 +1506,7 @@ mod tests {
.unwrap_or(10);
let len = rng.gen_range(0..30);
let buffer = if rng.gen() {
let buffer = if rng.r#gen() {
let text = util::RandomCharIter::new(&mut rng)
.take(len)
.collect::<String>();
@@ -1709,19 +1715,22 @@ mod tests {
buffer_point
);
if let Some(ch) = buffer_chars.next() {
if ch == '\n' {
buffer_point += Point::new(1, 0);
} else {
buffer_point += Point::new(0, ch.len_utf8() as u32);
}
match buffer_chars.next() {
Some(ch) => {
if ch == '\n' {
buffer_point += Point::new(1, 0);
} else {
buffer_point += Point::new(0, ch.len_utf8() as u32);
}
// Ensure that moving forward in the buffer always moves the inlay point forward as well.
let new_inlay_point = inlay_snapshot.to_inlay_point(buffer_point);
assert!(new_inlay_point > inlay_point);
inlay_point = new_inlay_point;
} else {
break;
// Ensure that moving forward in the buffer always moves the inlay point forward as well.
let new_inlay_point = inlay_snapshot.to_inlay_point(buffer_point);
assert!(new_inlay_point > inlay_point);
inlay_point = new_inlay_point;
}
_ => {
break;
}
}
}

View File

@@ -534,15 +534,18 @@ impl<'a> Iterator for TabChunks<'a> {
fn next(&mut self) -> Option<Self::Item> {
if self.chunk.text.is_empty() {
if let Some(chunk) = self.fold_chunks.next() {
self.chunk = chunk;
if self.inside_leading_tab {
self.chunk.text = &self.chunk.text[1..];
self.inside_leading_tab = false;
self.input_column += 1;
match self.fold_chunks.next() {
Some(chunk) => {
self.chunk = chunk;
if self.inside_leading_tab {
self.chunk.text = &self.chunk.text[1..];
self.inside_leading_tab = false;
self.input_column += 1;
}
}
_ => {
return None;
}
} else {
return None;
}
}
@@ -738,7 +741,7 @@ mod tests {
fn test_random_tabs(cx: &mut gpui::App, mut rng: StdRng) {
let tab_size = NonZeroU32::new(rng.gen_range(1..=4)).unwrap();
let len = rng.gen_range(0..30);
let buffer = if rng.gen() {
let buffer = if rng.r#gen() {
let text = util::RandomCharIter::new(&mut rng)
.take(len)
.collect::<String>();

View File

@@ -1207,7 +1207,7 @@ mod tests {
log::info!("Wrap width: {:?}", wrap_width);
let buffer = cx.update(|cx| {
if rng.gen() {
if rng.r#gen() {
MultiBuffer::build_random(&mut rng, cx)
} else {
let len = rng.gen_range(0..10);

File diff suppressed because it is too large Load Diff

View File

@@ -9526,15 +9526,17 @@ async fn test_word_completion(cx: &mut TestAppContext) {
cx.condition(|editor, _| editor.context_menu_visible())
.await;
cx.update_editor(|editor, window, cx| {
if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
{
assert_eq!(
completion_menu_entries(&menu),
&["first", "last"],
"When LSP server is fast to reply, no fallback word completions are used"
);
} else {
panic!("expected completion menu to be open");
match editor.context_menu.borrow_mut().as_ref() {
Some(CodeContextMenu::Completions(menu)) => {
assert_eq!(
completion_menu_entries(&menu),
&["first", "last"],
"When LSP server is fast to reply, no fallback word completions are used"
);
}
_ => {
panic!("expected completion menu to be open");
}
}
editor.cancel(&Cancel, window, cx);
});
@@ -9550,13 +9552,13 @@ async fn test_word_completion(cx: &mut TestAppContext) {
cx.condition(|editor, _| editor.context_menu_visible())
.await;
cx.update_editor(|editor, _, _| {
if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
{
match editor.context_menu.borrow_mut().as_ref()
{ Some(CodeContextMenu::Completions(menu)) => {
assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
"When LSP server is slow, document words can be shown instead, if configured accordingly");
} else {
} _ => {
panic!("expected completion menu to be open");
}
}}
});
}
@@ -9609,16 +9611,16 @@ async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext
cx.condition(|editor, _| editor.context_menu_visible())
.await;
cx.update_editor(|editor, _, _| {
if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
{
match editor.context_menu.borrow_mut().as_ref()
{ Some(CodeContextMenu::Completions(menu)) => {
assert_eq!(
completion_menu_entries(&menu),
&["first", "last", "second"],
"Word completions that has the same edit as the any of the LSP ones, should not be proposed"
);
} else {
} _ => {
panic!("expected completion menu to be open");
}
}}
});
}
@@ -9663,34 +9665,36 @@ async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
cx.executor().run_until_parked();
cx.condition(|editor, _| editor.context_menu_visible())
.await;
cx.update_editor(|editor, _, _| {
if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
{
assert_eq!(
completion_menu_entries(&menu),
&["first", "last", "second"],
"`ShowWordCompletions` action should show word completions"
);
} else {
panic!("expected completion menu to be open");
}
});
cx.update_editor(
|editor, _, _| match editor.context_menu.borrow_mut().as_ref() {
Some(CodeContextMenu::Completions(menu)) => {
assert_eq!(
completion_menu_entries(&menu),
&["first", "last", "second"],
"`ShowWordCompletions` action should show word completions"
);
}
_ => {
panic!("expected completion menu to be open");
}
},
);
cx.simulate_keystroke("l");
cx.executor().run_until_parked();
cx.condition(|editor, _| editor.context_menu_visible())
.await;
cx.update_editor(|editor, _, _| {
if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
{
match editor.context_menu.borrow_mut().as_ref()
{ Some(CodeContextMenu::Completions(menu)) => {
assert_eq!(
completion_menu_entries(&menu),
&["last"],
"After showing word completions, further editing should filter them and not query the LSP"
);
} else {
} _ => {
panic!("expected completion menu to be open");
}
}}
});
}
@@ -9719,16 +9723,16 @@ async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
cx.condition(|editor, _| editor.context_menu_visible())
.await;
cx.update_editor(|editor, window, cx| {
if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
{
match editor.context_menu.borrow_mut().as_ref()
{ Some(CodeContextMenu::Completions(menu)) => {
assert_eq!(
completion_menu_entries(&menu),
&["let"],
"With no digits in the completion query, no digits should be in the word completions"
);
} else {
} _ => {
panic!("expected completion menu to be open");
}
}}
editor.cancel(&Cancel, window, cx);
});
@@ -9745,13 +9749,13 @@ async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
cx.condition(|editor, _| editor.context_menu_visible())
.await;
cx.update_editor(|editor, _, _| {
if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
{
match editor.context_menu.borrow_mut().as_ref()
{ Some(CodeContextMenu::Completions(menu)) => {
assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
} else {
} _ => {
panic!("expected completion menu to be open");
}
}}
});
}
@@ -9914,8 +9918,8 @@ async fn test_multiline_completion(cx: &mut TestAppContext) {
editor.update(cx, |editor, _| {
assert!(editor.context_menu_visible());
if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
{
match editor.context_menu.borrow_mut().as_ref()
{ Some(CodeContextMenu::Completions(menu)) => {
let completion_labels = menu
.completions
.borrow()
@@ -9944,9 +9948,9 @@ async fn test_multiline_completion(cx: &mut TestAppContext) {
"Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
);
}
} else {
} _ => {
panic!("expected completion menu to be open");
}
}}
});
}
@@ -9981,38 +9985,44 @@ async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
cx.simulate_keystroke(".");
cx.executor().run_until_parked();
cx.update_editor(|editor, _, _| {
if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
{
assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
} else {
panic!("expected completion menu to be open");
}
});
cx.update_editor(
|editor, _, _| match editor.context_menu.borrow_mut().as_ref() {
Some(CodeContextMenu::Completions(menu)) => {
assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
}
_ => {
panic!("expected completion menu to be open");
}
},
);
cx.update_editor(|editor, window, cx| {
editor.move_page_down(&MovePageDown::default(), window, cx);
if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
{
assert!(
menu.selected_item == 1,
"expected PageDown to select the last item from the context menu"
);
} else {
panic!("expected completion menu to stay open after PageDown");
match editor.context_menu.borrow_mut().as_ref() {
Some(CodeContextMenu::Completions(menu)) => {
assert!(
menu.selected_item == 1,
"expected PageDown to select the last item from the context menu"
);
}
_ => {
panic!("expected completion menu to stay open after PageDown");
}
}
});
cx.update_editor(|editor, window, cx| {
editor.move_page_up(&MovePageUp::default(), window, cx);
if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
{
assert!(
menu.selected_item == 0,
"expected PageUp to select the first item from the context menu"
);
} else {
panic!("expected completion menu to stay open after PageUp");
match editor.context_menu.borrow_mut().as_ref() {
Some(CodeContextMenu::Completions(menu)) => {
assert!(
menu.selected_item == 0,
"expected PageUp to select the first item from the context menu"
);
}
_ => {
panic!("expected completion menu to stay open after PageUp");
}
}
});
}
@@ -10074,17 +10084,19 @@ async fn test_completion_sort(cx: &mut TestAppContext) {
});
cx.executor().run_until_parked();
cx.update_editor(|editor, _, _| {
if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
{
assert_eq!(
completion_menu_entries(&menu),
&["r", "ret", "Range", "return"]
);
} else {
panic!("expected completion menu to be open");
}
});
cx.update_editor(
|editor, _, _| match editor.context_menu.borrow_mut().as_ref() {
Some(CodeContextMenu::Completions(menu)) => {
assert_eq!(
completion_menu_entries(&menu),
&["r", "ret", "Range", "return"]
);
}
_ => {
panic!("expected completion menu to be open");
}
},
);
}
#[gpui::test]
@@ -13056,42 +13068,48 @@ async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestA
// word character in the 'element' scope, which contains the cursor.
cx.simulate_keystroke("-");
cx.executor().run_until_parked();
cx.update_editor(|editor, _, _| {
if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
{
assert_eq!(
completion_menu_entries(&menu),
&["bg-red", "bg-blue", "bg-yellow"]
);
} else {
panic!("expected completion menu to be open");
}
});
cx.update_editor(
|editor, _, _| match editor.context_menu.borrow_mut().as_ref() {
Some(CodeContextMenu::Completions(menu)) => {
assert_eq!(
completion_menu_entries(&menu),
&["bg-red", "bg-blue", "bg-yellow"]
);
}
_ => {
panic!("expected completion menu to be open");
}
},
);
cx.simulate_keystroke("l");
cx.executor().run_until_parked();
cx.update_editor(|editor, _, _| {
if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
{
assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
} else {
panic!("expected completion menu to be open");
}
});
cx.update_editor(
|editor, _, _| match editor.context_menu.borrow_mut().as_ref() {
Some(CodeContextMenu::Completions(menu)) => {
assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
}
_ => {
panic!("expected completion menu to be open");
}
},
);
// When filtering completions, consider the character after the '-' to
// be the start of a subword.
cx.set_state(r#"<p class="yelˇ" />"#);
cx.simulate_keystroke("l");
cx.executor().run_until_parked();
cx.update_editor(|editor, _, _| {
if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
{
assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
} else {
panic!("expected completion menu to be open");
}
});
cx.update_editor(
|editor, _, _| match editor.context_menu.borrow_mut().as_ref() {
Some(CodeContextMenu::Completions(menu)) => {
assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
}
_ => {
panic!("expected completion menu to be open");
}
},
);
}
fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
@@ -18691,7 +18709,7 @@ fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Cont
pub fn handle_signature_help_request(
cx: &mut EditorLspTestContext,
mocked_response: lsp::SignatureHelp,
) -> impl Future<Output = ()> {
) -> impl Future<Output = ()> + use<> {
let mut request =
cx.set_request_handler::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
let mocked_response = mocked_response.clone();
@@ -18711,7 +18729,7 @@ pub fn handle_completion_request(
marked_string: &str,
completions: Vec<&'static str>,
counter: Arc<AtomicUsize>,
) -> impl Future<Output = ()> {
) -> impl Future<Output = ()> + use<> {
let complete_from_marker: TextRangeMarker = '|'.into();
let replace_range_marker: TextRangeMarker = ('<', '>').into();
let (_, mut marked_ranges) = marked_text_ranges_by(
@@ -18758,7 +18776,7 @@ pub fn handle_completion_request(
fn handle_resolve_completion_request(
cx: &mut EditorLspTestContext,
edits: Option<Vec<(&'static str, &'static str)>>,
) -> impl Future<Output = ()> {
) -> impl Future<Output = ()> + use<> {
let edits = edits.map(|edits| {
edits
.iter()

View File

@@ -432,68 +432,95 @@ impl EditorElement {
register_action(editor, window, Editor::expand_all_diff_hunks);
register_action(editor, window, |editor, action, window, cx| {
if let Some(task) = editor.format(action, window, cx) {
task.detach_and_notify_err(window, cx);
} else {
cx.propagate();
match editor.format(action, window, cx) {
Some(task) => {
task.detach_and_notify_err(window, cx);
}
_ => {
cx.propagate();
}
}
});
register_action(editor, window, |editor, action, window, cx| {
if let Some(task) = editor.format_selections(action, window, cx) {
task.detach_and_notify_err(window, cx);
} else {
cx.propagate();
match editor.format_selections(action, window, cx) {
Some(task) => {
task.detach_and_notify_err(window, cx);
}
_ => {
cx.propagate();
}
}
});
register_action(editor, window, |editor, action, window, cx| {
if let Some(task) = editor.organize_imports(action, window, cx) {
task.detach_and_notify_err(window, cx);
} else {
cx.propagate();
match editor.organize_imports(action, window, cx) {
Some(task) => {
task.detach_and_notify_err(window, cx);
}
_ => {
cx.propagate();
}
}
});
register_action(editor, window, Editor::restart_language_server);
register_action(editor, window, Editor::show_character_palette);
register_action(editor, window, |editor, action, window, cx| {
if let Some(task) = editor.confirm_completion(action, window, cx) {
task.detach_and_notify_err(window, cx);
} else {
cx.propagate();
match editor.confirm_completion(action, window, cx) {
Some(task) => {
task.detach_and_notify_err(window, cx);
}
_ => {
cx.propagate();
}
}
});
register_action(editor, window, |editor, action, window, cx| {
if let Some(task) = editor.compose_completion(action, window, cx) {
task.detach_and_notify_err(window, cx);
} else {
cx.propagate();
match editor.compose_completion(action, window, cx) {
Some(task) => {
task.detach_and_notify_err(window, cx);
}
_ => {
cx.propagate();
}
}
});
register_action(editor, window, |editor, action, window, cx| {
if let Some(task) = editor.confirm_code_action(action, window, cx) {
task.detach_and_notify_err(window, cx);
} else {
cx.propagate();
match editor.confirm_code_action(action, window, cx) {
Some(task) => {
task.detach_and_notify_err(window, cx);
}
_ => {
cx.propagate();
}
}
});
register_action(editor, window, |editor, action, window, cx| {
if let Some(task) = editor.rename(action, window, cx) {
task.detach_and_notify_err(window, cx);
} else {
cx.propagate();
match editor.rename(action, window, cx) {
Some(task) => {
task.detach_and_notify_err(window, cx);
}
_ => {
cx.propagate();
}
}
});
register_action(editor, window, |editor, action, window, cx| {
if let Some(task) = editor.confirm_rename(action, window, cx) {
task.detach_and_notify_err(window, cx);
} else {
cx.propagate();
match editor.confirm_rename(action, window, cx) {
Some(task) => {
task.detach_and_notify_err(window, cx);
}
_ => {
cx.propagate();
}
}
});
register_action(editor, window, |editor, action, window, cx| {
if let Some(task) = editor.find_all_references(action, window, cx) {
task.detach_and_log_err(cx);
} else {
cx.propagate();
match editor.find_all_references(action, window, cx) {
Some(task) => {
task.detach_and_log_err(cx);
}
_ => {
cx.propagate();
}
}
});
register_action(editor, window, Editor::show_signature_help);
@@ -1097,81 +1124,87 @@ impl EditorElement {
selections.push((player, layouts));
}
if let Some(collaboration_hub) = &editor.collaboration_hub {
// When following someone, render the local selections in their color.
if let Some(leader_id) = editor.leader_peer_id {
if let Some(collaborator) = collaboration_hub.collaborators(cx).get(&leader_id)
{
if let Some(participant_index) = collaboration_hub
.user_participant_indices(cx)
.get(&collaborator.user_id)
match &editor.collaboration_hub {
Some(collaboration_hub) => {
// When following someone, render the local selections in their color.
if let Some(leader_id) = editor.leader_peer_id {
if let Some(collaborator) =
collaboration_hub.collaborators(cx).get(&leader_id)
{
if let Some((local_selection_style, _)) = selections.first_mut() {
*local_selection_style = cx
.theme()
.players()
.color_for_participant(participant_index.0);
if let Some(participant_index) = collaboration_hub
.user_participant_indices(cx)
.get(&collaborator.user_id)
{
if let Some((local_selection_style, _)) = selections.first_mut() {
*local_selection_style = cx
.theme()
.players()
.color_for_participant(participant_index.0);
}
}
}
}
}
let mut remote_selections = HashMap::default();
for selection in snapshot.remote_selections_in_range(
&(start_anchor..end_anchor),
collaboration_hub.as_ref(),
cx,
) {
let selection_style =
Self::get_participant_color(selection.participant_index, cx);
let mut remote_selections = HashMap::default();
for selection in snapshot.remote_selections_in_range(
&(start_anchor..end_anchor),
collaboration_hub.as_ref(),
cx,
) {
let selection_style =
Self::get_participant_color(selection.participant_index, cx);
// Don't re-render the leader's selections, since the local selections
// match theirs.
if Some(selection.peer_id) == editor.leader_peer_id {
continue;
// Don't re-render the leader's selections, since the local selections
// match theirs.
if Some(selection.peer_id) == editor.leader_peer_id {
continue;
}
let key = HoveredCursor {
replica_id: selection.replica_id,
selection_id: selection.selection.id,
};
let is_shown =
editor.show_cursor_names || editor.hovered_cursors.contains_key(&key);
remote_selections
.entry(selection.replica_id)
.or_insert((selection_style, Vec::new()))
.1
.push(SelectionLayout::new(
selection.selection,
selection.line_mode,
selection.cursor_shape,
&snapshot.display_snapshot,
false,
false,
if is_shown { selection.user_name } else { None },
));
}
let key = HoveredCursor {
replica_id: selection.replica_id,
selection_id: selection.selection.id,
};
let is_shown =
editor.show_cursor_names || editor.hovered_cursors.contains_key(&key);
remote_selections
.entry(selection.replica_id)
.or_insert((selection_style, Vec::new()))
.1
.push(SelectionLayout::new(
selection.selection,
selection.line_mode,
selection.cursor_shape,
&snapshot.display_snapshot,
false,
false,
if is_shown { selection.user_name } else { None },
));
selections.extend(remote_selections.into_values());
}
_ => {
if !editor.is_focused(window) && editor.show_cursor_when_unfocused {
let layouts = snapshot
.buffer_snapshot
.selections_in_range(&(start_anchor..end_anchor), true)
.map(move |(_, line_mode, cursor_shape, selection)| {
SelectionLayout::new(
selection,
line_mode,
cursor_shape,
&snapshot.display_snapshot,
false,
false,
None,
)
})
.collect::<Vec<_>>();
let player = editor.current_user_player_color(cx);
selections.push((player, layouts));
}
}
selections.extend(remote_selections.into_values());
} else if !editor.is_focused(window) && editor.show_cursor_when_unfocused {
let layouts = snapshot
.buffer_snapshot
.selections_in_range(&(start_anchor..end_anchor), true)
.map(move |(_, line_mode, cursor_shape, selection)| {
SelectionLayout::new(
selection,
line_mode,
cursor_shape,
&snapshot.display_snapshot,
false,
false,
None,
)
})
.collect::<Vec<_>>();
let player = editor.current_user_player_color(cx);
selections.push((player, layouts));
}
});
(selections, active_rows, newest_selection_head)
@@ -2056,21 +2089,18 @@ impl EditorElement {
cx: &mut App,
) -> Vec<AnyElement> {
self.editor.update(cx, |editor, cx| {
let active_task_indicator_row =
if let Some(crate::CodeContextMenu::CodeActions(CodeActionsMenu {
let active_task_indicator_row = match editor.context_menu.borrow().as_ref() {
Some(crate::CodeContextMenu::CodeActions(CodeActionsMenu {
deployed_from_indicator,
actions,
..
})) = editor.context_menu.borrow().as_ref()
{
actions
.tasks
.as_ref()
.map(|tasks| tasks.position.to_display_point(snapshot).row())
.or(*deployed_from_indicator)
} else {
None
};
})) => actions
.tasks
.as_ref()
.map(|tasks| tasks.position.to_display_point(snapshot).row())
.or(*deployed_from_indicator),
_ => None,
};
let offset_range_start =
snapshot.display_point_to_point(DisplayPoint::new(range.start, 0), Bias::Left);
@@ -4079,11 +4109,12 @@ impl EditorElement {
);
let maybe_element = self.editor.update(cx, |editor, cx| {
if let Some(popover) = editor.signature_help_state.popover_mut() {
let element = popover.render(max_size, cx);
Some(element)
} else {
None
match editor.signature_help_state.popover_mut() {
Some(popover) => {
let element = popover.render(max_size, cx);
Some(element)
}
_ => None,
}
});
if let Some(mut element) = maybe_element {
@@ -4189,7 +4220,11 @@ impl EditorElement {
None;
for (&new_row, &new_background) in &layout.highlighted_rows {
match &mut current_paint {
Some((current_background, current_range, mut edges)) => {
&mut Some((
ref mut current_background,
ref mut current_range,
mut edges,
)) => {
let current_background = *current_background;
let new_range_started = current_background != new_background
|| current_range.end.next_row() != new_row;
@@ -4916,7 +4951,7 @@ impl EditorElement {
}
editor.update(cx, |editor, cx| {
if let Some((scrollbar_layout, axis)) = event
match event
.pressed_button
.filter(|button| *button == MouseButton::Left)
.and(editor.scroll_manager.dragging_scrollbar_axis())
@@ -4924,27 +4959,29 @@ impl EditorElement {
scrollbars_layout
.iter_scrollbars()
.find(|(_, a)| *a == axis)
})
{
let ScrollbarLayout {
hitbox,
text_unit_size,
..
} = scrollbar_layout;
}) {
Some((scrollbar_layout, axis)) => {
let ScrollbarLayout {
hitbox,
text_unit_size,
..
} = scrollbar_layout;
let old_position = mouse_position.along(axis);
let new_position = event.position.along(axis);
if (hitbox.origin.along(axis)..hitbox.bottom_right().along(axis))
.contains(&old_position)
{
let position = editor.scroll_position(cx).apply_along(axis, |p| {
(p + (new_position - old_position) / *text_unit_size).max(0.)
});
editor.set_scroll_position(position, window, cx);
let old_position = mouse_position.along(axis);
let new_position = event.position.along(axis);
if (hitbox.origin.along(axis)..hitbox.bottom_right().along(axis))
.contains(&old_position)
{
let position = editor.scroll_position(cx).apply_along(axis, |p| {
(p + (new_position - old_position) / *text_unit_size).max(0.)
});
editor.set_scroll_position(position, window, cx);
}
cx.stop_propagation();
}
_ => {
editor.scroll_manager.reset_scrollbar_dragging_state(cx);
}
cx.stop_propagation();
} else {
editor.scroll_manager.reset_scrollbar_dragging_state(cx);
}
if scrollbars_layout.get_hovered_axis(window).is_some() {
@@ -5655,13 +5692,12 @@ pub struct AcceptEditPredictionBinding(pub(crate) Option<gpui::KeyBinding>);
impl AcceptEditPredictionBinding {
pub fn keystroke(&self) -> Option<&Keystroke> {
if let Some(binding) = self.0.as_ref() {
match &binding.keystrokes() {
match self.0.as_ref() {
Some(binding) => match &binding.keystrokes() {
[keystroke] => Some(keystroke),
_ => None,
}
} else {
None
},
_ => None,
}
}
}
@@ -5949,89 +5985,9 @@ impl LineWithInvisibles {
is_tab: false,
replacement: None,
}]) {
if let Some(replacement) = highlighted_chunk.replacement {
if !line.is_empty() {
let shaped_line = window
.text_system()
.shape_line(line.clone().into(), font_size, &styles)
.unwrap();
width += shaped_line.width;
len += shaped_line.len;
fragments.push(LineFragment::Text(shaped_line));
line.clear();
styles.clear();
}
match replacement {
ChunkReplacement::Renderer(renderer) => {
let available_width = if renderer.constrain_width {
let chunk = if highlighted_chunk.text == ellipsis.as_ref() {
ellipsis.clone()
} else {
SharedString::from(Arc::from(highlighted_chunk.text))
};
let shaped_line = window
.text_system()
.shape_line(
chunk,
font_size,
&[text_style.to_run(highlighted_chunk.text.len())],
)
.unwrap();
AvailableSpace::Definite(shaped_line.width)
} else {
AvailableSpace::MinContent
};
let mut element = (renderer.render)(&mut ChunkRendererContext {
context: cx,
window,
max_width: text_width,
});
let line_height = text_style.line_height_in_pixels(window.rem_size());
let size = element.layout_as_root(
size(available_width, AvailableSpace::Definite(line_height)),
window,
cx,
);
width += size.width;
len += highlighted_chunk.text.len();
fragments.push(LineFragment::Element {
element: Some(element),
size,
len: highlighted_chunk.text.len(),
});
}
ChunkReplacement::Str(x) => {
let text_style = if let Some(style) = highlighted_chunk.style {
Cow::Owned(text_style.clone().highlight(style))
} else {
Cow::Borrowed(text_style)
};
let run = TextRun {
len: x.len(),
font: text_style.font(),
color: text_style.color,
background_color: text_style.background_color,
underline: text_style.underline,
strikethrough: text_style.strikethrough,
};
let line_layout = window
.text_system()
.shape_line(x, font_size, &[run])
.unwrap()
.with_len(highlighted_chunk.text.len());
width += line_layout.width;
len += highlighted_chunk.text.len();
fragments.push(LineFragment::Text(line_layout))
}
}
} else {
for (ix, mut line_chunk) in highlighted_chunk.text.split('\n').enumerate() {
if ix > 0 {
match highlighted_chunk.replacement {
Some(replacement) => {
if !line.is_empty() {
let shaped_line = window
.text_system()
.shape_line(line.clone().into(), font_size, &styles)
@@ -6039,80 +5995,161 @@ impl LineWithInvisibles {
width += shaped_line.width;
len += shaped_line.len;
fragments.push(LineFragment::Text(shaped_line));
layouts.push(Self {
width: mem::take(&mut width),
len: mem::take(&mut len),
fragments: mem::take(&mut fragments),
invisibles: std::mem::take(&mut invisibles),
font_size,
});
line.clear();
styles.clear();
row += 1;
line_exceeded_max_len = false;
non_whitespace_added = false;
if row == max_line_count {
return layouts;
}
}
if !line_chunk.is_empty() && !line_exceeded_max_len {
let text_style = if let Some(style) = highlighted_chunk.style {
Cow::Owned(text_style.clone().highlight(style))
} else {
Cow::Borrowed(text_style)
};
if line.len() + line_chunk.len() > max_line_len {
let mut chunk_len = max_line_len - line.len();
while !line_chunk.is_char_boundary(chunk_len) {
chunk_len -= 1;
}
line_chunk = &line_chunk[..chunk_len];
line_exceeded_max_len = true;
}
styles.push(TextRun {
len: line_chunk.len(),
font: text_style.font(),
color: text_style.color,
background_color: text_style.background_color,
underline: text_style.underline,
strikethrough: text_style.strikethrough,
});
if editor_mode == EditorMode::Full {
// Line wrap pads its contents with fake whitespaces,
// avoid printing them
let is_soft_wrapped = is_row_soft_wrapped(row);
if highlighted_chunk.is_tab {
if non_whitespace_added || !is_soft_wrapped {
invisibles.push(Invisible::Tab {
line_start_offset: line.len(),
line_end_offset: line.len() + line_chunk.len(),
});
}
match replacement {
ChunkReplacement::Renderer(renderer) => {
let available_width = if renderer.constrain_width {
let chunk = if highlighted_chunk.text == ellipsis.as_ref() {
ellipsis.clone()
} else {
SharedString::from(Arc::from(highlighted_chunk.text))
};
let shaped_line = window
.text_system()
.shape_line(
chunk,
font_size,
&[text_style.to_run(highlighted_chunk.text.len())],
)
.unwrap();
AvailableSpace::Definite(shaped_line.width)
} else {
invisibles.extend(line_chunk.char_indices().filter_map(
|(index, c)| {
let is_whitespace = c.is_whitespace();
non_whitespace_added |= !is_whitespace;
if is_whitespace
&& (non_whitespace_added || !is_soft_wrapped)
{
Some(Invisible::Whitespace {
line_offset: line.len() + index,
})
} else {
None
}
},
))
AvailableSpace::MinContent
};
let mut element = (renderer.render)(&mut ChunkRendererContext {
context: cx,
window,
max_width: text_width,
});
let line_height = text_style.line_height_in_pixels(window.rem_size());
let size = element.layout_as_root(
size(available_width, AvailableSpace::Definite(line_height)),
window,
cx,
);
width += size.width;
len += highlighted_chunk.text.len();
fragments.push(LineFragment::Element {
element: Some(element),
size,
len: highlighted_chunk.text.len(),
});
}
ChunkReplacement::Str(x) => {
let text_style = match highlighted_chunk.style {
Some(style) => Cow::Owned(text_style.clone().highlight(style)),
_ => Cow::Borrowed(text_style),
};
let run = TextRun {
len: x.len(),
font: text_style.font(),
color: text_style.color,
background_color: text_style.background_color,
underline: text_style.underline,
strikethrough: text_style.strikethrough,
};
let line_layout = window
.text_system()
.shape_line(x, font_size, &[run])
.unwrap()
.with_len(highlighted_chunk.text.len());
width += line_layout.width;
len += highlighted_chunk.text.len();
fragments.push(LineFragment::Text(line_layout))
}
}
}
_ => {
for (ix, mut line_chunk) in highlighted_chunk.text.split('\n').enumerate() {
if ix > 0 {
let shaped_line = window
.text_system()
.shape_line(line.clone().into(), font_size, &styles)
.unwrap();
width += shaped_line.width;
len += shaped_line.len;
fragments.push(LineFragment::Text(shaped_line));
layouts.push(Self {
width: mem::take(&mut width),
len: mem::take(&mut len),
fragments: mem::take(&mut fragments),
invisibles: std::mem::take(&mut invisibles),
font_size,
});
line.clear();
styles.clear();
row += 1;
line_exceeded_max_len = false;
non_whitespace_added = false;
if row == max_line_count {
return layouts;
}
}
line.push_str(line_chunk);
if !line_chunk.is_empty() && !line_exceeded_max_len {
let text_style = match highlighted_chunk.style {
Some(style) => Cow::Owned(text_style.clone().highlight(style)),
_ => Cow::Borrowed(text_style),
};
if line.len() + line_chunk.len() > max_line_len {
let mut chunk_len = max_line_len - line.len();
while !line_chunk.is_char_boundary(chunk_len) {
chunk_len -= 1;
}
line_chunk = &line_chunk[..chunk_len];
line_exceeded_max_len = true;
}
styles.push(TextRun {
len: line_chunk.len(),
font: text_style.font(),
color: text_style.color,
background_color: text_style.background_color,
underline: text_style.underline,
strikethrough: text_style.strikethrough,
});
if editor_mode == EditorMode::Full {
// Line wrap pads its contents with fake whitespaces,
// avoid printing them
let is_soft_wrapped = is_row_soft_wrapped(row);
if highlighted_chunk.is_tab {
if non_whitespace_added || !is_soft_wrapped {
invisibles.push(Invisible::Tab {
line_start_offset: line.len(),
line_end_offset: line.len() + line_chunk.len(),
});
}
} else {
invisibles.extend(line_chunk.char_indices().filter_map(
|(index, c)| {
let is_whitespace = c.is_whitespace();
non_whitespace_added |= !is_whitespace;
if is_whitespace
&& (non_whitespace_added || !is_soft_wrapped)
{
Some(Invisible::Whitespace {
line_offset: line.len() + index,
})
} else {
None
}
},
))
}
}
line.push_str(line_chunk);
}
}
}
}
@@ -8067,17 +8104,18 @@ impl PositionMap {
let x = position.x + (scroll_position.x * self.em_width);
let row = ((y / self.line_height) + scroll_position.y) as u32;
let (column, x_overshoot_after_line_end) = if let Some(line) = self
let (column, x_overshoot_after_line_end) = match self
.line_layouts
.get(row as usize - scroll_position.y as usize)
{
if let Some(ix) = line.index_for_x(x) {
(ix as u32, px(0.))
} else {
(line.len as u32, px(0.).max(x - line.width))
Some(line) => {
if let Some(ix) = line.index_for_x(x) {
(ix as u32, px(0.))
} else {
(line.len as u32, px(0.).max(x - line.width))
}
}
} else {
(0, x)
_ => (0, x),
};
let mut exact_unclipped = DisplayPoint::new(DisplayRow(row), column);

View File

@@ -192,7 +192,7 @@ impl GitBlame {
&'a mut self,
rows: &'a [RowInfo],
cx: &App,
) -> impl 'a + Iterator<Item = Option<BlameEntry>> {
) -> impl 'a + Iterator<Item = Option<BlameEntry>> + use<'a> {
self.sync(cx);
let buffer_id = self.buffer_snapshot.remote_id();
@@ -480,15 +480,14 @@ async fn parse_commit_messages(
.and_then(|remote_url| parse_git_remote_url(provider_registry, remote_url));
for (oid, message) in messages {
let permalink = if let Some((provider, git_remote)) = parsed_remote_url.as_ref() {
Some(provider.build_commit_permalink(
let permalink = match parsed_remote_url.as_ref() {
Some((provider, git_remote)) => Some(provider.build_commit_permalink(
git_remote,
git::BuildCommitPermalinkParams {
sha: oid.to_string().as_str(),
},
))
} else {
None
)),
_ => None,
};
let remote = parsed_remote_url

View File

@@ -472,21 +472,19 @@ pub fn show_link_definition(
_ => GotoDefinitionKind::Type,
};
let (mut hovered_link_state, is_cached) =
if let Some(existing) = editor.hovered_link_state.take() {
(existing, true)
} else {
(
HoveredLinkState {
last_trigger_point: trigger_point.clone(),
symbol_range: None,
preferred_kind,
links: vec![],
task: None,
},
false,
)
};
let (mut hovered_link_state, is_cached) = match editor.hovered_link_state.take() {
Some(existing) => (existing, true),
_ => (
HoveredLinkState {
last_trigger_point: trigger_point.clone(),
symbol_range: None,
preferred_kind,
links: vec![],
task: None,
},
false,
),
};
if editor.pending_rename.is_some() {
return;
@@ -537,9 +535,9 @@ pub fn show_link_definition(
hovered_link_state.task = Some(cx.spawn_in(window, async move |this, cx| {
async move {
let result = match &trigger_point {
TriggerPoint::Text(_) => {
if let Some((url_range, url)) = find_url(&buffer, buffer_position, cx.clone()) {
this.update(cx, |_, _| {
TriggerPoint::Text(_) => match find_url(&buffer, buffer_position, cx.clone()) {
Some((url_range, url)) => this
.update(cx, |_, _| {
let range = maybe!({
let start =
snapshot.anchor_in_excerpt(excerpt_id, url_range.start)?;
@@ -548,46 +546,58 @@ pub fn show_link_definition(
});
(range, vec![HoverLink::Url(url)])
})
.ok()
} else if let Some((filename_range, filename)) =
find_file(&buffer, project.clone(), buffer_position, cx).await
{
let range = maybe!({
let start =
snapshot.anchor_in_excerpt(excerpt_id, filename_range.start)?;
let end = snapshot.anchor_in_excerpt(excerpt_id, filename_range.end)?;
Some(RangeInEditor::Text(start..end))
});
.ok(),
_ => match find_file(&buffer, project.clone(), buffer_position, cx).await {
Some((filename_range, filename)) => {
let range = maybe!({
let start =
snapshot.anchor_in_excerpt(excerpt_id, filename_range.start)?;
let end =
snapshot.anchor_in_excerpt(excerpt_id, filename_range.end)?;
Some(RangeInEditor::Text(start..end))
});
Some((range, vec![HoverLink::File(filename)]))
} else if let Some(provider) = provider {
let task = cx.update(|_, cx| {
provider.definitions(&buffer, buffer_position, preferred_kind, cx)
})?;
if let Some(task) = task {
task.await.ok().map(|definition_result| {
(
definition_result.iter().find_map(|link| {
link.origin.as_ref().and_then(|origin| {
let start = snapshot.anchor_in_excerpt(
excerpt_id,
origin.range.start,
)?;
let end = snapshot
.anchor_in_excerpt(excerpt_id, origin.range.end)?;
Some(RangeInEditor::Text(start..end))
})
}),
definition_result.into_iter().map(HoverLink::Text).collect(),
)
})
} else {
None
Some((range, vec![HoverLink::File(filename)]))
}
} else {
None
}
}
_ => match provider {
Some(provider) => {
let task = cx.update(|_, cx| {
provider.definitions(
&buffer,
buffer_position,
preferred_kind,
cx,
)
})?;
match task {
Some(task) => task.await.ok().map(|definition_result| {
(
definition_result.iter().find_map(|link| {
link.origin.as_ref().and_then(|origin| {
let start = snapshot.anchor_in_excerpt(
excerpt_id,
origin.range.start,
)?;
let end = snapshot.anchor_in_excerpt(
excerpt_id,
origin.range.end,
)?;
Some(RangeInEditor::Text(start..end))
})
}),
definition_result
.into_iter()
.map(HoverLink::Text)
.collect(),
)
}),
_ => None,
}
}
_ => None,
},
},
},
TriggerPoint::InlayHint(highlight, lsp_location, server_id) => Some((
Some(RangeInEditor::Inlay(highlight.clone())),
vec![HoverLink::InlayHint(lsp_location.clone(), *server_id)],
@@ -606,46 +616,57 @@ pub fn show_link_definition(
.as_ref()
.and_then(|(symbol_range, _)| symbol_range.clone());
if let Some((symbol_range, definitions)) = result {
hovered_link_state.links = definitions;
match result {
Some((symbol_range, definitions)) => {
hovered_link_state.links = definitions;
let underline_hovered_link = !hovered_link_state.links.is_empty()
|| hovered_link_state.symbol_range.is_some();
let underline_hovered_link = !hovered_link_state.links.is_empty()
|| hovered_link_state.symbol_range.is_some();
if underline_hovered_link {
let style = gpui::HighlightStyle {
underline: Some(gpui::UnderlineStyle {
thickness: px(1.),
if underline_hovered_link {
let style = gpui::HighlightStyle {
underline: Some(gpui::UnderlineStyle {
thickness: px(1.),
..Default::default()
}),
color: Some(cx.theme().colors().link_text_hover),
..Default::default()
}),
color: Some(cx.theme().colors().link_text_hover),
..Default::default()
};
let highlight_range =
symbol_range.unwrap_or_else(|| match &trigger_point {
TriggerPoint::Text(trigger_anchor) => {
// If no symbol range returned from language server, use the surrounding word.
let (offset_range, _) =
snapshot.surrounding_word(*trigger_anchor, false);
RangeInEditor::Text(
snapshot.anchor_before(offset_range.start)
..snapshot.anchor_after(offset_range.end),
)
}
TriggerPoint::InlayHint(highlight, _, _) => {
RangeInEditor::Inlay(highlight.clone())
}
});
};
let highlight_range =
symbol_range.unwrap_or_else(|| match &trigger_point {
TriggerPoint::Text(trigger_anchor) => {
// If no symbol range returned from language server, use the surrounding word.
let (offset_range, _) =
snapshot.surrounding_word(*trigger_anchor, false);
RangeInEditor::Text(
snapshot.anchor_before(offset_range.start)
..snapshot.anchor_after(offset_range.end),
)
}
TriggerPoint::InlayHint(highlight, _, _) => {
RangeInEditor::Inlay(highlight.clone())
}
});
match highlight_range {
RangeInEditor::Text(text_range) => editor
.highlight_text::<HoveredLinkState>(vec![text_range], style, cx),
RangeInEditor::Inlay(highlight) => editor
.highlight_inlays::<HoveredLinkState>(vec![highlight], style, cx),
match highlight_range {
RangeInEditor::Text(text_range) => editor
.highlight_text::<HoveredLinkState>(
vec![text_range],
style,
cx,
),
RangeInEditor::Inlay(highlight) => editor
.highlight_inlays::<HoveredLinkState>(
vec![highlight],
style,
cx,
),
}
}
}
} else {
editor.hide_hovered_link(cx);
_ => {
editor.hide_hovered_link(cx);
}
}
})?;

View File

@@ -289,126 +289,134 @@ fn show_hover(
// Find the entry with the most specific range
.min_by_key(|entry| entry.range.len());
let diagnostic_popover = if let Some(local_diagnostic) = local_diagnostic {
let text = match local_diagnostic.diagnostic.source {
Some(ref source) => {
format!("{source}: {}", local_diagnostic.diagnostic.message)
}
None => local_diagnostic.diagnostic.message.clone(),
};
let local_diagnostic = DiagnosticEntry {
diagnostic: local_diagnostic.diagnostic,
range: snapshot
.buffer_snapshot
.anchor_before(local_diagnostic.range.start)
..snapshot
let diagnostic_popover = match local_diagnostic {
Some(local_diagnostic) => {
let text = match local_diagnostic.diagnostic.source {
Some(ref source) => {
format!("{source}: {}", local_diagnostic.diagnostic.message)
}
None => local_diagnostic.diagnostic.message.clone(),
};
let local_diagnostic = DiagnosticEntry {
diagnostic: local_diagnostic.diagnostic,
range: snapshot
.buffer_snapshot
.anchor_after(local_diagnostic.range.end),
};
.anchor_before(local_diagnostic.range.start)
..snapshot
.buffer_snapshot
.anchor_after(local_diagnostic.range.end),
};
let mut border_color: Option<Hsla> = None;
let mut background_color: Option<Hsla> = None;
let mut border_color: Option<Hsla> = None;
let mut background_color: Option<Hsla> = None;
let parsed_content = cx
.new_window_entity(|window, cx| {
let status_colors = cx.theme().status();
let parsed_content = cx
.new_window_entity(|window, cx| {
let status_colors = cx.theme().status();
match local_diagnostic.diagnostic.severity {
DiagnosticSeverity::ERROR => {
background_color = Some(status_colors.error_background);
border_color = Some(status_colors.error_border);
}
DiagnosticSeverity::WARNING => {
background_color = Some(status_colors.warning_background);
border_color = Some(status_colors.warning_border);
}
DiagnosticSeverity::INFORMATION => {
background_color = Some(status_colors.info_background);
border_color = Some(status_colors.info_border);
}
DiagnosticSeverity::HINT => {
background_color = Some(status_colors.hint_background);
border_color = Some(status_colors.hint_border);
}
_ => {
background_color = Some(status_colors.ignored_background);
border_color = Some(status_colors.ignored_border);
}
};
let settings = ThemeSettings::get_global(cx);
let mut base_text_style = window.text_style();
base_text_style.refine(&TextStyleRefinement {
font_family: Some(settings.ui_font.family.clone()),
font_fallbacks: settings.ui_font.fallbacks.clone(),
font_size: Some(settings.ui_font_size(cx).into()),
color: Some(cx.theme().colors().editor_foreground),
background_color: Some(gpui::transparent_black()),
match local_diagnostic.diagnostic.severity {
DiagnosticSeverity::ERROR => {
background_color = Some(status_colors.error_background);
border_color = Some(status_colors.error_border);
}
DiagnosticSeverity::WARNING => {
background_color = Some(status_colors.warning_background);
border_color = Some(status_colors.warning_border);
}
DiagnosticSeverity::INFORMATION => {
background_color = Some(status_colors.info_background);
border_color = Some(status_colors.info_border);
}
DiagnosticSeverity::HINT => {
background_color = Some(status_colors.hint_background);
border_color = Some(status_colors.hint_border);
}
_ => {
background_color = Some(status_colors.ignored_background);
border_color = Some(status_colors.ignored_border);
}
};
let settings = ThemeSettings::get_global(cx);
let mut base_text_style = window.text_style();
base_text_style.refine(&TextStyleRefinement {
font_family: Some(settings.ui_font.family.clone()),
font_fallbacks: settings.ui_font.fallbacks.clone(),
font_size: Some(settings.ui_font_size(cx).into()),
color: Some(cx.theme().colors().editor_foreground),
background_color: Some(gpui::transparent_black()),
..Default::default()
});
let markdown_style = MarkdownStyle {
base_text_style,
selection_background_color: { cx.theme().players().local().selection },
link: TextStyleRefinement {
underline: Some(gpui::UnderlineStyle {
thickness: px(1.),
color: Some(cx.theme().colors().editor_foreground),
wavy: false,
}),
..Default::default()
},
..Default::default()
};
Markdown::new_text(SharedString::new(text), markdown_style.clone(), cx)
.open_url(open_markdown_url)
})
.ok();
});
let markdown_style = MarkdownStyle {
base_text_style,
selection_background_color: {
cx.theme().players().local().selection
},
link: TextStyleRefinement {
underline: Some(gpui::UnderlineStyle {
thickness: px(1.),
color: Some(cx.theme().colors().editor_foreground),
wavy: false,
}),
..Default::default()
},
..Default::default()
};
Markdown::new_text(SharedString::new(text), markdown_style.clone(), cx)
.open_url(open_markdown_url)
})
.ok();
Some(DiagnosticPopover {
local_diagnostic,
parsed_content,
border_color,
background_color,
keyboard_grace: Rc::new(RefCell::new(ignore_timeout)),
anchor: Some(anchor),
})
} else {
None
Some(DiagnosticPopover {
local_diagnostic,
parsed_content,
border_color,
background_color,
keyboard_grace: Rc::new(RefCell::new(ignore_timeout)),
anchor: Some(anchor),
})
}
_ => None,
};
this.update(cx, |this, _| {
this.hover_state.diagnostic_popover = diagnostic_popover;
})?;
let invisible_char = if let Some(invisible) = snapshot
let invisible_char = match snapshot
.buffer_snapshot
.chars_at(anchor)
.next()
.filter(|&c| is_invisible(c))
{
let after = snapshot.buffer_snapshot.anchor_after(
anchor.to_offset(&snapshot.buffer_snapshot) + invisible.len_utf8(),
);
Some((invisible, anchor..after))
} else if let Some(invisible) = snapshot
.buffer_snapshot
.reversed_chars_at(anchor)
.next()
.filter(|&c| is_invisible(c))
{
let before = snapshot.buffer_snapshot.anchor_before(
anchor.to_offset(&snapshot.buffer_snapshot) - invisible.len_utf8(),
);
Some(invisible) => {
let after = snapshot.buffer_snapshot.anchor_after(
anchor.to_offset(&snapshot.buffer_snapshot) + invisible.len_utf8(),
);
Some((invisible, anchor..after))
}
_ => {
match snapshot
.buffer_snapshot
.reversed_chars_at(anchor)
.next()
.filter(|&c| is_invisible(c))
{
Some(invisible) => {
let before = snapshot.buffer_snapshot.anchor_before(
anchor.to_offset(&snapshot.buffer_snapshot) - invisible.len_utf8(),
);
Some((invisible, before..anchor))
} else {
None
Some((invisible, before..anchor))
}
_ => None,
}
}
};
let hovers_response = if let Some(hover_request) = hover_request {
hover_request.await
} else {
Vec::new()
let hovers_response = match hover_request {
Some(hover_request) => hover_request.await,
_ => Vec::new(),
};
let snapshot = this.update_in(cx, |this, window, cx| this.snapshot(window, cx))?;
let mut hover_highlights = Vec::with_capacity(hovers_response.len());
@@ -543,11 +551,12 @@ async fn parse_blocks(
language: Option<Arc<Language>>,
cx: &mut AsyncWindowContext,
) -> Option<Entity<Markdown>> {
let fallback_language_name = if let Some(ref l) = language {
let l = Arc::clone(l);
Some(l.lsp_id().clone())
} else {
None
let fallback_language_name = match language {
Some(ref l) => {
let l = Arc::clone(l);
Some(l.lsp_id().clone())
}
_ => None,
};
let combined_text = blocks

View File

@@ -36,16 +36,17 @@ impl Editor {
cx: &mut Context<Editor>,
) -> Option<Vec<IndentGuide>> {
let show_indent_guides = self.should_show_indent_guides().unwrap_or_else(|| {
if let Some(buffer) = self.buffer().read(cx).as_singleton() {
language_settings(
buffer.read(cx).language().map(|l| l.name()),
buffer.read(cx).file(),
cx,
)
.indent_guides
.enabled
} else {
true
match self.buffer().read(cx).as_singleton() {
Some(buffer) => {
language_settings(
buffer.read(cx).language().map(|l| l.name()),
buffer.read(cx).file(),
cx,
)
.indent_guides
.enabled
}
_ => true,
}
});

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