Compare commits
2 Commits
commit-vie
...
rust-2024
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8f44ff7d31 | ||
|
|
e85ec8ffb7 |
24
Cargo.lock
generated
24
Cargo.lock
generated
@@ -1910,6 +1910,24 @@ name = "bindgen"
|
|||||||
version = "0.70.1"
|
version = "0.70.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f"
|
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 = [
|
dependencies = [
|
||||||
"bitflags 2.9.0",
|
"bitflags 2.9.0",
|
||||||
"cexpr",
|
"cexpr",
|
||||||
@@ -1920,7 +1938,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"regex",
|
"regex",
|
||||||
"rustc-hash 1.1.0",
|
"rustc-hash 2.1.1",
|
||||||
"shlex",
|
"shlex",
|
||||||
"syn 2.0.100",
|
"syn 2.0.100",
|
||||||
]
|
]
|
||||||
@@ -5848,7 +5866,7 @@ dependencies = [
|
|||||||
"ashpd",
|
"ashpd",
|
||||||
"async-task",
|
"async-task",
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"bindgen 0.70.1",
|
"bindgen 0.71.1",
|
||||||
"blade-graphics",
|
"blade-graphics",
|
||||||
"blade-macros",
|
"blade-macros",
|
||||||
"blade-util",
|
"blade-util",
|
||||||
@@ -8220,7 +8238,7 @@ name = "media"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bindgen 0.70.1",
|
"bindgen 0.71.1",
|
||||||
"core-foundation 0.10.0",
|
"core-foundation 0.10.0",
|
||||||
"core-video",
|
"core-video",
|
||||||
"ctor",
|
"ctor",
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ impl ActivityIndicator {
|
|||||||
fn pending_language_server_work<'a>(
|
fn pending_language_server_work<'a>(
|
||||||
&self,
|
&self,
|
||||||
cx: &'a App,
|
cx: &'a App,
|
||||||
) -> impl Iterator<Item = PendingWork<'a>> {
|
) -> impl Iterator<Item = PendingWork<'a>> + use<'a> {
|
||||||
self.project
|
self.project
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.language_server_statuses(cx)
|
.language_server_statuses(cx)
|
||||||
|
|||||||
@@ -89,14 +89,16 @@ impl AskPassSession {
|
|||||||
buffer.clear();
|
buffer.clear();
|
||||||
}
|
}
|
||||||
let prompt = String::from_utf8_lossy(&buffer);
|
let prompt = String::from_utf8_lossy(&buffer);
|
||||||
if let Some(password) = delegate
|
match delegate
|
||||||
.ask_password(prompt.to_string())
|
.ask_password(prompt.to_string())
|
||||||
.await
|
.await
|
||||||
.context("failed to get askpass password")
|
.context("failed to get askpass password")
|
||||||
.log_err()
|
.log_err()
|
||||||
{
|
{
|
||||||
|
Some(password) => {
|
||||||
stream.write_all(password.as_bytes()).await.log_err();
|
stream.write_all(password.as_bytes()).await.log_err();
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
if let Some(kill_tx) = kill_tx.take() {
|
if let Some(kill_tx) = kill_tx.take() {
|
||||||
kill_tx.send(()).log_err();
|
kill_tx.send(()).log_err();
|
||||||
}
|
}
|
||||||
@@ -107,6 +109,7 @@ impl AskPassSession {
|
|||||||
drop(stream);
|
drop(stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
drop(temp_dir)
|
drop(temp_dir)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -741,20 +741,29 @@ impl AssistantPanel {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(context_editor) = context_editor {
|
match context_editor {
|
||||||
Some(InlineAssistTarget::Editor(context_editor, false))
|
Some(context_editor) => Some(InlineAssistTarget::Editor(context_editor, false)),
|
||||||
} else if let Some(workspace_editor) = workspace
|
_ => {
|
||||||
|
match workspace
|
||||||
.active_item(cx)
|
.active_item(cx)
|
||||||
.and_then(|item| item.act_as::<Editor>(cx))
|
.and_then(|item| item.act_as::<Editor>(cx))
|
||||||
{
|
{
|
||||||
|
Some(workspace_editor) => {
|
||||||
Some(InlineAssistTarget::Editor(workspace_editor, true))
|
Some(InlineAssistTarget::Editor(workspace_editor, true))
|
||||||
} else if let Some(terminal_view) = workspace
|
}
|
||||||
|
_ => {
|
||||||
|
match workspace
|
||||||
.active_item(cx)
|
.active_item(cx)
|
||||||
.and_then(|item| item.act_as::<TerminalView>(cx))
|
.and_then(|item| item.act_as::<TerminalView>(cx))
|
||||||
{
|
{
|
||||||
|
Some(terminal_view) => {
|
||||||
Some(InlineAssistTarget::Terminal(terminal_view))
|
Some(InlineAssistTarget::Terminal(terminal_view))
|
||||||
} else {
|
}
|
||||||
None
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -250,9 +250,9 @@ impl InlineAssistant {
|
|||||||
selection.end.column = snapshot
|
selection.end.column = snapshot
|
||||||
.buffer_snapshot
|
.buffer_snapshot
|
||||||
.line_len(MultiBufferRow(selection.end.row));
|
.line_len(MultiBufferRow(selection.end.row));
|
||||||
} else if let Some(fold) =
|
} else {
|
||||||
snapshot.crease_for_buffer_row(MultiBufferRow(selection.end.row))
|
match snapshot.crease_for_buffer_row(MultiBufferRow(selection.end.row)) {
|
||||||
{
|
Some(fold) => {
|
||||||
selection.start = fold.range().start;
|
selection.start = fold.range().start;
|
||||||
selection.end = fold.range().end;
|
selection.end = fold.range().end;
|
||||||
if MultiBufferRow(selection.end.row) < snapshot.buffer_snapshot.max_row() {
|
if MultiBufferRow(selection.end.row) < snapshot.buffer_snapshot.max_row() {
|
||||||
@@ -267,10 +267,9 @@ impl InlineAssistant {
|
|||||||
if c.is_whitespace() {
|
if c.is_whitespace() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if snapshot
|
if snapshot.language_at(selection.end).is_some_and(|language| {
|
||||||
.language_at(selection.end)
|
language.config().brackets.is_closing_brace(c)
|
||||||
.is_some_and(|language| language.config().brackets.is_closing_brace(c))
|
}) {
|
||||||
{
|
|
||||||
selection.end.row += 1;
|
selection.end.row += 1;
|
||||||
selection.end.column = snapshot
|
selection.end.column = snapshot
|
||||||
.buffer_snapshot
|
.buffer_snapshot
|
||||||
@@ -279,6 +278,9 @@ impl InlineAssistant {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(prev_selection) = selections.last_mut() {
|
if let Some(prev_selection) = selections.last_mut() {
|
||||||
if selection.start <= prev_selection.end {
|
if selection.start <= prev_selection.end {
|
||||||
@@ -1031,7 +1033,8 @@ impl InlineAssistant {
|
|||||||
|
|
||||||
let mut scroll_target_top;
|
let mut scroll_target_top;
|
||||||
let mut scroll_target_bottom;
|
let mut scroll_target_bottom;
|
||||||
if let Some(decorations) = assist.decorations.as_ref() {
|
match assist.decorations.as_ref() {
|
||||||
|
Some(decorations) => {
|
||||||
scroll_target_top = editor
|
scroll_target_top = editor
|
||||||
.row_for_block(decorations.prompt_block_id, cx)
|
.row_for_block(decorations.prompt_block_id, cx)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@@ -1040,7 +1043,8 @@ impl InlineAssistant {
|
|||||||
.row_for_block(decorations.end_block_id, cx)
|
.row_for_block(decorations.end_block_id, cx)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.0 as f32;
|
.0 as f32;
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
let snapshot = editor.snapshot(window, cx);
|
let snapshot = editor.snapshot(window, cx);
|
||||||
let start_row = assist
|
let start_row = assist
|
||||||
.range
|
.range
|
||||||
@@ -1050,6 +1054,7 @@ impl InlineAssistant {
|
|||||||
scroll_target_top = start_row.0 as f32;
|
scroll_target_top = start_row.0 as f32;
|
||||||
scroll_target_bottom = scroll_target_top + 1.;
|
scroll_target_bottom = scroll_target_top + 1.;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
scroll_target_top -= editor.vertical_scroll_margin() as f32;
|
scroll_target_top -= editor.vertical_scroll_margin() as f32;
|
||||||
scroll_target_bottom += 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) {
|
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) {
|
let assist = match self.assists.get_mut(&assist_id) {
|
||||||
assist
|
Some(assist) => assist,
|
||||||
} else {
|
_ => {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let assist_group_id = assist.group_id;
|
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) {
|
pub fn stop_assist(&mut self, assist_id: InlineAssistId, cx: &mut App) {
|
||||||
let assist = if let Some(assist) = self.assists.get_mut(&assist_id) {
|
let assist = match self.assists.get_mut(&assist_id) {
|
||||||
assist
|
Some(assist) => assist,
|
||||||
} else {
|
_ => {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
assist.codegen.update(cx, |codegen, cx| codegen.stop(cx));
|
assist.codegen.update(cx, |codegen, cx| codegen.stop(cx));
|
||||||
@@ -2184,7 +2191,7 @@ impl PromptEditor {
|
|||||||
.into_any_element()
|
.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 model = LanguageModelRegistry::read_global(cx).active_model()?;
|
||||||
let token_counts = self.token_counts?;
|
let token_counts = self.token_counts?;
|
||||||
let max_token_count = model.max_token_count();
|
let max_token_count = model.max_token_count();
|
||||||
@@ -2212,7 +2219,8 @@ impl PromptEditor {
|
|||||||
.size(LabelSize::Small)
|
.size(LabelSize::Small)
|
||||||
.color(Color::Muted),
|
.color(Color::Muted),
|
||||||
);
|
);
|
||||||
if let Some(workspace) = self.workspace.clone() {
|
match self.workspace.clone() {
|
||||||
|
Some(workspace) => {
|
||||||
token_count = token_count
|
token_count = token_count
|
||||||
.tooltip(move |window, cx| {
|
.tooltip(move |window, cx| {
|
||||||
Tooltip::with_meta(
|
Tooltip::with_meta(
|
||||||
@@ -2236,16 +2244,18 @@ impl PromptEditor {
|
|||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
});
|
});
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
token_count = token_count
|
token_count = token_count
|
||||||
.cursor_default()
|
.cursor_default()
|
||||||
.tooltip(Tooltip::text("Tokens used"));
|
.tooltip(Tooltip::text("Tokens used"));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Some(token_count)
|
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 settings = ThemeSettings::get_global(cx);
|
||||||
let text_style = TextStyle {
|
let text_style = TextStyle {
|
||||||
color: if self.editor.read(cx).read_only(cx) {
|
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(
|
Popover::new().child(
|
||||||
v_flex()
|
v_flex()
|
||||||
.occlude()
|
.occlude()
|
||||||
@@ -2430,10 +2440,11 @@ impl InlineAssist {
|
|||||||
InlineAssistant::update_global(cx, |this, cx| match event {
|
InlineAssistant::update_global(cx, |this, cx| match event {
|
||||||
CodegenEvent::Undone => this.finish_assist(assist_id, false, window, cx),
|
CodegenEvent::Undone => this.finish_assist(assist_id, false, window, cx),
|
||||||
CodegenEvent::Finished => {
|
CodegenEvent::Finished => {
|
||||||
let assist = if let Some(assist) = this.assists.get(&assist_id) {
|
let assist = match this.assists.get(&assist_id) {
|
||||||
assist
|
Some(assist) => assist,
|
||||||
} else {
|
_ => {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let CodegenStatus::Error(error) = codegen.read(cx).status(cx) {
|
if let CodegenStatus::Error(error) = codegen.read(cx).status(cx) {
|
||||||
@@ -2865,7 +2876,8 @@ impl CodegenAlternative {
|
|||||||
assistant_panel_context: Option<LanguageModelRequest>,
|
assistant_panel_context: Option<LanguageModelRequest>,
|
||||||
cx: &App,
|
cx: &App,
|
||||||
) -> BoxFuture<'static, Result<TokenCounts>> {
|
) -> BoxFuture<'static, Result<TokenCounts>> {
|
||||||
if let Some(model) = LanguageModelRegistry::read_global(cx).active_model() {
|
match LanguageModelRegistry::read_global(cx).active_model() {
|
||||||
|
Some(model) => {
|
||||||
let request = self.build_request(user_prompt, assistant_panel_context.clone(), cx);
|
let request = self.build_request(user_prompt, assistant_panel_context.clone(), cx);
|
||||||
match request {
|
match request {
|
||||||
Ok(request) => {
|
Ok(request) => {
|
||||||
@@ -2884,8 +2896,8 @@ impl CodegenAlternative {
|
|||||||
}
|
}
|
||||||
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,11 +3233,14 @@ impl CodegenAlternative {
|
|||||||
.update(cx, |this, cx| {
|
.update(cx, |this, cx| {
|
||||||
this.message_id = message_id;
|
this.message_id = message_id;
|
||||||
this.last_equal_ranges.clear();
|
this.last_equal_ranges.clear();
|
||||||
if let Err(error) = result {
|
match result {
|
||||||
|
Err(error) => {
|
||||||
this.status = CodegenStatus::Error(error);
|
this.status = CodegenStatus::Error(error);
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
this.status = CodegenStatus::Done;
|
this.status = CodegenStatus::Done;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
this.elapsed_time = Some(elapsed_time);
|
this.elapsed_time = Some(elapsed_time);
|
||||||
this.completion = Some(completion.lock().clone());
|
this.completion = Some(completion.lock().clone());
|
||||||
cx.emit(CodegenEvent::Finished);
|
cx.emit(CodegenEvent::Finished);
|
||||||
|
|||||||
@@ -184,10 +184,11 @@ impl TerminalInlineAssistant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn start_assist(&mut self, assist_id: TerminalInlineAssistId, cx: &mut App) {
|
fn start_assist(&mut self, assist_id: TerminalInlineAssistId, cx: &mut App) {
|
||||||
let assist = if let Some(assist) = self.assists.get_mut(&assist_id) {
|
let assist = match self.assists.get_mut(&assist_id) {
|
||||||
assist
|
Some(assist) => assist,
|
||||||
} else {
|
_ => {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(user_prompt) = assist
|
let Some(user_prompt) = assist
|
||||||
@@ -222,10 +223,11 @@ impl TerminalInlineAssistant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn stop_assist(&mut self, assist_id: TerminalInlineAssistId, cx: &mut App) {
|
fn stop_assist(&mut self, assist_id: TerminalInlineAssistId, cx: &mut App) {
|
||||||
let assist = if let Some(assist) = self.assists.get_mut(&assist_id) {
|
let assist = match self.assists.get_mut(&assist_id) {
|
||||||
assist
|
Some(assist) => assist,
|
||||||
} else {
|
_ => {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
assist.codegen.update(cx, |codegen, cx| codegen.stop(cx));
|
assist.codegen.update(cx, |codegen, cx| codegen.stop(cx));
|
||||||
@@ -436,10 +438,11 @@ impl TerminalInlineAssist {
|
|||||||
window.subscribe(&codegen, cx, move |codegen, event, window, cx| {
|
window.subscribe(&codegen, cx, move |codegen, event, window, cx| {
|
||||||
TerminalInlineAssistant::update_global(cx, |this, cx| match event {
|
TerminalInlineAssistant::update_global(cx, |this, cx| match event {
|
||||||
CodegenEvent::Finished => {
|
CodegenEvent::Finished => {
|
||||||
let assist = if let Some(assist) = this.assists.get(&assist_id) {
|
let assist = match this.assists.get(&assist_id) {
|
||||||
assist
|
Some(assist) => assist,
|
||||||
} else {
|
_ => {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let CodegenStatus::Error(error) = &codegen.read(cx).status {
|
if let CodegenStatus::Error(error) = &codegen.read(cx).status {
|
||||||
@@ -664,8 +667,8 @@ impl Render for PromptEditor {
|
|||||||
},
|
},
|
||||||
gpui::Corner::TopRight,
|
gpui::Corner::TopRight,
|
||||||
))
|
))
|
||||||
.children(
|
.children(match &self.codegen.read(cx).status {
|
||||||
if let CodegenStatus::Error(error) = &self.codegen.read(cx).status {
|
CodegenStatus::Error(error) => {
|
||||||
let error_message = SharedString::from(error.to_string());
|
let error_message = SharedString::from(error.to_string());
|
||||||
Some(
|
Some(
|
||||||
div()
|
div()
|
||||||
@@ -677,10 +680,9 @@ impl Render for PromptEditor {
|
|||||||
.color(Color::Error),
|
.color(Color::Error),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
} else {
|
}
|
||||||
None
|
_ => None,
|
||||||
},
|
}),
|
||||||
),
|
|
||||||
)
|
)
|
||||||
.child(div().flex_1().child(self.render_prompt_editor(cx)))
|
.child(div().flex_1().child(self.render_prompt_editor(cx)))
|
||||||
.child(
|
.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 model = LanguageModelRegistry::read_global(cx).active_model()?;
|
||||||
let token_count = self.token_count?;
|
let token_count = self.token_count?;
|
||||||
let max_token_count = model.max_token_count();
|
let max_token_count = model.max_token_count();
|
||||||
@@ -1007,7 +1009,8 @@ impl PromptEditor {
|
|||||||
.size(LabelSize::Small)
|
.size(LabelSize::Small)
|
||||||
.color(Color::Muted),
|
.color(Color::Muted),
|
||||||
);
|
);
|
||||||
if let Some(workspace) = self.workspace.clone() {
|
match self.workspace.clone() {
|
||||||
|
Some(workspace) => {
|
||||||
token_count = token_count
|
token_count = token_count
|
||||||
.tooltip(|window, cx| {
|
.tooltip(|window, cx| {
|
||||||
Tooltip::with_meta(
|
Tooltip::with_meta(
|
||||||
@@ -1028,16 +1031,18 @@ impl PromptEditor {
|
|||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
});
|
});
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
token_count = token_count
|
token_count = token_count
|
||||||
.cursor_default()
|
.cursor_default()
|
||||||
.tooltip(Tooltip::text("Tokens Used by Inline Assistant"));
|
.tooltip(Tooltip::text("Tokens Used by Inline Assistant"));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Some(token_count)
|
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 settings = ThemeSettings::get_global(cx);
|
||||||
let text_style = TextStyle {
|
let text_style = TextStyle {
|
||||||
color: if self.editor.read(cx).read_only(cx) {
|
color: if self.editor.read(cx).read_only(cx) {
|
||||||
@@ -1217,11 +1222,14 @@ impl Codegen {
|
|||||||
let result = generate.await;
|
let result = generate.await;
|
||||||
|
|
||||||
this.update(cx, |this, cx| {
|
this.update(cx, |this, cx| {
|
||||||
if let Err(error) = result {
|
match result {
|
||||||
|
Err(error) => {
|
||||||
this.status = CodegenStatus::Error(error);
|
this.status = CodegenStatus::Error(error);
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
this.status = CodegenStatus::Done;
|
this.status = CodegenStatus::Done;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
cx.emit(CodegenEvent::Finished);
|
cx.emit(CodegenEvent::Finished);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -77,27 +77,36 @@ impl RenderedMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn append_thinking(&mut self, text: &String, window: &Window, cx: &mut App) {
|
fn append_thinking(&mut self, text: &String, window: &Window, cx: &mut App) {
|
||||||
if let Some(RenderedMessageSegment::Thinking {
|
match self.segments.last_mut() {
|
||||||
|
Some(RenderedMessageSegment::Thinking {
|
||||||
content,
|
content,
|
||||||
scroll_handle,
|
scroll_handle,
|
||||||
}) = self.segments.last_mut()
|
}) => {
|
||||||
{
|
|
||||||
content.update(cx, |markdown, cx| {
|
content.update(cx, |markdown, cx| {
|
||||||
markdown.append(text, cx);
|
markdown.append(text, cx);
|
||||||
});
|
});
|
||||||
scroll_handle.scroll_to_bottom();
|
scroll_handle.scroll_to_bottom();
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
self.segments.push(RenderedMessageSegment::Thinking {
|
self.segments.push(RenderedMessageSegment::Thinking {
|
||||||
content: render_markdown(text.into(), self.language_registry.clone(), window, cx),
|
content: render_markdown(
|
||||||
|
text.into(),
|
||||||
|
self.language_registry.clone(),
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
),
|
||||||
scroll_handle: ScrollHandle::default(),
|
scroll_handle: ScrollHandle::default(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn append_text(&mut self, text: &String, window: &Window, cx: &mut App) {
|
fn append_text(&mut self, text: &String, window: &Window, cx: &mut App) {
|
||||||
if let Some(RenderedMessageSegment::Text(markdown)) = self.segments.last_mut() {
|
match self.segments.last_mut() {
|
||||||
|
Some(RenderedMessageSegment::Text(markdown)) => {
|
||||||
markdown.update(cx, |markdown, cx| markdown.append(text, cx));
|
markdown.update(cx, |markdown, cx| markdown.append(text, cx));
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
self.segments
|
self.segments
|
||||||
.push(RenderedMessageSegment::Text(render_markdown(
|
.push(RenderedMessageSegment::Text(render_markdown(
|
||||||
SharedString::from(text),
|
SharedString::from(text),
|
||||||
@@ -107,6 +116,7 @@ impl RenderedMessage {
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn push_segment(&mut self, segment: &MessageSegment, window: &Window, cx: &mut App) {
|
fn push_segment(&mut self, segment: &MessageSegment, window: &Window, cx: &mut App) {
|
||||||
let rendered_segment = match segment {
|
let rendered_segment = match segment {
|
||||||
@@ -929,21 +939,18 @@ impl ActiveThread {
|
|||||||
let message_content =
|
let message_content =
|
||||||
v_flex()
|
v_flex()
|
||||||
.gap_1p5()
|
.gap_1p5()
|
||||||
.child(
|
.child(match edit_message_editor.clone() {
|
||||||
if let Some(edit_message_editor) = edit_message_editor.clone() {
|
Some(edit_message_editor) => div()
|
||||||
div()
|
|
||||||
.key_context("EditMessageEditor")
|
.key_context("EditMessageEditor")
|
||||||
.on_action(cx.listener(Self::cancel_editing_message))
|
.on_action(cx.listener(Self::cancel_editing_message))
|
||||||
.on_action(cx.listener(Self::confirm_editing_message))
|
.on_action(cx.listener(Self::confirm_editing_message))
|
||||||
.min_h_6()
|
.min_h_6()
|
||||||
.child(edit_message_editor)
|
.child(edit_message_editor),
|
||||||
} else {
|
_ => div()
|
||||||
div()
|
|
||||||
.min_h_6()
|
.min_h_6()
|
||||||
.text_ui(cx)
|
.text_ui(cx)
|
||||||
.child(self.render_message_content(message_id, rendered_message, cx))
|
.child(self.render_message_content(message_id, rendered_message, cx)),
|
||||||
},
|
})
|
||||||
)
|
|
||||||
.when_some(context, |parent, context| {
|
.when_some(context, |parent, context| {
|
||||||
if !context.is_empty() {
|
if !context.is_empty() {
|
||||||
parent.child(h_flex().flex_wrap().gap_1().children(
|
parent.child(h_flex().flex_wrap().gap_1().children(
|
||||||
@@ -1204,7 +1211,7 @@ impl ActiveThread {
|
|||||||
message_id: MessageId,
|
message_id: MessageId,
|
||||||
rendered_message: &RenderedMessage,
|
rendered_message: &RenderedMessage,
|
||||||
cx: &Context<Self>,
|
cx: &Context<Self>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement + use<> {
|
||||||
let pending_thinking_segment_index = rendered_message
|
let pending_thinking_segment_index = rendered_message
|
||||||
.segments
|
.segments
|
||||||
.iter()
|
.iter()
|
||||||
@@ -1259,7 +1266,7 @@ impl ActiveThread {
|
|||||||
scroll_handle: &ScrollHandle,
|
scroll_handle: &ScrollHandle,
|
||||||
pending: bool,
|
pending: bool,
|
||||||
cx: &Context<Self>,
|
cx: &Context<Self>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement + use<> {
|
||||||
let is_open = self
|
let is_open = self
|
||||||
.expanded_thinking_segments
|
.expanded_thinking_segments
|
||||||
.get(&(message_id, ix))
|
.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
|
let is_open = self
|
||||||
.expanded_tool_uses
|
.expanded_tool_uses
|
||||||
.get(&tool_use.id)
|
.get(&tool_use.id)
|
||||||
@@ -1822,7 +1833,7 @@ impl ActiveThread {
|
|||||||
fn render_confirmations<'a>(
|
fn render_confirmations<'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
cx: &'a mut Context<Self>,
|
cx: &'a mut Context<Self>,
|
||||||
) -> impl Iterator<Item = AnyElement> + 'a {
|
) -> impl Iterator<Item = AnyElement> + 'a + use<'a> {
|
||||||
let thread = self.thread.read(cx);
|
let thread = self.thread.read(cx);
|
||||||
|
|
||||||
thread
|
thread
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ impl AssistantConfiguration {
|
|||||||
&mut self,
|
&mut self,
|
||||||
provider: &Arc<dyn LanguageModelProvider>,
|
provider: &Arc<dyn LanguageModelProvider>,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement + use<> {
|
||||||
let provider_id = provider.id().0.clone();
|
let provider_id = provider.id().0.clone();
|
||||||
let provider_name = provider.name().0.clone();
|
let provider_name = provider.name().0.clone();
|
||||||
let configuration_view = self
|
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 context_servers = self.context_server_manager.read(cx).all_servers().clone();
|
||||||
let tools_by_source = self.tools.tools_by_source(cx);
|
let tools_by_source = self.tools.tools_by_source(cx);
|
||||||
let empty = Vec::new();
|
let empty = Vec::new();
|
||||||
|
|||||||
@@ -318,7 +318,7 @@ impl ManageProfilesModal {
|
|||||||
mode: ChooseProfileMode,
|
mode: ChooseProfileMode,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement + use<> {
|
||||||
Navigable::new(
|
Navigable::new(
|
||||||
div()
|
div()
|
||||||
.track_focus(&self.focus_handle(cx))
|
.track_focus(&self.focus_handle(cx))
|
||||||
@@ -418,7 +418,7 @@ impl ManageProfilesModal {
|
|||||||
mode: NewProfileMode,
|
mode: NewProfileMode,
|
||||||
_window: &mut Window,
|
_window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement + use<> {
|
||||||
let settings = AssistantSettings::get_global(cx);
|
let settings = AssistantSettings::get_global(cx);
|
||||||
|
|
||||||
let base_profile_name = mode.base_profile_id.as_ref().map(|base_profile_id| {
|
let base_profile_name = mode.base_profile_id.as_ref().map(|base_profile_id| {
|
||||||
@@ -448,7 +448,7 @@ impl ManageProfilesModal {
|
|||||||
mode: ViewProfileMode,
|
mode: ViewProfileMode,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement + use<> {
|
||||||
let settings = AssistantSettings::get_global(cx);
|
let settings = AssistantSettings::get_global(cx);
|
||||||
|
|
||||||
let profile_name = settings
|
let profile_name = settings
|
||||||
|
|||||||
@@ -48,11 +48,11 @@ impl AssistantDiff {
|
|||||||
.items_of_type::<AssistantDiff>(cx)
|
.items_of_type::<AssistantDiff>(cx)
|
||||||
.find(|diff| diff.read(cx).thread == thread)
|
.find(|diff| diff.read(cx).thread == thread)
|
||||||
})?;
|
})?;
|
||||||
if let Some(existing_diff) = existing_diff {
|
match existing_diff {
|
||||||
workspace.update(cx, |workspace, cx| {
|
Some(existing_diff) => workspace.update(cx, |workspace, cx| {
|
||||||
workspace.activate_item(&existing_diff, true, true, window, cx);
|
workspace.activate_item(&existing_diff, true, true, window, cx);
|
||||||
})
|
}),
|
||||||
} else {
|
_ => {
|
||||||
let assistant_diff =
|
let assistant_diff =
|
||||||
cx.new(|cx| AssistantDiff::new(thread.clone(), workspace.clone(), window, cx));
|
cx.new(|cx| AssistantDiff::new(thread.clone(), workspace.clone(), window, cx));
|
||||||
workspace.update(cx, |workspace, cx| {
|
workspace.update(cx, |workspace, cx| {
|
||||||
@@ -60,6 +60,7 @@ impl AssistantDiff {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
thread: Entity<Thread>,
|
thread: Entity<Thread>,
|
||||||
|
|||||||
@@ -584,20 +584,14 @@ impl Focusable for AssistantPanel {
|
|||||||
match self.active_view {
|
match self.active_view {
|
||||||
ActiveView::Thread => self.message_editor.focus_handle(cx),
|
ActiveView::Thread => self.message_editor.focus_handle(cx),
|
||||||
ActiveView::History => self.history.focus_handle(cx),
|
ActiveView::History => self.history.focus_handle(cx),
|
||||||
ActiveView::PromptEditor => {
|
ActiveView::PromptEditor => match self.context_editor.as_ref() {
|
||||||
if let Some(context_editor) = self.context_editor.as_ref() {
|
Some(context_editor) => context_editor.focus_handle(cx),
|
||||||
context_editor.focus_handle(cx)
|
_ => cx.focus_handle(),
|
||||||
} else {
|
},
|
||||||
cx.focus_handle()
|
ActiveView::Configuration => match self.configuration.as_ref() {
|
||||||
}
|
Some(configuration) => configuration.focus_handle(cx),
|
||||||
}
|
_ => cx.focus_handle(),
|
||||||
ActiveView::Configuration => {
|
},
|
||||||
if let Some(configuration) = self.configuration.as_ref() {
|
|
||||||
configuration.focus_handle(cx)
|
|
||||||
} else {
|
|
||||||
cx.focus_handle()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -683,7 +677,11 @@ impl Panel for AssistantPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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 thread = self.thread.read(cx);
|
||||||
let focus_handle = self.focus_handle(cx);
|
let focus_handle = self.focus_handle(cx);
|
||||||
|
|
||||||
@@ -825,7 +823,7 @@ impl AssistantPanel {
|
|||||||
&self,
|
&self,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement + use<> {
|
||||||
let recent_history = self
|
let recent_history = self
|
||||||
.history_store
|
.history_store
|
||||||
.update(cx, |this, cx| this.recent_entries(6, cx));
|
.update(cx, |this, cx| this.recent_entries(6, cx));
|
||||||
|
|||||||
@@ -676,11 +676,14 @@ impl CodegenAlternative {
|
|||||||
.update(cx, |this, cx| {
|
.update(cx, |this, cx| {
|
||||||
this.message_id = message_id;
|
this.message_id = message_id;
|
||||||
this.last_equal_ranges.clear();
|
this.last_equal_ranges.clear();
|
||||||
if let Err(error) = result {
|
match result {
|
||||||
|
Err(error) => {
|
||||||
this.status = CodegenStatus::Error(error);
|
this.status = CodegenStatus::Error(error);
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
this.status = CodegenStatus::Done;
|
this.status = CodegenStatus::Done;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
this.elapsed_time = Some(elapsed_time);
|
this.elapsed_time = Some(elapsed_time);
|
||||||
this.completion = Some(completion.lock().clone());
|
this.completion = Some(completion.lock().clone());
|
||||||
if let Some(usage) = token_usage {
|
if let Some(usage) = token_usage {
|
||||||
|
|||||||
@@ -581,15 +581,14 @@ impl CompletionProvider for ContextPickerCompletionProvider {
|
|||||||
let line_start = Point::new(position.row, 0);
|
let line_start = Point::new(position.row, 0);
|
||||||
let offset_to_line = buffer.point_to_offset(line_start);
|
let offset_to_line = buffer.point_to_offset(line_start);
|
||||||
let mut lines = buffer.text_for_range(line_start..position).lines();
|
let mut lines = buffer.text_for_range(line_start..position).lines();
|
||||||
if let Some(line) = lines.next() {
|
match lines.next() {
|
||||||
MentionCompletion::try_parse(line, offset_to_line)
|
Some(line) => MentionCompletion::try_parse(line, offset_to_line)
|
||||||
.map(|completion| {
|
.map(|completion| {
|
||||||
completion.source_range.start <= offset_to_line + position.column as usize
|
completion.source_range.start <= offset_to_line + position.column as usize
|
||||||
&& completion.source_range.end >= offset_to_line + position.column as usize
|
&& completion.source_range.end >= offset_to_line + position.column as usize
|
||||||
})
|
})
|
||||||
.unwrap_or(false)
|
.unwrap_or(false),
|
||||||
} else {
|
_ => false,
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -360,14 +360,17 @@ impl ContextStore {
|
|||||||
remove_if_exists: bool,
|
remove_if_exists: bool,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) {
|
) {
|
||||||
if let Some(context_id) = self.includes_thread(&thread.read(cx).id()) {
|
match self.includes_thread(&thread.read(cx).id()) {
|
||||||
|
Some(context_id) => {
|
||||||
if remove_if_exists {
|
if remove_if_exists {
|
||||||
self.remove_context(context_id);
|
self.remove_context(context_id);
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
self.insert_thread(thread, cx);
|
self.insert_thread(thread, cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn insert_thread(&mut self, thread: Entity<Thread>, cx: &App) {
|
fn insert_thread(&mut self, thread: Entity<Thread>, cx: &App) {
|
||||||
let id = self.next_context_id.post_inc();
|
let id = self.next_context_id.post_inc();
|
||||||
@@ -687,7 +690,7 @@ pub fn refresh_context_store_text(
|
|||||||
context_store: Entity<ContextStore>,
|
context_store: Entity<ContextStore>,
|
||||||
changed_buffers: &HashSet<Entity<Buffer>>,
|
changed_buffers: &HashSet<Entity<Buffer>>,
|
||||||
cx: &App,
|
cx: &App,
|
||||||
) -> impl Future<Output = Vec<ContextId>> {
|
) -> impl Future<Output = Vec<ContextId>> + use<> {
|
||||||
let mut tasks = Vec::new();
|
let mut tasks = Vec::new();
|
||||||
|
|
||||||
for context in &context_store.read(cx).context {
|
for context in &context_store.read(cx).context {
|
||||||
@@ -756,8 +759,8 @@ fn refresh_file_text(
|
|||||||
) -> Option<Task<()>> {
|
) -> Option<Task<()>> {
|
||||||
let id = file_context.id;
|
let id = file_context.id;
|
||||||
let task = refresh_context_buffer(&file_context.context_buffer, cx);
|
let task = refresh_context_buffer(&file_context.context_buffer, cx);
|
||||||
if let Some(task) = task {
|
match task {
|
||||||
Some(cx.spawn(async move |cx| {
|
Some(task) => Some(cx.spawn(async move |cx| {
|
||||||
let context_buffer = task.await;
|
let context_buffer = task.await;
|
||||||
context_store
|
context_store
|
||||||
.update(cx, |context_store, _| {
|
.update(cx, |context_store, _| {
|
||||||
@@ -765,9 +768,8 @@ fn refresh_file_text(
|
|||||||
context_store.replace_context(AssistantContext::File(new_file_context));
|
context_store.replace_context(AssistantContext::File(new_file_context));
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
}))
|
})),
|
||||||
} else {
|
_ => None,
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -780,14 +782,15 @@ fn refresh_directory_text(
|
|||||||
let futures = directory_context
|
let futures = directory_context
|
||||||
.context_buffers
|
.context_buffers
|
||||||
.iter()
|
.iter()
|
||||||
.map(|context_buffer| {
|
.map(
|
||||||
if let Some(refresh_task) = refresh_context_buffer(context_buffer, cx) {
|
|context_buffer| match refresh_context_buffer(context_buffer, cx) {
|
||||||
|
Some(refresh_task) => {
|
||||||
stale = true;
|
stale = true;
|
||||||
future::Either::Left(refresh_task)
|
future::Either::Left(refresh_task)
|
||||||
} else {
|
|
||||||
future::Either::Right(future::ready((*context_buffer).clone()))
|
|
||||||
}
|
}
|
||||||
})
|
_ => future::Either::Right(future::ready((*context_buffer).clone())),
|
||||||
|
},
|
||||||
|
)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
if !stale {
|
if !stale {
|
||||||
@@ -816,8 +819,8 @@ fn refresh_symbol_text(
|
|||||||
) -> Option<Task<()>> {
|
) -> Option<Task<()>> {
|
||||||
let id = symbol_context.id;
|
let id = symbol_context.id;
|
||||||
let task = refresh_context_symbol(&symbol_context.context_symbol, cx);
|
let task = refresh_context_symbol(&symbol_context.context_symbol, cx);
|
||||||
if let Some(task) = task {
|
match task {
|
||||||
Some(cx.spawn(async move |cx| {
|
Some(task) => Some(cx.spawn(async move |cx| {
|
||||||
let context_symbol = task.await;
|
let context_symbol = task.await;
|
||||||
context_store
|
context_store
|
||||||
.update(cx, |context_store, _| {
|
.update(cx, |context_store, _| {
|
||||||
@@ -825,9 +828,8 @@ fn refresh_symbol_text(
|
|||||||
context_store.replace_context(AssistantContext::Symbol(new_symbol_context));
|
context_store.replace_context(AssistantContext::Symbol(new_symbol_context));
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
}))
|
})),
|
||||||
} else {
|
_ => None,
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -855,7 +857,7 @@ fn refresh_thread_text(
|
|||||||
fn refresh_context_buffer(
|
fn refresh_context_buffer(
|
||||||
context_buffer: &ContextBuffer,
|
context_buffer: &ContextBuffer,
|
||||||
cx: &App,
|
cx: &App,
|
||||||
) -> Option<impl Future<Output = ContextBuffer>> {
|
) -> Option<impl Future<Output = ContextBuffer> + use<>> {
|
||||||
let buffer = context_buffer.buffer.read(cx);
|
let buffer = context_buffer.buffer.read(cx);
|
||||||
let path = buffer_path_log_err(buffer)?;
|
let path = buffer_path_log_err(buffer)?;
|
||||||
if buffer.version.changed_since(&context_buffer.version) {
|
if buffer.version.changed_since(&context_buffer.version) {
|
||||||
@@ -875,7 +877,7 @@ fn refresh_context_buffer(
|
|||||||
fn refresh_context_symbol(
|
fn refresh_context_symbol(
|
||||||
context_symbol: &ContextSymbol,
|
context_symbol: &ContextSymbol,
|
||||||
cx: &App,
|
cx: &App,
|
||||||
) -> Option<impl Future<Output = ContextSymbol>> {
|
) -> Option<impl Future<Output = ContextSymbol> + use<>> {
|
||||||
let buffer = context_symbol.buffer.read(cx);
|
let buffer = context_symbol.buffer.read(cx);
|
||||||
let path = buffer_path_log_err(buffer)?;
|
let path = buffer_path_log_err(buffer)?;
|
||||||
let project_path = buffer.project_path(cx)?;
|
let project_path = buffer.project_path(cx)?;
|
||||||
|
|||||||
@@ -341,9 +341,9 @@ impl InlineAssistant {
|
|||||||
selection.end.column = snapshot
|
selection.end.column = snapshot
|
||||||
.buffer_snapshot
|
.buffer_snapshot
|
||||||
.line_len(MultiBufferRow(selection.end.row));
|
.line_len(MultiBufferRow(selection.end.row));
|
||||||
} else if let Some(fold) =
|
} else {
|
||||||
snapshot.crease_for_buffer_row(MultiBufferRow(selection.end.row))
|
match snapshot.crease_for_buffer_row(MultiBufferRow(selection.end.row)) {
|
||||||
{
|
Some(fold) => {
|
||||||
selection.start = fold.range().start;
|
selection.start = fold.range().start;
|
||||||
selection.end = fold.range().end;
|
selection.end = fold.range().end;
|
||||||
if MultiBufferRow(selection.end.row) < snapshot.buffer_snapshot.max_row() {
|
if MultiBufferRow(selection.end.row) < snapshot.buffer_snapshot.max_row() {
|
||||||
@@ -358,10 +358,9 @@ impl InlineAssistant {
|
|||||||
if c.is_whitespace() {
|
if c.is_whitespace() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if snapshot
|
if snapshot.language_at(selection.end).is_some_and(|language| {
|
||||||
.language_at(selection.end)
|
language.config().brackets.is_closing_brace(c)
|
||||||
.is_some_and(|language| language.config().brackets.is_closing_brace(c))
|
}) {
|
||||||
{
|
|
||||||
selection.end.row += 1;
|
selection.end.row += 1;
|
||||||
selection.end.column = snapshot
|
selection.end.column = snapshot
|
||||||
.buffer_snapshot
|
.buffer_snapshot
|
||||||
@@ -370,6 +369,9 @@ impl InlineAssistant {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(prev_selection) = selections.last_mut() {
|
if let Some(prev_selection) = selections.last_mut() {
|
||||||
if selection.start <= prev_selection.end {
|
if selection.start <= prev_selection.end {
|
||||||
@@ -1129,7 +1131,8 @@ impl InlineAssistant {
|
|||||||
|
|
||||||
let mut scroll_target_top;
|
let mut scroll_target_top;
|
||||||
let mut scroll_target_bottom;
|
let mut scroll_target_bottom;
|
||||||
if let Some(decorations) = assist.decorations.as_ref() {
|
match assist.decorations.as_ref() {
|
||||||
|
Some(decorations) => {
|
||||||
scroll_target_top = editor
|
scroll_target_top = editor
|
||||||
.row_for_block(decorations.prompt_block_id, cx)
|
.row_for_block(decorations.prompt_block_id, cx)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@@ -1138,7 +1141,8 @@ impl InlineAssistant {
|
|||||||
.row_for_block(decorations.end_block_id, cx)
|
.row_for_block(decorations.end_block_id, cx)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.0 as f32;
|
.0 as f32;
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
let snapshot = editor.snapshot(window, cx);
|
let snapshot = editor.snapshot(window, cx);
|
||||||
let start_row = assist
|
let start_row = assist
|
||||||
.range
|
.range
|
||||||
@@ -1148,6 +1152,7 @@ impl InlineAssistant {
|
|||||||
scroll_target_top = start_row.0 as f32;
|
scroll_target_top = start_row.0 as f32;
|
||||||
scroll_target_bottom = scroll_target_top + 1.;
|
scroll_target_bottom = scroll_target_top + 1.;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
scroll_target_top -= editor.vertical_scroll_margin() as f32;
|
scroll_target_top -= editor.vertical_scroll_margin() as f32;
|
||||||
scroll_target_bottom += 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) {
|
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) {
|
let assist = match self.assists.get_mut(&assist_id) {
|
||||||
assist
|
Some(assist) => assist,
|
||||||
} else {
|
_ => {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let assist_group_id = assist.group_id;
|
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) {
|
pub fn stop_assist(&mut self, assist_id: InlineAssistId, cx: &mut App) {
|
||||||
let assist = if let Some(assist) = self.assists.get_mut(&assist_id) {
|
let assist = match self.assists.get_mut(&assist_id) {
|
||||||
assist
|
Some(assist) => assist,
|
||||||
} else {
|
_ => {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
assist.codegen.update(cx, |codegen, cx| codegen.stop(cx));
|
assist.codegen.update(cx, |codegen, cx| codegen.stop(cx));
|
||||||
@@ -1449,20 +1456,27 @@ impl InlineAssistant {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(context_editor) = context_editor {
|
match context_editor {
|
||||||
Some(InlineAssistTarget::Editor(context_editor))
|
Some(context_editor) => Some(InlineAssistTarget::Editor(context_editor)),
|
||||||
} else if let Some(workspace_editor) = workspace
|
_ => {
|
||||||
|
match workspace
|
||||||
.active_item(cx)
|
.active_item(cx)
|
||||||
.and_then(|item| item.act_as::<Editor>(cx))
|
.and_then(|item| item.act_as::<Editor>(cx))
|
||||||
{
|
{
|
||||||
Some(InlineAssistTarget::Editor(workspace_editor))
|
Some(workspace_editor) => Some(InlineAssistTarget::Editor(workspace_editor)),
|
||||||
} else if let Some(terminal_view) = workspace
|
_ => {
|
||||||
|
match workspace
|
||||||
.active_item(cx)
|
.active_item(cx)
|
||||||
.and_then(|item| item.act_as::<TerminalView>(cx))
|
.and_then(|item| item.act_as::<TerminalView>(cx))
|
||||||
{
|
{
|
||||||
|
Some(terminal_view) => {
|
||||||
Some(InlineAssistTarget::Terminal(terminal_view))
|
Some(InlineAssistTarget::Terminal(terminal_view))
|
||||||
} else {
|
}
|
||||||
None
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1654,10 +1668,11 @@ impl InlineAssist {
|
|||||||
InlineAssistant::update_global(cx, |this, cx| match event {
|
InlineAssistant::update_global(cx, |this, cx| match event {
|
||||||
CodegenEvent::Undone => this.finish_assist(assist_id, false, window, cx),
|
CodegenEvent::Undone => this.finish_assist(assist_id, false, window, cx),
|
||||||
CodegenEvent::Finished => {
|
CodegenEvent::Finished => {
|
||||||
let assist = if let Some(assist) = this.assists.get(&assist_id) {
|
let assist = match this.assists.get(&assist_id) {
|
||||||
assist
|
Some(assist) => assist,
|
||||||
} else {
|
_ => {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let CodegenStatus::Error(error) = codegen.read(cx).status(cx) {
|
if let CodegenStatus::Error(error) = codegen.read(cx).status(cx) {
|
||||||
|
|||||||
@@ -677,7 +677,7 @@ impl<T: 'static> PromptEditor<T> {
|
|||||||
.into_any_element()
|
.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(
|
Popover::new().child(
|
||||||
v_flex()
|
v_flex()
|
||||||
.occlude()
|
.occlude()
|
||||||
|
|||||||
@@ -117,11 +117,14 @@ impl TerminalCodegen {
|
|||||||
let result = generate.await;
|
let result = generate.await;
|
||||||
|
|
||||||
this.update(cx, |this, cx| {
|
this.update(cx, |this, cx| {
|
||||||
if let Err(error) = result {
|
match result {
|
||||||
|
Err(error) => {
|
||||||
this.status = CodegenStatus::Error(error);
|
this.status = CodegenStatus::Error(error);
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
this.status = CodegenStatus::Done;
|
this.status = CodegenStatus::Done;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
cx.emit(CodegenEvent::Finished);
|
cx.emit(CodegenEvent::Finished);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -164,10 +164,11 @@ impl TerminalInlineAssistant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn start_assist(&mut self, assist_id: TerminalInlineAssistId, cx: &mut App) {
|
fn start_assist(&mut self, assist_id: TerminalInlineAssistId, cx: &mut App) {
|
||||||
let assist = if let Some(assist) = self.assists.get_mut(&assist_id) {
|
let assist = match self.assists.get_mut(&assist_id) {
|
||||||
assist
|
Some(assist) => assist,
|
||||||
} else {
|
_ => {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(user_prompt) = assist
|
let Some(user_prompt) = assist
|
||||||
@@ -202,10 +203,11 @@ impl TerminalInlineAssistant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn stop_assist(&mut self, assist_id: TerminalInlineAssistId, cx: &mut App) {
|
fn stop_assist(&mut self, assist_id: TerminalInlineAssistId, cx: &mut App) {
|
||||||
let assist = if let Some(assist) = self.assists.get_mut(&assist_id) {
|
let assist = match self.assists.get_mut(&assist_id) {
|
||||||
assist
|
Some(assist) => assist,
|
||||||
} else {
|
_ => {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
assist.codegen.update(cx, |codegen, cx| codegen.stop(cx));
|
assist.codegen.update(cx, |codegen, cx| codegen.stop(cx));
|
||||||
@@ -402,10 +404,11 @@ impl TerminalInlineAssist {
|
|||||||
window.subscribe(&codegen, cx, move |codegen, event, window, cx| {
|
window.subscribe(&codegen, cx, move |codegen, event, window, cx| {
|
||||||
TerminalInlineAssistant::update_global(cx, |this, cx| match event {
|
TerminalInlineAssistant::update_global(cx, |this, cx| match event {
|
||||||
CodegenEvent::Finished => {
|
CodegenEvent::Finished => {
|
||||||
let assist = if let Some(assist) = this.assists.get(&assist_id) {
|
let assist = match this.assists.get(&assist_id) {
|
||||||
assist
|
Some(assist) => assist,
|
||||||
} else {
|
_ => {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let CodegenStatus::Error(error) = &codegen.read(cx).status {
|
if let CodegenStatus::Error(error) = &codegen.read(cx).status {
|
||||||
|
|||||||
@@ -379,15 +379,18 @@ impl Thread {
|
|||||||
cx.spawn(async move |this, cx| {
|
cx.spawn(async move |this, cx| {
|
||||||
let result = restore.await;
|
let result = restore.await;
|
||||||
this.update(cx, |this, cx| {
|
this.update(cx, |this, cx| {
|
||||||
if let Err(err) = result.as_ref() {
|
match result.as_ref() {
|
||||||
|
Err(err) => {
|
||||||
this.last_restore_checkpoint = Some(LastRestoreCheckpoint::Error {
|
this.last_restore_checkpoint = Some(LastRestoreCheckpoint::Error {
|
||||||
message_id: checkpoint.message_id,
|
message_id: checkpoint.message_id,
|
||||||
error: err.to_string(),
|
error: err.to_string(),
|
||||||
});
|
});
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
this.truncate(checkpoint.message_id, cx);
|
this.truncate(checkpoint.message_id, cx);
|
||||||
this.last_restore_checkpoint = None;
|
this.last_restore_checkpoint = None;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
this.pending_checkpoint = None;
|
this.pending_checkpoint = None;
|
||||||
cx.emit(ThreadEvent::CheckpointChanged);
|
cx.emit(ThreadEvent::CheckpointChanged);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
@@ -721,8 +724,8 @@ impl Thread {
|
|||||||
})
|
})
|
||||||
.next();
|
.next();
|
||||||
|
|
||||||
if let Some((rel_rules_path, abs_rules_path)) = selected_rules_file {
|
match selected_rules_file {
|
||||||
cx.spawn(async move |_| {
|
Some((rel_rules_path, abs_rules_path)) => cx.spawn(async move |_| {
|
||||||
let rules_file_result = maybe!(async move {
|
let rules_file_result = maybe!(async move {
|
||||||
let abs_rules_path = abs_rules_path?;
|
let abs_rules_path = abs_rules_path?;
|
||||||
let text = fs.load(&abs_rules_path).await.with_context(|| {
|
let text = fs.load(&abs_rules_path).await.with_context(|| {
|
||||||
@@ -751,16 +754,15 @@ impl Thread {
|
|||||||
rules_file,
|
rules_file,
|
||||||
};
|
};
|
||||||
(worktree_info, rules_file_error)
|
(worktree_info, rules_file_error)
|
||||||
})
|
}),
|
||||||
} else {
|
_ => Task::ready((
|
||||||
Task::ready((
|
|
||||||
WorktreeInfoForSystemPrompt {
|
WorktreeInfoForSystemPrompt {
|
||||||
root_name,
|
root_name,
|
||||||
abs_path,
|
abs_path,
|
||||||
rules_file: None,
|
rules_file: None,
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
))
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1186,7 +1188,7 @@ impl Thread {
|
|||||||
pub fn use_pending_tools(
|
pub fn use_pending_tools(
|
||||||
&mut self,
|
&mut self,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> impl IntoIterator<Item = PendingToolUse> {
|
) -> impl IntoIterator<Item = PendingToolUse> + use<> {
|
||||||
let request = self.to_completion_request(RequestKind::Chat, cx);
|
let request = self.to_completion_request(RequestKind::Chat, cx);
|
||||||
let messages = Arc::new(request.messages);
|
let messages = Arc::new(request.messages);
|
||||||
let pending_tool_uses = self
|
let pending_tool_uses = self
|
||||||
@@ -1198,7 +1200,8 @@ impl Thread {
|
|||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
for tool_use in pending_tool_uses.iter() {
|
for tool_use in pending_tool_uses.iter() {
|
||||||
if let Some(tool) = self.tools.tool(&tool_use.name, cx) {
|
match self.tools.tool(&tool_use.name, cx) {
|
||||||
|
Some(tool) => {
|
||||||
if tool.needs_confirmation()
|
if tool.needs_confirmation()
|
||||||
&& !AssistantSettings::get_global(cx).always_allow_tool_actions
|
&& !AssistantSettings::get_global(cx).always_allow_tool_actions
|
||||||
{
|
{
|
||||||
@@ -1220,7 +1223,9 @@ impl Thread {
|
|||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if let Some(tool) = self.tools.tool(&tool_use.name, cx) {
|
}
|
||||||
|
_ => match self.tools.tool(&tool_use.name, cx) {
|
||||||
|
Some(tool) => {
|
||||||
self.run_tool(
|
self.run_tool(
|
||||||
tool_use.id.clone(),
|
tool_use.id.clone(),
|
||||||
tool_use.ui_text.clone(),
|
tool_use.ui_text.clone(),
|
||||||
@@ -1230,6 +1235,9 @@ impl Thread {
|
|||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pending_tool_uses
|
pending_tool_uses
|
||||||
|
|||||||
@@ -166,8 +166,8 @@ impl ToolUseState {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(pending_tool_use) = self.pending_tool_uses_by_id.get(&tool_use.id) {
|
match self.pending_tool_uses_by_id.get(&tool_use.id) {
|
||||||
match pending_tool_use.status {
|
Some(pending_tool_use) => match pending_tool_use.status {
|
||||||
PendingToolUseStatus::Idle => ToolUseStatus::Pending,
|
PendingToolUseStatus::Idle => ToolUseStatus::Pending,
|
||||||
PendingToolUseStatus::NeedsConfirmation { .. } => {
|
PendingToolUseStatus::NeedsConfirmation { .. } => {
|
||||||
ToolUseStatus::NeedsConfirmation
|
ToolUseStatus::NeedsConfirmation
|
||||||
@@ -176,17 +176,14 @@ impl ToolUseState {
|
|||||||
PendingToolUseStatus::Error(ref err) => {
|
PendingToolUseStatus::Error(ref err) => {
|
||||||
ToolUseStatus::Error(err.clone().into())
|
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)
|
let (icon, needs_confirmation) = match self.tools.tool(&tool_use.name, cx) {
|
||||||
{
|
Some(tool) => (tool.icon(), tool.needs_confirmation()),
|
||||||
(tool.icon(), tool.needs_confirmation())
|
_ => (IconName::Cog, false),
|
||||||
} else {
|
|
||||||
(IconName::Cog, false)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
tool_uses.push(ToolUse {
|
tool_uses.push(ToolUse {
|
||||||
@@ -209,10 +206,9 @@ impl ToolUseState {
|
|||||||
input: &serde_json::Value,
|
input: &serde_json::Value,
|
||||||
cx: &App,
|
cx: &App,
|
||||||
) -> SharedString {
|
) -> SharedString {
|
||||||
if let Some(tool) = self.tools.tool(tool_name, cx) {
|
match self.tools.tool(tool_name, cx) {
|
||||||
tool.ui_text(input).into()
|
Some(tool) => tool.ui_text(input).into(),
|
||||||
} else {
|
_ => format!("Unknown tool {tool_name:?}").into(),
|
||||||
format!("Unknown tool {tool_name:?}").into()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1877,16 +1877,19 @@ impl AssistantContext {
|
|||||||
|
|
||||||
if let Some(mut pending_patch) = pending_patch {
|
if let Some(mut pending_patch) = pending_patch {
|
||||||
let patch_start = pending_patch.range.start.to_offset(buffer);
|
let patch_start = pending_patch.range.start.to_offset(buffer);
|
||||||
if let Some(message) = self.message_for_offset(patch_start, cx) {
|
match self.message_for_offset(patch_start, cx) {
|
||||||
|
Some(message) => {
|
||||||
if message.anchor_range.end == text::Anchor::MAX {
|
if message.anchor_range.end == text::Anchor::MAX {
|
||||||
pending_patch.range.end = text::Anchor::MAX;
|
pending_patch.range.end = text::Anchor::MAX;
|
||||||
} else {
|
} else {
|
||||||
let message_end = buffer.anchor_after(message.offset_range.end - 1);
|
let message_end = buffer.anchor_after(message.offset_range.end - 1);
|
||||||
pending_patch.range.end = message_end;
|
pending_patch.range.end = message_end;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
pending_patch.range.end = text::Anchor::MAX;
|
pending_patch.range.end = text::Anchor::MAX;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
new_patches.push(pending_patch);
|
new_patches.push(pending_patch);
|
||||||
}
|
}
|
||||||
@@ -2453,7 +2456,7 @@ impl AssistantContext {
|
|||||||
let result = stream_completion.await;
|
let result = stream_completion.await;
|
||||||
|
|
||||||
this.update(cx, |this, cx| {
|
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>() {
|
if error.is::<PaymentRequiredError>() {
|
||||||
cx.emit(ContextEvent::ShowPaymentRequiredError);
|
cx.emit(ContextEvent::ShowPaymentRequiredError);
|
||||||
this.update_metadata(assistant_message_id, cx, |metadata| {
|
this.update_metadata(assistant_message_id, cx, |metadata| {
|
||||||
@@ -2481,12 +2484,12 @@ impl AssistantContext {
|
|||||||
});
|
});
|
||||||
Some(error_message)
|
Some(error_message)
|
||||||
}
|
}
|
||||||
} else {
|
} _ => {
|
||||||
this.update_metadata(assistant_message_id, cx, |metadata| {
|
this.update_metadata(assistant_message_id, cx, |metadata| {
|
||||||
metadata.status = MessageStatus::Done;
|
metadata.status = MessageStatus::Done;
|
||||||
});
|
});
|
||||||
None
|
None
|
||||||
};
|
}};
|
||||||
|
|
||||||
let language_name = this
|
let language_name = this
|
||||||
.buffer
|
.buffer
|
||||||
@@ -2631,15 +2634,16 @@ impl AssistantContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn cancel_last_assist(&mut self, cx: &mut Context<Self>) -> bool {
|
pub fn cancel_last_assist(&mut self, cx: &mut Context<Self>) -> bool {
|
||||||
if let Some(pending_completion) = self.pending_completions.pop() {
|
match self.pending_completions.pop() {
|
||||||
|
Some(pending_completion) => {
|
||||||
self.update_metadata(pending_completion.assistant_message_id, cx, |metadata| {
|
self.update_metadata(pending_completion.assistant_message_id, cx, |metadata| {
|
||||||
if metadata.status == MessageStatus::Pending {
|
if metadata.status == MessageStatus::Pending {
|
||||||
metadata.status = MessageStatus::Canceled;
|
metadata.status = MessageStatus::Canceled;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
true
|
true
|
||||||
} else {
|
}
|
||||||
false
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2799,7 +2803,8 @@ impl AssistantContext {
|
|||||||
) -> (Option<MessageAnchor>, Option<MessageAnchor>) {
|
) -> (Option<MessageAnchor>, Option<MessageAnchor>) {
|
||||||
let start_message = self.message_for_offset(range.start, cx);
|
let start_message = self.message_for_offset(range.start, cx);
|
||||||
let end_message = self.message_for_offset(range.end, cx);
|
let end_message = self.message_for_offset(range.end, cx);
|
||||||
if let Some((start_message, end_message)) = start_message.zip(end_message) {
|
match start_message.zip(end_message) {
|
||||||
|
Some((start_message, end_message)) => {
|
||||||
// Prevent splitting when range spans multiple messages.
|
// Prevent splitting when range spans multiple messages.
|
||||||
if start_message.id != end_message.id {
|
if start_message.id != end_message.id {
|
||||||
return (None, None);
|
return (None, None);
|
||||||
@@ -2817,7 +2822,8 @@ impl AssistantContext {
|
|||||||
{
|
{
|
||||||
if self.buffer.read(cx).chars_at(range.end).next() == Some('\n') {
|
if self.buffer.read(cx).chars_at(range.end).next() == Some('\n') {
|
||||||
suffix_start = Some(range.end + 1);
|
suffix_start = Some(range.end + 1);
|
||||||
} else if self.buffer.read(cx).reversed_chars_at(range.end).next() == Some('\n') {
|
} else if self.buffer.read(cx).reversed_chars_at(range.end).next() == Some('\n')
|
||||||
|
{
|
||||||
suffix_start = Some(range.end);
|
suffix_start = Some(range.end);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2912,8 +2918,8 @@ impl AssistantContext {
|
|||||||
cx.emit(ContextEvent::MessagesEdited);
|
cx.emit(ContextEvent::MessagesEdited);
|
||||||
}
|
}
|
||||||
new_messages
|
new_messages
|
||||||
} else {
|
}
|
||||||
(None, None)
|
_ => (None, None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -403,10 +403,12 @@ impl ContextEditor {
|
|||||||
if request_type == RequestType::SuggestEdits && !self.context.read(cx).contains_files(cx) {
|
if request_type == RequestType::SuggestEdits && !self.context.read(cx).contains_files(cx) {
|
||||||
self.last_error = Some(AssistError::FileRequired);
|
self.last_error = Some(AssistError::FileRequired);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
} else if let Some(user_message) = self
|
} else {
|
||||||
|
match self
|
||||||
.context
|
.context
|
||||||
.update(cx, |context, cx| context.assist(request_type, cx))
|
.update(cx, |context, cx| context.assist(request_type, cx))
|
||||||
{
|
{
|
||||||
|
Some(user_message) => {
|
||||||
let new_selection = {
|
let new_selection = {
|
||||||
let cursor = user_message
|
let cursor = user_message
|
||||||
.start
|
.start
|
||||||
@@ -414,13 +416,19 @@ impl ContextEditor {
|
|||||||
cursor..cursor
|
cursor..cursor
|
||||||
};
|
};
|
||||||
self.editor.update(cx, |editor, cx| {
|
self.editor.update(cx, |editor, cx| {
|
||||||
editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
|
editor.change_selections(
|
||||||
selections.select_ranges([new_selection])
|
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.
|
// Avoid scrolling to the new cursor position so the assistant's output is stable.
|
||||||
cx.defer_in(window, |this, _, _| this.scroll_position = None);
|
cx.defer_in(window, |this, _, _| this.scroll_position = None);
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
@@ -817,10 +825,9 @@ impl ContextEditor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.editor.update(cx, |editor, cx| {
|
self.editor.update(cx, |editor, cx| {
|
||||||
if let Some(invoked_slash_command) =
|
match self.context.read(cx).invoked_slash_command(&command_id) {
|
||||||
self.context.read(cx).invoked_slash_command(&command_id)
|
Some(invoked_slash_command) => match invoked_slash_command.status {
|
||||||
{
|
InvokedSlashCommandStatus::Finished => {
|
||||||
if let InvokedSlashCommandStatus::Finished = invoked_slash_command.status {
|
|
||||||
let buffer = editor.buffer().read(cx).snapshot(cx);
|
let buffer = editor.buffer().read(cx).snapshot(cx);
|
||||||
let (&excerpt_id, _buffer_id, _buffer_snapshot) =
|
let (&excerpt_id, _buffer_id, _buffer_snapshot) =
|
||||||
buffer.as_singleton().unwrap();
|
buffer.as_singleton().unwrap();
|
||||||
@@ -839,10 +846,14 @@ impl ContextEditor {
|
|||||||
);
|
);
|
||||||
|
|
||||||
editor.remove_creases(
|
editor.remove_creases(
|
||||||
HashSet::from_iter(self.invoked_slash_command_creases.remove(&command_id)),
|
HashSet::from_iter(
|
||||||
|
self.invoked_slash_command_creases.remove(&command_id),
|
||||||
|
),
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
} else if let hash_map::Entry::Vacant(entry) =
|
}
|
||||||
|
_ => {
|
||||||
|
if let hash_map::Entry::Vacant(entry) =
|
||||||
self.invoked_slash_command_creases.entry(command_id)
|
self.invoked_slash_command_creases.entry(command_id)
|
||||||
{
|
{
|
||||||
let buffer = editor.buffer().read(cx).snapshot(cx);
|
let buffer = editor.buffer().read(cx).snapshot(cx);
|
||||||
@@ -867,12 +878,15 @@ impl ContextEditor {
|
|||||||
} else {
|
} else {
|
||||||
cx.notify()
|
cx.notify()
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
editor.remove_creases(
|
editor.remove_creases(
|
||||||
HashSet::from_iter(self.invoked_slash_command_creases.remove(&command_id)),
|
HashSet::from_iter(self.invoked_slash_command_creases.remove(&command_id)),
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -958,7 +972,8 @@ impl ContextEditor {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let should_refold;
|
let should_refold;
|
||||||
if let Some(state) = self.patches.get_mut(&range) {
|
match self.patches.get_mut(&range) {
|
||||||
|
Some(state) => {
|
||||||
if let Some(editor_state) = &state.editor {
|
if let Some(editor_state) = &state.editor {
|
||||||
if editor_state.opened_patch != patch {
|
if editor_state.opened_patch != patch {
|
||||||
state.update_task = Some({
|
state.update_task = Some({
|
||||||
@@ -972,9 +987,10 @@ impl ContextEditor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
should_refold =
|
should_refold = snapshot
|
||||||
snapshot.intersects_fold(patch_start.to_offset(&snapshot.buffer_snapshot));
|
.intersects_fold(patch_start.to_offset(&snapshot.buffer_snapshot));
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
let crease_id = editor.insert_creases([crease.clone()], cx)[0];
|
let crease_id = editor.insert_creases([crease.clone()], cx)[0];
|
||||||
self.patches.insert(
|
self.patches.insert(
|
||||||
range.clone(),
|
range.clone(),
|
||||||
@@ -987,6 +1003,7 @@ impl ContextEditor {
|
|||||||
|
|
||||||
should_refold = true;
|
should_refold = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if should_refold {
|
if should_refold {
|
||||||
editor.unfold_ranges(&[patch_start..patch_end], true, false, cx);
|
editor.unfold_ranges(&[patch_start..patch_end], true, false, cx);
|
||||||
@@ -1175,20 +1192,24 @@ impl ContextEditor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(editor) = editor {
|
match editor {
|
||||||
|
Some(editor) => {
|
||||||
self.workspace
|
self.workspace
|
||||||
.update(cx, |workspace, cx| {
|
.update(cx, |workspace, cx| {
|
||||||
workspace.activate_item(&editor, true, false, window, cx);
|
workspace.activate_item(&editor, true, false, window, cx);
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
} else {
|
}
|
||||||
patch_state.update_task = Some(cx.spawn_in(window, async move |this, cx| {
|
_ => {
|
||||||
|
patch_state.update_task =
|
||||||
|
Some(cx.spawn_in(window, async move |this, cx| {
|
||||||
Self::open_patch_editor(this, new_patch, cx).await.log_err();
|
Self::open_patch_editor(this, new_patch, cx).await.log_err();
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn close_patch_editor(
|
fn close_patch_editor(
|
||||||
&mut self,
|
&mut self,
|
||||||
@@ -1282,7 +1303,8 @@ impl ContextEditor {
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if let Some(state) = &mut patch_state.editor {
|
if let Some(state) = &mut patch_state.editor {
|
||||||
if let Some(editor) = state.editor.upgrade() {
|
match state.editor.upgrade() {
|
||||||
|
Some(editor) => {
|
||||||
editor.update(cx, |editor, cx| {
|
editor.update(cx, |editor, cx| {
|
||||||
editor.set_title(patch.title.clone(), cx);
|
editor.set_title(patch.title.clone(), cx);
|
||||||
editor.reset_locations(locations, window, cx);
|
editor.reset_locations(locations, window, cx);
|
||||||
@@ -1290,10 +1312,12 @@ impl ContextEditor {
|
|||||||
});
|
});
|
||||||
|
|
||||||
state.opened_patch = patch;
|
state.opened_patch = patch;
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
patch_state.editor.take();
|
patch_state.editor.take();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
patch_state.update_task.take();
|
patch_state.update_task.take();
|
||||||
|
|
||||||
Some(())
|
Some(())
|
||||||
@@ -1573,7 +1597,8 @@ impl ContextEditor {
|
|||||||
let mut new_blocks = vec![];
|
let mut new_blocks = vec![];
|
||||||
let mut block_index_to_message = vec![];
|
let mut block_index_to_message = vec![];
|
||||||
for message in self.context.read(cx).messages(cx) {
|
for message in self.context.read(cx).messages(cx) {
|
||||||
if let Some(_) = blocks_to_remove.remove(&message.id) {
|
match blocks_to_remove.remove(&message.id) {
|
||||||
|
Some(_) => {
|
||||||
// This is an old message that we might modify.
|
// This is an old message that we might modify.
|
||||||
let Some((meta, block_id)) = old_blocks.get_mut(&message.id) else {
|
let Some((meta, block_id)) = old_blocks.get_mut(&message.id) else {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
@@ -1588,12 +1613,14 @@ impl ContextEditor {
|
|||||||
blocks_to_replace.insert(*block_id, render_block(message_meta.clone()));
|
blocks_to_replace.insert(*block_id, render_block(message_meta.clone()));
|
||||||
*meta = message_meta;
|
*meta = message_meta;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
// This is a new message.
|
// This is a new message.
|
||||||
new_blocks.push(create_block_properties(&message));
|
new_blocks.push(create_block_properties(&message));
|
||||||
block_index_to_message.push((message.id, MessageMetadata::from(&message)));
|
block_index_to_message.push((message.id, MessageMetadata::from(&message)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
editor.replace_blocks(blocks_to_replace, None, cx);
|
editor.replace_blocks(blocks_to_replace, None, cx);
|
||||||
editor.remove_blocks(blocks_to_remove.into_values().collect(), None, cx);
|
editor.remove_blocks(blocks_to_remove.into_values().collect(), None, cx);
|
||||||
|
|
||||||
@@ -2321,10 +2348,14 @@ impl ContextEditor {
|
|||||||
)
|
)
|
||||||
.into_any_element(),
|
.into_any_element(),
|
||||||
)
|
)
|
||||||
} else if let Some(configuration_error) = configuration_error(cx) {
|
} else {
|
||||||
|
match configuration_error(cx) {
|
||||||
|
Some(configuration_error) => {
|
||||||
let label = match configuration_error {
|
let label = match configuration_error {
|
||||||
ConfigurationError::NoProvider => "No LLM provider selected.",
|
ConfigurationError::NoProvider => "No LLM provider selected.",
|
||||||
ConfigurationError::ProviderNotAuthenticated => "LLM provider is not configured.",
|
ConfigurationError::ProviderNotAuthenticated => {
|
||||||
|
"LLM provider is not configured."
|
||||||
|
}
|
||||||
ConfigurationError::ProviderPendingTermsAcceptance(_) => {
|
ConfigurationError::ProviderPendingTermsAcceptance(_) => {
|
||||||
"LLM provider requires accepting the Terms of Service."
|
"LLM provider requires accepting the Terms of Service."
|
||||||
}
|
}
|
||||||
@@ -2357,18 +2388,27 @@ impl ContextEditor {
|
|||||||
.on_click({
|
.on_click({
|
||||||
let focus_handle = self.focus_handle(cx).clone();
|
let focus_handle = self.focus_handle(cx).clone();
|
||||||
move |_event, window, cx| {
|
move |_event, window, cx| {
|
||||||
focus_handle.dispatch_action(&ShowConfiguration, window, cx);
|
focus_handle.dispatch_action(
|
||||||
|
&ShowConfiguration,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.into_any_element(),
|
.into_any_element(),
|
||||||
)
|
)
|
||||||
} else {
|
}
|
||||||
None
|
_ => 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 focus_handle = self.focus_handle(cx).clone();
|
||||||
|
|
||||||
let (style, tooltip) = match token_state(&self.context, cx) {
|
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 focus_handle = self.focus_handle(cx).clone();
|
||||||
|
|
||||||
let (style, tooltip) = match token_state(&self.context, cx) {
|
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(
|
slash_command_picker::SlashCommandSelector::new(
|
||||||
self.slash_commands.clone(),
|
self.slash_commands.clone(),
|
||||||
cx.entity().downgrade(),
|
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 active_model = LanguageModelRegistry::read_global(cx).active_model();
|
||||||
let focus_handle = self.editor().focus_handle(cx).clone();
|
let focus_handle = self.editor().focus_handle(cx).clone();
|
||||||
let model_name = match active_model {
|
let model_name = match active_model {
|
||||||
@@ -3283,12 +3327,9 @@ impl FollowableItem for ContextEditor {
|
|||||||
Some(proto::view::Variant::ContextEditor(
|
Some(proto::view::Variant::ContextEditor(
|
||||||
proto::view::ContextEditor {
|
proto::view::ContextEditor {
|
||||||
context_id: context.id().to_proto(),
|
context_id: context.id().to_proto(),
|
||||||
editor: if let Some(proto::view::Variant::Editor(proto)) =
|
editor: match self.editor.read(cx).to_state_proto(window, cx) {
|
||||||
self.editor.read(cx).to_state_proto(window, cx)
|
Some(proto::view::Variant::Editor(proto)) => Some(proto),
|
||||||
{
|
_ => None,
|
||||||
Some(proto)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
@@ -3413,7 +3454,7 @@ impl ContextEditorToolbarItem {
|
|||||||
pub fn render_remaining_tokens(
|
pub fn render_remaining_tokens(
|
||||||
context_editor: &Entity<ContextEditor>,
|
context_editor: &Entity<ContextEditor>,
|
||||||
cx: &App,
|
cx: &App,
|
||||||
) -> Option<impl IntoElement> {
|
) -> Option<impl IntoElement + use<>> {
|
||||||
let context = &context_editor.read(cx).context;
|
let context = &context_editor.read(cx).context;
|
||||||
|
|
||||||
let (token_count_color, token_count, max_token_count, tooltip) = match token_state(context, cx)?
|
let (token_count_color, token_count, max_token_count, tooltip) = match token_state(context, cx)?
|
||||||
|
|||||||
@@ -299,13 +299,12 @@ impl ContextStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if is_shared {
|
if is_shared {
|
||||||
self.contexts.retain_mut(|context| {
|
self.contexts.retain_mut(|context| match context.upgrade() {
|
||||||
if let Some(strong_context) = context.upgrade() {
|
Some(strong_context) => {
|
||||||
*context = ContextHandle::Strong(strong_context);
|
*context = ContextHandle::Strong(strong_context);
|
||||||
true
|
true
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
_ => false,
|
||||||
});
|
});
|
||||||
let remote_id = self.project.read(cx).remote_id().unwrap();
|
let remote_id = self.project.read(cx).remote_id().unwrap();
|
||||||
self.client_subscription = self
|
self.client_subscription = self
|
||||||
@@ -336,8 +335,8 @@ impl ContextStore {
|
|||||||
self.synchronize_contexts(cx);
|
self.synchronize_contexts(cx);
|
||||||
}
|
}
|
||||||
project::Event::DisconnectedFromHost => {
|
project::Event::DisconnectedFromHost => {
|
||||||
self.contexts.retain_mut(|context| {
|
self.contexts.retain_mut(|context| match context.upgrade() {
|
||||||
if let Some(strong_context) = context.upgrade() {
|
Some(strong_context) => {
|
||||||
*context = ContextHandle::Weak(context.downgrade());
|
*context = ContextHandle::Weak(context.downgrade());
|
||||||
strong_context.update(cx, |context, cx| {
|
strong_context.update(cx, |context, cx| {
|
||||||
if context.replica_id() != ReplicaId::default() {
|
if context.replica_id() != ReplicaId::default() {
|
||||||
@@ -345,9 +344,8 @@ impl ContextStore {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
true
|
true
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
_ => false,
|
||||||
});
|
});
|
||||||
self.host_contexts.clear();
|
self.host_contexts.clear();
|
||||||
cx.notify();
|
cx.notify();
|
||||||
@@ -422,13 +420,14 @@ impl ContextStore {
|
|||||||
.await?;
|
.await?;
|
||||||
context.update(cx, |context, cx| context.apply_ops(operations, cx))?;
|
context.update(cx, |context, cx| context.apply_ops(operations, cx))?;
|
||||||
this.update(cx, |this, cx| {
|
this.update(cx, |this, cx| {
|
||||||
if let Some(existing_context) = this.loaded_context_for_id(&context_id, cx) {
|
match this.loaded_context_for_id(&context_id, cx) {
|
||||||
existing_context
|
Some(existing_context) => existing_context,
|
||||||
} else {
|
_ => {
|
||||||
this.register_context(&context, cx);
|
this.register_context(&context, cx);
|
||||||
this.synchronize_contexts(cx);
|
this.synchronize_contexts(cx);
|
||||||
context
|
context
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -471,12 +470,13 @@ impl ContextStore {
|
|||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
this.update(cx, |this, cx| {
|
this.update(cx, |this, cx| {
|
||||||
if let Some(existing_context) = this.loaded_context_for_path(&path, cx) {
|
match this.loaded_context_for_path(&path, cx) {
|
||||||
existing_context
|
Some(existing_context) => existing_context,
|
||||||
} else {
|
_ => {
|
||||||
this.register_context(&context, cx);
|
this.register_context(&context, cx);
|
||||||
context
|
context
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -591,13 +591,14 @@ impl ContextStore {
|
|||||||
.await?;
|
.await?;
|
||||||
context.update(cx, |context, cx| context.apply_ops(operations, cx))?;
|
context.update(cx, |context, cx| context.apply_ops(operations, cx))?;
|
||||||
this.update(cx, |this, cx| {
|
this.update(cx, |this, cx| {
|
||||||
if let Some(existing_context) = this.loaded_context_for_id(&context_id, cx) {
|
match this.loaded_context_for_id(&context_id, cx) {
|
||||||
existing_context
|
Some(existing_context) => existing_context,
|
||||||
} else {
|
_ => {
|
||||||
this.register_context(&context, cx);
|
this.register_context(&context, cx);
|
||||||
this.synchronize_contexts(cx);
|
this.synchronize_contexts(cx);
|
||||||
context
|
context
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -151,7 +151,8 @@ impl SlashCommandCompletionProvider {
|
|||||||
let mut flag = self.cancel_flag.lock();
|
let mut flag = self.cancel_flag.lock();
|
||||||
flag.store(true, SeqCst);
|
flag.store(true, SeqCst);
|
||||||
*flag = new_cancel_flag.clone();
|
*flag = new_cancel_flag.clone();
|
||||||
if let Some(command) = self.slash_commands.command(command_name, cx) {
|
match self.slash_commands.command(command_name, cx) {
|
||||||
|
Some(command) => {
|
||||||
let completions = command.complete_argument(
|
let completions = command.complete_argument(
|
||||||
arguments,
|
arguments,
|
||||||
new_cancel_flag.clone(),
|
new_cancel_flag.clone(),
|
||||||
@@ -169,11 +170,8 @@ impl SlashCommandCompletionProvider {
|
|||||||
.await?
|
.await?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|new_argument| {
|
.map(|new_argument| {
|
||||||
let confirm =
|
let confirm = editor.clone().zip(workspace.clone()).map(
|
||||||
editor
|
|(editor, workspace)| {
|
||||||
.clone()
|
|
||||||
.zip(workspace.clone())
|
|
||||||
.map(|(editor, workspace)| {
|
|
||||||
Arc::new({
|
Arc::new({
|
||||||
let mut completed_arguments = arguments.clone();
|
let mut completed_arguments = arguments.clone();
|
||||||
if new_argument.replace_previous_arguments {
|
if new_argument.replace_previous_arguments {
|
||||||
@@ -210,7 +208,8 @@ impl SlashCommandCompletionProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}) as Arc<_>
|
}) as Arc<_>
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
let mut new_text = new_argument.new_text.clone();
|
let mut new_text = new_argument.new_text.clone();
|
||||||
if new_argument.after_completion == AfterCompletion::Continue {
|
if new_argument.after_completion == AfterCompletion::Continue {
|
||||||
@@ -234,8 +233,8 @@ impl SlashCommandCompletionProvider {
|
|||||||
.collect(),
|
.collect(),
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
} else {
|
}
|
||||||
Task::ready(Ok(Some(Vec::new())))
|
_ => Task::ready(Ok(Some(Vec::new()))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -333,10 +332,9 @@ impl CompletionProvider for SlashCommandCompletionProvider {
|
|||||||
let position = position.to_point(buffer);
|
let position = position.to_point(buffer);
|
||||||
let line_start = Point::new(position.row, 0);
|
let line_start = Point::new(position.row, 0);
|
||||||
let mut lines = buffer.text_for_range(line_start..position).lines();
|
let mut lines = buffer.text_for_range(line_start..position).lines();
|
||||||
if let Some(line) = lines.next() {
|
match lines.next() {
|
||||||
SlashCommandLine::parse(line).is_some()
|
Some(line) => SlashCommandLine::parse(line).is_some(),
|
||||||
} else {
|
_ => false,
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -105,8 +105,8 @@ impl JsonSchema for AssistantSettingsContent {
|
|||||||
VersionedAssistantSettingsContent::schema_name()
|
VersionedAssistantSettingsContent::schema_name()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> Schema {
|
fn json_schema(r#gen: &mut schemars::r#gen::SchemaGenerator) -> Schema {
|
||||||
VersionedAssistantSettingsContent::json_schema(gen)
|
VersionedAssistantSettingsContent::json_schema(r#gen)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_referenceable() -> bool {
|
fn is_referenceable() -> bool {
|
||||||
@@ -416,7 +416,7 @@ pub struct LanguageModelSelection {
|
|||||||
pub model: String,
|
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 {
|
schemars::schema::SchemaObject {
|
||||||
enum_values: Some(vec![
|
enum_values: Some(vec![
|
||||||
"anthropic".into(),
|
"anthropic".into(),
|
||||||
|
|||||||
@@ -88,8 +88,8 @@ impl SlashCommand for ContextServerSlashCommand {
|
|||||||
let server_id = self.server_id.clone();
|
let server_id = self.server_id.clone();
|
||||||
let prompt_name = self.prompt.name.clone();
|
let prompt_name = self.prompt.name.clone();
|
||||||
|
|
||||||
if let Some(server) = self.server_manager.read(cx).get_server(&server_id) {
|
match self.server_manager.read(cx).get_server(&server_id) {
|
||||||
cx.foreground_executor().spawn(async move {
|
Some(server) => cx.foreground_executor().spawn(async move {
|
||||||
let Some(protocol) = server.client() else {
|
let Some(protocol) = server.client() else {
|
||||||
return Err(anyhow!("Context server not initialized"));
|
return Err(anyhow!("Context server not initialized"));
|
||||||
};
|
};
|
||||||
@@ -118,9 +118,8 @@ impl SlashCommand for ContextServerSlashCommand {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
Ok(completions)
|
Ok(completions)
|
||||||
})
|
}),
|
||||||
} else {
|
_ => Task::ready(Err(anyhow!("Context server not found"))),
|
||||||
Task::ready(Err(anyhow!("Context server not found")))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,7 +142,8 @@ impl SlashCommand for ContextServerSlashCommand {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let manager = self.server_manager.read(cx);
|
let manager = self.server_manager.read(cx);
|
||||||
if let Some(server) = manager.get_server(&server_id) {
|
match manager.get_server(&server_id) {
|
||||||
|
Some(server) => {
|
||||||
cx.foreground_executor().spawn(async move {
|
cx.foreground_executor().spawn(async move {
|
||||||
let Some(protocol) = server.client() else {
|
let Some(protocol) = server.client() else {
|
||||||
return Err(anyhow!("Context server not initialized"));
|
return Err(anyhow!("Context server not initialized"));
|
||||||
@@ -191,8 +191,8 @@ impl SlashCommand for ContextServerSlashCommand {
|
|||||||
}
|
}
|
||||||
.to_event_stream())
|
.to_event_stream())
|
||||||
})
|
})
|
||||||
} else {
|
}
|
||||||
Task::ready(Err(anyhow!("Context server not found")))
|
_ => Task::ready(Err(anyhow!("Context server not found"))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -229,19 +229,20 @@ fn collect_diagnostics(
|
|||||||
options: Options,
|
options: Options,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> Task<Result<Option<SlashCommandOutput>>> {
|
) -> Task<Result<Option<SlashCommandOutput>>> {
|
||||||
let error_source = if let Some(path_matcher) = &options.path_matcher {
|
let error_source = match &options.path_matcher {
|
||||||
|
Some(path_matcher) => {
|
||||||
debug_assert_eq!(path_matcher.sources().len(), 1);
|
debug_assert_eq!(path_matcher.sources().len(), 1);
|
||||||
Some(path_matcher.sources().first().cloned().unwrap_or_default())
|
Some(path_matcher.sources().first().cloned().unwrap_or_default())
|
||||||
} else {
|
}
|
||||||
None
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let glob_is_exact_file_match = if let Some(path) = options
|
let glob_is_exact_file_match = match options
|
||||||
.path_matcher
|
.path_matcher
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|pm| pm.sources().first())
|
.and_then(|pm| pm.sources().first())
|
||||||
{
|
{
|
||||||
PathBuf::try_from(path)
|
Some(path) => PathBuf::try_from(path)
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|path| {
|
.and_then(|path| {
|
||||||
project.read(cx).worktrees(cx).find_map(|worktree| {
|
project.read(cx).worktrees(cx).find_map(|worktree| {
|
||||||
@@ -251,9 +252,8 @@ fn collect_diagnostics(
|
|||||||
worktree.absolutize(&relative_path).ok()
|
worktree.absolutize(&relative_path).ok()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.is_some()
|
.is_some(),
|
||||||
} else {
|
_ => false,
|
||||||
false
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let project_handle = project.downgrade();
|
let project_handle = project.downgrade();
|
||||||
|
|||||||
@@ -221,7 +221,7 @@ fn collect_files(
|
|||||||
project: Entity<Project>,
|
project: Entity<Project>,
|
||||||
glob_inputs: &[String],
|
glob_inputs: &[String],
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> impl Stream<Item = Result<SlashCommandEvent>> {
|
) -> impl Stream<Item = Result<SlashCommandEvent>> + use<> {
|
||||||
let Ok(matchers) = glob_inputs
|
let Ok(matchers) = glob_inputs
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|glob_input| {
|
.map(|glob_input| {
|
||||||
@@ -285,7 +285,8 @@ fn collect_files(
|
|||||||
if entry.is_dir() {
|
if entry.is_dir() {
|
||||||
// Auto-fold directories that contain no files
|
// Auto-fold directories that contain no files
|
||||||
let mut child_entries = snapshot.child_entries(&entry.path);
|
let mut child_entries = snapshot.child_entries(&entry.path);
|
||||||
if let Some(child) = child_entries.next() {
|
match child_entries.next() {
|
||||||
|
Some(child) => {
|
||||||
if child_entries.next().is_none() && child.kind.is_dir() {
|
if child_entries.next().is_none() && child.kind.is_dir() {
|
||||||
if is_top_level_directory {
|
if is_top_level_directory {
|
||||||
is_top_level_directory = false;
|
is_top_level_directory = false;
|
||||||
@@ -297,11 +298,13 @@ fn collect_files(
|
|||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
// Skip empty directories
|
// Skip empty directories
|
||||||
folded_directory_names_stack.clear();
|
folded_directory_names_stack.clear();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
let prefix_paths = folded_directory_names_stack.drain(..).as_slice().join("/");
|
let prefix_paths = folded_directory_names_stack.drain(..).as_slice().join("/");
|
||||||
if prefix_paths.is_empty() {
|
if prefix_paths.is_empty() {
|
||||||
let label = if is_top_level_directory {
|
let label = if is_top_level_directory {
|
||||||
|
|||||||
@@ -218,12 +218,9 @@ async fn project_symbols(
|
|||||||
|
|
||||||
for symbol in symbols
|
for symbol in symbols
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|symbol| {
|
.filter(|symbol| match ®ex {
|
||||||
if let Some(regex) = ®ex {
|
Some(regex) => regex.is_match(&symbol.name),
|
||||||
regex.is_match(&symbol.name)
|
_ => true,
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.skip(offset as usize)
|
.skip(offset as usize)
|
||||||
// Take 1 more than RESULTS_PER_PAGE so we can tell if there are more results.
|
// 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();
|
.collect();
|
||||||
|
|
||||||
let lang_name = lang.name();
|
let lang_name = lang.name();
|
||||||
if let Some(lsp_adapter) = registry.lsp_adapters(&lang_name).first().cloned() {
|
match registry.lsp_adapters(&lang_name).first().cloned() {
|
||||||
lsp_adapter
|
Some(lsp_adapter) => lsp_adapter
|
||||||
.labels_for_symbols(&entries_for_labels, lang)
|
.labels_for_symbols(&entries_for_labels, lang)
|
||||||
.await
|
.await
|
||||||
.ok()
|
.ok(),
|
||||||
} else {
|
_ => None,
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => None,
|
None => None,
|
||||||
|
|||||||
@@ -63,16 +63,16 @@ impl Tool for DiagnosticsTool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn ui_text(&self, input: &serde_json::Value) -> String {
|
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()
|
.ok()
|
||||||
.and_then(|input| match input.path {
|
.and_then(|input| match input.path {
|
||||||
Some(path) if !path.is_empty() => Some(MarkdownString::inline_code(&path)),
|
Some(path) if !path.is_empty() => Some(MarkdownString::inline_code(&path)),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
}) {
|
||||||
{
|
Some(path) => {
|
||||||
format!("Check diagnostics for {path}")
|
format!("Check diagnostics for {path}")
|
||||||
} else {
|
}
|
||||||
"Check project diagnostics".to_string()
|
_ => "Check project diagnostics".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ impl Tool for FindReplaceFileTool {
|
|||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
if let Some(diff) = result {
|
match result { Some(diff) => {
|
||||||
let edit_ids = buffer.update(cx, |buffer, cx| {
|
let edit_ids = buffer.update(cx, |buffer, cx| {
|
||||||
buffer.finalize_last_transaction();
|
buffer.finalize_last_transaction();
|
||||||
buffer.apply_diff(diff, false, cx);
|
buffer.apply_diff(diff, false, cx);
|
||||||
@@ -205,7 +205,7 @@ impl Tool for FindReplaceFileTool {
|
|||||||
})?.await?;
|
})?.await?;
|
||||||
|
|
||||||
Ok(format!("Edited {}", input.path.display()))
|
Ok(format!("Edited {}", input.path.display()))
|
||||||
} else {
|
} _ => {
|
||||||
let err = buffer.read_with(cx, |buffer, _cx| {
|
let err = buffer.read_with(cx, |buffer, _cx| {
|
||||||
let file_exists = buffer
|
let file_exists = buffer
|
||||||
.file()
|
.file()
|
||||||
@@ -224,7 +224,7 @@ impl Tool for FindReplaceFileTool {
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
Err(err)
|
Err(err)
|
||||||
}
|
}}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ impl SoundRegistry {
|
|||||||
cx.set_global(GlobalSoundRegistry(SoundRegistry::new(source)));
|
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) {
|
if let Some(wav) = self.cache.lock().get(name) {
|
||||||
return Ok(wav.clone());
|
return Ok(wav.clone());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -201,9 +201,11 @@ pub fn check(_: &Check, window: &mut Window, cx: &mut App) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(updater) = AutoUpdater::get(cx) {
|
match AutoUpdater::get(cx) {
|
||||||
|
Some(updater) => {
|
||||||
updater.update(cx, |updater, cx| updater.poll(cx));
|
updater.update(cx, |updater, cx| updater.poll(cx));
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
drop(window.prompt(
|
drop(window.prompt(
|
||||||
gpui::PromptLevel::Info,
|
gpui::PromptLevel::Info,
|
||||||
"Could not check for updates",
|
"Could not check for updates",
|
||||||
@@ -213,6 +215,7 @@ pub fn check(_: &Check, window: &mut Window, cx: &mut App) {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn view_release_notes(_: &ViewReleaseNotes, cx: &mut App) -> Option<()> {
|
pub fn view_release_notes(_: &ViewReleaseNotes, cx: &mut App) -> Option<()> {
|
||||||
let auto_updater = AutoUpdater::get(cx)?;
|
let auto_updater = AutoUpdater::get(cx)?;
|
||||||
|
|||||||
@@ -110,8 +110,8 @@ impl Render for Breadcrumbs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.tooltip(move |window, cx| {
|
.tooltip(move |window, cx| match editor.upgrade() {
|
||||||
if let Some(editor) = editor.upgrade() {
|
Some(editor) => {
|
||||||
let focus_handle = editor.read(cx).focus_handle(cx);
|
let focus_handle = editor.read(cx).focus_handle(cx);
|
||||||
Tooltip::for_action_in(
|
Tooltip::for_action_in(
|
||||||
"Show Symbol Outline",
|
"Show Symbol Outline",
|
||||||
@@ -120,14 +120,13 @@ impl Render for Breadcrumbs {
|
|||||||
window,
|
window,
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
} else {
|
}
|
||||||
Tooltip::for_action(
|
_ => Tooltip::for_action(
|
||||||
"Show Symbol Outline",
|
"Show Symbol Outline",
|
||||||
&zed_actions::outline::ToggleOutline,
|
&zed_actions::outline::ToggleOutline,
|
||||||
window,
|
window,
|
||||||
cx,
|
cx,
|
||||||
)
|
),
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
None => element
|
None => element
|
||||||
|
|||||||
@@ -608,7 +608,8 @@ fn compute_hunks(
|
|||||||
) -> SumTree<InternalDiffHunk> {
|
) -> SumTree<InternalDiffHunk> {
|
||||||
let mut tree = SumTree::new(&buffer);
|
let mut tree = SumTree::new(&buffer);
|
||||||
|
|
||||||
if let Some((diff_base, diff_base_rope)) = diff_base {
|
match diff_base {
|
||||||
|
Some((diff_base, diff_base_rope)) => {
|
||||||
let buffer_text = buffer.as_rope().to_string();
|
let buffer_text = buffer.as_rope().to_string();
|
||||||
|
|
||||||
let mut options = GitOptions::default();
|
let mut options = GitOptions::default();
|
||||||
@@ -649,7 +650,8 @@ fn compute_hunks(
|
|||||||
tree.push(hunk, &buffer);
|
tree.push(hunk, &buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
tree.push(
|
tree.push(
|
||||||
InternalDiffHunk {
|
InternalDiffHunk {
|
||||||
buffer_range: Anchor::MIN..Anchor::MAX,
|
buffer_range: Anchor::MIN..Anchor::MAX,
|
||||||
@@ -658,6 +660,7 @@ fn compute_hunks(
|
|||||||
&buffer,
|
&buffer,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tree
|
tree
|
||||||
}
|
}
|
||||||
@@ -776,7 +779,7 @@ impl BufferDiff {
|
|||||||
language: Option<Arc<Language>>,
|
language: Option<Arc<Language>>,
|
||||||
language_registry: Option<Arc<LanguageRegistry>>,
|
language_registry: Option<Arc<LanguageRegistry>>,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> impl Future<Output = BufferDiffInner> {
|
) -> impl Future<Output = BufferDiffInner> + use<> {
|
||||||
let base_text_pair;
|
let base_text_pair;
|
||||||
let base_text_exists;
|
let base_text_exists;
|
||||||
let base_text_snapshot;
|
let base_text_snapshot;
|
||||||
@@ -818,7 +821,7 @@ impl BufferDiff {
|
|||||||
base_text: Option<Arc<String>>,
|
base_text: Option<Arc<String>>,
|
||||||
base_text_snapshot: language::BufferSnapshot,
|
base_text_snapshot: language::BufferSnapshot,
|
||||||
cx: &App,
|
cx: &App,
|
||||||
) -> impl Future<Output = BufferDiffInner> {
|
) -> impl Future<Output = BufferDiffInner> + use<> {
|
||||||
let base_text_exists = base_text.is_some();
|
let base_text_exists = base_text.is_some();
|
||||||
let base_text_pair = base_text.map(|text| (text, base_text_snapshot.as_rope().clone()));
|
let base_text_pair = base_text.map(|text| (text, base_text_snapshot.as_rope().clone()));
|
||||||
cx.background_spawn(async move {
|
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 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())
|
Rope::from(head_text.as_str())
|
||||||
} else {
|
} else {
|
||||||
working_copy.as_rope().clone()
|
working_copy.as_rope().clone()
|
||||||
|
|||||||
@@ -179,23 +179,21 @@ impl ActiveCall {
|
|||||||
return Task::ready(Ok(()));
|
return Task::ready(Ok(()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let room = if let Some(room) = self.room().cloned() {
|
let room = match self.room().cloned() {
|
||||||
Some(Task::ready(Ok(room)).shared())
|
Some(room) => Some(Task::ready(Ok(room)).shared()),
|
||||||
} else {
|
_ => self.pending_room_creation.clone(),
|
||||||
self.pending_room_creation.clone()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let invite = if let Some(room) = room {
|
let invite = match room {
|
||||||
cx.spawn(async move |_, cx| {
|
Some(room) => cx.spawn(async move |_, cx| {
|
||||||
let room = room.await.map_err(|err| anyhow!("{:?}", err))?;
|
let room = room.await.map_err(|err| anyhow!("{:?}", err))?;
|
||||||
|
|
||||||
let initial_project_id = if let Some(initial_project) = initial_project {
|
let initial_project_id = match initial_project {
|
||||||
Some(
|
Some(initial_project) => Some(
|
||||||
room.update(cx, |room, cx| room.share_project(initial_project, cx))?
|
room.update(cx, |room, cx| room.share_project(initial_project, cx))?
|
||||||
.await?,
|
.await?,
|
||||||
)
|
),
|
||||||
} else {
|
_ => None,
|
||||||
None
|
|
||||||
};
|
};
|
||||||
|
|
||||||
room.update(cx, move |room, cx| {
|
room.update(cx, move |room, cx| {
|
||||||
@@ -204,8 +202,8 @@ impl ActiveCall {
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
anyhow::Ok(())
|
anyhow::Ok(())
|
||||||
})
|
}),
|
||||||
} else {
|
_ => {
|
||||||
let client = self.client.clone();
|
let client = self.client.clone();
|
||||||
let user_store = self.user_store.clone();
|
let user_store = self.user_store.clone();
|
||||||
let room = cx
|
let room = cx
|
||||||
@@ -239,6 +237,7 @@ impl ActiveCall {
|
|||||||
room.await.map_err(|err| anyhow!("{:?}", err))?;
|
room.await.map_err(|err| anyhow!("{:?}", err))?;
|
||||||
anyhow::Ok(())
|
anyhow::Ok(())
|
||||||
})
|
})
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
cx.spawn(async move |this, cx| {
|
cx.spawn(async move |this, cx| {
|
||||||
@@ -292,10 +291,11 @@ impl ActiveCall {
|
|||||||
return Task::ready(Err(anyhow!("cannot join while on another call")));
|
return Task::ready(Err(anyhow!("cannot join while on another call")));
|
||||||
}
|
}
|
||||||
|
|
||||||
let call = if let Some(call) = self.incoming_call.0.borrow_mut().take() {
|
let call = match self.incoming_call.0.borrow_mut().take() {
|
||||||
call
|
Some(call) => call,
|
||||||
} else {
|
_ => {
|
||||||
return Task::ready(Err(anyhow!("no incoming call")));
|
return Task::ready(Err(anyhow!("no incoming call")));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.pending_room_creation.is_some() {
|
if self.pending_room_creation.is_some() {
|
||||||
@@ -373,11 +373,12 @@ impl ActiveCall {
|
|||||||
Audio::end_call(cx);
|
Audio::end_call(cx);
|
||||||
|
|
||||||
let channel_id = self.channel_id(cx);
|
let channel_id = self.channel_id(cx);
|
||||||
if let Some((room, _)) = self.room.take() {
|
match self.room.take() {
|
||||||
|
Some((room, _)) => {
|
||||||
cx.emit(Event::RoomLeft { channel_id });
|
cx.emit(Event::RoomLeft { channel_id });
|
||||||
room.update(cx, |room, cx| room.leave(cx))
|
room.update(cx, |room, cx| room.leave(cx))
|
||||||
} else {
|
}
|
||||||
Task::ready(Ok(()))
|
_ => Task::ready(Ok(())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,11 +387,12 @@ impl ActiveCall {
|
|||||||
project: Entity<Project>,
|
project: Entity<Project>,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Task<Result<u64>> {
|
) -> Task<Result<u64>> {
|
||||||
if let Some((room, _)) = self.room.as_ref() {
|
match self.room.as_ref() {
|
||||||
|
Some((room, _)) => {
|
||||||
self.report_call_event("Project Shared", cx);
|
self.report_call_event("Project Shared", cx);
|
||||||
room.update(cx, |room, cx| room.share_project(project, cx))
|
room.update(cx, |room, cx| room.share_project(project, cx))
|
||||||
} else {
|
}
|
||||||
Task::ready(Err(anyhow!("no active call")))
|
_ => Task::ready(Err(anyhow!("no active call"))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -399,11 +401,12 @@ impl ActiveCall {
|
|||||||
project: Entity<Project>,
|
project: Entity<Project>,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
if let Some((room, _)) = self.room.as_ref() {
|
match self.room.as_ref() {
|
||||||
|
Some((room, _)) => {
|
||||||
self.report_call_event("Project Unshared", cx);
|
self.report_call_event("Project Unshared", cx);
|
||||||
room.update(cx, |room, cx| room.unshare_project(project, cx))
|
room.update(cx, |room, cx| room.unshare_project(project, cx))
|
||||||
} else {
|
}
|
||||||
Err(anyhow!("no active call"))
|
_ => Err(anyhow!("no active call")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,7 +433,8 @@ impl ActiveCall {
|
|||||||
Task::ready(Ok(()))
|
Task::ready(Ok(()))
|
||||||
} else {
|
} else {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
if let Some(room) = room {
|
match room {
|
||||||
|
Some(room) => {
|
||||||
if room.read(cx).status().is_offline() {
|
if room.read(cx).status().is_offline() {
|
||||||
self.room = None;
|
self.room = None;
|
||||||
Task::ready(Ok(()))
|
Task::ready(Ok(()))
|
||||||
@@ -454,12 +458,14 @@ impl ActiveCall {
|
|||||||
cx.emit(Event::RoomJoined { channel_id });
|
cx.emit(Event::RoomJoined { channel_id });
|
||||||
room.update(cx, |room, cx| room.set_location(location.as_ref(), cx))
|
room.update(cx, |room, cx| room.set_location(location.as_ref(), cx))
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
self.room = None;
|
self.room = None;
|
||||||
Task::ready(Ok(()))
|
Task::ready(Ok(()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn room(&self) -> Option<&Entity<Room>> {
|
pub fn room(&self) -> Option<&Entity<Room>> {
|
||||||
self.room.as_ref().map(|(room, _)| room)
|
self.room.as_ref().map(|(room, _)| room)
|
||||||
|
|||||||
@@ -96,10 +96,11 @@ impl Room {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_connected(&self, _: &App) -> bool {
|
pub fn is_connected(&self, _: &App) -> bool {
|
||||||
if let Some(live_kit) = self.live_kit.as_ref() {
|
match self.live_kit.as_ref() {
|
||||||
|
Some(live_kit) => {
|
||||||
live_kit.room.connection_state() == livekit::ConnectionState::Connected
|
live_kit.room.connection_state() == livekit::ConnectionState::Connected
|
||||||
} else {
|
}
|
||||||
false
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,15 +182,16 @@ impl Room {
|
|||||||
room
|
room
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let initial_project_id = if let Some(initial_project) = initial_project {
|
let initial_project_id = match initial_project {
|
||||||
|
Some(initial_project) => {
|
||||||
let initial_project_id = room
|
let initial_project_id = room
|
||||||
.update(cx, |room, cx| {
|
.update(cx, |room, cx| {
|
||||||
room.share_project(initial_project.clone(), cx)
|
room.share_project(initial_project.clone(), cx)
|
||||||
})?
|
})?
|
||||||
.await?;
|
.await?;
|
||||||
Some(initial_project_id)
|
Some(initial_project_id)
|
||||||
} else {
|
}
|
||||||
None
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let did_join = room
|
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 task = if self.status.is_online() {
|
||||||
let leave = self.leave_internal(cx);
|
let leave = self.leave_internal(cx);
|
||||||
Some(cx.background_spawn(async move {
|
Some(cx.background_spawn(async move {
|
||||||
@@ -665,7 +667,7 @@ impl Room {
|
|||||||
Ok(())
|
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();
|
let mut done_rx = self.room_update_completed_rx.clone();
|
||||||
async move {
|
async move {
|
||||||
while let Some(result) = done_rx.next().await {
|
while let Some(result) = done_rx.next().await {
|
||||||
@@ -728,13 +730,13 @@ impl Room {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.joined_projects.retain(|project| {
|
this.joined_projects
|
||||||
if let Some(project) = project.upgrade() {
|
.retain(|project| match project.upgrade() {
|
||||||
|
Some(project) => {
|
||||||
project.update(cx, |project, cx| project.set_role(role, cx));
|
project.update(cx, |project, cx| project.set_role(role, cx));
|
||||||
true
|
true
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
_ => false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -778,19 +780,17 @@ impl Room {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for unshared_project_id in old_projects.difference(&new_projects) {
|
for unshared_project_id in old_projects.difference(&new_projects) {
|
||||||
this.joined_projects.retain(|project| {
|
this.joined_projects
|
||||||
if let Some(project) = project.upgrade() {
|
.retain(|project| match project.upgrade() {
|
||||||
project.update(cx, |project, cx| {
|
Some(project) => project.update(cx, |project, cx| {
|
||||||
if project.remote_id() == Some(*unshared_project_id) {
|
if project.remote_id() == Some(*unshared_project_id) {
|
||||||
project.disconnected_from_host(cx);
|
project.disconnected_from_host(cx);
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
} else {
|
_ => false,
|
||||||
false
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
cx.emit(Event::RemoteProjectUnshared {
|
cx.emit(Event::RemoteProjectUnshared {
|
||||||
project_id: *unshared_project_id,
|
project_id: *unshared_project_id,
|
||||||
@@ -800,9 +800,8 @@ impl Room {
|
|||||||
let role = participant.role();
|
let role = participant.role();
|
||||||
let location = ParticipantLocation::from_proto(participant.location)
|
let location = ParticipantLocation::from_proto(participant.location)
|
||||||
.unwrap_or(ParticipantLocation::External);
|
.unwrap_or(ParticipantLocation::External);
|
||||||
if let Some(remote_participant) =
|
match this.remote_participants.get_mut(&participant.user_id) {
|
||||||
this.remote_participants.get_mut(&participant.user_id)
|
Some(remote_participant) => {
|
||||||
{
|
|
||||||
remote_participant.peer_id = peer_id;
|
remote_participant.peer_id = peer_id;
|
||||||
remote_participant.projects = participant.projects;
|
remote_participant.projects = participant.projects;
|
||||||
remote_participant.participant_index = participant_index;
|
remote_participant.participant_index = participant_index;
|
||||||
@@ -815,7 +814,8 @@ impl Room {
|
|||||||
participant_id: peer_id,
|
participant_id: peer_id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
this.remote_participants.insert(
|
this.remote_participants.insert(
|
||||||
participant.user_id,
|
participant.user_id,
|
||||||
RemoteParticipant {
|
RemoteParticipant {
|
||||||
@@ -856,6 +856,7 @@ impl Room {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.remote_participants.retain(|user_id, participant| {
|
this.remote_participants.retain(|user_id, participant| {
|
||||||
if this.participant_user_ids.contains(user_id) {
|
if this.participant_user_ids.contains(user_id) {
|
||||||
@@ -1141,12 +1142,10 @@ impl Room {
|
|||||||
Project::in_room(id, client, user_store, language_registry, fs, cx.clone()).await?;
|
Project::in_room(id, client, user_store, language_registry, fs, cx.clone()).await?;
|
||||||
|
|
||||||
this.update(cx, |this, cx| {
|
this.update(cx, |this, cx| {
|
||||||
this.joined_projects.retain(|project| {
|
this.joined_projects
|
||||||
if let Some(project) = project.upgrade() {
|
.retain(|project| match project.upgrade() {
|
||||||
!project.read(cx).is_disconnected(cx)
|
Some(project) => !project.read(cx).is_disconnected(cx),
|
||||||
} else {
|
_ => false,
|
||||||
false
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
this.joined_projects.insert(project.downgrade());
|
this.joined_projects.insert(project.downgrade());
|
||||||
})?;
|
})?;
|
||||||
@@ -1309,13 +1308,16 @@ impl Room {
|
|||||||
return Task::ready(Err(anyhow!("room is offline")));
|
return Task::ready(Err(anyhow!("room is offline")));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (room, publish_id) = if let Some(live_kit) = self.live_kit.as_mut() {
|
let (room, publish_id) = match self.live_kit.as_mut() {
|
||||||
|
Some(live_kit) => {
|
||||||
let publish_id = post_inc(&mut live_kit.next_publish_id);
|
let publish_id = post_inc(&mut live_kit.next_publish_id);
|
||||||
live_kit.microphone_track = LocalTrack::Pending { publish_id };
|
live_kit.microphone_track = LocalTrack::Pending { publish_id };
|
||||||
cx.notify();
|
cx.notify();
|
||||||
(live_kit.room.clone(), publish_id)
|
(live_kit.room.clone(), publish_id)
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
return Task::ready(Err(anyhow!("live-kit was not initialized")));
|
return Task::ready(Err(anyhow!("live-kit was not initialized")));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
cx.spawn(async move |this, cx| {
|
cx.spawn(async move |this, cx| {
|
||||||
@@ -1326,13 +1328,11 @@ impl Room {
|
|||||||
.as_mut()
|
.as_mut()
|
||||||
.ok_or_else(|| anyhow!("live-kit was not initialized"))?;
|
.ok_or_else(|| anyhow!("live-kit was not initialized"))?;
|
||||||
|
|
||||||
let canceled = if let LocalTrack::Pending {
|
let canceled = match &live_kit.microphone_track {
|
||||||
|
LocalTrack::Pending {
|
||||||
publish_id: cur_publish_id,
|
publish_id: cur_publish_id,
|
||||||
} = &live_kit.microphone_track
|
} => *cur_publish_id != publish_id,
|
||||||
{
|
_ => true,
|
||||||
*cur_publish_id != publish_id
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
};
|
};
|
||||||
|
|
||||||
match publication {
|
match publication {
|
||||||
@@ -1376,13 +1376,16 @@ impl Room {
|
|||||||
return Task::ready(Err(anyhow!("screen was already shared")));
|
return Task::ready(Err(anyhow!("screen was already shared")));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (participant, publish_id) = if let Some(live_kit) = self.live_kit.as_mut() {
|
let (participant, publish_id) = match self.live_kit.as_mut() {
|
||||||
|
Some(live_kit) => {
|
||||||
let publish_id = post_inc(&mut live_kit.next_publish_id);
|
let publish_id = post_inc(&mut live_kit.next_publish_id);
|
||||||
live_kit.screen_track = LocalTrack::Pending { publish_id };
|
live_kit.screen_track = LocalTrack::Pending { publish_id };
|
||||||
cx.notify();
|
cx.notify();
|
||||||
(live_kit.room.local_participant(), publish_id)
|
(live_kit.room.local_participant(), publish_id)
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
return Task::ready(Err(anyhow!("live-kit was not initialized")));
|
return Task::ready(Err(anyhow!("live-kit was not initialized")));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let sources = cx.screen_capture_sources();
|
let sources = cx.screen_capture_sources();
|
||||||
@@ -1399,13 +1402,11 @@ impl Room {
|
|||||||
.as_mut()
|
.as_mut()
|
||||||
.ok_or_else(|| anyhow!("live-kit was not initialized"))?;
|
.ok_or_else(|| anyhow!("live-kit was not initialized"))?;
|
||||||
|
|
||||||
let canceled = if let LocalTrack::Pending {
|
let canceled = match &live_kit.screen_track {
|
||||||
|
LocalTrack::Pending {
|
||||||
publish_id: cur_publish_id,
|
publish_id: cur_publish_id,
|
||||||
} = &live_kit.screen_track
|
} => *cur_publish_id != publish_id,
|
||||||
{
|
_ => true,
|
||||||
*cur_publish_id != publish_id
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
};
|
};
|
||||||
|
|
||||||
match publication {
|
match publication {
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ impl ChannelChat {
|
|||||||
|
|
||||||
let channel_id = self.channel_id;
|
let channel_id = self.channel_id;
|
||||||
let pending_id = ChannelMessageId::Pending(post_inc(&mut self.next_pending_message_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(
|
self.insert_messages(
|
||||||
SumTree::from_item(
|
SumTree::from_item(
|
||||||
ChannelMessage {
|
ChannelMessage {
|
||||||
@@ -257,7 +257,7 @@ impl ChannelChat {
|
|||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
|
||||||
let nonce: u128 = self.rng.gen();
|
let nonce: u128 = self.rng.r#gen();
|
||||||
|
|
||||||
let request = self.rpc.request(proto::UpdateChannelMessage {
|
let request = self.rpc.request(proto::UpdateChannelMessage {
|
||||||
channel_id: self.channel_id.0,
|
channel_id: self.channel_id.0,
|
||||||
|
|||||||
@@ -329,17 +329,16 @@ impl ChannelStore {
|
|||||||
.request(proto::GetChannelMessagesById { message_ids }),
|
.request(proto::GetChannelMessagesById { message_ids }),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
cx.spawn(async move |this, cx| {
|
cx.spawn(async move |this, cx| match request {
|
||||||
if let Some(request) = request {
|
Some(request) => {
|
||||||
let response = request.await?;
|
let response = request.await?;
|
||||||
let this = this
|
let this = this
|
||||||
.upgrade()
|
.upgrade()
|
||||||
.ok_or_else(|| anyhow!("channel store dropped"))?;
|
.ok_or_else(|| anyhow!("channel store dropped"))?;
|
||||||
let user_store = this.update(cx, |this, _| this.user_store.clone())?;
|
let user_store = this.update(cx, |this, _| this.user_store.clone())?;
|
||||||
ChannelMessage::from_proto_vec(response.messages, &user_store, cx).await
|
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 {
|
let task = loop {
|
||||||
match get_map(self).entry(channel_id) {
|
match get_map(self).entry(channel_id) {
|
||||||
hash_map::Entry::Occupied(e) => match e.get() {
|
hash_map::Entry::Occupied(e) => match e.get() {
|
||||||
OpenEntityHandle::Open(entity) => {
|
OpenEntityHandle::Open(entity) => match entity.upgrade() {
|
||||||
if let Some(entity) = entity.upgrade() {
|
Some(entity) => {
|
||||||
break Task::ready(Ok(entity)).shared();
|
break Task::ready(Ok(entity)).shared();
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
get_map(self).remove(&channel_id);
|
get_map(self).remove(&channel_id);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
OpenEntityHandle::Loading(task) => {
|
OpenEntityHandle::Loading(task) => {
|
||||||
break task.clone();
|
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();
|
let client = self.client.clone();
|
||||||
async move {
|
async move {
|
||||||
client
|
client
|
||||||
|
|||||||
@@ -228,13 +228,16 @@ fn main() -> Result<()> {
|
|||||||
paths.push(file.path().to_string_lossy().to_string());
|
paths.push(file.path().to_string_lossy().to_string());
|
||||||
let (file, _) = file.keep()?;
|
let (file, _) = file.keep()?;
|
||||||
stdin_tmp_file = Some(file);
|
stdin_tmp_file = Some(file);
|
||||||
} else if let Some(file) = anonymous_fd(path) {
|
} else {
|
||||||
|
match anonymous_fd(path) {
|
||||||
|
Some(file) => {
|
||||||
let tmp_file = NamedTempFile::new()?;
|
let tmp_file = NamedTempFile::new()?;
|
||||||
paths.push(tmp_file.path().to_string_lossy().to_string());
|
paths.push(tmp_file.path().to_string_lossy().to_string());
|
||||||
let (tmp_file, _) = tmp_file.keep()?;
|
let (tmp_file, _) = tmp_file.keep()?;
|
||||||
anonymous_fd_tmp_files.push((file, tmp_file));
|
anonymous_fd_tmp_files.push((file, tmp_file));
|
||||||
} else {
|
}
|
||||||
paths.push(parse_path_with_position(path)?)
|
_ => paths.push(parse_path_with_position(path)?),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -775,7 +778,7 @@ mod mac_os {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Detect {
|
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 {
|
let bundle_path = if let Some(bundle_path) = path {
|
||||||
bundle_path
|
bundle_path
|
||||||
.canonicalize()
|
.canonicalize()
|
||||||
|
|||||||
@@ -618,10 +618,9 @@ impl Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn peer_id(&self) -> Option<PeerId> {
|
pub fn peer_id(&self) -> Option<PeerId> {
|
||||||
if let Status::Connected { peer_id, .. } = &*self.status().borrow() {
|
match &*self.status().borrow() {
|
||||||
Some(*peer_id)
|
Status::Connected { peer_id, .. } => Some(*peer_id),
|
||||||
} else {
|
_ => None,
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1024,7 +1023,7 @@ impl Client {
|
|||||||
&self,
|
&self,
|
||||||
http: Arc<HttpClientWithUrl>,
|
http: Arc<HttpClientWithUrl>,
|
||||||
release_channel: Option<ReleaseChannel>,
|
release_channel: Option<ReleaseChannel>,
|
||||||
) -> impl Future<Output = Result<url::Url>> {
|
) -> impl Future<Output = Result<url::Url>> + use<> {
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
let url_override = self.rpc_url.read().clone();
|
let url_override = self.rpc_url.read().clone();
|
||||||
|
|
||||||
@@ -1429,10 +1428,9 @@ impl Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn connection_id(&self) -> Result<ConnectionId> {
|
fn connection_id(&self) -> Result<ConnectionId> {
|
||||||
if let Status::Connected { connection_id, .. } = *self.status().borrow() {
|
match *self.status().borrow() {
|
||||||
Ok(connection_id)
|
Status::Connected { connection_id, .. } => Ok(connection_id),
|
||||||
} else {
|
_ => Err(anyhow!("not connected")),
|
||||||
Err(anyhow!("not connected"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1444,7 +1442,7 @@ impl Client {
|
|||||||
pub fn request<T: RequestMessage>(
|
pub fn request<T: RequestMessage>(
|
||||||
&self,
|
&self,
|
||||||
request: T,
|
request: T,
|
||||||
) -> impl Future<Output = Result<T::Response>> {
|
) -> impl Future<Output = Result<T::Response>> + use<T> {
|
||||||
self.request_envelope(request)
|
self.request_envelope(request)
|
||||||
.map_ok(|envelope| envelope.payload)
|
.map_ok(|envelope| envelope.payload)
|
||||||
}
|
}
|
||||||
@@ -1452,7 +1450,8 @@ impl Client {
|
|||||||
pub fn request_stream<T: RequestMessage>(
|
pub fn request_stream<T: RequestMessage>(
|
||||||
&self,
|
&self,
|
||||||
request: T,
|
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);
|
let client_id = self.id.load(Ordering::SeqCst);
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"rpc request start. client_id:{}. name:{}",
|
"rpc request start. client_id:{}. name:{}",
|
||||||
@@ -1476,7 +1475,7 @@ impl Client {
|
|||||||
pub fn request_envelope<T: RequestMessage>(
|
pub fn request_envelope<T: RequestMessage>(
|
||||||
&self,
|
&self,
|
||||||
request: T,
|
request: T,
|
||||||
) -> impl Future<Output = Result<TypedEnvelope<T::Response>>> {
|
) -> impl Future<Output = Result<TypedEnvelope<T::Response>>> + use<T> {
|
||||||
let client_id = self.id();
|
let client_id = self.id();
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"rpc request start. client_id:{}. name:{}",
|
"rpc request start. client_id:{}. name:{}",
|
||||||
@@ -1501,7 +1500,7 @@ impl Client {
|
|||||||
&self,
|
&self,
|
||||||
envelope: proto::Envelope,
|
envelope: proto::Envelope,
|
||||||
request_type: &'static str,
|
request_type: &'static str,
|
||||||
) -> impl Future<Output = Result<proto::Envelope>> {
|
) -> impl Future<Output = Result<proto::Envelope>> + use<> {
|
||||||
let client_id = self.id();
|
let client_id = self.id();
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"rpc request start. client_id:{}. name:{}",
|
"rpc request start. client_id:{}. name:{}",
|
||||||
@@ -1528,12 +1527,13 @@ impl Client {
|
|||||||
let type_name = message.payload_type_name();
|
let type_name = message.payload_type_name();
|
||||||
let original_sender_id = message.original_sender_id();
|
let original_sender_id = message.original_sender_id();
|
||||||
|
|
||||||
if let Some(future) = ProtoMessageHandlerSet::handle_message(
|
match ProtoMessageHandlerSet::handle_message(
|
||||||
&self.handler_set,
|
&self.handler_set,
|
||||||
message,
|
message,
|
||||||
self.clone().into(),
|
self.clone().into(),
|
||||||
cx.clone(),
|
cx.clone(),
|
||||||
) {
|
) {
|
||||||
|
Some(future) => {
|
||||||
let client_id = self.id();
|
let client_id = self.id();
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"rpc message received. client_id:{}, sender_id:{:?}, type:{}",
|
"rpc message received. client_id:{}, sender_id:{:?}, type:{}",
|
||||||
@@ -1561,13 +1561,15 @@ impl Client {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
log::info!("unhandled message {}", type_name);
|
log::info!("unhandled message {}", type_name);
|
||||||
self.peer
|
self.peer
|
||||||
.respond_with_unhandled_message(sender_id.into(), request_id, type_name)
|
.respond_with_unhandled_message(sender_id.into(), request_id, type_name)
|
||||||
.log_err();
|
.log_err();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn telemetry(&self) -> &Arc<Telemetry> {
|
pub fn telemetry(&self) -> &Arc<Telemetry> {
|
||||||
&self.telemetry
|
&self.telemetry
|
||||||
|
|||||||
@@ -273,7 +273,7 @@ impl Telemetry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[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(())
|
Task::ready(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -171,13 +171,15 @@ impl UserStore {
|
|||||||
_maintain_contacts: cx.spawn(async move |this, cx| {
|
_maintain_contacts: cx.spawn(async move |this, cx| {
|
||||||
let _subscriptions = rpc_subscriptions;
|
let _subscriptions = rpc_subscriptions;
|
||||||
while let Some(message) = update_contacts_rx.next().await {
|
while let Some(message) = update_contacts_rx.next().await {
|
||||||
if let Ok(task) = this.update(cx, |this, cx| this.update_contacts(message, cx))
|
match this.update(cx, |this, cx| this.update_contacts(message, cx)) {
|
||||||
{
|
Ok(task) => {
|
||||||
task.log_err().await;
|
task.log_err().await;
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
_maintain_current_user: cx.spawn(async move |this, cx| {
|
_maintain_current_user: cx.spawn(async move |this, cx| {
|
||||||
let mut status = client.status();
|
let mut status = client.status();
|
||||||
@@ -191,12 +193,13 @@ impl UserStore {
|
|||||||
match status {
|
match status {
|
||||||
Status::Connected { .. } => {
|
Status::Connected { .. } => {
|
||||||
if let Some(user_id) = client.user_id() {
|
if let Some(user_id) = client.user_id() {
|
||||||
let fetch_user = if let Ok(fetch_user) =
|
let fetch_user = match this
|
||||||
this.update(cx, |this, cx| this.get_user(user_id, cx).log_err())
|
.update(cx, |this, cx| this.get_user(user_id, cx).log_err())
|
||||||
{
|
{
|
||||||
fetch_user
|
Ok(fetch_user) => fetch_user,
|
||||||
} else {
|
_ => {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let fetch_private_user_info =
|
let fetch_private_user_info =
|
||||||
client.request(proto::GetPrivateUserInfo {}).log_err();
|
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();
|
let (tx, mut rx) = postage::barrier::channel();
|
||||||
self.update_contacts_tx
|
self.update_contacts_tx
|
||||||
.unbounded_send(UpdateContacts::Clear(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();
|
let (tx, mut rx) = postage::barrier::channel();
|
||||||
self.update_contacts_tx
|
self.update_contacts_tx
|
||||||
.unbounded_send(UpdateContacts::Wait(tx))
|
.unbounded_send(UpdateContacts::Wait(tx))
|
||||||
@@ -703,8 +706,8 @@ impl UserStore {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let client = self.client.clone();
|
let client = self.client.clone();
|
||||||
cx.spawn(async move |this, cx| {
|
cx.spawn(async move |this, cx| match client.upgrade() {
|
||||||
if let Some(client) = client.upgrade() {
|
Some(client) => {
|
||||||
let response = client
|
let response = client
|
||||||
.request(proto::AcceptTermsOfService {})
|
.request(proto::AcceptTermsOfService {})
|
||||||
.await
|
.await
|
||||||
@@ -714,9 +717,8 @@ impl UserStore {
|
|||||||
this.set_current_user_accepted_tos_at(Some(response.accepted_tos_at));
|
this.set_current_user_accepted_tos_at(Some(response.accepted_tos_at));
|
||||||
cx.emit(Event::PrivateUserInfoUpdated);
|
cx.emit(Event::PrivateUserInfoUpdated);
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
Err(anyhow!("client not found"))
|
|
||||||
}
|
}
|
||||||
|
_ => Err(anyhow!("client not found")),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -732,15 +734,14 @@ impl UserStore {
|
|||||||
cx: &Context<Self>,
|
cx: &Context<Self>,
|
||||||
) -> Task<Result<Vec<Arc<User>>>> {
|
) -> Task<Result<Vec<Arc<User>>>> {
|
||||||
let client = self.client.clone();
|
let client = self.client.clone();
|
||||||
cx.spawn(async move |this, cx| {
|
cx.spawn(async move |this, cx| match client.upgrade() {
|
||||||
if let Some(rpc) = client.upgrade() {
|
Some(rpc) => {
|
||||||
let response = rpc.request(request).await.context("error loading users")?;
|
let response = rpc.request(request).await.context("error loading users")?;
|
||||||
let users = response.users;
|
let users = response.users;
|
||||||
|
|
||||||
this.update(cx, |this, _| this.insert(users))
|
this.update(cx, |this, _| this.insert(users))
|
||||||
} else {
|
|
||||||
Ok(Vec::new())
|
|
||||||
}
|
}
|
||||||
|
_ => Ok(Vec::new()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -219,14 +219,17 @@ async fn create_access_token(
|
|||||||
let mut impersonated_user_id = None;
|
let mut impersonated_user_id = None;
|
||||||
if let Some(impersonate) = params.impersonate {
|
if let Some(impersonate) = params.impersonate {
|
||||||
if user.admin {
|
if user.admin {
|
||||||
if let Some(impersonated_user) = app.db.get_user_by_github_login(&impersonate).await? {
|
match app.db.get_user_by_github_login(&impersonate).await? {
|
||||||
|
Some(impersonated_user) => {
|
||||||
impersonated_user_id = Some(impersonated_user.id);
|
impersonated_user_id = Some(impersonated_user.id);
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
return Err(Error::http(
|
return Err(Error::http(
|
||||||
StatusCode::UNPROCESSABLE_ENTITY,
|
StatusCode::UNPROCESSABLE_ENTITY,
|
||||||
format!("user {impersonate} does not exist"),
|
format!("user {impersonate} does not exist"),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::http(
|
return Err(Error::http(
|
||||||
StatusCode::UNAUTHORIZED,
|
StatusCode::UNAUTHORIZED,
|
||||||
|
|||||||
@@ -103,8 +103,8 @@ async fn update_billing_preferences(
|
|||||||
let max_monthly_llm_usage_spending_in_cents =
|
let max_monthly_llm_usage_spending_in_cents =
|
||||||
body.max_monthly_llm_usage_spending_in_cents.max(0);
|
body.max_monthly_llm_usage_spending_in_cents.max(0);
|
||||||
|
|
||||||
let billing_preferences =
|
let billing_preferences = match app.db.get_billing_preferences(user.id).await? {
|
||||||
if let Some(_billing_preferences) = app.db.get_billing_preferences(user.id).await? {
|
Some(_billing_preferences) => {
|
||||||
app.db
|
app.db
|
||||||
.update_billing_preferences(
|
.update_billing_preferences(
|
||||||
user.id,
|
user.id,
|
||||||
@@ -115,7 +115,8 @@ async fn update_billing_preferences(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
app.db
|
app.db
|
||||||
.create_billing_preferences(
|
.create_billing_preferences(
|
||||||
user.id,
|
user.id,
|
||||||
@@ -124,6 +125,7 @@ async fn update_billing_preferences(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
SnowflakeRow::new(
|
SnowflakeRow::new(
|
||||||
@@ -624,11 +626,12 @@ async fn handle_customer_event(
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(existing_customer) = app
|
match app
|
||||||
.db
|
.db
|
||||||
.get_billing_customer_by_stripe_customer_id(&customer.id)
|
.get_billing_customer_by_stripe_customer_id(&customer.id)
|
||||||
.await?
|
.await?
|
||||||
{
|
{
|
||||||
|
Some(existing_customer) => {
|
||||||
app.db
|
app.db
|
||||||
.update_billing_customer(
|
.update_billing_customer(
|
||||||
existing_customer.id,
|
existing_customer.id,
|
||||||
@@ -639,7 +642,8 @@ async fn handle_customer_event(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
app.db
|
app.db
|
||||||
.create_billing_customer(&CreateBillingCustomerParams {
|
.create_billing_customer(&CreateBillingCustomerParams {
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
@@ -647,6 +651,7 @@ async fn handle_customer_event(
|
|||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -689,11 +694,12 @@ async fn handle_customer_subscription_event(
|
|||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(existing_subscription) = app
|
match app
|
||||||
.db
|
.db
|
||||||
.get_billing_subscription_by_stripe_subscription_id(&subscription.id)
|
.get_billing_subscription_by_stripe_subscription_id(&subscription.id)
|
||||||
.await?
|
.await?
|
||||||
{
|
{
|
||||||
|
Some(existing_subscription) => {
|
||||||
app.db
|
app.db
|
||||||
.update_billing_subscription(
|
.update_billing_subscription(
|
||||||
existing_subscription.id,
|
existing_subscription.id,
|
||||||
@@ -716,7 +722,8 @@ async fn handle_customer_subscription_event(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
// If the user already has an active billing subscription, ignore the
|
// If the user already has an active billing subscription, ignore the
|
||||||
// event and return an `Ok` to signal that it was processed
|
// event and return an `Ok` to signal that it was processed
|
||||||
// successfully.
|
// successfully.
|
||||||
@@ -756,6 +763,7 @@ async fn handle_customer_subscription_event(
|
|||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// When the user's subscription changes, we want to refresh their LLM tokens
|
// When the user's subscription changes, we want to refresh their LLM tokens
|
||||||
// to either grant/revoke access.
|
// to either grant/revoke access.
|
||||||
|
|||||||
@@ -47,10 +47,15 @@ impl IpsFile {
|
|||||||
Some(panic_message) => format!("Panic `{}`", panic_message),
|
Some(panic_message) => format!("Panic `{}`", panic_message),
|
||||||
None => "Crash `Abort trap: 6` (possible panic)".into(),
|
None => "Crash `Abort trap: 6` (possible panic)".into(),
|
||||||
}
|
}
|
||||||
} else if let Some(msg) = &self.body.exception.message {
|
|
||||||
format!("Exception `{}`", msg)
|
|
||||||
} else {
|
} else {
|
||||||
|
match &self.body.exception.message {
|
||||||
|
Some(msg) => {
|
||||||
|
format!("Exception `{}`", msg)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
format!("Crash `{}`", self.body.termination.indicator)
|
format!("Crash `{}`", self.body.termination.indicator)
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
if let Some(thread) = self.faulting_thread() {
|
if let Some(thread) = self.faulting_thread() {
|
||||||
if let Some(queue) = thread.queue.as_ref() {
|
if let Some(queue) = thread.queue.as_ref() {
|
||||||
@@ -81,10 +86,13 @@ impl IpsFile {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
Some(format!("{:#}", rustc_demangle::demangle(name)))
|
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 {
|
} 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<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|||||||
@@ -105,10 +105,11 @@ impl Database {
|
|||||||
let mut accept_invite_result = None;
|
let mut accept_invite_result = None;
|
||||||
|
|
||||||
if role.is_none() {
|
if role.is_none() {
|
||||||
if let Some(invitation) = self
|
match self
|
||||||
.pending_invite_for_channel(&channel, user_id, &tx)
|
.pending_invite_for_channel(&channel, user_id, &tx)
|
||||||
.await?
|
.await?
|
||||||
{
|
{
|
||||||
|
Some(invitation) => {
|
||||||
// note, this may be a parent channel
|
// note, this may be a parent channel
|
||||||
role = Some(invitation.role);
|
role = Some(invitation.role);
|
||||||
channel_member::Entity::update(channel_member::ActiveModel {
|
channel_member::Entity::update(channel_member::ActiveModel {
|
||||||
@@ -126,7 +127,9 @@ impl Database {
|
|||||||
debug_assert!(
|
debug_assert!(
|
||||||
self.channel_role_for_user(&channel, user_id, &tx).await? == role
|
self.channel_role_for_user(&channel, user_id, &tx).await? == role
|
||||||
);
|
);
|
||||||
} else if channel.visibility == ChannelVisibility::Public {
|
}
|
||||||
|
_ => {
|
||||||
|
if channel.visibility == ChannelVisibility::Public {
|
||||||
role = Some(ChannelRole::Guest);
|
role = Some(ChannelRole::Guest);
|
||||||
channel_member::Entity::insert(channel_member::ActiveModel {
|
channel_member::Entity::insert(channel_member::ActiveModel {
|
||||||
id: ActiveValue::NotSet,
|
id: ActiveValue::NotSet,
|
||||||
@@ -148,6 +151,8 @@ impl Database {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if role.is_none() || role == Some(ChannelRole::Banned) {
|
if role.is_none() || role == Some(ChannelRole::Banned) {
|
||||||
Err(ErrorCode::Forbidden.anyhow())?
|
Err(ErrorCode::Forbidden.anyhow())?
|
||||||
|
|||||||
@@ -101,15 +101,18 @@ impl Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(wasm_api_version) = version.wasm_api_version.as_ref() {
|
if let Some(wasm_api_version) = version.wasm_api_version.as_ref() {
|
||||||
if let Some(version) = SemanticVersion::from_str(wasm_api_version).log_err() {
|
match SemanticVersion::from_str(wasm_api_version).log_err() {
|
||||||
|
Some(version) => {
|
||||||
if !constraints.wasm_api_versions.contains(&version) {
|
if !constraints.wasm_api_versions.contains(&version) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
max_versions.insert(version.extension_id, (version, extension_version));
|
max_versions.insert(version.extension_id, (version, extension_version));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -192,10 +192,11 @@ impl Database {
|
|||||||
response: Option<bool>,
|
response: Option<bool>,
|
||||||
tx: &DatabaseTransaction,
|
tx: &DatabaseTransaction,
|
||||||
) -> Result<Option<(UserId, proto::Notification)>> {
|
) -> Result<Option<(UserId, proto::Notification)>> {
|
||||||
if let Some(id) = self
|
match self
|
||||||
.find_notification(recipient_id, notification, tx)
|
.find_notification(recipient_id, notification, tx)
|
||||||
.await?
|
.await?
|
||||||
{
|
{
|
||||||
|
Some(id) => {
|
||||||
let row = notification::Entity::update(notification::ActiveModel {
|
let row = notification::Entity::update(notification::ActiveModel {
|
||||||
id: ActiveValue::Unchanged(id),
|
id: ActiveValue::Unchanged(id),
|
||||||
recipient_id: ActiveValue::Unchanged(recipient_id),
|
recipient_id: ActiveValue::Unchanged(recipient_id),
|
||||||
@@ -212,8 +213,8 @@ impl Database {
|
|||||||
Ok(model_to_proto(self, row)
|
Ok(model_to_proto(self, row)
|
||||||
.map(|notification| (recipient_id, notification))
|
.map(|notification| (recipient_id, notification))
|
||||||
.ok())
|
.ok())
|
||||||
} else {
|
}
|
||||||
Ok(None)
|
_ => Ok(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -132,13 +132,15 @@ impl Database {
|
|||||||
initial_channel_id: Option<ChannelId>,
|
initial_channel_id: Option<ChannelId>,
|
||||||
tx: &DatabaseTransaction,
|
tx: &DatabaseTransaction,
|
||||||
) -> Result<User> {
|
) -> 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)
|
.get_user_by_github_user_id_or_github_login(github_user_id, github_login, tx)
|
||||||
.await?
|
.await?
|
||||||
{
|
{
|
||||||
|
Some(existing_user) => {
|
||||||
let mut existing_user = existing_user.into_active_model();
|
let mut existing_user = existing_user.into_active_model();
|
||||||
existing_user.github_login = ActiveValue::set(github_login.into());
|
existing_user.github_login = ActiveValue::set(github_login.into());
|
||||||
existing_user.github_user_created_at = ActiveValue::set(Some(github_user_created_at));
|
existing_user.github_user_created_at =
|
||||||
|
ActiveValue::set(Some(github_user_created_at));
|
||||||
|
|
||||||
if let Some(github_email) = github_email {
|
if let Some(github_email) = github_email {
|
||||||
existing_user.email_address = ActiveValue::set(Some(github_email.into()));
|
existing_user.email_address = ActiveValue::set(Some(github_email.into()));
|
||||||
@@ -149,7 +151,8 @@ impl Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(existing_user.update(tx).await?)
|
Ok(existing_user.update(tx).await?)
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
let user = user::Entity::insert(user::ActiveModel {
|
let user = user::Entity::insert(user::ActiveModel {
|
||||||
email_address: ActiveValue::set(github_email.map(|email| email.into())),
|
email_address: ActiveValue::set(github_email.map(|email| email.into())),
|
||||||
name: ActiveValue::set(github_name.map(|name| name.into())),
|
name: ActiveValue::set(github_name.map(|name| name.into())),
|
||||||
@@ -178,6 +181,7 @@ impl Database {
|
|||||||
Ok(user)
|
Ok(user)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Tries to retrieve a user, first by their GitHub user ID, and then by their GitHub login.
|
/// Tries to retrieve a user, first by their GitHub user ID, and then by their GitHub login.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ impl TestDb {
|
|||||||
let mut rng = StdRng::from_entropy();
|
let mut rng = StdRng::from_entropy();
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"postgres://postgres@localhost/zed-test-{}",
|
"postgres://postgres@localhost/zed-test-{}",
|
||||||
rng.gen::<u128>()
|
rng.r#gen::<u128>()
|
||||||
);
|
);
|
||||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||||
.enable_io()
|
.enable_io()
|
||||||
|
|||||||
@@ -101,11 +101,12 @@ async fn test_channel_buffers(db: &Arc<Database>) {
|
|||||||
);
|
);
|
||||||
buffer_b.apply_ops(buffer_response_b.operations.into_iter().map(|operation| {
|
buffer_b.apply_ops(buffer_response_b.operations.into_iter().map(|operation| {
|
||||||
let operation = proto::deserialize_operation(operation).unwrap();
|
let operation = proto::deserialize_operation(operation).unwrap();
|
||||||
if let language::Operation::Buffer(operation) = operation {
|
match operation {
|
||||||
operation
|
language::Operation::Buffer(operation) => operation,
|
||||||
} else {
|
_ => {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
assert_eq!(buffer_b.text(), "hello, cruel world");
|
assert_eq!(buffer_b.text(), "hello, cruel world");
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ pub fn get_dotenv_vars(current_dir: impl AsRef<Path>) -> Result<Vec<(String, Str
|
|||||||
|
|
||||||
pub fn load_dotenv() -> Result<()> {
|
pub fn load_dotenv() -> Result<()> {
|
||||||
for (key, value) in get_dotenv_vars("./crates/collab")? {
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
let this = self.clone();
|
||||||
async move {
|
async move {
|
||||||
match this {
|
match this {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ impl TestLlmDb {
|
|||||||
let mut rng = StdRng::from_entropy();
|
let mut rng = StdRng::from_entropy();
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"postgres://postgres@localhost/zed-llm-test-{}",
|
"postgres://postgres@localhost/zed-llm-test-{}",
|
||||||
rng.gen::<u128>()
|
rng.r#gen::<u128>()
|
||||||
);
|
);
|
||||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||||
.enable_io()
|
.enable_io()
|
||||||
|
|||||||
@@ -716,7 +716,7 @@ impl Server {
|
|||||||
system_id: Option<String>,
|
system_id: Option<String>,
|
||||||
send_connection_id: Option<oneshot::Sender<ConnectionId>>,
|
send_connection_id: Option<oneshot::Sender<ConnectionId>>,
|
||||||
executor: Executor,
|
executor: Executor,
|
||||||
) -> impl Future<Output = ()> {
|
) -> impl Future<Output = ()> + use<> {
|
||||||
let this = self.clone();
|
let this = self.clone();
|
||||||
let span = info_span!("handle connection", %address,
|
let span = info_span!("handle connection", %address,
|
||||||
connection_id=field::Empty,
|
connection_id=field::Empty,
|
||||||
@@ -810,7 +810,7 @@ impl Server {
|
|||||||
_ = foreground_message_handlers.next() => {}
|
_ = foreground_message_handlers.next() => {}
|
||||||
next_message = next_message => {
|
next_message = next_message => {
|
||||||
let (permit, message) = next_message;
|
let (permit, message) = next_message;
|
||||||
if let Some(message) = message {
|
match message { Some(message) => {
|
||||||
let type_name = message.payload_type_name();
|
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.
|
// 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).
|
// (https://github.com/tokio-rs/tracing/issues/2670).
|
||||||
@@ -821,7 +821,7 @@ impl Server {
|
|||||||
);
|
);
|
||||||
principal.update_span(&span);
|
principal.update_span(&span);
|
||||||
let span_enter = span.enter();
|
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 is_background = message.is_background();
|
||||||
let handle_message = (handler)(message, session.clone());
|
let handle_message = (handler)(message, session.clone());
|
||||||
drop(span_enter);
|
drop(span_enter);
|
||||||
@@ -835,13 +835,13 @@ impl Server {
|
|||||||
} else {
|
} else {
|
||||||
foreground_message_handlers.push(handle_message);
|
foreground_message_handlers.push(handle_message);
|
||||||
}
|
}
|
||||||
} else {
|
} _ => {
|
||||||
tracing::error!("no message handler");
|
tracing::error!("no message handler");
|
||||||
}
|
}}
|
||||||
} else {
|
} _ => {
|
||||||
tracing::info!("connection closed");
|
tracing::info!("connection closed");
|
||||||
break;
|
break;
|
||||||
}
|
}}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1313,9 +1313,8 @@ async fn join_room(
|
|||||||
.trace_err();
|
.trace_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
let live_kit_connection_info = if let Some(live_kit) = session.app_state.livekit_client.as_ref()
|
let live_kit_connection_info = match session.app_state.livekit_client.as_ref() {
|
||||||
{
|
Some(live_kit) => live_kit
|
||||||
live_kit
|
|
||||||
.room_token(
|
.room_token(
|
||||||
&joined_room.room.livekit_room,
|
&joined_room.room.livekit_room,
|
||||||
&session.user_id().to_string(),
|
&session.user_id().to_string(),
|
||||||
@@ -1325,9 +1324,8 @@ async fn join_room(
|
|||||||
server_url: live_kit.url().into(),
|
server_url: live_kit.url().into(),
|
||||||
token,
|
token,
|
||||||
can_publish: true,
|
can_publish: true,
|
||||||
})
|
}),
|
||||||
} else {
|
_ => None,
|
||||||
None
|
|
||||||
};
|
};
|
||||||
|
|
||||||
response.send(proto::JoinRoomResponse {
|
response.send(proto::JoinRoomResponse {
|
||||||
@@ -4393,7 +4391,8 @@ async fn leave_room_for_session(session: &Session, connection_id: ConnectionId)
|
|||||||
let room;
|
let room;
|
||||||
let channel;
|
let channel;
|
||||||
|
|
||||||
if let Some(mut left_room) = session.db().await.leave_room(connection_id).await? {
|
match session.db().await.leave_room(connection_id).await? {
|
||||||
|
Some(mut left_room) => {
|
||||||
contacts_to_update.insert(session.user_id());
|
contacts_to_update.insert(session.user_id());
|
||||||
|
|
||||||
for project in left_room.left_projects.values() {
|
for project in left_room.left_projects.values() {
|
||||||
@@ -4408,9 +4407,11 @@ async fn leave_room_for_session(session: &Session, connection_id: ConnectionId)
|
|||||||
channel = mem::take(&mut left_room.channel);
|
channel = mem::take(&mut left_room.channel);
|
||||||
|
|
||||||
room_updated(&room, &session.peer);
|
room_updated(&room, &session.peer);
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(channel) = channel {
|
if let Some(channel) = channel {
|
||||||
channel_updated(
|
channel_updated(
|
||||||
|
|||||||
@@ -130,9 +130,9 @@ impl StripeBilling {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut state = self.state.write().await;
|
let mut state = self.state.write().await;
|
||||||
let meter = if let Some(meter) = state.meters_by_event_name.get(meter_event_name) {
|
let meter = match state.meters_by_event_name.get(meter_event_name) {
|
||||||
meter.clone()
|
Some(meter) => meter.clone(),
|
||||||
} else {
|
_ => {
|
||||||
let meter = StripeMeter::create(
|
let meter = StripeMeter::create(
|
||||||
&self.client,
|
&self.client,
|
||||||
StripeCreateMeterParams {
|
StripeCreateMeterParams {
|
||||||
@@ -146,11 +146,12 @@ impl StripeBilling {
|
|||||||
.meters_by_event_name
|
.meters_by_event_name
|
||||||
.insert(meter_event_name.to_string(), meter.clone());
|
.insert(meter_event_name.to_string(), meter.clone());
|
||||||
meter
|
meter
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let price_id = if let Some(price_id) = state.price_ids_by_meter_id.get(&meter.id) {
|
let price_id = match state.price_ids_by_meter_id.get(&meter.id) {
|
||||||
price_id.clone()
|
Some(price_id) => price_id.clone(),
|
||||||
} else {
|
_ => {
|
||||||
let price = stripe::Price::create(
|
let price = stripe::Price::create(
|
||||||
&self.client,
|
&self.client,
|
||||||
stripe::CreatePrice {
|
stripe::CreatePrice {
|
||||||
@@ -198,6 +199,7 @@ impl StripeBilling {
|
|||||||
.price_ids_by_meter_id
|
.price_ids_by_meter_id
|
||||||
.insert(meter.id, price.id.clone());
|
.insert(meter.id, price.id.clone());
|
||||||
price.id
|
price.id
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(StripeBillingPrice {
|
Ok(StripeBillingPrice {
|
||||||
|
|||||||
@@ -5602,7 +5602,7 @@ async fn test_open_buffer_while_getting_definition_pointing_to_it(
|
|||||||
|
|
||||||
let definitions;
|
let definitions;
|
||||||
let buffer_b2;
|
let buffer_b2;
|
||||||
if rng.gen() {
|
if rng.r#gen() {
|
||||||
definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
|
definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
|
||||||
(buffer_b2, _) = project_b
|
(buffer_b2, _) = project_b
|
||||||
.update(cx_b, |p, cx| {
|
.update(cx_b, |p, cx| {
|
||||||
|
|||||||
@@ -216,7 +216,8 @@ impl RandomizedTest for ProjectCollaborationTest {
|
|||||||
// Open a new project
|
// Open a new project
|
||||||
0..=70 => {
|
0..=70 => {
|
||||||
// Open a remote project
|
// Open a remote project
|
||||||
if let Some(room) = call.read_with(cx, |call, _| call.room().cloned()) {
|
match call.read_with(cx, |call, _| call.room().cloned()) {
|
||||||
|
Some(room) => {
|
||||||
let existing_dev_server_project_ids = cx.read(|cx| {
|
let existing_dev_server_project_ids = cx.read(|cx| {
|
||||||
client
|
client
|
||||||
.dev_server_projects()
|
.dev_server_projects()
|
||||||
@@ -229,7 +230,8 @@ impl RandomizedTest for ProjectCollaborationTest {
|
|||||||
.values()
|
.values()
|
||||||
.flat_map(|participant| {
|
.flat_map(|participant| {
|
||||||
participant.projects.iter().filter_map(|project| {
|
participant.projects.iter().filter_map(|project| {
|
||||||
if existing_dev_server_project_ids.contains(&project.id)
|
if existing_dev_server_project_ids
|
||||||
|
.contains(&project.id)
|
||||||
{
|
{
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
@@ -251,12 +253,12 @@ impl RandomizedTest for ProjectCollaborationTest {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Open a local project
|
_ => {
|
||||||
else {
|
|
||||||
let first_root_name = plan.next_root_dir_name();
|
let first_root_name = plan.next_root_dir_name();
|
||||||
break ClientOperation::OpenLocalProject { first_root_name };
|
break ClientOperation::OpenLocalProject { first_root_name };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Close a remote project
|
// Close a remote project
|
||||||
71..=80 => {
|
71..=80 => {
|
||||||
@@ -279,7 +281,7 @@ impl RandomizedTest for ProjectCollaborationTest {
|
|||||||
let project_root_name = root_name_for_project(&project, cx);
|
let project_root_name = root_name_for_project(&project, cx);
|
||||||
let mut paths = client.fs().paths(false);
|
let mut paths = client.fs().paths(false);
|
||||||
paths.remove(0);
|
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())
|
Path::new(path!("/")).join(plan.next_root_dir_name())
|
||||||
} else {
|
} else {
|
||||||
paths.choose(rng).unwrap().clone()
|
paths.choose(rng).unwrap().clone()
|
||||||
@@ -309,7 +311,7 @@ impl RandomizedTest for ProjectCollaborationTest {
|
|||||||
.choose(rng)
|
.choose(rng)
|
||||||
});
|
});
|
||||||
let Some(worktree) = worktree else { continue };
|
let Some(worktree) = worktree else { continue };
|
||||||
let is_dir = rng.gen::<bool>();
|
let is_dir = rng.r#gen::<bool>();
|
||||||
let mut full_path =
|
let mut full_path =
|
||||||
worktree.read_with(cx, |w, _| PathBuf::from(w.root_name()));
|
worktree.read_with(cx, |w, _| PathBuf::from(w.root_name()));
|
||||||
full_path.push(gen_file_name(rng));
|
full_path.push(gen_file_name(rng));
|
||||||
@@ -387,7 +389,7 @@ impl RandomizedTest for ProjectCollaborationTest {
|
|||||||
language::Bias::Left,
|
language::Bias::Left,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
let detach = rng.gen();
|
let detach = rng.r#gen();
|
||||||
break ClientOperation::RequestLspDataInBuffer {
|
break ClientOperation::RequestLspDataInBuffer {
|
||||||
project_root_name,
|
project_root_name,
|
||||||
full_path,
|
full_path,
|
||||||
@@ -460,7 +462,7 @@ impl RandomizedTest for ProjectCollaborationTest {
|
|||||||
|
|
||||||
// Create or update a file or directory
|
// Create or update a file or directory
|
||||||
96.. => {
|
96.. => {
|
||||||
let is_dir = rng.gen::<bool>();
|
let is_dir = rng.r#gen::<bool>();
|
||||||
let content;
|
let content;
|
||||||
let mut path;
|
let mut path;
|
||||||
let dir_paths = client.fs().directories(false);
|
let dir_paths = client.fs().directories(false);
|
||||||
@@ -1270,11 +1272,13 @@ impl RandomizedTest for ProjectCollaborationTest {
|
|||||||
Some((client.user_id().unwrap(), project, cx))
|
Some((client.user_id().unwrap(), project, cx))
|
||||||
});
|
});
|
||||||
|
|
||||||
let (host_user_id, host_project, host_cx) =
|
let (host_user_id, host_project, host_cx) = match host_project {
|
||||||
if let Some((host_user_id, host_project, host_cx)) = host_project {
|
Some((host_user_id, host_project, host_cx)) => {
|
||||||
(host_user_id, host_project, host_cx)
|
(host_user_id, host_project, host_cx)
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for guest_buffer in guest_buffers {
|
for guest_buffer in guest_buffers {
|
||||||
|
|||||||
@@ -175,10 +175,9 @@ impl TestServer {
|
|||||||
|
|
||||||
let clock = Arc::new(FakeSystemClock::new());
|
let clock = Arc::new(FakeSystemClock::new());
|
||||||
let http = FakeHttpClient::with_404_response();
|
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
|
let user_id = match self.app_state.db.get_user_by_github_login(name).await {
|
||||||
{
|
Ok(Some(user)) => user.id,
|
||||||
user.id
|
_ => {
|
||||||
} else {
|
|
||||||
let github_user_id = self.next_github_user_id;
|
let github_user_id = self.next_github_user_id;
|
||||||
self.next_github_user_id += 1;
|
self.next_github_user_id += 1;
|
||||||
self.app_state
|
self.app_state
|
||||||
@@ -195,6 +194,7 @@ impl TestServer {
|
|||||||
.await
|
.await
|
||||||
.expect("creating user failed")
|
.expect("creating user failed")
|
||||||
.user_id
|
.user_id
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let client_name = name.to_string();
|
let client_name = name.to_string();
|
||||||
let mut client = cx.update(|cx| Client::new(clock, http.clone(), cx));
|
let mut client = cx.update(|cx| Client::new(clock, http.clone(), cx));
|
||||||
@@ -660,7 +660,7 @@ impl TestClient {
|
|||||||
pub fn buffers_for_project<'a>(
|
pub fn buffers_for_project<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
project: &Entity<Project>,
|
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| {
|
RefMut::map(self.state.borrow_mut(), |state| {
|
||||||
state.buffers.entry(project.clone()).or_default()
|
state.buffers.entry(project.clone()).or_default()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -556,12 +556,9 @@ impl FollowableItem for ChannelView {
|
|||||||
Some(proto::view::Variant::ChannelView(
|
Some(proto::view::Variant::ChannelView(
|
||||||
proto::view::ChannelView {
|
proto::view::ChannelView {
|
||||||
channel_id: channel_buffer.channel_id.0,
|
channel_id: channel_buffer.channel_id.0,
|
||||||
editor: if let Some(proto::view::Variant::Editor(proto)) =
|
editor: match self.editor.read(cx).to_state_proto(window, cx) {
|
||||||
self.editor.read(cx).to_state_proto(window, cx)
|
Some(proto::view::Variant::Editor(proto)) => Some(proto),
|
||||||
{
|
_ => None,
|
||||||
Some(proto)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -102,14 +102,11 @@ impl ChatPanel {
|
|||||||
0,
|
0,
|
||||||
gpui::ListAlignment::Bottom,
|
gpui::ListAlignment::Bottom,
|
||||||
px(1000.),
|
px(1000.),
|
||||||
move |ix, window, cx| {
|
move |ix, window, cx| match entity.upgrade() {
|
||||||
if let Some(entity) = entity.upgrade() {
|
Some(entity) => entity.update(cx, |this: &mut Self, cx| {
|
||||||
entity.update(cx, |this: &mut Self, cx| {
|
|
||||||
this.render_message(ix, window, cx).into_any_element()
|
this.render_message(ix, window, cx).into_any_element()
|
||||||
})
|
}),
|
||||||
} else {
|
_ => div().into_any(),
|
||||||
div().into_any()
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -200,15 +197,14 @@ impl ChatPanel {
|
|||||||
cx: AsyncWindowContext,
|
cx: AsyncWindowContext,
|
||||||
) -> Task<Result<Entity<Self>>> {
|
) -> Task<Result<Entity<Self>>> {
|
||||||
cx.spawn(async move |cx| {
|
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) })
|
.background_spawn(async move { KEY_VALUE_STORE.read_kvp(CHAT_PANEL_KEY) })
|
||||||
.await
|
.await
|
||||||
.log_err()
|
.log_err()
|
||||||
.flatten()
|
.flatten()
|
||||||
{
|
{
|
||||||
Some(serde_json::from_str::<SerializedChatPanel>(&panel)?)
|
Some(panel) => Some(serde_json::from_str::<SerializedChatPanel>(&panel)?),
|
||||||
} else {
|
_ => None,
|
||||||
None
|
|
||||||
};
|
};
|
||||||
|
|
||||||
workspace.update_in(cx, |workspace, window, cx| {
|
workspace.update_in(cx, |workspace, window, cx| {
|
||||||
@@ -314,7 +310,7 @@ impl ChatPanel {
|
|||||||
message_id: Option<ChannelMessageId>,
|
message_id: Option<ChannelMessageId>,
|
||||||
reply_to_message: &Option<ChannelMessage>,
|
reply_to_message: &Option<ChannelMessage>,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement + use<> {
|
||||||
let reply_to_message = match reply_to_message {
|
let reply_to_message = match reply_to_message {
|
||||||
None => {
|
None => {
|
||||||
return div().child(
|
return div().child(
|
||||||
@@ -393,7 +389,7 @@ impl ChatPanel {
|
|||||||
ix: usize,
|
ix: usize,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement + use<> {
|
||||||
let active_chat = &self.active_chat.as_ref().unwrap().0;
|
let active_chat = &self.active_chat.as_ref().unwrap().0;
|
||||||
let (message, is_continuation_from_previous, is_admin) =
|
let (message, is_continuation_from_previous, is_admin) =
|
||||||
active_chat.update(cx, |active_chat, cx| {
|
active_chat.update(cx, |active_chat, cx| {
|
||||||
@@ -812,7 +808,8 @@ impl ChatPanel {
|
|||||||
.message_editor
|
.message_editor
|
||||||
.update(cx, |editor, cx| editor.take_message(window, cx));
|
.update(cx, |editor, cx| editor.take_message(window, cx));
|
||||||
|
|
||||||
if let Some(id) = self.message_editor.read(cx).edit_message_id() {
|
match self.message_editor.read(cx).edit_message_id() {
|
||||||
|
Some(id) => {
|
||||||
self.message_editor.update(cx, |editor, _| {
|
self.message_editor.update(cx, |editor, _| {
|
||||||
editor.clear_edit_message_id();
|
editor.clear_edit_message_id();
|
||||||
});
|
});
|
||||||
@@ -823,12 +820,19 @@ impl ChatPanel {
|
|||||||
{
|
{
|
||||||
task.detach();
|
task.detach();
|
||||||
}
|
}
|
||||||
} else if let Some(task) = chat
|
}
|
||||||
|
_ => {
|
||||||
|
match chat
|
||||||
.update(cx, |chat, cx| chat.send_message(message, cx))
|
.update(cx, |chat, cx| chat.send_message(message, cx))
|
||||||
.log_err()
|
.log_err()
|
||||||
{
|
{
|
||||||
|
Some(task) => {
|
||||||
task.detach();
|
task.detach();
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -244,12 +244,11 @@ impl CollabPanel {
|
|||||||
0,
|
0,
|
||||||
gpui::ListAlignment::Top,
|
gpui::ListAlignment::Top,
|
||||||
px(1000.),
|
px(1000.),
|
||||||
move |ix, window, cx| {
|
move |ix, window, cx| match entity.upgrade() {
|
||||||
if let Some(entity) = entity.upgrade() {
|
Some(entity) => {
|
||||||
entity.update(cx, |this, cx| this.render_list_entry(ix, window, cx))
|
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,
|
is_selected: bool,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement + use<> {
|
||||||
let project_name: SharedString = if worktree_root_names.is_empty() {
|
let project_name: SharedString = if worktree_root_names.is_empty() {
|
||||||
"untitled".to_string()
|
"untitled".to_string()
|
||||||
} else {
|
} else {
|
||||||
@@ -919,7 +918,7 @@ impl CollabPanel {
|
|||||||
is_selected: bool,
|
is_selected: bool,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement + use<> {
|
||||||
let id = peer_id.map_or(usize::MAX, |id| id.as_u64() as usize);
|
let id = peer_id.map_or(usize::MAX, |id| id.as_u64() as usize);
|
||||||
|
|
||||||
ListItem::new(("screen", id))
|
ListItem::new(("screen", id))
|
||||||
@@ -960,7 +959,7 @@ impl CollabPanel {
|
|||||||
is_selected: bool,
|
is_selected: bool,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement + use<> {
|
||||||
let channel_store = self.channel_store.read(cx);
|
let channel_store = self.channel_store.read(cx);
|
||||||
let has_channel_buffer_changed = channel_store.has_channel_buffer_changed(channel_id);
|
let has_channel_buffer_changed = channel_store.has_channel_buffer_changed(channel_id);
|
||||||
ListItem::new("channel-notes")
|
ListItem::new("channel-notes")
|
||||||
@@ -993,7 +992,7 @@ impl CollabPanel {
|
|||||||
is_selected: bool,
|
is_selected: bool,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement + use<> {
|
||||||
let channel_store = self.channel_store.read(cx);
|
let channel_store = self.channel_store.read(cx);
|
||||||
let has_messages_notification = channel_store.has_new_messages(channel_id);
|
let has_messages_notification = channel_store.has_new_messages(channel_id);
|
||||||
ListItem::new("channel-chat")
|
ListItem::new("channel-chat")
|
||||||
@@ -2278,7 +2277,7 @@ impl CollabPanel {
|
|||||||
&self,
|
&self,
|
||||||
editor: &Entity<Editor>,
|
editor: &Entity<Editor>,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement + use<> {
|
||||||
let settings = ThemeSettings::get_global(cx);
|
let settings = ThemeSettings::get_global(cx);
|
||||||
let text_style = TextStyle {
|
let text_style = TextStyle {
|
||||||
color: if editor.read(cx).read_only(cx) {
|
color: if editor.read(cx).read_only(cx) {
|
||||||
@@ -2312,7 +2311,7 @@ impl CollabPanel {
|
|||||||
is_selected: bool,
|
is_selected: bool,
|
||||||
is_collapsed: bool,
|
is_collapsed: bool,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement + use<> {
|
||||||
let mut channel_link = None;
|
let mut channel_link = None;
|
||||||
let mut channel_tooltip_text = None;
|
let mut channel_tooltip_text = None;
|
||||||
let mut channel_icon = None;
|
let mut channel_icon = None;
|
||||||
@@ -2411,7 +2410,7 @@ impl CollabPanel {
|
|||||||
calling: bool,
|
calling: bool,
|
||||||
is_selected: bool,
|
is_selected: bool,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement + use<> {
|
||||||
let online = contact.online;
|
let online = contact.online;
|
||||||
let busy = contact.busy || calling;
|
let busy = contact.busy || calling;
|
||||||
let github_login = SharedString::from(contact.user.github_login.clone());
|
let github_login = SharedString::from(contact.user.github_login.clone());
|
||||||
@@ -2492,7 +2491,7 @@ impl CollabPanel {
|
|||||||
is_incoming: bool,
|
is_incoming: bool,
|
||||||
is_selected: bool,
|
is_selected: bool,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement + use<> {
|
||||||
let github_login = SharedString::from(user.github_login.clone());
|
let github_login = SharedString::from(user.github_login.clone());
|
||||||
let user_id = user.id;
|
let user_id = user.id;
|
||||||
let is_response_pending = self.user_store.read(cx).is_contact_request_pending(user);
|
let is_response_pending = self.user_store.read(cx).is_contact_request_pending(user);
|
||||||
@@ -2605,7 +2604,7 @@ impl CollabPanel {
|
|||||||
is_selected: bool,
|
is_selected: bool,
|
||||||
ix: usize,
|
ix: usize,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement + use<> {
|
||||||
let channel_id = channel.id;
|
let channel_id = channel.id;
|
||||||
|
|
||||||
let is_active = maybe!({
|
let is_active = maybe!({
|
||||||
@@ -2803,7 +2802,7 @@ impl CollabPanel {
|
|||||||
depth: usize,
|
depth: usize,
|
||||||
_window: &mut Window,
|
_window: &mut Window,
|
||||||
_cx: &mut Context<Self>,
|
_cx: &mut Context<Self>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement + use<> {
|
||||||
let item = ListItem::new("channel-editor")
|
let item = ListItem::new("channel-editor")
|
||||||
.inset(false)
|
.inset(false)
|
||||||
// Add one level of depth for the disclosure arrow.
|
// Add one level of depth for the disclosure arrow.
|
||||||
@@ -2832,7 +2831,7 @@ fn render_tree_branch(
|
|||||||
overdraw: bool,
|
overdraw: bool,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement + use<> {
|
||||||
let rem_size = window.rem_size();
|
let rem_size = window.rem_size();
|
||||||
let line_height = window.text_style().line_height_in_pixels(rem_size);
|
let line_height = window.text_style().line_height_in_pixels(rem_size);
|
||||||
let width = rem_size * 1.5;
|
let width = rem_size * 1.5;
|
||||||
|
|||||||
@@ -421,20 +421,17 @@ impl PickerDelegate for ChannelModalDelegate {
|
|||||||
el.child(IconButton::new("ellipsis", IconName::Ellipsis))
|
el.child(IconButton::new("ellipsis", IconName::Ellipsis))
|
||||||
})
|
})
|
||||||
.when(is_me, |el| el.child(Label::new("You").color(Color::Muted)))
|
.when(is_me, |el| el.child(Label::new("You").color(Color::Muted)))
|
||||||
.children(
|
.children(match (&self.context_menu, selected) {
|
||||||
if let (Some((menu, _)), true) = (&self.context_menu, selected) {
|
(Some((menu, _)), true) => Some(
|
||||||
Some(
|
|
||||||
deferred(
|
deferred(
|
||||||
anchored()
|
anchored()
|
||||||
.anchor(gpui::Corner::TopRight)
|
.anchor(gpui::Corner::TopRight)
|
||||||
.child(menu.clone()),
|
.child(menu.clone()),
|
||||||
)
|
)
|
||||||
.with_priority(1),
|
.with_priority(1),
|
||||||
)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
|
_ => None,
|
||||||
|
}),
|
||||||
Mode::InviteMembers => match request_status {
|
Mode::InviteMembers => match request_status {
|
||||||
Some(proto::channel_member::Kind::Invitee) => {
|
Some(proto::channel_member::Kind::Invitee) => {
|
||||||
slot.children(Some(Label::new("Invited")))
|
slot.children(Some(Label::new("Invited")))
|
||||||
|
|||||||
@@ -182,15 +182,14 @@ impl NotificationPanel {
|
|||||||
cx: AsyncWindowContext,
|
cx: AsyncWindowContext,
|
||||||
) -> Task<Result<Entity<Self>>> {
|
) -> Task<Result<Entity<Self>>> {
|
||||||
cx.spawn(async move |cx| {
|
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) })
|
.background_spawn(async move { KEY_VALUE_STORE.read_kvp(NOTIFICATION_PANEL_KEY) })
|
||||||
.await
|
.await
|
||||||
.log_err()
|
.log_err()
|
||||||
.flatten()
|
.flatten()
|
||||||
{
|
{
|
||||||
Some(serde_json::from_str::<SerializedNotificationPanel>(&panel)?)
|
Some(panel) => Some(serde_json::from_str::<SerializedNotificationPanel>(&panel)?),
|
||||||
} else {
|
_ => None,
|
||||||
None
|
|
||||||
};
|
};
|
||||||
|
|
||||||
workspace.update_in(cx, |workspace, window, cx| {
|
workspace.update_in(cx, |workspace, window, cx| {
|
||||||
@@ -494,14 +493,15 @@ impl NotificationPanel {
|
|||||||
|
|
||||||
if let Notification::ChannelMessageMention { channel_id, .. } = ¬ification {
|
if let Notification::ChannelMessageMention { channel_id, .. } = ¬ification {
|
||||||
if let Some(workspace) = self.workspace.upgrade() {
|
if let Some(workspace) = self.workspace.upgrade() {
|
||||||
return if let Some(panel) = workspace.read(cx).panel::<ChatPanel>(cx) {
|
return match workspace.read(cx).panel::<ChatPanel>(cx) {
|
||||||
|
Some(panel) => {
|
||||||
let panel = panel.read(cx);
|
let panel = panel.read(cx);
|
||||||
panel.is_scrolled_to_bottom()
|
panel.is_scrolled_to_bottom()
|
||||||
&& panel
|
&& panel
|
||||||
.active_chat()
|
.active_chat()
|
||||||
.map_or(false, |chat| chat.read(cx).channel_id.0 == *channel_id)
|
.map_or(false, |chat| chat.read(cx).channel_id.0 == *channel_id)
|
||||||
} else {
|
}
|
||||||
false
|
_ => false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,10 +133,9 @@ impl CommandPaletteInterceptor {
|
|||||||
|
|
||||||
/// Intercepts the given query from the command palette.
|
/// Intercepts the given query from the command palette.
|
||||||
pub fn intercept(&self, query: &str, cx: &App) -> Vec<CommandInterceptResult> {
|
pub fn intercept(&self, query: &str, cx: &App) -> Vec<CommandInterceptResult> {
|
||||||
if let Some(handler) = self.0.as_ref() {
|
match self.0.as_ref() {
|
||||||
(handler)(query, cx)
|
Some(handler) => (handler)(query, cx),
|
||||||
} else {
|
_ => Vec::new(),
|
||||||
Vec::new()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ pub fn components() -> AllComponents {
|
|||||||
let data = COMPONENT_DATA.read();
|
let data = COMPONENT_DATA.read();
|
||||||
let mut all_components = AllComponents::new();
|
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 preview = data.previews.get(name).cloned();
|
||||||
let component_name = SharedString::new_static(name);
|
let component_name = SharedString::new_static(name);
|
||||||
let id = ComponentId(name);
|
let id = ComponentId(name);
|
||||||
|
|||||||
@@ -244,7 +244,7 @@ impl ComponentPreview {
|
|||||||
ix: usize,
|
ix: usize,
|
||||||
entry: &PreviewEntry,
|
entry: &PreviewEntry,
|
||||||
cx: &Context<Self>,
|
cx: &Context<Self>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement + use<> {
|
||||||
match entry {
|
match entry {
|
||||||
PreviewEntry::Component(component_metadata) => {
|
PreviewEntry::Component(component_metadata) => {
|
||||||
let id = component_metadata.id();
|
let id = component_metadata.id();
|
||||||
@@ -318,7 +318,7 @@ impl ComponentPreview {
|
|||||||
title: SharedString,
|
title: SharedString,
|
||||||
_window: &Window,
|
_window: &Window,
|
||||||
_cx: &App,
|
_cx: &App,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement + use<> {
|
||||||
h_flex()
|
h_flex()
|
||||||
.w_full()
|
.w_full()
|
||||||
.h_10()
|
.h_10()
|
||||||
@@ -332,7 +332,7 @@ impl ComponentPreview {
|
|||||||
component: &ComponentMetadata,
|
component: &ComponentMetadata,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement + use<> {
|
||||||
let name = component.name();
|
let name = component.name();
|
||||||
let scope = component.scope();
|
let scope = component.scope();
|
||||||
|
|
||||||
@@ -379,7 +379,7 @@ impl ComponentPreview {
|
|||||||
.into_any_element()
|
.into_any_element()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_all_components(&self) -> impl IntoElement {
|
fn render_all_components(&self) -> impl IntoElement + use<> {
|
||||||
v_flex()
|
v_flex()
|
||||||
.id("component-list")
|
.id("component-list")
|
||||||
.px_8()
|
.px_8()
|
||||||
@@ -397,7 +397,7 @@ impl ComponentPreview {
|
|||||||
component_id: &ComponentId,
|
component_id: &ComponentId,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement + use<> {
|
||||||
let component = self.component_map.get(&component_id);
|
let component = self.component_map.get(&component_id);
|
||||||
|
|
||||||
if let Some(component) = component {
|
if let Some(component) = component {
|
||||||
|
|||||||
@@ -62,10 +62,9 @@ pub struct Client {
|
|||||||
pub struct ContextServerId(pub Arc<str>);
|
pub struct ContextServerId(pub Arc<str>);
|
||||||
|
|
||||||
fn is_null_value<T: Serialize>(value: &T) -> bool {
|
fn is_null_value<T: Serialize>(value: &T) -> bool {
|
||||||
if let Ok(Value::Null) = serde_json::to_value(value) {
|
match serde_json::to_value(value) {
|
||||||
true
|
Ok(Value::Null) => true,
|
||||||
} else {
|
_ => false,
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,12 +231,19 @@ impl Client {
|
|||||||
handler(Ok(message.to_string()));
|
handler(Ok(message.to_string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let Ok(notification) = serde_json::from_str::<AnyNotification>(&message) {
|
} else {
|
||||||
|
match serde_json::from_str::<AnyNotification>(&message) {
|
||||||
|
Ok(notification) => {
|
||||||
let mut notification_handlers = notification_handlers.lock();
|
let mut notification_handlers = notification_handlers.lock();
|
||||||
if let Some(handler) = notification_handlers.get_mut(notification.method.as_str()) {
|
if let Some(handler) =
|
||||||
|
notification_handlers.get_mut(notification.method.as_str())
|
||||||
|
{
|
||||||
handler(notification.params.unwrap_or(Value::Null), cx.clone());
|
handler(notification.params.unwrap_or(Value::Null), cx.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
smol::future::yield_now().await;
|
smol::future::yield_now().await;
|
||||||
|
|||||||
@@ -77,7 +77,8 @@ impl Tool for ContextServerTool {
|
|||||||
_action_log: Entity<ActionLog>,
|
_action_log: Entity<ActionLog>,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> Task<Result<String>> {
|
) -> Task<Result<String>> {
|
||||||
if let Some(server) = self.server_manager.read(cx).get_server(&self.server_id) {
|
match self.server_manager.read(cx).get_server(&self.server_id) {
|
||||||
|
Some(server) => {
|
||||||
let tool_name = self.tool.name.clone();
|
let tool_name = self.tool.name.clone();
|
||||||
let server_clone = server.clone();
|
let server_clone = server.clone();
|
||||||
let input_clone = input.clone();
|
let input_clone = input.clone();
|
||||||
@@ -87,10 +88,9 @@ impl Tool for ContextServerTool {
|
|||||||
bail!("Context server not initialized");
|
bail!("Context server not initialized");
|
||||||
};
|
};
|
||||||
|
|
||||||
let arguments = if let serde_json::Value::Object(map) = input_clone {
|
let arguments = match input_clone {
|
||||||
Some(map.into_iter().collect())
|
serde_json::Value::Object(map) => Some(map.into_iter().collect()),
|
||||||
} else {
|
_ => None,
|
||||||
None
|
|
||||||
};
|
};
|
||||||
|
|
||||||
log::trace!(
|
log::trace!(
|
||||||
@@ -116,8 +116,8 @@ impl Tool for ContextServerTool {
|
|||||||
}
|
}
|
||||||
Ok(result)
|
Ok(result)
|
||||||
})
|
})
|
||||||
} else {
|
}
|
||||||
Task::ready(Err(anyhow!("Context server not found")))
|
_ => Task::ready(Err(anyhow!("Context server not found"))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use gpui::App;
|
use gpui::App;
|
||||||
use schemars::gen::SchemaGenerator;
|
use schemars::r#gen::SchemaGenerator;
|
||||||
use schemars::schema::{InstanceType, Schema, SchemaObject};
|
use schemars::schema::{InstanceType, Schema, SchemaObject};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|||||||
@@ -348,7 +348,10 @@ impl Copilot {
|
|||||||
this
|
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) {
|
let shutdown = match mem::replace(&mut self.server, CopilotServer::Disabled) {
|
||||||
CopilotServer::Running(server) => Some(Box::pin(async move { server.lsp.shutdown() })),
|
CopilotServer::Running(server) => Some(Box::pin(async move { server.lsp.shutdown() })),
|
||||||
_ => None,
|
_ => None,
|
||||||
@@ -553,8 +556,10 @@ impl Copilot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn sign_in(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
|
pub fn sign_in(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
|
||||||
if let CopilotServer::Running(server) = &mut self.server {
|
match &mut self.server {
|
||||||
let task = match &server.sign_in_status {
|
CopilotServer::Running(server) => {
|
||||||
|
let task =
|
||||||
|
match &server.sign_in_status {
|
||||||
SignInStatus::Authorized { .. } => Task::ready(Ok(())).shared(),
|
SignInStatus::Authorized { .. } => Task::ready(Ok(())).shared(),
|
||||||
SignInStatus::SigningIn { task, .. } => {
|
SignInStatus::SigningIn { task, .. } => {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
@@ -564,7 +569,8 @@ impl Copilot {
|
|||||||
let lsp = server.lsp.clone();
|
let lsp = server.lsp.clone();
|
||||||
let task = cx
|
let task = cx
|
||||||
.spawn(async move |this, cx| {
|
.spawn(async move |this, cx| {
|
||||||
let sign_in = async {
|
let sign_in =
|
||||||
|
async {
|
||||||
let sign_in = lsp
|
let sign_in = lsp
|
||||||
.request::<request::SignInInitiate>(
|
.request::<request::SignInInitiate>(
|
||||||
request::SignInInitiateParams {},
|
request::SignInInitiateParams {},
|
||||||
@@ -629,12 +635,14 @@ impl Copilot {
|
|||||||
};
|
};
|
||||||
|
|
||||||
cx.background_spawn(task.map_err(|err| anyhow!("{:?}", err)))
|
cx.background_spawn(task.map_err(|err| anyhow!("{:?}", err)))
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
// If we're downloading, wait until download is finished
|
// If we're downloading, wait until download is finished
|
||||||
// If we're in a stuck state, display to the user
|
// If we're in a stuck state, display to the user
|
||||||
Task::ready(Err(anyhow!("copilot hasn't started yet")))
|
Task::ready(Err(anyhow!("copilot hasn't started yet")))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn sign_out(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
|
pub fn sign_out(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
|
||||||
self.update_sign_in_status(request::SignInStatus::NotSignedIn, cx);
|
self.update_sign_in_status(request::SignInStatus::NotSignedIn, cx);
|
||||||
@@ -680,10 +688,9 @@ impl Copilot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn language_server(&self) -> Option<&Arc<LanguageServer>> {
|
pub fn language_server(&self) -> Option<&Arc<LanguageServer>> {
|
||||||
if let CopilotServer::Running(server) = &self.server {
|
match &self.server {
|
||||||
Some(&server.lsp)
|
CopilotServer::Running(server) => Some(&server.lsp),
|
||||||
} else {
|
_ => None,
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1078,7 +1078,7 @@ mod tests {
|
|||||||
cx: &mut EditorLspTestContext,
|
cx: &mut EditorLspTestContext,
|
||||||
marked_string: &str,
|
marked_string: &str,
|
||||||
completions: Vec<&'static str>,
|
completions: Vec<&'static str>,
|
||||||
) -> impl Future<Output = ()> {
|
) -> impl Future<Output = ()> + use<> {
|
||||||
let complete_from_marker: TextRangeMarker = '|'.into();
|
let complete_from_marker: TextRangeMarker = '|'.into();
|
||||||
let replace_range_marker: TextRangeMarker = ('<', '>').into();
|
let replace_range_marker: TextRangeMarker = ('<', '>').into();
|
||||||
let (_, mut marked_ranges) = marked_text_ranges_by(
|
let (_, mut marked_ranges) = marked_text_ranges_by(
|
||||||
|
|||||||
@@ -139,7 +139,10 @@ impl CopilotCodeVerification {
|
|||||||
cx.notify();
|
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
|
let copied = cx
|
||||||
.read_from_clipboard()
|
.read_from_clipboard()
|
||||||
.map(|item| item.text().as_ref() == Some(&data.user_code))
|
.map(|item| item.text().as_ref() == Some(&data.user_code))
|
||||||
@@ -172,7 +175,7 @@ impl CopilotCodeVerification {
|
|||||||
data: &PromptUserDeviceFlow,
|
data: &PromptUserDeviceFlow,
|
||||||
|
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> impl Element {
|
) -> impl Element + use<> {
|
||||||
let connect_button_label = if connect_clicked {
|
let connect_button_label = if connect_clicked {
|
||||||
"Waiting for connection..."
|
"Waiting for connection..."
|
||||||
} else {
|
} 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()
|
v_flex()
|
||||||
.gap_2()
|
.gap_2()
|
||||||
.child(Headline::new("Copilot Enabled!").size(HeadlineSize::Large))
|
.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()
|
v_flex()
|
||||||
.child(Headline::new("You must have an active GitHub Copilot subscription.").size(HeadlineSize::Large))
|
.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()
|
let loading_icon = svg()
|
||||||
.size_8()
|
.size_8()
|
||||||
.path(IconName::ArrowCircle.path())
|
.path(IconName::ArrowCircle.path())
|
||||||
|
|||||||
@@ -222,13 +222,12 @@ impl TransportDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn send_message(&self, message: Message) -> Result<()> {
|
pub(crate) async fn send_message(&self, message: Message) -> Result<()> {
|
||||||
if let Some(server_tx) = self.server_tx.lock().await.as_ref() {
|
match self.server_tx.lock().await.as_ref() {
|
||||||
server_tx
|
Some(server_tx) => server_tx
|
||||||
.send(message)
|
.send(message)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| anyhow!("Failed to send message: {}", e))
|
.map_err(|e| anyhow!("Failed to send message: {}", e)),
|
||||||
} else {
|
_ => Err(anyhow!("Server tx already dropped")),
|
||||||
Err(anyhow!("Server tx already dropped"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,12 +342,15 @@ impl TransportDelegate {
|
|||||||
|
|
||||||
match message {
|
match message {
|
||||||
Ok(Message::Response(res)) => {
|
Ok(Message::Response(res)) => {
|
||||||
if let Some(tx) = pending_requests.lock().await.remove(&res.request_seq) {
|
match pending_requests.lock().await.remove(&res.request_seq) {
|
||||||
|
Some(tx) => {
|
||||||
if let Err(e) = tx.send(Self::process_response(res)) {
|
if let Err(e) = tx.send(Self::process_response(res)) {
|
||||||
log::trace!("Did not send response `{:?}` for a cancelled", e);
|
log::trace!("Did not send response `{:?}` for a cancelled", e);
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
client_tx.send(Message::Response(res)).await?;
|
client_tx.send(Message::Response(res)).await?;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Ok(message) => {
|
Ok(message) => {
|
||||||
@@ -816,18 +818,20 @@ impl FakeTransport {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
writer.flush().await.unwrap();
|
writer.flush().await.unwrap();
|
||||||
} else {
|
} else {
|
||||||
if let Some(handle) = request_handlers
|
match request_handlers
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
.get_mut(request.command.as_str())
|
.get_mut(request.command.as_str())
|
||||||
{
|
{
|
||||||
|
Some(handle) => {
|
||||||
handle(
|
handle(
|
||||||
request.seq,
|
request.seq,
|
||||||
request.arguments.unwrap_or(json!({})),
|
request.arguments.unwrap_or(json!({})),
|
||||||
stdout_writer.clone(),
|
stdout_writer.clone(),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
log::error!(
|
log::error!(
|
||||||
"No request handler for {}",
|
"No request handler for {}",
|
||||||
request.command
|
request.command
|
||||||
@@ -835,6 +839,7 @@ impl FakeTransport {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Message::Event(event) => {
|
Message::Event(event) => {
|
||||||
let message =
|
let message =
|
||||||
serde_json::to_string(&Message::Event(event)).unwrap();
|
serde_json::to_string(&Message::Event(event)).unwrap();
|
||||||
@@ -850,14 +855,20 @@ impl FakeTransport {
|
|||||||
writer.flush().await.unwrap();
|
writer.flush().await.unwrap();
|
||||||
}
|
}
|
||||||
Message::Response(response) => {
|
Message::Response(response) => {
|
||||||
if let Some(handle) = response_handlers
|
match response_handlers
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
.get(response.command.as_str())
|
.get(response.command.as_str())
|
||||||
{
|
{
|
||||||
|
Some(handle) => {
|
||||||
handle(response);
|
handle(response);
|
||||||
} else {
|
}
|
||||||
log::error!("No response handler for {}", response.command);
|
_ => {
|
||||||
|
log::error!(
|
||||||
|
"No response handler for {}",
|
||||||
|
response.command
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,17 +94,16 @@ impl DebugAdapter for PythonDebugAdapter {
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let python_path = if let Some(toolchain) = toolchain {
|
let python_path = match toolchain {
|
||||||
Some(toolchain.path.to_string())
|
Some(toolchain) => Some(toolchain.path.to_string()),
|
||||||
} else {
|
_ => BINARY_NAMES
|
||||||
BINARY_NAMES
|
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|cmd| {
|
.filter_map(|cmd| {
|
||||||
delegate
|
delegate
|
||||||
.which(OsStr::new(cmd))
|
.which(OsStr::new(cmd))
|
||||||
.map(|path| path.to_string_lossy().to_string())
|
.map(|path| path.to_string_lossy().to_string())
|
||||||
})
|
})
|
||||||
.find(|_| true)
|
.find(|_| true),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(DebugAdapterBinary {
|
Ok(DebugAdapterBinary {
|
||||||
|
|||||||
@@ -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
|
/// Implements a basic DB wrapper for a given domain
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! define_connection {
|
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>);
|
pub struct $t($crate::sqlez::thread_safe_connection::ThreadSafeConnection<$t>);
|
||||||
|
|
||||||
impl ::std::ops::Deref for $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)))
|
$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 )>);
|
pub struct $t($crate::sqlez::thread_safe_connection::ThreadSafeConnection<( $($d),+, $t )>);
|
||||||
|
|
||||||
impl ::std::ops::Deref for $t {
|
impl ::std::ops::Deref for $t {
|
||||||
|
|||||||
@@ -284,7 +284,7 @@ impl Render for InertState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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 settings = ThemeSettings::get_global(cx);
|
||||||
let text_style = TextStyle {
|
let text_style = TextStyle {
|
||||||
color: cx.theme().colors().text,
|
color: cx.theme().colors().text,
|
||||||
|
|||||||
@@ -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 settings = ThemeSettings::get_global(cx);
|
||||||
let text_style = TextStyle {
|
let text_style = TextStyle {
|
||||||
color: if self.console.read(cx).read_only(cx) {
|
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 settings = ThemeSettings::get_global(cx);
|
||||||
let text_style = TextStyle {
|
let text_style = TextStyle {
|
||||||
color: if self.console.read(cx).read_only(cx) {
|
color: if self.console.read(cx).read_only(cx) {
|
||||||
|
|||||||
@@ -807,14 +807,16 @@ impl VariableList {
|
|||||||
)
|
)
|
||||||
.when(!dap.value.is_empty(), |this| {
|
.when(!dap.value.is_empty(), |this| {
|
||||||
this.child(div().w_full().id(variable.item_value_id()).map(|this| {
|
this.child(div().w_full().id(variable.item_value_id()).map(|this| {
|
||||||
if let Some((_, editor)) = self
|
match self
|
||||||
.edited_path
|
.edited_path
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.filter(|(path, _)| path == &variable.path)
|
.filter(|(path, _)| path == &variable.path)
|
||||||
{
|
{
|
||||||
|
Some((_, editor)) => {
|
||||||
this.child(div().size_full().px_2().child(editor.clone()))
|
this.child(div().size_full().px_2().child(editor.clone()))
|
||||||
} else {
|
}
|
||||||
this.text_color(cx.theme().colors().text_muted)
|
_ => this
|
||||||
|
.text_color(cx.theme().colors().text_muted)
|
||||||
.when(
|
.when(
|
||||||
!self.disabled
|
!self.disabled
|
||||||
&& self
|
&& self
|
||||||
@@ -853,7 +855,7 @@ impl VariableList {
|
|||||||
.when_some(variable_color, |this, color| {
|
.when_some(variable_color, |this, color| {
|
||||||
this.color(Color::from(color))
|
this.color(Color::from(color))
|
||||||
}),
|
}),
|
||||||
)
|
),
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -32,12 +32,9 @@ impl StartingState {
|
|||||||
let _notify_parent = cx.spawn(async move |this, cx| {
|
let _notify_parent = cx.spawn(async move |this, cx| {
|
||||||
let entity = task.await;
|
let entity = task.await;
|
||||||
|
|
||||||
this.update(cx, |_, cx| {
|
this.update(cx, |_, cx| match entity {
|
||||||
if let Ok(entity) = entity {
|
Ok(entity) => cx.emit(StartingEvent::Finished(entity)),
|
||||||
cx.emit(StartingEvent::Finished(entity))
|
_ => cx.emit(StartingEvent::Failed),
|
||||||
} else {
|
|
||||||
cx.emit(StartingEvent::Failed)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -305,12 +305,14 @@ impl ProjectDiagnosticsEditor {
|
|||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Workspace>,
|
cx: &mut Context<Workspace>,
|
||||||
) {
|
) {
|
||||||
if let Some(existing) = workspace.item_of_type::<ProjectDiagnosticsEditor>(cx) {
|
match workspace.item_of_type::<ProjectDiagnosticsEditor>(cx) {
|
||||||
|
Some(existing) => {
|
||||||
let is_active = workspace
|
let is_active = workspace
|
||||||
.active_item(cx)
|
.active_item(cx)
|
||||||
.is_some_and(|item| item.item_id() == existing.item_id());
|
.is_some_and(|item| item.item_id() == existing.item_id());
|
||||||
workspace.activate_item(&existing, true, !is_active, window, cx);
|
workspace.activate_item(&existing, true, !is_active, window, cx);
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
let workspace_handle = cx.entity().downgrade();
|
let workspace_handle = cx.entity().downgrade();
|
||||||
|
|
||||||
let include_warnings = match cx.try_global::<IncludeWarnings>() {
|
let include_warnings = match cx.try_global::<IncludeWarnings>() {
|
||||||
@@ -330,6 +332,7 @@ impl ProjectDiagnosticsEditor {
|
|||||||
workspace.add_item_to_active_pane(Box::new(diagnostics), None, true, window, cx);
|
workspace.add_item_to_active_pane(Box::new(diagnostics), None, true, window, cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn toggle_warnings(&mut self, _: &ToggleWarnings, window: &mut Window, cx: &mut Context<Self>) {
|
fn toggle_warnings(&mut self, _: &ToggleWarnings, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
self.include_warnings = !self.include_warnings;
|
self.include_warnings = !self.include_warnings;
|
||||||
@@ -472,7 +475,8 @@ impl ProjectDiagnosticsEditor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((language_server_id, group)) = to_insert {
|
match to_insert {
|
||||||
|
Some((language_server_id, group)) => {
|
||||||
let mut group_state = DiagnosticGroupState {
|
let mut group_state = DiagnosticGroupState {
|
||||||
language_server_id,
|
language_server_id,
|
||||||
primary_diagnostic: group.entries[group.primary_ix].clone(),
|
primary_diagnostic: group.entries[group.primary_ix].clone(),
|
||||||
@@ -483,10 +487,11 @@ impl ProjectDiagnosticsEditor {
|
|||||||
};
|
};
|
||||||
let mut pending_range: Option<(Range<Point>, Range<Point>, usize)> = None;
|
let mut pending_range: Option<(Range<Point>, Range<Point>, usize)> = None;
|
||||||
let mut is_first_excerpt_for_group = true;
|
let mut is_first_excerpt_for_group = true;
|
||||||
for (ix, entry) in group.entries.iter().map(Some).chain([None]).enumerate() {
|
for (ix, entry) in group.entries.iter().map(Some).chain([None]).enumerate()
|
||||||
|
{
|
||||||
let resolved_entry = entry.map(|e| e.resolve::<Point>(&snapshot));
|
let resolved_entry = entry.map(|e| e.resolve::<Point>(&snapshot));
|
||||||
let expanded_range = if let Some(entry) = &resolved_entry {
|
let expanded_range = match &resolved_entry {
|
||||||
Some(
|
Some(entry) => Some(
|
||||||
context_range_for_entry(
|
context_range_for_entry(
|
||||||
entry.range.clone(),
|
entry.range.clone(),
|
||||||
context,
|
context,
|
||||||
@@ -494,15 +499,15 @@ impl ProjectDiagnosticsEditor {
|
|||||||
(**cx).clone(),
|
(**cx).clone(),
|
||||||
)
|
)
|
||||||
.await,
|
.await,
|
||||||
)
|
),
|
||||||
} else {
|
_ => None,
|
||||||
None
|
|
||||||
};
|
};
|
||||||
if let Some((range, context_range, start_ix)) = &mut pending_range {
|
if let Some((range, context_range, start_ix)) = &mut pending_range {
|
||||||
if let Some(expanded_range) = expanded_range.clone() {
|
if let Some(expanded_range) = expanded_range.clone() {
|
||||||
// If the entries are overlapping or next to each-other, merge them into one excerpt.
|
// 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 {
|
if context_range.end.row + 1 >= expanded_range.start.row {
|
||||||
context_range.end = context_range.end.max(expanded_range.end);
|
context_range.end =
|
||||||
|
context_range.end.max(expanded_range.end);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -546,7 +551,8 @@ impl ProjectDiagnosticsEditor {
|
|||||||
for entry in &group.entries[*start_ix..ix] {
|
for entry in &group.entries[*start_ix..ix] {
|
||||||
let mut diagnostic = entry.diagnostic.clone();
|
let mut diagnostic = entry.diagnostic.clone();
|
||||||
if diagnostic.is_primary {
|
if diagnostic.is_primary {
|
||||||
group_state.primary_excerpt_ix = group_state.excerpts.len() - 1;
|
group_state.primary_excerpt_ix =
|
||||||
|
group_state.excerpts.len() - 1;
|
||||||
diagnostic.message =
|
diagnostic.message =
|
||||||
entry.diagnostic.message.split('\n').skip(1).collect();
|
entry.diagnostic.message.split('\n').skip(1).collect();
|
||||||
}
|
}
|
||||||
@@ -558,9 +564,12 @@ impl ProjectDiagnosticsEditor {
|
|||||||
excerpt_id,
|
excerpt_id,
|
||||||
entry.range.start,
|
entry.range.start,
|
||||||
)),
|
)),
|
||||||
height: diagnostic.message.matches('\n').count() as u32 + 1,
|
height: diagnostic.message.matches('\n').count() as u32
|
||||||
|
+ 1,
|
||||||
style: BlockStyle::Fixed,
|
style: BlockStyle::Fixed,
|
||||||
render: diagnostic_block_renderer(diagnostic, None, true),
|
render: diagnostic_block_renderer(
|
||||||
|
diagnostic, None, true,
|
||||||
|
),
|
||||||
priority: 0,
|
priority: 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -581,12 +590,16 @@ impl ProjectDiagnosticsEditor {
|
|||||||
.diagnostic_groups
|
.diagnostic_groups
|
||||||
.push(group_state);
|
.push(group_state);
|
||||||
})?;
|
})?;
|
||||||
} else if let Some((_, group_state)) = to_remove {
|
}
|
||||||
|
_ => match to_remove {
|
||||||
|
Some((_, group_state)) => {
|
||||||
excerpts.update(cx, |excerpts, cx| {
|
excerpts.update(cx, |excerpts, cx| {
|
||||||
excerpts.remove_excerpts(group_state.excerpts.iter().copied(), cx)
|
excerpts.remove_excerpts(group_state.excerpts.iter().copied(), cx)
|
||||||
})?;
|
})?;
|
||||||
blocks_to_remove.extend(group_state.blocks.iter().copied());
|
blocks_to_remove.extend(group_state.blocks.iter().copied());
|
||||||
} else if let Some((_, group_state)) = to_keep {
|
}
|
||||||
|
_ => match to_keep {
|
||||||
|
Some((_, group_state)) => {
|
||||||
prev_excerpt_id = *group_state.excerpts.last().unwrap();
|
prev_excerpt_id = *group_state.excerpts.last().unwrap();
|
||||||
first_excerpt_id.get_or_insert(prev_excerpt_id);
|
first_excerpt_id.get_or_insert(prev_excerpt_id);
|
||||||
|
|
||||||
@@ -596,6 +609,10 @@ impl ProjectDiagnosticsEditor {
|
|||||||
.push(group_state)
|
.push(group_state)
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let excerpts_snapshot = excerpts.update(cx, |excerpts, cx| excerpts.snapshot(cx))?;
|
let excerpts_snapshot = excerpts.update(cx, |excerpts, cx| excerpts.snapshot(cx))?;
|
||||||
|
|||||||
@@ -62,7 +62,8 @@ impl Render for DiagnosticIndicator {
|
|||||||
.child(Label::new(warning_count.to_string()).size(LabelSize::Small)),
|
.child(Label::new(warning_count.to_string()).size(LabelSize::Small)),
|
||||||
};
|
};
|
||||||
|
|
||||||
let status = if let Some(diagnostic) = &self.current_diagnostic {
|
let status = match &self.current_diagnostic {
|
||||||
|
Some(diagnostic) => {
|
||||||
let message = diagnostic.message.split('\n').next().unwrap().to_string();
|
let message = diagnostic.message.split('\n').next().unwrap().to_string();
|
||||||
Some(
|
Some(
|
||||||
Button::new("diagnostic_message", message)
|
Button::new("diagnostic_message", message)
|
||||||
@@ -80,8 +81,8 @@ impl Render for DiagnosticIndicator {
|
|||||||
}))
|
}))
|
||||||
.into_any_element(),
|
.into_any_element(),
|
||||||
)
|
)
|
||||||
} else {
|
}
|
||||||
None
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
h_flex()
|
h_flex()
|
||||||
@@ -187,15 +188,18 @@ impl StatusItemView for DiagnosticIndicator {
|
|||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) {
|
) {
|
||||||
if let Some(editor) = active_pane_item.and_then(|item| item.downcast::<Editor>()) {
|
match active_pane_item.and_then(|item| item.downcast::<Editor>()) {
|
||||||
|
Some(editor) => {
|
||||||
self.active_editor = Some(editor.downgrade());
|
self.active_editor = Some(editor.downgrade());
|
||||||
self._observe_active_editor = Some(cx.observe_in(&editor, window, Self::update));
|
self._observe_active_editor = Some(cx.observe_in(&editor, window, Self::update));
|
||||||
self.update(editor, window, cx);
|
self.update(editor, window, cx);
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
self.active_editor = None;
|
self.active_editor = None;
|
||||||
self.current_diagnostic = None;
|
self.current_diagnostic = None;
|
||||||
self._observe_active_editor = None;
|
self._observe_active_editor = None;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,11 +83,12 @@ impl ToolbarItemView for ToolbarControls {
|
|||||||
_: &mut Context<Self>,
|
_: &mut Context<Self>,
|
||||||
) -> ToolbarItemLocation {
|
) -> ToolbarItemLocation {
|
||||||
if let Some(pane_item) = active_pane_item.as_ref() {
|
if let Some(pane_item) = active_pane_item.as_ref() {
|
||||||
if let Some(editor) = pane_item.downcast::<ProjectDiagnosticsEditor>() {
|
match pane_item.downcast::<ProjectDiagnosticsEditor>() {
|
||||||
|
Some(editor) => {
|
||||||
self.editor = Some(editor.downgrade());
|
self.editor = Some(editor.downgrade());
|
||||||
ToolbarItemLocation::PrimaryRight
|
ToolbarItemLocation::PrimaryRight
|
||||||
} else {
|
}
|
||||||
ToolbarItemLocation::Hidden
|
_ => ToolbarItemLocation::Hidden,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ToolbarItemLocation::Hidden
|
ToolbarItemLocation::Hidden
|
||||||
|
|||||||
@@ -21,11 +21,14 @@ fn main() -> Result<()> {
|
|||||||
let preprocessor =
|
let preprocessor =
|
||||||
ZedDocsPreprocessor::new().context("Failed to create ZedDocsPreprocessor")?;
|
ZedDocsPreprocessor::new().context("Failed to create ZedDocsPreprocessor")?;
|
||||||
|
|
||||||
if let Some(sub_args) = matches.subcommand_matches("supports") {
|
match matches.subcommand_matches("supports") {
|
||||||
|
Some(sub_args) => {
|
||||||
handle_supports(&preprocessor, sub_args);
|
handle_supports(&preprocessor, sub_args);
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
handle_preprocessing(&preprocessor)?;
|
handle_preprocessing(&preprocessor)?;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -735,11 +735,11 @@ impl CompletionsMenu {
|
|||||||
|
|
||||||
let completion = &completions[mat.candidate_id];
|
let completion = &completions[mat.candidate_id];
|
||||||
let sort_key = completion.sort_key();
|
let sort_key = completion.sort_key();
|
||||||
let sort_text =
|
let sort_text = match &completion.source {
|
||||||
if let CompletionSource::Lsp { lsp_completion, .. } = &completion.source {
|
CompletionSource::Lsp { lsp_completion, .. } => {
|
||||||
lsp_completion.sort_text.as_deref()
|
lsp_completion.sort_text.as_deref()
|
||||||
} else {
|
}
|
||||||
None
|
_ => None,
|
||||||
};
|
};
|
||||||
let score = Reverse(OrderedFloat(mat.score));
|
let score = Reverse(OrderedFloat(mat.score));
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ impl<'a> CommitAvatar<'a> {
|
|||||||
&'a self,
|
&'a self,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<CommitTooltip>,
|
cx: &mut Context<CommitTooltip>,
|
||||||
) -> Option<impl IntoElement> {
|
) -> Option<impl IntoElement + use<>> {
|
||||||
let remote = self
|
let remote = self
|
||||||
.commit
|
.commit
|
||||||
.message
|
.message
|
||||||
|
|||||||
@@ -226,26 +226,22 @@ impl DisplayMap {
|
|||||||
.wrap_map
|
.wrap_map
|
||||||
.update(cx, |map, cx| map.sync(snapshot, edits, cx));
|
.update(cx, |map, cx| map.sync(snapshot, edits, cx));
|
||||||
let mut block_map = self.block_map.write(snapshot, edits);
|
let mut block_map = self.block_map.write(snapshot, edits);
|
||||||
let blocks = creases.into_iter().filter_map(|crease| {
|
let blocks = creases.into_iter().filter_map(|crease| match crease {
|
||||||
if let Crease::Block {
|
Crease::Block {
|
||||||
range,
|
range,
|
||||||
block_height,
|
block_height,
|
||||||
render_block,
|
render_block,
|
||||||
block_style,
|
block_style,
|
||||||
block_priority,
|
block_priority,
|
||||||
..
|
..
|
||||||
} = crease
|
} => Some((
|
||||||
{
|
|
||||||
Some((
|
|
||||||
range,
|
range,
|
||||||
render_block,
|
render_block,
|
||||||
block_height,
|
block_height,
|
||||||
block_style,
|
block_style,
|
||||||
block_priority,
|
block_priority,
|
||||||
))
|
)),
|
||||||
} else {
|
_ => None,
|
||||||
None
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
block_map.insert(
|
block_map.insert(
|
||||||
blocks
|
blocks
|
||||||
@@ -954,10 +950,9 @@ impl DisplaySnapshot {
|
|||||||
for chunk in self.highlighted_chunks(range, false, editor_style) {
|
for chunk in self.highlighted_chunks(range, false, editor_style) {
|
||||||
line.push_str(chunk.text);
|
line.push_str(chunk.text);
|
||||||
|
|
||||||
let text_style = if let Some(style) = chunk.style {
|
let text_style = match chunk.style {
|
||||||
Cow::Owned(editor_style.text.clone().highlight(style))
|
Some(style) => Cow::Owned(editor_style.text.clone().highlight(style)),
|
||||||
} else {
|
_ => Cow::Borrowed(&editor_style.text),
|
||||||
Cow::Borrowed(&editor_style.text)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
runs.push(text_style.to_run(chunk.text.len()))
|
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>> {
|
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));
|
let start = MultiBufferPoint::new(buffer_row.0, self.buffer_snapshot.line_len(buffer_row));
|
||||||
if let Some(crease) = self
|
match self
|
||||||
.crease_snapshot
|
.crease_snapshot
|
||||||
.query_row(buffer_row, &self.buffer_snapshot)
|
.query_row(buffer_row, &self.buffer_snapshot)
|
||||||
{
|
{
|
||||||
match crease {
|
Some(crease) => match crease {
|
||||||
Crease::Inline {
|
Crease::Inline {
|
||||||
range,
|
range,
|
||||||
placeholder,
|
placeholder,
|
||||||
@@ -1219,8 +1214,9 @@ impl DisplaySnapshot {
|
|||||||
block_priority: *block_priority,
|
block_priority: *block_priority,
|
||||||
render_toggle: render_toggle.clone(),
|
render_toggle: render_toggle.clone(),
|
||||||
}),
|
}),
|
||||||
}
|
},
|
||||||
} else if self.starts_indent(MultiBufferRow(start.row))
|
_ => {
|
||||||
|
if self.starts_indent(MultiBufferRow(start.row))
|
||||||
&& !self.is_line_folded(MultiBufferRow(start.row))
|
&& !self.is_line_folded(MultiBufferRow(start.row))
|
||||||
{
|
{
|
||||||
let start_line_indent = self.line_indent_for_buffer_row(buffer_row);
|
let start_line_indent = self.line_indent_for_buffer_row(buffer_row);
|
||||||
@@ -1267,6 +1263,8 @@ impl DisplaySnapshot {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
pub fn text_highlight_ranges<Tag: ?Sized + 'static>(
|
pub fn text_highlight_ranges<Tag: ?Sized + 'static>(
|
||||||
@@ -1480,7 +1478,7 @@ pub mod tests {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let buffer = cx.update(|cx| {
|
let buffer = cx.update(|cx| {
|
||||||
if rng.gen() {
|
if rng.r#gen() {
|
||||||
let len = rng.gen_range(0..10);
|
let len = rng.gen_range(0..10);
|
||||||
let text = util::RandomCharIter::new(&mut rng)
|
let text = util::RandomCharIter::new(&mut rng)
|
||||||
.take(len)
|
.take(len)
|
||||||
@@ -1542,7 +1540,7 @@ pub mod tests {
|
|||||||
}
|
}
|
||||||
30..=44 => {
|
30..=44 => {
|
||||||
map.update(cx, |map, cx| {
|
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 buffer = map.snapshot(cx).buffer_snapshot;
|
||||||
let block_properties = (0..rng.gen_range(1..=1))
|
let block_properties = (0..rng.gen_range(1..=1))
|
||||||
.map(|_| {
|
.map(|_| {
|
||||||
@@ -1552,7 +1550,7 @@ pub mod tests {
|
|||||||
Bias::Left,
|
Bias::Left,
|
||||||
));
|
));
|
||||||
|
|
||||||
let placement = if rng.gen() {
|
let placement = if rng.r#gen() {
|
||||||
BlockPlacement::Above(position)
|
BlockPlacement::Above(position)
|
||||||
} else {
|
} else {
|
||||||
BlockPlacement::Below(position)
|
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);
|
log::info!("unfolding ranges: {:?}", ranges);
|
||||||
map.update(cx, |map, cx| {
|
map.update(cx, |map, cx| {
|
||||||
map.unfold_intersecting(ranges, true, cx);
|
map.unfold_intersecting(ranges, true, cx);
|
||||||
|
|||||||
@@ -1351,13 +1351,16 @@ impl BlockSnapshot {
|
|||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if let Some(block) = &transform.block {
|
match &transform.block {
|
||||||
|
Some(block) => {
|
||||||
cursor.next(&());
|
cursor.next(&());
|
||||||
return Some((start_row, block));
|
return Some((start_row, block));
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
cursor.next(&());
|
cursor.next(&());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
None
|
None
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1405,13 +1408,18 @@ impl BlockSnapshot {
|
|||||||
cursor.seek(&wrap_row, Bias::Left, &());
|
cursor.seek(&wrap_row, Bias::Left, &());
|
||||||
|
|
||||||
while let Some(transform) = cursor.item() {
|
while let Some(transform) = cursor.item() {
|
||||||
if let Some(block) = transform.block.as_ref() {
|
match transform.block.as_ref() {
|
||||||
|
Some(block) => {
|
||||||
if block.id() == block_id {
|
if block.id() == block_id {
|
||||||
return Some(block.clone());
|
return Some(block.clone());
|
||||||
}
|
}
|
||||||
} else if *cursor.start() > wrap_row {
|
}
|
||||||
|
_ => {
|
||||||
|
if *cursor.start() > wrap_row {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cursor.next(&());
|
cursor.next(&());
|
||||||
}
|
}
|
||||||
@@ -1482,7 +1490,8 @@ impl BlockSnapshot {
|
|||||||
pub(super) fn line_len(&self, row: BlockRow) -> u32 {
|
pub(super) fn line_len(&self, row: BlockRow) -> u32 {
|
||||||
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
|
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
|
||||||
cursor.seek(&BlockRow(row.0), Bias::Right, &());
|
cursor.seek(&BlockRow(row.0), Bias::Right, &());
|
||||||
if let Some(transform) = cursor.item() {
|
match cursor.item() {
|
||||||
|
Some(transform) => {
|
||||||
let (output_start, input_start) = cursor.start();
|
let (output_start, input_start) = cursor.start();
|
||||||
let overshoot = row.0 - output_start.0;
|
let overshoot = row.0 - output_start.0;
|
||||||
if transform.block.is_some() {
|
if transform.block.is_some() {
|
||||||
@@ -1490,12 +1499,16 @@ impl BlockSnapshot {
|
|||||||
} else {
|
} else {
|
||||||
self.wrap_snapshot.line_len(input_start.0 + overshoot)
|
self.wrap_snapshot.line_len(input_start.0 + overshoot)
|
||||||
}
|
}
|
||||||
} else if row.0 == 0 {
|
}
|
||||||
|
_ => {
|
||||||
|
if row.0 == 0 {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
panic!("row out of range");
|
panic!("row out of range");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn is_block_line(&self, row: BlockRow) -> bool {
|
pub(super) fn is_block_line(&self, row: BlockRow) -> bool {
|
||||||
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
|
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
|
||||||
@@ -1536,7 +1549,8 @@ impl BlockSnapshot {
|
|||||||
let mut reversed = false;
|
let mut reversed = false;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if let Some(transform) = cursor.item() {
|
match cursor.item() {
|
||||||
|
Some(transform) => {
|
||||||
let (output_start_row, input_start_row) = cursor.start();
|
let (output_start_row, input_start_row) = cursor.start();
|
||||||
let (output_end_row, input_end_row) = cursor.end(&());
|
let (output_end_row, input_end_row) = cursor.end(&());
|
||||||
let output_start = Point::new(output_start_row.0, 0);
|
let output_start = Point::new(output_start_row.0, 0);
|
||||||
@@ -1576,7 +1590,9 @@ impl BlockSnapshot {
|
|||||||
} else {
|
} else {
|
||||||
cursor.next(&());
|
cursor.next(&());
|
||||||
}
|
}
|
||||||
} else if reversed {
|
}
|
||||||
|
_ => {
|
||||||
|
if reversed {
|
||||||
return self.max_point();
|
return self.max_point();
|
||||||
} else {
|
} else {
|
||||||
reversed = true;
|
reversed = true;
|
||||||
@@ -1585,11 +1601,14 @@ impl BlockSnapshot {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn to_block_point(&self, wrap_point: WrapPoint) -> BlockPoint {
|
pub fn to_block_point(&self, wrap_point: WrapPoint) -> BlockPoint {
|
||||||
let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>(&());
|
let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>(&());
|
||||||
cursor.seek(&WrapRow(wrap_point.row()), Bias::Right, &());
|
cursor.seek(&WrapRow(wrap_point.row()), Bias::Right, &());
|
||||||
if let Some(transform) = cursor.item() {
|
match cursor.item() {
|
||||||
|
Some(transform) => {
|
||||||
if transform.block.is_some() {
|
if transform.block.is_some() {
|
||||||
BlockPoint::new(cursor.start().1 .0, 0)
|
BlockPoint::new(cursor.start().1 .0, 0)
|
||||||
} else {
|
} else {
|
||||||
@@ -1599,16 +1618,16 @@ impl BlockSnapshot {
|
|||||||
let input_overshoot = wrap_point.0 - input_start;
|
let input_overshoot = wrap_point.0 - input_start;
|
||||||
BlockPoint(output_start + input_overshoot)
|
BlockPoint(output_start + input_overshoot)
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
self.max_point()
|
_ => self.max_point(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_wrap_point(&self, block_point: BlockPoint, bias: Bias) -> WrapPoint {
|
pub fn to_wrap_point(&self, block_point: BlockPoint, bias: Bias) -> WrapPoint {
|
||||||
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
|
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
|
||||||
cursor.seek(&BlockRow(block_point.row), Bias::Right, &());
|
cursor.seek(&BlockRow(block_point.row), Bias::Right, &());
|
||||||
if let Some(transform) = cursor.item() {
|
match cursor.item() {
|
||||||
match transform.block.as_ref() {
|
Some(transform) => match transform.block.as_ref() {
|
||||||
Some(block) => {
|
Some(block) => {
|
||||||
if block.place_below() {
|
if block.place_below() {
|
||||||
let wrap_row = cursor.start().1 .0 - 1;
|
let wrap_row = cursor.start().1 .0 - 1;
|
||||||
@@ -1627,9 +1646,8 @@ impl BlockSnapshot {
|
|||||||
let wrap_row = cursor.start().1 .0 + overshoot;
|
let wrap_row = cursor.start().1 .0 + overshoot;
|
||||||
WrapPoint::new(wrap_row, block_point.column)
|
WrapPoint::new(wrap_row, block_point.column)
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
} else {
|
_ => self.wrap_snapshot.max_point(),
|
||||||
self.wrap_snapshot.max_point()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1702,9 +1720,11 @@ impl<'a> Iterator for BlockChunks<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if self.input_chunk.text.is_empty() {
|
if self.input_chunk.text.is_empty() {
|
||||||
if let Some(input_chunk) = self.input_chunks.next() {
|
match self.input_chunks.next() {
|
||||||
|
Some(input_chunk) => {
|
||||||
self.input_chunk = input_chunk;
|
self.input_chunk = input_chunk;
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
if self.output_row < self.max_output_row {
|
if self.output_row < self.max_output_row {
|
||||||
self.output_row += 1;
|
self.output_row += 1;
|
||||||
self.advance();
|
self.advance();
|
||||||
@@ -1718,6 +1738,7 @@ impl<'a> Iterator for BlockChunks<'a> {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let transform_end = self.transforms.end(&()).0 .0;
|
let transform_end = self.transforms.end(&()).0 .0;
|
||||||
let (prefix_rows, prefix_bytes) =
|
let (prefix_rows, prefix_bytes) =
|
||||||
@@ -1783,7 +1804,8 @@ impl Iterator for BlockRows<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let transform = self.transforms.item()?;
|
let transform = self.transforms.item()?;
|
||||||
if let Some(block) = transform.block.as_ref() {
|
match transform.block.as_ref() {
|
||||||
|
Some(block) => {
|
||||||
if block.is_replacement() && self.transforms.start().0 == self.output_row {
|
if block.is_replacement() && self.transforms.start().0 == self.output_row {
|
||||||
if matches!(block, Block::FoldedBuffer { .. }) {
|
if matches!(block, Block::FoldedBuffer { .. }) {
|
||||||
Some(RowInfo::default())
|
Some(RowInfo::default())
|
||||||
@@ -1793,8 +1815,8 @@ impl Iterator for BlockRows<'_> {
|
|||||||
} else {
|
} else {
|
||||||
Some(RowInfo::default())
|
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!("Wrap width: {:?}", wrap_width);
|
||||||
log::info!("Excerpt Header Height: {:?}", excerpt_header_height);
|
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 buffer = if is_singleton {
|
||||||
let len = rng.gen_range(0..10);
|
let len = rng.gen_range(0..10);
|
||||||
let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
|
let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
|
||||||
|
|||||||
@@ -602,12 +602,14 @@ impl FoldSnapshot {
|
|||||||
if let Some(transform) = cursor.item() {
|
if let Some(transform) = cursor.item() {
|
||||||
let start_in_transform = range.start.0 - cursor.start().0 .0;
|
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;
|
let end_in_transform = cmp::min(range.end, cursor.end(&()).0).0 - cursor.start().0 .0;
|
||||||
if let Some(placeholder) = transform.placeholder.as_ref() {
|
match transform.placeholder.as_ref() {
|
||||||
|
Some(placeholder) => {
|
||||||
summary = TextSummary::from(
|
summary = TextSummary::from(
|
||||||
&placeholder.text
|
&placeholder.text
|
||||||
[start_in_transform.column as usize..end_in_transform.column as usize],
|
[start_in_transform.column as usize..end_in_transform.column as usize],
|
||||||
);
|
);
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
let inlay_start = self
|
let inlay_start = self
|
||||||
.inlay_snapshot
|
.inlay_snapshot
|
||||||
.to_offset(InlayPoint(cursor.start().1 .0 + start_in_transform));
|
.to_offset(InlayPoint(cursor.start().1 .0 + start_in_transform));
|
||||||
@@ -619,6 +621,7 @@ impl FoldSnapshot {
|
|||||||
.text_summary_for_range(inlay_start..inlay_end);
|
.text_summary_for_range(inlay_start..inlay_end);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if range.end > cursor.end(&()).0 {
|
if range.end > cursor.end(&()).0 {
|
||||||
cursor.next(&());
|
cursor.next(&());
|
||||||
@@ -627,10 +630,13 @@ impl FoldSnapshot {
|
|||||||
.output;
|
.output;
|
||||||
if let Some(transform) = cursor.item() {
|
if let Some(transform) = cursor.item() {
|
||||||
let end_in_transform = range.end.0 - cursor.start().0 .0;
|
let end_in_transform = range.end.0 - cursor.start().0 .0;
|
||||||
if let Some(placeholder) = transform.placeholder.as_ref() {
|
match transform.placeholder.as_ref() {
|
||||||
summary +=
|
Some(placeholder) => {
|
||||||
TextSummary::from(&placeholder.text[..end_in_transform.column as usize]);
|
summary += TextSummary::from(
|
||||||
} else {
|
&placeholder.text[..end_in_transform.column as usize],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
let inlay_start = self.inlay_snapshot.to_offset(cursor.start().1);
|
let inlay_start = self.inlay_snapshot.to_offset(cursor.start().1);
|
||||||
let inlay_end = self
|
let inlay_end = self
|
||||||
.inlay_snapshot
|
.inlay_snapshot
|
||||||
@@ -641,6 +647,7 @@ impl FoldSnapshot {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
summary
|
summary
|
||||||
}
|
}
|
||||||
@@ -822,7 +829,8 @@ impl FoldSnapshot {
|
|||||||
pub fn clip_point(&self, point: FoldPoint, bias: Bias) -> FoldPoint {
|
pub fn clip_point(&self, point: FoldPoint, bias: Bias) -> FoldPoint {
|
||||||
let mut cursor = self.transforms.cursor::<(FoldPoint, InlayPoint)>(&());
|
let mut cursor = self.transforms.cursor::<(FoldPoint, InlayPoint)>(&());
|
||||||
cursor.seek(&point, Bias::Right, &());
|
cursor.seek(&point, Bias::Right, &());
|
||||||
if let Some(transform) = cursor.item() {
|
match cursor.item() {
|
||||||
|
Some(transform) => {
|
||||||
let transform_start = cursor.start().0 .0;
|
let transform_start = cursor.start().0 .0;
|
||||||
if transform.placeholder.is_some() {
|
if transform.placeholder.is_some() {
|
||||||
if point.0 == transform_start || matches!(bias, Bias::Left) {
|
if point.0 == transform_start || matches!(bias, Bias::Left) {
|
||||||
@@ -836,8 +844,8 @@ impl FoldSnapshot {
|
|||||||
let clipped_inlay_point = self.inlay_snapshot.clip_point(inlay_point, bias);
|
let clipped_inlay_point = self.inlay_snapshot.clip_point(inlay_point, bias);
|
||||||
FoldPoint(cursor.start().0 .0 + (clipped_inlay_point - cursor.start().1).0)
|
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 len = rng.gen_range(0..10);
|
||||||
let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
|
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)
|
MultiBuffer::build_simple(&text, cx)
|
||||||
} else {
|
} else {
|
||||||
MultiBuffer::build_random(&mut rng, cx)
|
MultiBuffer::build_random(&mut rng, cx)
|
||||||
@@ -1962,7 +1970,7 @@ mod tests {
|
|||||||
let start = buffer.clip_offset(rng.gen_range(0..=end), Left);
|
let start = buffer.clip_offset(rng.gen_range(0..=end), Left);
|
||||||
to_unfold.push(start..end);
|
to_unfold.push(start..end);
|
||||||
}
|
}
|
||||||
let inclusive = rng.gen();
|
let inclusive = rng.r#gen();
|
||||||
log::info!("unfolding {:?} (inclusive: {})", to_unfold, inclusive);
|
log::info!("unfolding {:?} (inclusive: {})", to_unfold, inclusive);
|
||||||
let (mut writer, snapshot, edits) = self.write(inlay_snapshot, vec![]);
|
let (mut writer, snapshot, edits) = self.write(inlay_snapshot, vec![]);
|
||||||
snapshot_edits.push((snapshot, edits));
|
snapshot_edits.push((snapshot, edits));
|
||||||
|
|||||||
@@ -610,9 +610,9 @@ impl InlayMap {
|
|||||||
let mut to_insert = Vec::new();
|
let mut to_insert = Vec::new();
|
||||||
let snapshot = &mut self.snapshot;
|
let snapshot = &mut self.snapshot;
|
||||||
for i in 0..rng.gen_range(1..=5) {
|
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 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) {
|
let len = if rng.gen_bool(0.01) {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
@@ -809,7 +809,8 @@ impl InlaySnapshot {
|
|||||||
match cursor.item() {
|
match cursor.item() {
|
||||||
Some(Transform::Isomorphic(transform)) => {
|
Some(Transform::Isomorphic(transform)) => {
|
||||||
if cursor.start().0 == point {
|
if cursor.start().0 == point {
|
||||||
if let Some(Transform::Inlay(inlay)) = cursor.prev_item() {
|
match cursor.prev_item() {
|
||||||
|
Some(Transform::Inlay(inlay)) => {
|
||||||
if inlay.position.bias() == Bias::Left {
|
if inlay.position.bias() == Bias::Left {
|
||||||
return point;
|
return point;
|
||||||
} else if bias == Bias::Left {
|
} else if bias == Bias::Left {
|
||||||
@@ -819,11 +820,14 @@ impl InlaySnapshot {
|
|||||||
} else {
|
} else {
|
||||||
point.0 += Point::new(0, 1);
|
point.0 += Point::new(0, 1);
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
return point;
|
return point;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else if cursor.end(&()).0 == point {
|
} else if cursor.end(&()).0 == point {
|
||||||
if let Some(Transform::Inlay(inlay)) = cursor.next_item() {
|
match cursor.next_item() {
|
||||||
|
Some(Transform::Inlay(inlay)) => {
|
||||||
if inlay.position.bias() == Bias::Right {
|
if inlay.position.bias() == Bias::Right {
|
||||||
return point;
|
return point;
|
||||||
} else if bias == Bias::Right {
|
} else if bias == Bias::Right {
|
||||||
@@ -834,9 +838,11 @@ impl InlaySnapshot {
|
|||||||
} else {
|
} else {
|
||||||
point.0.column -= 1;
|
point.0.column -= 1;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
return point;
|
return point;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let overshoot = point.0 - cursor.start().0 .0;
|
let overshoot = point.0 - cursor.start().0 .0;
|
||||||
let buffer_point = cursor.start().1 + overshoot;
|
let buffer_point = cursor.start().1 + overshoot;
|
||||||
@@ -1500,7 +1506,7 @@ mod tests {
|
|||||||
.unwrap_or(10);
|
.unwrap_or(10);
|
||||||
|
|
||||||
let len = rng.gen_range(0..30);
|
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)
|
let text = util::RandomCharIter::new(&mut rng)
|
||||||
.take(len)
|
.take(len)
|
||||||
.collect::<String>();
|
.collect::<String>();
|
||||||
@@ -1709,7 +1715,8 @@ mod tests {
|
|||||||
buffer_point
|
buffer_point
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(ch) = buffer_chars.next() {
|
match buffer_chars.next() {
|
||||||
|
Some(ch) => {
|
||||||
if ch == '\n' {
|
if ch == '\n' {
|
||||||
buffer_point += Point::new(1, 0);
|
buffer_point += Point::new(1, 0);
|
||||||
} else {
|
} else {
|
||||||
@@ -1720,10 +1727,12 @@ mod tests {
|
|||||||
let new_inlay_point = inlay_snapshot.to_inlay_point(buffer_point);
|
let new_inlay_point = inlay_snapshot.to_inlay_point(buffer_point);
|
||||||
assert!(new_inlay_point > inlay_point);
|
assert!(new_inlay_point > inlay_point);
|
||||||
inlay_point = new_inlay_point;
|
inlay_point = new_inlay_point;
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut inlay_point = InlayPoint::default();
|
let mut inlay_point = InlayPoint::default();
|
||||||
let mut inlay_offset = InlayOffset::default();
|
let mut inlay_offset = InlayOffset::default();
|
||||||
|
|||||||
@@ -534,17 +534,20 @@ impl<'a> Iterator for TabChunks<'a> {
|
|||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
if self.chunk.text.is_empty() {
|
if self.chunk.text.is_empty() {
|
||||||
if let Some(chunk) = self.fold_chunks.next() {
|
match self.fold_chunks.next() {
|
||||||
|
Some(chunk) => {
|
||||||
self.chunk = chunk;
|
self.chunk = chunk;
|
||||||
if self.inside_leading_tab {
|
if self.inside_leading_tab {
|
||||||
self.chunk.text = &self.chunk.text[1..];
|
self.chunk.text = &self.chunk.text[1..];
|
||||||
self.inside_leading_tab = false;
|
self.inside_leading_tab = false;
|
||||||
self.input_column += 1;
|
self.input_column += 1;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (ix, c) in self.chunk.text.char_indices() {
|
for (ix, c) in self.chunk.text.char_indices() {
|
||||||
match c {
|
match c {
|
||||||
@@ -738,7 +741,7 @@ mod tests {
|
|||||||
fn test_random_tabs(cx: &mut gpui::App, mut rng: StdRng) {
|
fn test_random_tabs(cx: &mut gpui::App, mut rng: StdRng) {
|
||||||
let tab_size = NonZeroU32::new(rng.gen_range(1..=4)).unwrap();
|
let tab_size = NonZeroU32::new(rng.gen_range(1..=4)).unwrap();
|
||||||
let len = rng.gen_range(0..30);
|
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)
|
let text = util::RandomCharIter::new(&mut rng)
|
||||||
.take(len)
|
.take(len)
|
||||||
.collect::<String>();
|
.collect::<String>();
|
||||||
|
|||||||
@@ -1207,7 +1207,7 @@ mod tests {
|
|||||||
log::info!("Wrap width: {:?}", wrap_width);
|
log::info!("Wrap width: {:?}", wrap_width);
|
||||||
|
|
||||||
let buffer = cx.update(|cx| {
|
let buffer = cx.update(|cx| {
|
||||||
if rng.gen() {
|
if rng.r#gen() {
|
||||||
MultiBuffer::build_random(&mut rng, cx)
|
MultiBuffer::build_random(&mut rng, cx)
|
||||||
} else {
|
} else {
|
||||||
let len = rng.gen_range(0..10);
|
let len = rng.gen_range(0..10);
|
||||||
|
|||||||
@@ -1700,7 +1700,8 @@ impl Editor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(singleton_buffer) = self.buffer.read(cx).as_singleton() {
|
match self.buffer.read(cx).as_singleton() {
|
||||||
|
Some(singleton_buffer) => {
|
||||||
if let Some(extension) = singleton_buffer
|
if let Some(extension) = singleton_buffer
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.file()
|
.file()
|
||||||
@@ -1708,9 +1709,11 @@ impl Editor {
|
|||||||
{
|
{
|
||||||
key_context.set("extension", extension.to_string());
|
key_context.set("extension", extension.to_string());
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
key_context.add("multibuffer");
|
key_context.add("multibuffer");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if has_active_edit_prediction {
|
if has_active_edit_prediction {
|
||||||
if self.edit_prediction_in_conflict() {
|
if self.edit_prediction_in_conflict() {
|
||||||
@@ -1901,14 +1904,13 @@ impl Editor {
|
|||||||
pub fn snapshot(&self, window: &mut Window, cx: &mut App) -> EditorSnapshot {
|
pub fn snapshot(&self, window: &mut Window, cx: &mut App) -> EditorSnapshot {
|
||||||
let git_blame_gutter_max_author_length = self
|
let git_blame_gutter_max_author_length = self
|
||||||
.render_git_blame_gutter(cx)
|
.render_git_blame_gutter(cx)
|
||||||
.then(|| {
|
.then(|| match self.blame.as_ref() {
|
||||||
if let Some(blame) = self.blame.as_ref() {
|
Some(blame) => {
|
||||||
let max_author_length =
|
let max_author_length =
|
||||||
blame.update(cx, |blame, cx| blame.max_author_length(cx));
|
blame.update(cx, |blame, cx| blame.max_author_length(cx));
|
||||||
Some(max_author_length)
|
Some(max_author_length)
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
_ => None,
|
||||||
})
|
})
|
||||||
.flatten();
|
.flatten();
|
||||||
|
|
||||||
@@ -2791,7 +2793,9 @@ impl Editor {
|
|||||||
if let Some(tail) = self.columnar_selection_tail.as_ref() {
|
if let Some(tail) = self.columnar_selection_tail.as_ref() {
|
||||||
let tail = tail.to_display_point(&display_map);
|
let tail = tail.to_display_point(&display_map);
|
||||||
self.select_columns(tail, position, goal_column, &display_map, window, cx);
|
self.select_columns(tail, position, goal_column, &display_map, window, cx);
|
||||||
} else if let Some(mut pending) = self.selections.pending_anchor() {
|
} else {
|
||||||
|
match self.selections.pending_anchor() {
|
||||||
|
Some(mut pending) => {
|
||||||
let buffer = self.buffer.read(cx).snapshot(cx);
|
let buffer = self.buffer.read(cx).snapshot(cx);
|
||||||
let head;
|
let head;
|
||||||
let tail;
|
let tail;
|
||||||
@@ -2802,9 +2806,11 @@ impl Editor {
|
|||||||
tail = pending.tail().to_point(&buffer);
|
tail = pending.tail().to_point(&buffer);
|
||||||
}
|
}
|
||||||
SelectMode::Word(original_range) => {
|
SelectMode::Word(original_range) => {
|
||||||
let original_display_range = original_range.start.to_display_point(&display_map)
|
let original_display_range =
|
||||||
|
original_range.start.to_display_point(&display_map)
|
||||||
..original_range.end.to_display_point(&display_map);
|
..original_range.end.to_display_point(&display_map);
|
||||||
let original_buffer_range = original_display_range.start.to_point(&display_map)
|
let original_buffer_range =
|
||||||
|
original_display_range.start.to_point(&display_map)
|
||||||
..original_display_range.end.to_point(&display_map);
|
..original_display_range.end.to_point(&display_map);
|
||||||
if movement::is_inside_word(&display_map, position)
|
if movement::is_inside_word(&display_map, position)
|
||||||
|| original_display_range.contains(&position)
|
|| original_display_range.contains(&position)
|
||||||
@@ -2826,7 +2832,8 @@ impl Editor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
SelectMode::Line(original_range) => {
|
SelectMode::Line(original_range) => {
|
||||||
let original_range = original_range.to_point(&display_map.buffer_snapshot);
|
let original_range =
|
||||||
|
original_range.to_point(&display_map.buffer_snapshot);
|
||||||
|
|
||||||
let position = display_map
|
let position = display_map
|
||||||
.clip_point(position, Bias::Left)
|
.clip_point(position, Bias::Left)
|
||||||
@@ -2867,10 +2874,13 @@ impl Editor {
|
|||||||
self.change_selections(None, window, cx, |s| {
|
self.change_selections(None, window, cx, |s| {
|
||||||
s.set_pending(pending, mode);
|
s.set_pending(pending, mode);
|
||||||
});
|
});
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
log::error!("update_selection dispatched with no pending selection");
|
log::error!("update_selection dispatched with no pending selection");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.apply_scroll_delta(scroll_delta, window, cx);
|
self.apply_scroll_delta(scroll_delta, window, cx);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
@@ -3492,9 +3502,8 @@ impl Editor {
|
|||||||
let end = selection.end;
|
let end = selection.end;
|
||||||
let selection_is_empty = start == end;
|
let selection_is_empty = start == end;
|
||||||
let language_scope = buffer.language_scope_at(start);
|
let language_scope = buffer.language_scope_at(start);
|
||||||
let (comment_delimiter, insert_extra_newline) = if let Some(language) =
|
let (comment_delimiter, insert_extra_newline) = match &language_scope {
|
||||||
&language_scope
|
Some(language) => {
|
||||||
{
|
|
||||||
let insert_extra_newline =
|
let insert_extra_newline =
|
||||||
insert_extra_newline_brackets(&buffer, start..end, language)
|
insert_extra_newline_brackets(&buffer, start..end, language)
|
||||||
|| insert_extra_newline_tree_sitter(&buffer, start..end);
|
|| insert_extra_newline_tree_sitter(&buffer, start..end);
|
||||||
@@ -3505,15 +3514,16 @@ impl Editor {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !multi_buffer.language_settings(cx).extend_comment_on_newline {
|
if !multi_buffer.language_settings(cx).extend_comment_on_newline
|
||||||
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let delimiters = language.line_comment_prefixes();
|
let delimiters = language.line_comment_prefixes();
|
||||||
let max_len_of_delimiter =
|
let max_len_of_delimiter =
|
||||||
delimiters.iter().map(|delimiter| delimiter.len()).max()?;
|
delimiters.iter().map(|delimiter| delimiter.len()).max()?;
|
||||||
let (snapshot, range) =
|
let (snapshot, range) = buffer
|
||||||
buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
|
.buffer_line_for_row(MultiBufferRow(start_point.row))?;
|
||||||
|
|
||||||
let mut index_of_first_non_whitespace = 0;
|
let mut index_of_first_non_whitespace = 0;
|
||||||
let comment_candidate = snapshot
|
let comment_candidate = snapshot
|
||||||
@@ -3527,7 +3537,8 @@ impl Editor {
|
|||||||
})
|
})
|
||||||
.take(max_len_of_delimiter)
|
.take(max_len_of_delimiter)
|
||||||
.collect::<String>();
|
.collect::<String>();
|
||||||
let comment_prefix = delimiters.iter().find(|comment_prefix| {
|
let comment_prefix =
|
||||||
|
delimiters.iter().find(|comment_prefix| {
|
||||||
comment_candidate.starts_with(comment_prefix.as_ref())
|
comment_candidate.starts_with(comment_prefix.as_ref())
|
||||||
})?;
|
})?;
|
||||||
let cursor_is_placed_after_comment_marker =
|
let cursor_is_placed_after_comment_marker =
|
||||||
@@ -3540,8 +3551,8 @@ impl Editor {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
(comment_delimiter, insert_extra_newline)
|
(comment_delimiter, insert_extra_newline)
|
||||||
} else {
|
}
|
||||||
(None, false)
|
_ => (None, false),
|
||||||
};
|
};
|
||||||
|
|
||||||
let capacity_for_delimiter = comment_delimiter
|
let capacity_for_delimiter = comment_delimiter
|
||||||
@@ -3810,16 +3821,15 @@ impl Editor {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(completion_provider) = &self.completion_provider {
|
match &self.completion_provider {
|
||||||
completion_provider.is_completion_trigger(
|
Some(completion_provider) => completion_provider.is_completion_trigger(
|
||||||
&buffer,
|
&buffer,
|
||||||
position.text_anchor,
|
position.text_anchor,
|
||||||
text,
|
text,
|
||||||
trigger_in_words,
|
trigger_in_words,
|
||||||
cx,
|
cx,
|
||||||
)
|
),
|
||||||
} else {
|
_ => false,
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4268,10 +4278,11 @@ impl Editor {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let (buffer, buffer_position) =
|
let (buffer, buffer_position) =
|
||||||
if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
|
match self.buffer.read(cx).text_anchor_for_position(position, cx) {
|
||||||
output
|
Some(output) => output,
|
||||||
} else {
|
_ => {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let buffer_snapshot = buffer.read(cx).snapshot();
|
let buffer_snapshot = buffer.read(cx).snapshot();
|
||||||
let show_completion_documentation = buffer_snapshot
|
let show_completion_documentation = buffer_snapshot
|
||||||
@@ -4499,11 +4510,12 @@ impl Editor {
|
|||||||
#[cfg(feature = "test-support")]
|
#[cfg(feature = "test-support")]
|
||||||
pub fn current_completions(&self) -> Option<Vec<project::Completion>> {
|
pub fn current_completions(&self) -> Option<Vec<project::Completion>> {
|
||||||
let menu = self.context_menu.borrow();
|
let menu = self.context_menu.borrow();
|
||||||
if let CodeContextMenu::Completions(menu) = menu.as_ref()? {
|
match menu.as_ref()? {
|
||||||
|
CodeContextMenu::Completions(menu) => {
|
||||||
let completions = menu.completions.borrow();
|
let completions = menu.completions.borrow();
|
||||||
Some(completions.to_vec())
|
Some(completions.to_vec())
|
||||||
} else {
|
}
|
||||||
None
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4536,11 +4548,11 @@ impl Editor {
|
|||||||
) -> Option<Task<Result<()>>> {
|
) -> Option<Task<Result<()>>> {
|
||||||
use language::ToOffset as _;
|
use language::ToOffset as _;
|
||||||
|
|
||||||
let completions_menu =
|
let completions_menu = match self.hide_context_menu(window, cx)? {
|
||||||
if let CodeContextMenu::Completions(menu) = self.hide_context_menu(window, cx)? {
|
CodeContextMenu::Completions(menu) => menu,
|
||||||
menu
|
_ => {
|
||||||
} else {
|
|
||||||
return None;
|
return None;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let candidate_id = {
|
let candidate_id = {
|
||||||
@@ -4650,7 +4662,8 @@ impl Editor {
|
|||||||
});
|
});
|
||||||
|
|
||||||
self.transact(window, cx, |this, window, cx| {
|
self.transact(window, cx, |this, window, cx| {
|
||||||
if let Some(mut snippet) = snippet {
|
match snippet {
|
||||||
|
Some(mut snippet) => {
|
||||||
snippet.text = text.to_string();
|
snippet.text = text.to_string();
|
||||||
for tabstop in snippet
|
for tabstop in snippet
|
||||||
.tabstops
|
.tabstops
|
||||||
@@ -4662,12 +4675,14 @@ impl Editor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.insert_snippet(&ranges, snippet, window, cx).log_err();
|
this.insert_snippet(&ranges, snippet, window, cx).log_err();
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
this.buffer.update(cx, |buffer, cx| {
|
this.buffer.update(cx, |buffer, cx| {
|
||||||
let edits = ranges.iter().map(|range| (range.clone(), text));
|
let edits = ranges.iter().map(|range| (range.clone(), text));
|
||||||
buffer.edit(edits, this.autoindent_mode.clone(), cx);
|
buffer.edit(edits, this.autoindent_mode.clone(), cx);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
for (buffer, edits) in linked_edits {
|
for (buffer, edits) in linked_edits {
|
||||||
buffer.update(cx, |buffer, cx| {
|
buffer.update(cx, |buffer, cx| {
|
||||||
let snapshot = buffer.snapshot();
|
let snapshot = buffer.snapshot();
|
||||||
@@ -4818,7 +4833,7 @@ impl Editor {
|
|||||||
&& code_actions
|
&& code_actions
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or(true, |actions| actions.is_empty());
|
.map_or(true, |actions| actions.is_empty());
|
||||||
if let Ok(task) = editor.update_in(cx, |editor, window, cx| {
|
match editor.update_in(cx, |editor, window, cx| {
|
||||||
*editor.context_menu.borrow_mut() =
|
*editor.context_menu.borrow_mut() =
|
||||||
Some(CodeContextMenu::CodeActions(CodeActionsMenu {
|
Some(CodeContextMenu::CodeActions(CodeActionsMenu {
|
||||||
buffer,
|
buffer,
|
||||||
@@ -4843,9 +4858,8 @@ impl Editor {
|
|||||||
cx.notify();
|
cx.notify();
|
||||||
Task::ready(Ok(()))
|
Task::ready(Ok(()))
|
||||||
}) {
|
}) {
|
||||||
task.await
|
Ok(task) => task.await,
|
||||||
} else {
|
_ => Ok(()),
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
@@ -4869,11 +4883,11 @@ impl Editor {
|
|||||||
) -> Option<Task<Result<()>>> {
|
) -> Option<Task<Result<()>>> {
|
||||||
self.hide_mouse_cursor(&HideMouseCursorOrigin::TypingAction);
|
self.hide_mouse_cursor(&HideMouseCursorOrigin::TypingAction);
|
||||||
|
|
||||||
let actions_menu =
|
let actions_menu = match self.hide_context_menu(window, cx)? {
|
||||||
if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
|
CodeContextMenu::CodeActions(menu) => menu,
|
||||||
menu
|
_ => {
|
||||||
} else {
|
|
||||||
return None;
|
return None;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
|
let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
|
||||||
@@ -4911,7 +4925,8 @@ impl Editor {
|
|||||||
return Some(Task::ready(Ok(())));
|
return Some(Task::ready(Ok(())));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(project) = self.project.as_ref() {
|
match self.project.as_ref() {
|
||||||
|
Some(project) => {
|
||||||
project
|
project
|
||||||
.update(cx, |project, cx| {
|
.update(cx, |project, cx| {
|
||||||
project.start_debug_session(
|
project.start_debug_session(
|
||||||
@@ -4921,8 +4936,8 @@ impl Editor {
|
|||||||
})
|
})
|
||||||
.detach_and_log_err(cx);
|
.detach_and_log_err(cx);
|
||||||
Some(Task::ready(Ok(())))
|
Some(Task::ready(Ok(())))
|
||||||
} else {
|
}
|
||||||
Some(Task::ready(Ok(())))
|
_ => Some(Task::ready(Ok(()))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4967,7 +4982,8 @@ impl Editor {
|
|||||||
// If the project transaction's edits are all contained within this editor, then
|
// If the project transaction's edits are all contained within this editor, then
|
||||||
// avoid opening a new editor to display them.
|
// avoid opening a new editor to display them.
|
||||||
|
|
||||||
if let Some((buffer, transaction)) = entries.first() {
|
match entries.first() {
|
||||||
|
Some((buffer, transaction)) => {
|
||||||
if entries.len() == 1 {
|
if entries.len() == 1 {
|
||||||
let excerpt = this.update(cx, |editor, cx| {
|
let excerpt = this.update(cx, |editor, cx| {
|
||||||
editor
|
editor
|
||||||
@@ -4993,9 +5009,11 @@ impl Editor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut ranges_to_highlight = Vec::new();
|
let mut ranges_to_highlight = Vec::new();
|
||||||
let excerpt_buffer = cx.new(|cx| {
|
let excerpt_buffer = cx.new(|cx| {
|
||||||
@@ -5166,16 +5184,15 @@ impl Editor {
|
|||||||
.timer(Duration::from_millis(debounce))
|
.timer(Duration::from_millis(debounce))
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let highlights = if let Some(highlights) = cx
|
let highlights = match cx
|
||||||
.update(|cx| {
|
.update(|cx| {
|
||||||
provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
|
provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
|
||||||
})
|
})
|
||||||
.ok()
|
.ok()
|
||||||
.flatten()
|
.flatten()
|
||||||
{
|
{
|
||||||
highlights.await.log_err()
|
Some(highlights) => highlights.await.log_err(),
|
||||||
} else {
|
_ => None,
|
||||||
None
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(highlights) = highlights {
|
if let Some(highlights) = highlights {
|
||||||
@@ -5489,12 +5506,11 @@ impl Editor {
|
|||||||
|
|
||||||
pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
|
pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
|
||||||
let cursor = self.selections.newest_anchor().head();
|
let cursor = self.selections.newest_anchor().head();
|
||||||
if let Some((buffer, cursor_position)) =
|
match self.buffer.read(cx).text_anchor_for_position(cursor, cx) {
|
||||||
self.buffer.read(cx).text_anchor_for_position(cursor, cx)
|
Some((buffer, cursor_position)) => {
|
||||||
{
|
|
||||||
self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
|
self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
|
||||||
} else {
|
}
|
||||||
false
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -9930,13 +9946,13 @@ impl Editor {
|
|||||||
self.hide_mouse_cursor(&HideMouseCursorOrigin::TypingAction);
|
self.hide_mouse_cursor(&HideMouseCursorOrigin::TypingAction);
|
||||||
|
|
||||||
if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
|
if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
|
||||||
if let Some((selections, _)) =
|
match self.selection_history.transaction(transaction_id).cloned() {
|
||||||
self.selection_history.transaction(transaction_id).cloned()
|
Some((selections, _)) => {
|
||||||
{
|
|
||||||
self.change_selections(None, window, cx, |s| {
|
self.change_selections(None, window, cx, |s| {
|
||||||
s.select_anchors(selections.to_vec());
|
s.select_anchors(selections.to_vec());
|
||||||
});
|
});
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
log::error!(
|
log::error!(
|
||||||
"No entry in selection_history found for undo. \
|
"No entry in selection_history found for undo. \
|
||||||
This may correspond to a bug where undo does not update the selection. \
|
This may correspond to a bug where undo does not update the selection. \
|
||||||
@@ -9944,6 +9960,7 @@ impl Editor {
|
|||||||
https://github.com/zed-industries/zed/issues/22692"
|
https://github.com/zed-industries/zed/issues/22692"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
self.request_autoscroll(Autoscroll::fit(), cx);
|
self.request_autoscroll(Autoscroll::fit(), cx);
|
||||||
self.unmark_text(window, cx);
|
self.unmark_text(window, cx);
|
||||||
self.refresh_inline_completion(true, false, window, cx);
|
self.refresh_inline_completion(true, false, window, cx);
|
||||||
@@ -9960,13 +9977,13 @@ impl Editor {
|
|||||||
self.hide_mouse_cursor(&HideMouseCursorOrigin::TypingAction);
|
self.hide_mouse_cursor(&HideMouseCursorOrigin::TypingAction);
|
||||||
|
|
||||||
if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
|
if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
|
||||||
if let Some((_, Some(selections))) =
|
match self.selection_history.transaction(transaction_id).cloned() {
|
||||||
self.selection_history.transaction(transaction_id).cloned()
|
Some((_, Some(selections))) => {
|
||||||
{
|
|
||||||
self.change_selections(None, window, cx, |s| {
|
self.change_selections(None, window, cx, |s| {
|
||||||
s.select_anchors(selections.to_vec());
|
s.select_anchors(selections.to_vec());
|
||||||
});
|
});
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
log::error!(
|
log::error!(
|
||||||
"No entry in selection_history found for redo. \
|
"No entry in selection_history found for redo. \
|
||||||
This may correspond to a bug where undo does not update the selection. \
|
This may correspond to a bug where undo does not update the selection. \
|
||||||
@@ -9974,6 +9991,7 @@ impl Editor {
|
|||||||
https://github.com/zed-industries/zed/issues/22692"
|
https://github.com/zed-industries/zed/issues/22692"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
self.request_autoscroll(Autoscroll::fit(), cx);
|
self.request_autoscroll(Autoscroll::fit(), cx);
|
||||||
self.unmark_text(window, cx);
|
self.unmark_text(window, cx);
|
||||||
self.refresh_inline_completion(true, false, window, cx);
|
self.refresh_inline_completion(true, false, window, cx);
|
||||||
@@ -11377,7 +11395,8 @@ impl Editor {
|
|||||||
|
|
||||||
let buffer = &display_map.buffer_snapshot;
|
let buffer = &display_map.buffer_snapshot;
|
||||||
let mut selections = self.selections.all::<usize>(cx);
|
let mut selections = self.selections.all::<usize>(cx);
|
||||||
if let Some(mut select_next_state) = self.select_next_state.take() {
|
match self.select_next_state.take() {
|
||||||
|
Some(mut select_next_state) => {
|
||||||
let query = &select_next_state.query;
|
let query = &select_next_state.query;
|
||||||
if !select_next_state.done {
|
if !select_next_state.done {
|
||||||
let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
|
let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
|
||||||
@@ -11386,7 +11405,8 @@ impl Editor {
|
|||||||
|
|
||||||
let bytes_after_last_selection =
|
let bytes_after_last_selection =
|
||||||
buffer.bytes_in_range(last_selection.end..buffer.len());
|
buffer.bytes_in_range(last_selection.end..buffer.len());
|
||||||
let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
|
let bytes_before_first_selection =
|
||||||
|
buffer.bytes_in_range(0..first_selection.start);
|
||||||
let query_matches = query
|
let query_matches = query
|
||||||
.stream_find_iter(bytes_after_last_selection)
|
.stream_find_iter(bytes_after_last_selection)
|
||||||
.map(|result| (last_selection.end, result))
|
.map(|result| (last_selection.end, result))
|
||||||
@@ -11433,7 +11453,8 @@ impl Editor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.select_next_state = Some(select_next_state);
|
self.select_next_state = Some(select_next_state);
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
let mut only_carets = true;
|
let mut only_carets = true;
|
||||||
let mut same_text_selected = true;
|
let mut same_text_selected = true;
|
||||||
let mut selected_text = None;
|
let mut selected_text = None;
|
||||||
@@ -11519,6 +11540,7 @@ impl Editor {
|
|||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11639,7 +11661,8 @@ impl Editor {
|
|||||||
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
|
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||||
let buffer = &display_map.buffer_snapshot;
|
let buffer = &display_map.buffer_snapshot;
|
||||||
let mut selections = self.selections.all::<usize>(cx);
|
let mut selections = self.selections.all::<usize>(cx);
|
||||||
if let Some(mut select_prev_state) = self.select_prev_state.take() {
|
match self.select_prev_state.take() {
|
||||||
|
Some(mut select_prev_state) => {
|
||||||
let query = &select_prev_state.query;
|
let query = &select_prev_state.query;
|
||||||
if !select_prev_state.done {
|
if !select_prev_state.done {
|
||||||
let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
|
let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
|
||||||
@@ -11688,7 +11711,8 @@ impl Editor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.select_prev_state = Some(select_prev_state);
|
self.select_prev_state = Some(select_prev_state);
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
let mut only_carets = true;
|
let mut only_carets = true;
|
||||||
let mut same_text_selected = true;
|
let mut same_text_selected = true;
|
||||||
let mut selected_text = None;
|
let mut selected_text = None;
|
||||||
@@ -11762,13 +11786,17 @@ impl Editor {
|
|||||||
});
|
});
|
||||||
} else if let Some(selected_text) = selected_text {
|
} else if let Some(selected_text) = selected_text {
|
||||||
self.select_prev_state = Some(SelectNextState {
|
self.select_prev_state = Some(SelectNextState {
|
||||||
query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
|
query: AhoCorasick::new(&[selected_text
|
||||||
|
.chars()
|
||||||
|
.rev()
|
||||||
|
.collect::<String>()])?,
|
||||||
wordwise: false,
|
wordwise: false,
|
||||||
done: false,
|
done: false,
|
||||||
});
|
});
|
||||||
self.select_previous(action, window, cx)?;
|
self.select_previous(action, window, cx)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11873,12 +11901,13 @@ impl Editor {
|
|||||||
let start_column = snapshot
|
let start_column = snapshot
|
||||||
.indent_size_for_line(MultiBufferRow(selection.start.row))
|
.indent_size_for_line(MultiBufferRow(selection.start.row))
|
||||||
.len;
|
.len;
|
||||||
let language = if let Some(language) =
|
let language = match snapshot
|
||||||
snapshot.language_scope_at(Point::new(selection.start.row, start_column))
|
.language_scope_at(Point::new(selection.start.row, start_column))
|
||||||
{
|
{
|
||||||
language
|
Some(language) => language,
|
||||||
} else {
|
_ => {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
selection_edit_ranges.clear();
|
selection_edit_ranges.clear();
|
||||||
@@ -11969,11 +11998,12 @@ impl Editor {
|
|||||||
(position..position, first_prefix.clone())
|
(position..position, first_prefix.clone())
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
} else if let Some((full_comment_prefix, comment_suffix)) =
|
} else {
|
||||||
language.block_comment_delimiters()
|
match language.block_comment_delimiters() {
|
||||||
{
|
Some((full_comment_prefix, comment_suffix)) => {
|
||||||
let comment_prefix = full_comment_prefix.trim_end_matches(' ');
|
let comment_prefix = full_comment_prefix.trim_end_matches(' ');
|
||||||
let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
|
let comment_prefix_whitespace =
|
||||||
|
&full_comment_prefix[comment_prefix.len()..];
|
||||||
let prefix_range = comment_prefix_range(
|
let prefix_range = comment_prefix_range(
|
||||||
snapshot.deref(),
|
snapshot.deref(),
|
||||||
start_row,
|
start_row,
|
||||||
@@ -11993,16 +12023,22 @@ impl Editor {
|
|||||||
prefix_range.start..prefix_range.start,
|
prefix_range.start..prefix_range.start,
|
||||||
full_comment_prefix.clone(),
|
full_comment_prefix.clone(),
|
||||||
));
|
));
|
||||||
edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
|
edits.push((
|
||||||
|
suffix_range.end..suffix_range.end,
|
||||||
|
comment_suffix.clone(),
|
||||||
|
));
|
||||||
suffixes_inserted.push((end_row, comment_suffix.len()));
|
suffixes_inserted.push((end_row, comment_suffix.len()));
|
||||||
} else {
|
} else {
|
||||||
edits.push((prefix_range, empty_str.clone()));
|
edits.push((prefix_range, empty_str.clone()));
|
||||||
edits.push((suffix_range, empty_str.clone()));
|
edits.push((suffix_range, empty_str.clone()));
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
drop(snapshot);
|
drop(snapshot);
|
||||||
this.buffer.update(cx, |buffer, cx| {
|
this.buffer.update(cx, |buffer, cx| {
|
||||||
@@ -13023,10 +13059,11 @@ impl Editor {
|
|||||||
};
|
};
|
||||||
let head = self.selections.newest::<usize>(cx).head();
|
let head = self.selections.newest::<usize>(cx).head();
|
||||||
let buffer = self.buffer.read(cx);
|
let buffer = self.buffer.read(cx);
|
||||||
let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
|
let (buffer, head) = match buffer.text_anchor_for_position(head, cx) {
|
||||||
text_anchor
|
Some(text_anchor) => text_anchor,
|
||||||
} else {
|
_ => {
|
||||||
return Task::ready(Ok(Navigated::No));
|
return Task::ready(Ok(Navigated::No));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
|
let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
|
||||||
@@ -13163,20 +13200,17 @@ impl Editor {
|
|||||||
cx.open_url(&url);
|
cx.open_url(&url);
|
||||||
Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
|
Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
|
||||||
}
|
}
|
||||||
HoverLink::File(path) => {
|
HoverLink::File(path) => match self.workspace() {
|
||||||
if let Some(workspace) = self.workspace() {
|
Some(workspace) => cx.spawn_in(window, async move |_, cx| {
|
||||||
cx.spawn_in(window, async move |_, cx| {
|
|
||||||
workspace
|
workspace
|
||||||
.update_in(cx, |workspace, window, cx| {
|
.update_in(cx, |workspace, window, cx| {
|
||||||
workspace.open_resolved_path(path, window, cx)
|
workspace.open_resolved_path(path, window, cx)
|
||||||
})?
|
})?
|
||||||
.await
|
.await
|
||||||
.map(|_| TargetTaskResult::AlreadyNavigated)
|
.map(|_| TargetTaskResult::AlreadyNavigated)
|
||||||
})
|
}),
|
||||||
} else {
|
_ => Task::ready(Ok(TargetTaskResult::Location(None))),
|
||||||
Task::ready(Ok(TargetTaskResult::Location(None)))
|
},
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
cx.spawn_in(window, async move |editor, cx| {
|
cx.spawn_in(window, async move |editor, cx| {
|
||||||
let target = match target_task.await.context("target resolution task")? {
|
let target = match target_task.await.context("target resolution task")? {
|
||||||
@@ -13572,10 +13606,9 @@ impl Editor {
|
|||||||
drop(snapshot);
|
drop(snapshot);
|
||||||
|
|
||||||
Some(cx.spawn_in(window, async move |this, cx| {
|
Some(cx.spawn_in(window, async move |this, cx| {
|
||||||
let rename_range = if let Some(range) = prepare_rename.await? {
|
let rename_range = match prepare_rename.await? {
|
||||||
Some(range)
|
Some(range) => Some(range),
|
||||||
} else {
|
_ => this.update(cx, |this, cx| {
|
||||||
this.update(cx, |this, cx| {
|
|
||||||
let buffer = this.buffer.read(cx).snapshot(cx);
|
let buffer = this.buffer.read(cx).snapshot(cx);
|
||||||
let mut buffer_highlights = this
|
let mut buffer_highlights = this
|
||||||
.document_highlights_for_position(selection.head(), &buffer)
|
.document_highlights_for_position(selection.head(), &buffer)
|
||||||
@@ -13586,7 +13619,7 @@ impl Editor {
|
|||||||
buffer_highlights
|
buffer_highlights
|
||||||
.next()
|
.next()
|
||||||
.map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
|
.map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
|
||||||
})?
|
})?,
|
||||||
};
|
};
|
||||||
if let Some(rename_range) = rename_range {
|
if let Some(rename_range) = rename_range {
|
||||||
this.update_in(cx, |this, window, cx| {
|
this.update_in(cx, |this, window, cx| {
|
||||||
@@ -14321,22 +14354,26 @@ impl Editor {
|
|||||||
now: Instant,
|
now: Instant,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Option<TransactionId> {
|
) -> Option<TransactionId> {
|
||||||
if let Some(transaction_id) = self
|
match self
|
||||||
.buffer
|
.buffer
|
||||||
.update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
|
.update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
|
||||||
{
|
{
|
||||||
if let Some((_, end_selections)) =
|
Some(transaction_id) => {
|
||||||
self.selection_history.transaction_mut(transaction_id)
|
match self.selection_history.transaction_mut(transaction_id) {
|
||||||
{
|
Some((_, end_selections)) => {
|
||||||
*end_selections = Some(self.selections.disjoint_anchors());
|
*end_selections = Some(self.selections.disjoint_anchors());
|
||||||
} else {
|
}
|
||||||
log::error!("unexpectedly ended a transaction that wasn't started by this editor");
|
_ => {
|
||||||
|
log::error!(
|
||||||
|
"unexpectedly ended a transaction that wasn't started by this editor"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cx.emit(EditorEvent::Edited { transaction_id });
|
cx.emit(EditorEvent::Edited { transaction_id });
|
||||||
Some(transaction_id)
|
Some(transaction_id)
|
||||||
} else {
|
}
|
||||||
None
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -14454,13 +14491,13 @@ impl Editor {
|
|||||||
let mut found = false;
|
let mut found = false;
|
||||||
let mut row = range.start.row;
|
let mut row = range.start.row;
|
||||||
while row <= range.end.row {
|
while row <= range.end.row {
|
||||||
if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
|
match display_map.crease_for_buffer_row(MultiBufferRow(row)) {
|
||||||
{
|
Some(crease) => {
|
||||||
found = true;
|
found = true;
|
||||||
row = crease.range().end.row + 1;
|
row = crease.range().end.row + 1;
|
||||||
to_fold.push(crease);
|
to_fold.push(crease);
|
||||||
} else {
|
}
|
||||||
row += 1
|
_ => row += 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if found {
|
if found {
|
||||||
@@ -15067,10 +15104,9 @@ impl Editor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(project) = &self.project {
|
match &self.project {
|
||||||
project.update(cx, |project, cx| project.save_buffers(buffers, cx))
|
Some(project) => project.update(cx, |project, cx| project.save_buffers(buffers, cx)),
|
||||||
} else {
|
_ => Task::ready(Ok(())),
|
||||||
Task::ready(Ok(()))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -15715,10 +15751,9 @@ impl Editor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn project_path(&self, cx: &App) -> Option<ProjectPath> {
|
pub fn project_path(&self, cx: &App) -> Option<ProjectPath> {
|
||||||
if let Some(buffer) = self.buffer.read(cx).as_singleton() {
|
match self.buffer.read(cx).as_singleton() {
|
||||||
buffer.read(cx).project_path(cx)
|
Some(buffer) => buffer.read(cx).project_path(cx),
|
||||||
} else {
|
_ => None,
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17401,13 +17436,15 @@ impl Editor {
|
|||||||
fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
cx.emit(EditorEvent::Focused);
|
cx.emit(EditorEvent::Focused);
|
||||||
|
|
||||||
if let Some(descendant) = self
|
match self
|
||||||
.last_focused_descendant
|
.last_focused_descendant
|
||||||
.take()
|
.take()
|
||||||
.and_then(|descendant| descendant.upgrade())
|
.and_then(|descendant| descendant.upgrade())
|
||||||
{
|
{
|
||||||
|
Some(descendant) => {
|
||||||
window.focus(&descendant);
|
window.focus(&descendant);
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
if let Some(blame) = self.blame.as_ref() {
|
if let Some(blame) = self.blame.as_ref() {
|
||||||
blame.update(cx, GitBlame::focus)
|
blame.update(cx, GitBlame::focus)
|
||||||
}
|
}
|
||||||
@@ -17427,6 +17464,7 @@ impl Editor {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
|
fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
|
||||||
cx.emit(EditorEvent::FocusedIn)
|
cx.emit(EditorEvent::FocusedIn)
|
||||||
@@ -18700,7 +18738,7 @@ impl EditorSnapshot {
|
|||||||
range: &'a Range<Anchor>,
|
range: &'a Range<Anchor>,
|
||||||
collaboration_hub: &dyn CollaborationHub,
|
collaboration_hub: &dyn CollaborationHub,
|
||||||
cx: &'a App,
|
cx: &'a App,
|
||||||
) -> impl 'a + Iterator<Item = RemoteSelection> {
|
) -> impl 'a + Iterator<Item = RemoteSelection> + use<'a> {
|
||||||
let participant_names = collaboration_hub.user_names(cx);
|
let participant_names = collaboration_hub.user_names(cx);
|
||||||
let participant_indices = collaboration_hub.user_participant_indices(cx);
|
let participant_indices = collaboration_hub.user_participant_indices(cx);
|
||||||
let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
|
let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
|
||||||
@@ -18993,14 +19031,15 @@ impl EditorSnapshot {
|
|||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> Option<AnyElement> {
|
) -> Option<AnyElement> {
|
||||||
let folded = self.is_line_folded(buffer_row);
|
let folded = self.is_line_folded(buffer_row);
|
||||||
if let Crease::Inline { render_trailer, .. } = self
|
match self
|
||||||
.crease_snapshot
|
.crease_snapshot
|
||||||
.query_row(buffer_row, &self.buffer_snapshot)?
|
.query_row(buffer_row, &self.buffer_snapshot)?
|
||||||
{
|
{
|
||||||
|
Crease::Inline { render_trailer, .. } => {
|
||||||
let render_trailer = render_trailer.as_ref()?;
|
let render_trailer = render_trailer.as_ref()?;
|
||||||
Some(render_trailer(buffer_row, folded, window, cx))
|
Some(render_trailer(buffer_row, folded, window, cx))
|
||||||
} else {
|
}
|
||||||
None
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -19995,7 +20034,7 @@ impl BreakpointPromptEditor {
|
|||||||
.log_err();
|
.log_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
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 settings = ThemeSettings::get_global(cx);
|
||||||
let text_style = TextStyle {
|
let text_style = TextStyle {
|
||||||
color: if self.prompt.read(cx).read_only(cx) {
|
color: if self.prompt.read(cx).read_only(cx) {
|
||||||
|
|||||||
@@ -9526,16 +9526,18 @@ async fn test_word_completion(cx: &mut TestAppContext) {
|
|||||||
cx.condition(|editor, _| editor.context_menu_visible())
|
cx.condition(|editor, _| editor.context_menu_visible())
|
||||||
.await;
|
.await;
|
||||||
cx.update_editor(|editor, window, cx| {
|
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!(
|
assert_eq!(
|
||||||
completion_menu_entries(&menu),
|
completion_menu_entries(&menu),
|
||||||
&["first", "last"],
|
&["first", "last"],
|
||||||
"When LSP server is fast to reply, no fallback word completions are used"
|
"When LSP server is fast to reply, no fallback word completions are used"
|
||||||
);
|
);
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
panic!("expected completion menu to be open");
|
panic!("expected completion menu to be open");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
editor.cancel(&Cancel, window, cx);
|
editor.cancel(&Cancel, window, cx);
|
||||||
});
|
});
|
||||||
cx.executor().run_until_parked();
|
cx.executor().run_until_parked();
|
||||||
@@ -9550,13 +9552,13 @@ async fn test_word_completion(cx: &mut TestAppContext) {
|
|||||||
cx.condition(|editor, _| editor.context_menu_visible())
|
cx.condition(|editor, _| editor.context_menu_visible())
|
||||||
.await;
|
.await;
|
||||||
cx.update_editor(|editor, _, _| {
|
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"],
|
assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
|
||||||
"When LSP server is slow, document words can be shown instead, if configured accordingly");
|
"When LSP server is slow, document words can be shown instead, if configured accordingly");
|
||||||
} else {
|
} _ => {
|
||||||
panic!("expected completion menu to be open");
|
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())
|
cx.condition(|editor, _| editor.context_menu_visible())
|
||||||
.await;
|
.await;
|
||||||
cx.update_editor(|editor, _, _| {
|
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!(
|
assert_eq!(
|
||||||
completion_menu_entries(&menu),
|
completion_menu_entries(&menu),
|
||||||
&["first", "last", "second"],
|
&["first", "last", "second"],
|
||||||
"Word completions that has the same edit as the any of the LSP ones, should not be proposed"
|
"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");
|
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.executor().run_until_parked();
|
||||||
cx.condition(|editor, _| editor.context_menu_visible())
|
cx.condition(|editor, _| editor.context_menu_visible())
|
||||||
.await;
|
.await;
|
||||||
cx.update_editor(|editor, _, _| {
|
cx.update_editor(
|
||||||
if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
|
|editor, _, _| match editor.context_menu.borrow_mut().as_ref() {
|
||||||
{
|
Some(CodeContextMenu::Completions(menu)) => {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
completion_menu_entries(&menu),
|
completion_menu_entries(&menu),
|
||||||
&["first", "last", "second"],
|
&["first", "last", "second"],
|
||||||
"`ShowWordCompletions` action should show word completions"
|
"`ShowWordCompletions` action should show word completions"
|
||||||
);
|
);
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
panic!("expected completion menu to be open");
|
panic!("expected completion menu to be open");
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
cx.simulate_keystroke("l");
|
cx.simulate_keystroke("l");
|
||||||
cx.executor().run_until_parked();
|
cx.executor().run_until_parked();
|
||||||
cx.condition(|editor, _| editor.context_menu_visible())
|
cx.condition(|editor, _| editor.context_menu_visible())
|
||||||
.await;
|
.await;
|
||||||
cx.update_editor(|editor, _, _| {
|
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!(
|
assert_eq!(
|
||||||
completion_menu_entries(&menu),
|
completion_menu_entries(&menu),
|
||||||
&["last"],
|
&["last"],
|
||||||
"After showing word completions, further editing should filter them and not query the LSP"
|
"After showing word completions, further editing should filter them and not query the LSP"
|
||||||
);
|
);
|
||||||
} else {
|
} _ => {
|
||||||
panic!("expected completion menu to be open");
|
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())
|
cx.condition(|editor, _| editor.context_menu_visible())
|
||||||
.await;
|
.await;
|
||||||
cx.update_editor(|editor, window, cx| {
|
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!(
|
assert_eq!(
|
||||||
completion_menu_entries(&menu),
|
completion_menu_entries(&menu),
|
||||||
&["let"],
|
&["let"],
|
||||||
"With no digits in the completion query, no digits should be in the word completions"
|
"With no digits in the completion query, no digits should be in the word completions"
|
||||||
);
|
);
|
||||||
} else {
|
} _ => {
|
||||||
panic!("expected completion menu to be open");
|
panic!("expected completion menu to be open");
|
||||||
}
|
}}
|
||||||
editor.cancel(&Cancel, window, cx);
|
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())
|
cx.condition(|editor, _| editor.context_menu_visible())
|
||||||
.await;
|
.await;
|
||||||
cx.update_editor(|editor, _, _| {
|
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, \
|
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`)");
|
return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
|
||||||
} else {
|
} _ => {
|
||||||
panic!("expected completion menu to be open");
|
panic!("expected completion menu to be open");
|
||||||
}
|
}}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -9914,8 +9918,8 @@ async fn test_multiline_completion(cx: &mut TestAppContext) {
|
|||||||
|
|
||||||
editor.update(cx, |editor, _| {
|
editor.update(cx, |editor, _| {
|
||||||
assert!(editor.context_menu_visible());
|
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
|
let completion_labels = menu
|
||||||
.completions
|
.completions
|
||||||
.borrow()
|
.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:?}"
|
"Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} _ => {
|
||||||
panic!("expected completion menu to be open");
|
panic!("expected completion menu to be open");
|
||||||
}
|
}}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -9981,39 +9985,45 @@ async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
|
|||||||
cx.simulate_keystroke(".");
|
cx.simulate_keystroke(".");
|
||||||
cx.executor().run_until_parked();
|
cx.executor().run_until_parked();
|
||||||
|
|
||||||
cx.update_editor(|editor, _, _| {
|
cx.update_editor(
|
||||||
if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
|
|editor, _, _| match editor.context_menu.borrow_mut().as_ref() {
|
||||||
{
|
Some(CodeContextMenu::Completions(menu)) => {
|
||||||
assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
|
assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
panic!("expected completion menu to be open");
|
panic!("expected completion menu to be open");
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
cx.update_editor(|editor, window, cx| {
|
cx.update_editor(|editor, window, cx| {
|
||||||
editor.move_page_down(&MovePageDown::default(), window, cx);
|
editor.move_page_down(&MovePageDown::default(), 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!(
|
assert!(
|
||||||
menu.selected_item == 1,
|
menu.selected_item == 1,
|
||||||
"expected PageDown to select the last item from the context menu"
|
"expected PageDown to select the last item from the context menu"
|
||||||
);
|
);
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
panic!("expected completion menu to stay open after PageDown");
|
panic!("expected completion menu to stay open after PageDown");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
cx.update_editor(|editor, window, cx| {
|
cx.update_editor(|editor, window, cx| {
|
||||||
editor.move_page_up(&MovePageUp::default(), window, cx);
|
editor.move_page_up(&MovePageUp::default(), 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!(
|
assert!(
|
||||||
menu.selected_item == 0,
|
menu.selected_item == 0,
|
||||||
"expected PageUp to select the first item from the context menu"
|
"expected PageUp to select the first item from the context menu"
|
||||||
);
|
);
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
panic!("expected completion menu to stay open after PageUp");
|
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.executor().run_until_parked();
|
||||||
|
|
||||||
cx.update_editor(|editor, _, _| {
|
cx.update_editor(
|
||||||
if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
|
|editor, _, _| match editor.context_menu.borrow_mut().as_ref() {
|
||||||
{
|
Some(CodeContextMenu::Completions(menu)) => {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
completion_menu_entries(&menu),
|
completion_menu_entries(&menu),
|
||||||
&["r", "ret", "Range", "return"]
|
&["r", "ret", "Range", "return"]
|
||||||
);
|
);
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
panic!("expected completion menu to be open");
|
panic!("expected completion menu to be open");
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[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.
|
// word character in the 'element' scope, which contains the cursor.
|
||||||
cx.simulate_keystroke("-");
|
cx.simulate_keystroke("-");
|
||||||
cx.executor().run_until_parked();
|
cx.executor().run_until_parked();
|
||||||
cx.update_editor(|editor, _, _| {
|
cx.update_editor(
|
||||||
if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
|
|editor, _, _| match editor.context_menu.borrow_mut().as_ref() {
|
||||||
{
|
Some(CodeContextMenu::Completions(menu)) => {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
completion_menu_entries(&menu),
|
completion_menu_entries(&menu),
|
||||||
&["bg-red", "bg-blue", "bg-yellow"]
|
&["bg-red", "bg-blue", "bg-yellow"]
|
||||||
);
|
);
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
panic!("expected completion menu to be open");
|
panic!("expected completion menu to be open");
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
cx.simulate_keystroke("l");
|
cx.simulate_keystroke("l");
|
||||||
cx.executor().run_until_parked();
|
cx.executor().run_until_parked();
|
||||||
cx.update_editor(|editor, _, _| {
|
cx.update_editor(
|
||||||
if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
|
|editor, _, _| match editor.context_menu.borrow_mut().as_ref() {
|
||||||
{
|
Some(CodeContextMenu::Completions(menu)) => {
|
||||||
assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
|
assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
panic!("expected completion menu to be open");
|
panic!("expected completion menu to be open");
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// When filtering completions, consider the character after the '-' to
|
// When filtering completions, consider the character after the '-' to
|
||||||
// be the start of a subword.
|
// be the start of a subword.
|
||||||
cx.set_state(r#"<p class="yelˇ" />"#);
|
cx.set_state(r#"<p class="yelˇ" />"#);
|
||||||
cx.simulate_keystroke("l");
|
cx.simulate_keystroke("l");
|
||||||
cx.executor().run_until_parked();
|
cx.executor().run_until_parked();
|
||||||
cx.update_editor(|editor, _, _| {
|
cx.update_editor(
|
||||||
if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
|
|editor, _, _| match editor.context_menu.borrow_mut().as_ref() {
|
||||||
{
|
Some(CodeContextMenu::Completions(menu)) => {
|
||||||
assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
|
assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
panic!("expected completion menu to be open");
|
panic!("expected completion menu to be open");
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
|
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(
|
pub fn handle_signature_help_request(
|
||||||
cx: &mut EditorLspTestContext,
|
cx: &mut EditorLspTestContext,
|
||||||
mocked_response: lsp::SignatureHelp,
|
mocked_response: lsp::SignatureHelp,
|
||||||
) -> impl Future<Output = ()> {
|
) -> impl Future<Output = ()> + use<> {
|
||||||
let mut request =
|
let mut request =
|
||||||
cx.set_request_handler::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
|
cx.set_request_handler::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
|
||||||
let mocked_response = mocked_response.clone();
|
let mocked_response = mocked_response.clone();
|
||||||
@@ -18711,7 +18729,7 @@ pub fn handle_completion_request(
|
|||||||
marked_string: &str,
|
marked_string: &str,
|
||||||
completions: Vec<&'static str>,
|
completions: Vec<&'static str>,
|
||||||
counter: Arc<AtomicUsize>,
|
counter: Arc<AtomicUsize>,
|
||||||
) -> impl Future<Output = ()> {
|
) -> impl Future<Output = ()> + use<> {
|
||||||
let complete_from_marker: TextRangeMarker = '|'.into();
|
let complete_from_marker: TextRangeMarker = '|'.into();
|
||||||
let replace_range_marker: TextRangeMarker = ('<', '>').into();
|
let replace_range_marker: TextRangeMarker = ('<', '>').into();
|
||||||
let (_, mut marked_ranges) = marked_text_ranges_by(
|
let (_, mut marked_ranges) = marked_text_ranges_by(
|
||||||
@@ -18758,7 +18776,7 @@ pub fn handle_completion_request(
|
|||||||
fn handle_resolve_completion_request(
|
fn handle_resolve_completion_request(
|
||||||
cx: &mut EditorLspTestContext,
|
cx: &mut EditorLspTestContext,
|
||||||
edits: Option<Vec<(&'static str, &'static str)>>,
|
edits: Option<Vec<(&'static str, &'static str)>>,
|
||||||
) -> impl Future<Output = ()> {
|
) -> impl Future<Output = ()> + use<> {
|
||||||
let edits = edits.map(|edits| {
|
let edits = edits.map(|edits| {
|
||||||
edits
|
edits
|
||||||
.iter()
|
.iter()
|
||||||
|
|||||||
@@ -432,69 +432,96 @@ impl EditorElement {
|
|||||||
register_action(editor, window, Editor::expand_all_diff_hunks);
|
register_action(editor, window, Editor::expand_all_diff_hunks);
|
||||||
|
|
||||||
register_action(editor, window, |editor, action, window, cx| {
|
register_action(editor, window, |editor, action, window, cx| {
|
||||||
if let Some(task) = editor.format(action, window, cx) {
|
match editor.format(action, window, cx) {
|
||||||
|
Some(task) => {
|
||||||
task.detach_and_notify_err(window, cx);
|
task.detach_and_notify_err(window, cx);
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
cx.propagate();
|
cx.propagate();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
register_action(editor, window, |editor, action, window, cx| {
|
register_action(editor, window, |editor, action, window, cx| {
|
||||||
if let Some(task) = editor.format_selections(action, window, cx) {
|
match editor.format_selections(action, window, cx) {
|
||||||
|
Some(task) => {
|
||||||
task.detach_and_notify_err(window, cx);
|
task.detach_and_notify_err(window, cx);
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
cx.propagate();
|
cx.propagate();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
register_action(editor, window, |editor, action, window, cx| {
|
register_action(editor, window, |editor, action, window, cx| {
|
||||||
if let Some(task) = editor.organize_imports(action, window, cx) {
|
match editor.organize_imports(action, window, cx) {
|
||||||
|
Some(task) => {
|
||||||
task.detach_and_notify_err(window, cx);
|
task.detach_and_notify_err(window, cx);
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
cx.propagate();
|
cx.propagate();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
register_action(editor, window, Editor::restart_language_server);
|
register_action(editor, window, Editor::restart_language_server);
|
||||||
register_action(editor, window, Editor::show_character_palette);
|
register_action(editor, window, Editor::show_character_palette);
|
||||||
register_action(editor, window, |editor, action, window, cx| {
|
register_action(editor, window, |editor, action, window, cx| {
|
||||||
if let Some(task) = editor.confirm_completion(action, window, cx) {
|
match editor.confirm_completion(action, window, cx) {
|
||||||
|
Some(task) => {
|
||||||
task.detach_and_notify_err(window, cx);
|
task.detach_and_notify_err(window, cx);
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
cx.propagate();
|
cx.propagate();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
register_action(editor, window, |editor, action, window, cx| {
|
register_action(editor, window, |editor, action, window, cx| {
|
||||||
if let Some(task) = editor.compose_completion(action, window, cx) {
|
match editor.compose_completion(action, window, cx) {
|
||||||
|
Some(task) => {
|
||||||
task.detach_and_notify_err(window, cx);
|
task.detach_and_notify_err(window, cx);
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
cx.propagate();
|
cx.propagate();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
register_action(editor, window, |editor, action, window, cx| {
|
register_action(editor, window, |editor, action, window, cx| {
|
||||||
if let Some(task) = editor.confirm_code_action(action, window, cx) {
|
match editor.confirm_code_action(action, window, cx) {
|
||||||
|
Some(task) => {
|
||||||
task.detach_and_notify_err(window, cx);
|
task.detach_and_notify_err(window, cx);
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
cx.propagate();
|
cx.propagate();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
register_action(editor, window, |editor, action, window, cx| {
|
register_action(editor, window, |editor, action, window, cx| {
|
||||||
if let Some(task) = editor.rename(action, window, cx) {
|
match editor.rename(action, window, cx) {
|
||||||
|
Some(task) => {
|
||||||
task.detach_and_notify_err(window, cx);
|
task.detach_and_notify_err(window, cx);
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
cx.propagate();
|
cx.propagate();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
register_action(editor, window, |editor, action, window, cx| {
|
register_action(editor, window, |editor, action, window, cx| {
|
||||||
if let Some(task) = editor.confirm_rename(action, window, cx) {
|
match editor.confirm_rename(action, window, cx) {
|
||||||
|
Some(task) => {
|
||||||
task.detach_and_notify_err(window, cx);
|
task.detach_and_notify_err(window, cx);
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
cx.propagate();
|
cx.propagate();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
register_action(editor, window, |editor, action, window, cx| {
|
register_action(editor, window, |editor, action, window, cx| {
|
||||||
if let Some(task) = editor.find_all_references(action, window, cx) {
|
match editor.find_all_references(action, window, cx) {
|
||||||
|
Some(task) => {
|
||||||
task.detach_and_log_err(cx);
|
task.detach_and_log_err(cx);
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
cx.propagate();
|
cx.propagate();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
register_action(editor, window, Editor::show_signature_help);
|
register_action(editor, window, Editor::show_signature_help);
|
||||||
register_action(editor, window, Editor::next_edit_prediction);
|
register_action(editor, window, Editor::next_edit_prediction);
|
||||||
@@ -1097,10 +1124,12 @@ impl EditorElement {
|
|||||||
selections.push((player, layouts));
|
selections.push((player, layouts));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(collaboration_hub) = &editor.collaboration_hub {
|
match &editor.collaboration_hub {
|
||||||
|
Some(collaboration_hub) => {
|
||||||
// When following someone, render the local selections in their color.
|
// When following someone, render the local selections in their color.
|
||||||
if let Some(leader_id) = editor.leader_peer_id {
|
if let Some(leader_id) = editor.leader_peer_id {
|
||||||
if let Some(collaborator) = collaboration_hub.collaborators(cx).get(&leader_id)
|
if let Some(collaborator) =
|
||||||
|
collaboration_hub.collaborators(cx).get(&leader_id)
|
||||||
{
|
{
|
||||||
if let Some(participant_index) = collaboration_hub
|
if let Some(participant_index) = collaboration_hub
|
||||||
.user_participant_indices(cx)
|
.user_participant_indices(cx)
|
||||||
@@ -1154,7 +1183,9 @@ impl EditorElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
selections.extend(remote_selections.into_values());
|
selections.extend(remote_selections.into_values());
|
||||||
} else if !editor.is_focused(window) && editor.show_cursor_when_unfocused {
|
}
|
||||||
|
_ => {
|
||||||
|
if !editor.is_focused(window) && editor.show_cursor_when_unfocused {
|
||||||
let layouts = snapshot
|
let layouts = snapshot
|
||||||
.buffer_snapshot
|
.buffer_snapshot
|
||||||
.selections_in_range(&(start_anchor..end_anchor), true)
|
.selections_in_range(&(start_anchor..end_anchor), true)
|
||||||
@@ -1173,6 +1204,8 @@ impl EditorElement {
|
|||||||
let player = editor.current_user_player_color(cx);
|
let player = editor.current_user_player_color(cx);
|
||||||
selections.push((player, layouts));
|
selections.push((player, layouts));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
(selections, active_rows, newest_selection_head)
|
(selections, active_rows, newest_selection_head)
|
||||||
}
|
}
|
||||||
@@ -2056,20 +2089,17 @@ impl EditorElement {
|
|||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> Vec<AnyElement> {
|
) -> Vec<AnyElement> {
|
||||||
self.editor.update(cx, |editor, cx| {
|
self.editor.update(cx, |editor, cx| {
|
||||||
let active_task_indicator_row =
|
let active_task_indicator_row = match editor.context_menu.borrow().as_ref() {
|
||||||
if let Some(crate::CodeContextMenu::CodeActions(CodeActionsMenu {
|
Some(crate::CodeContextMenu::CodeActions(CodeActionsMenu {
|
||||||
deployed_from_indicator,
|
deployed_from_indicator,
|
||||||
actions,
|
actions,
|
||||||
..
|
..
|
||||||
})) = editor.context_menu.borrow().as_ref()
|
})) => actions
|
||||||
{
|
|
||||||
actions
|
|
||||||
.tasks
|
.tasks
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|tasks| tasks.position.to_display_point(snapshot).row())
|
.map(|tasks| tasks.position.to_display_point(snapshot).row())
|
||||||
.or(*deployed_from_indicator)
|
.or(*deployed_from_indicator),
|
||||||
} else {
|
_ => None,
|
||||||
None
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let offset_range_start =
|
let offset_range_start =
|
||||||
@@ -4079,11 +4109,12 @@ impl EditorElement {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let maybe_element = self.editor.update(cx, |editor, cx| {
|
let maybe_element = self.editor.update(cx, |editor, cx| {
|
||||||
if let Some(popover) = editor.signature_help_state.popover_mut() {
|
match editor.signature_help_state.popover_mut() {
|
||||||
|
Some(popover) => {
|
||||||
let element = popover.render(max_size, cx);
|
let element = popover.render(max_size, cx);
|
||||||
Some(element)
|
Some(element)
|
||||||
} else {
|
}
|
||||||
None
|
_ => None,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if let Some(mut element) = maybe_element {
|
if let Some(mut element) = maybe_element {
|
||||||
@@ -4189,7 +4220,11 @@ impl EditorElement {
|
|||||||
None;
|
None;
|
||||||
for (&new_row, &new_background) in &layout.highlighted_rows {
|
for (&new_row, &new_background) in &layout.highlighted_rows {
|
||||||
match &mut current_paint {
|
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 current_background = *current_background;
|
||||||
let new_range_started = current_background != new_background
|
let new_range_started = current_background != new_background
|
||||||
|| current_range.end.next_row() != new_row;
|
|| current_range.end.next_row() != new_row;
|
||||||
@@ -4916,7 +4951,7 @@ impl EditorElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
editor.update(cx, |editor, cx| {
|
editor.update(cx, |editor, cx| {
|
||||||
if let Some((scrollbar_layout, axis)) = event
|
match event
|
||||||
.pressed_button
|
.pressed_button
|
||||||
.filter(|button| *button == MouseButton::Left)
|
.filter(|button| *button == MouseButton::Left)
|
||||||
.and(editor.scroll_manager.dragging_scrollbar_axis())
|
.and(editor.scroll_manager.dragging_scrollbar_axis())
|
||||||
@@ -4924,8 +4959,8 @@ impl EditorElement {
|
|||||||
scrollbars_layout
|
scrollbars_layout
|
||||||
.iter_scrollbars()
|
.iter_scrollbars()
|
||||||
.find(|(_, a)| *a == axis)
|
.find(|(_, a)| *a == axis)
|
||||||
})
|
}) {
|
||||||
{
|
Some((scrollbar_layout, axis)) => {
|
||||||
let ScrollbarLayout {
|
let ScrollbarLayout {
|
||||||
hitbox,
|
hitbox,
|
||||||
text_unit_size,
|
text_unit_size,
|
||||||
@@ -4943,9 +4978,11 @@ impl EditorElement {
|
|||||||
editor.set_scroll_position(position, window, cx);
|
editor.set_scroll_position(position, window, cx);
|
||||||
}
|
}
|
||||||
cx.stop_propagation();
|
cx.stop_propagation();
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
editor.scroll_manager.reset_scrollbar_dragging_state(cx);
|
editor.scroll_manager.reset_scrollbar_dragging_state(cx);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if scrollbars_layout.get_hovered_axis(window).is_some() {
|
if scrollbars_layout.get_hovered_axis(window).is_some() {
|
||||||
editor.scroll_manager.show_scrollbars(window, cx);
|
editor.scroll_manager.show_scrollbars(window, cx);
|
||||||
@@ -5655,13 +5692,12 @@ pub struct AcceptEditPredictionBinding(pub(crate) Option<gpui::KeyBinding>);
|
|||||||
|
|
||||||
impl AcceptEditPredictionBinding {
|
impl AcceptEditPredictionBinding {
|
||||||
pub fn keystroke(&self) -> Option<&Keystroke> {
|
pub fn keystroke(&self) -> Option<&Keystroke> {
|
||||||
if let Some(binding) = self.0.as_ref() {
|
match self.0.as_ref() {
|
||||||
match &binding.keystrokes() {
|
Some(binding) => match &binding.keystrokes() {
|
||||||
[keystroke] => Some(keystroke),
|
[keystroke] => Some(keystroke),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
},
|
||||||
} else {
|
_ => None,
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5949,7 +5985,8 @@ impl LineWithInvisibles {
|
|||||||
is_tab: false,
|
is_tab: false,
|
||||||
replacement: None,
|
replacement: None,
|
||||||
}]) {
|
}]) {
|
||||||
if let Some(replacement) = highlighted_chunk.replacement {
|
match highlighted_chunk.replacement {
|
||||||
|
Some(replacement) => {
|
||||||
if !line.is_empty() {
|
if !line.is_empty() {
|
||||||
let shaped_line = window
|
let shaped_line = window
|
||||||
.text_system()
|
.text_system()
|
||||||
@@ -6004,10 +6041,9 @@ impl LineWithInvisibles {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
ChunkReplacement::Str(x) => {
|
ChunkReplacement::Str(x) => {
|
||||||
let text_style = if let Some(style) = highlighted_chunk.style {
|
let text_style = match highlighted_chunk.style {
|
||||||
Cow::Owned(text_style.clone().highlight(style))
|
Some(style) => Cow::Owned(text_style.clone().highlight(style)),
|
||||||
} else {
|
_ => Cow::Borrowed(text_style),
|
||||||
Cow::Borrowed(text_style)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let run = TextRun {
|
let run = TextRun {
|
||||||
@@ -6029,7 +6065,8 @@ impl LineWithInvisibles {
|
|||||||
fragments.push(LineFragment::Text(line_layout))
|
fragments.push(LineFragment::Text(line_layout))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
for (ix, mut line_chunk) in highlighted_chunk.text.split('\n').enumerate() {
|
for (ix, mut line_chunk) in highlighted_chunk.text.split('\n').enumerate() {
|
||||||
if ix > 0 {
|
if ix > 0 {
|
||||||
let shaped_line = window
|
let shaped_line = window
|
||||||
@@ -6058,10 +6095,9 @@ impl LineWithInvisibles {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !line_chunk.is_empty() && !line_exceeded_max_len {
|
if !line_chunk.is_empty() && !line_exceeded_max_len {
|
||||||
let text_style = if let Some(style) = highlighted_chunk.style {
|
let text_style = match highlighted_chunk.style {
|
||||||
Cow::Owned(text_style.clone().highlight(style))
|
Some(style) => Cow::Owned(text_style.clone().highlight(style)),
|
||||||
} else {
|
_ => Cow::Borrowed(text_style),
|
||||||
Cow::Borrowed(text_style)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if line.len() + line_chunk.len() > max_line_len {
|
if line.len() + line_chunk.len() > max_line_len {
|
||||||
@@ -6117,6 +6153,7 @@ impl LineWithInvisibles {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
layouts
|
layouts
|
||||||
}
|
}
|
||||||
@@ -8067,17 +8104,18 @@ impl PositionMap {
|
|||||||
let x = position.x + (scroll_position.x * self.em_width);
|
let x = position.x + (scroll_position.x * self.em_width);
|
||||||
let row = ((y / self.line_height) + scroll_position.y) as u32;
|
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
|
.line_layouts
|
||||||
.get(row as usize - scroll_position.y as usize)
|
.get(row as usize - scroll_position.y as usize)
|
||||||
{
|
{
|
||||||
|
Some(line) => {
|
||||||
if let Some(ix) = line.index_for_x(x) {
|
if let Some(ix) = line.index_for_x(x) {
|
||||||
(ix as u32, px(0.))
|
(ix as u32, px(0.))
|
||||||
} else {
|
} else {
|
||||||
(line.len as u32, px(0.).max(x - line.width))
|
(line.len as u32, px(0.).max(x - line.width))
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
(0, x)
|
_ => (0, x),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut exact_unclipped = DisplayPoint::new(DisplayRow(row), column);
|
let mut exact_unclipped = DisplayPoint::new(DisplayRow(row), column);
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ impl GitBlame {
|
|||||||
&'a mut self,
|
&'a mut self,
|
||||||
rows: &'a [RowInfo],
|
rows: &'a [RowInfo],
|
||||||
cx: &App,
|
cx: &App,
|
||||||
) -> impl 'a + Iterator<Item = Option<BlameEntry>> {
|
) -> impl 'a + Iterator<Item = Option<BlameEntry>> + use<'a> {
|
||||||
self.sync(cx);
|
self.sync(cx);
|
||||||
|
|
||||||
let buffer_id = self.buffer_snapshot.remote_id();
|
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));
|
.and_then(|remote_url| parse_git_remote_url(provider_registry, remote_url));
|
||||||
|
|
||||||
for (oid, message) in messages {
|
for (oid, message) in messages {
|
||||||
let permalink = if let Some((provider, git_remote)) = parsed_remote_url.as_ref() {
|
let permalink = match parsed_remote_url.as_ref() {
|
||||||
Some(provider.build_commit_permalink(
|
Some((provider, git_remote)) => Some(provider.build_commit_permalink(
|
||||||
git_remote,
|
git_remote,
|
||||||
git::BuildCommitPermalinkParams {
|
git::BuildCommitPermalinkParams {
|
||||||
sha: oid.to_string().as_str(),
|
sha: oid.to_string().as_str(),
|
||||||
},
|
},
|
||||||
))
|
)),
|
||||||
} else {
|
_ => None,
|
||||||
None
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let remote = parsed_remote_url
|
let remote = parsed_remote_url
|
||||||
|
|||||||
@@ -472,11 +472,9 @@ pub fn show_link_definition(
|
|||||||
_ => GotoDefinitionKind::Type,
|
_ => GotoDefinitionKind::Type,
|
||||||
};
|
};
|
||||||
|
|
||||||
let (mut hovered_link_state, is_cached) =
|
let (mut hovered_link_state, is_cached) = match editor.hovered_link_state.take() {
|
||||||
if let Some(existing) = editor.hovered_link_state.take() {
|
Some(existing) => (existing, true),
|
||||||
(existing, true)
|
_ => (
|
||||||
} else {
|
|
||||||
(
|
|
||||||
HoveredLinkState {
|
HoveredLinkState {
|
||||||
last_trigger_point: trigger_point.clone(),
|
last_trigger_point: trigger_point.clone(),
|
||||||
symbol_range: None,
|
symbol_range: None,
|
||||||
@@ -485,7 +483,7 @@ pub fn show_link_definition(
|
|||||||
task: None,
|
task: None,
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
)
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
if editor.pending_rename.is_some() {
|
if editor.pending_rename.is_some() {
|
||||||
@@ -537,9 +535,9 @@ pub fn show_link_definition(
|
|||||||
hovered_link_state.task = Some(cx.spawn_in(window, async move |this, cx| {
|
hovered_link_state.task = Some(cx.spawn_in(window, async move |this, cx| {
|
||||||
async move {
|
async move {
|
||||||
let result = match &trigger_point {
|
let result = match &trigger_point {
|
||||||
TriggerPoint::Text(_) => {
|
TriggerPoint::Text(_) => match find_url(&buffer, buffer_position, cx.clone()) {
|
||||||
if let Some((url_range, url)) = find_url(&buffer, buffer_position, cx.clone()) {
|
Some((url_range, url)) => this
|
||||||
this.update(cx, |_, _| {
|
.update(cx, |_, _| {
|
||||||
let range = maybe!({
|
let range = maybe!({
|
||||||
let start =
|
let start =
|
||||||
snapshot.anchor_in_excerpt(excerpt_id, url_range.start)?;
|
snapshot.anchor_in_excerpt(excerpt_id, url_range.start)?;
|
||||||
@@ -548,24 +546,31 @@ pub fn show_link_definition(
|
|||||||
});
|
});
|
||||||
(range, vec![HoverLink::Url(url)])
|
(range, vec![HoverLink::Url(url)])
|
||||||
})
|
})
|
||||||
.ok()
|
.ok(),
|
||||||
} else if let Some((filename_range, filename)) =
|
_ => match find_file(&buffer, project.clone(), buffer_position, cx).await {
|
||||||
find_file(&buffer, project.clone(), buffer_position, cx).await
|
Some((filename_range, filename)) => {
|
||||||
{
|
|
||||||
let range = maybe!({
|
let range = maybe!({
|
||||||
let start =
|
let start =
|
||||||
snapshot.anchor_in_excerpt(excerpt_id, filename_range.start)?;
|
snapshot.anchor_in_excerpt(excerpt_id, filename_range.start)?;
|
||||||
let end = snapshot.anchor_in_excerpt(excerpt_id, filename_range.end)?;
|
let end =
|
||||||
|
snapshot.anchor_in_excerpt(excerpt_id, filename_range.end)?;
|
||||||
Some(RangeInEditor::Text(start..end))
|
Some(RangeInEditor::Text(start..end))
|
||||||
});
|
});
|
||||||
|
|
||||||
Some((range, vec![HoverLink::File(filename)]))
|
Some((range, vec![HoverLink::File(filename)]))
|
||||||
} else if let Some(provider) = provider {
|
}
|
||||||
|
_ => match provider {
|
||||||
|
Some(provider) => {
|
||||||
let task = cx.update(|_, cx| {
|
let task = cx.update(|_, cx| {
|
||||||
provider.definitions(&buffer, buffer_position, preferred_kind, cx)
|
provider.definitions(
|
||||||
|
&buffer,
|
||||||
|
buffer_position,
|
||||||
|
preferred_kind,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
})?;
|
})?;
|
||||||
if let Some(task) = task {
|
match task {
|
||||||
task.await.ok().map(|definition_result| {
|
Some(task) => task.await.ok().map(|definition_result| {
|
||||||
(
|
(
|
||||||
definition_result.iter().find_map(|link| {
|
definition_result.iter().find_map(|link| {
|
||||||
link.origin.as_ref().and_then(|origin| {
|
link.origin.as_ref().and_then(|origin| {
|
||||||
@@ -573,21 +578,26 @@ pub fn show_link_definition(
|
|||||||
excerpt_id,
|
excerpt_id,
|
||||||
origin.range.start,
|
origin.range.start,
|
||||||
)?;
|
)?;
|
||||||
let end = snapshot
|
let end = snapshot.anchor_in_excerpt(
|
||||||
.anchor_in_excerpt(excerpt_id, origin.range.end)?;
|
excerpt_id,
|
||||||
|
origin.range.end,
|
||||||
|
)?;
|
||||||
Some(RangeInEditor::Text(start..end))
|
Some(RangeInEditor::Text(start..end))
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
definition_result.into_iter().map(HoverLink::Text).collect(),
|
definition_result
|
||||||
|
.into_iter()
|
||||||
|
.map(HoverLink::Text)
|
||||||
|
.collect(),
|
||||||
)
|
)
|
||||||
})
|
}),
|
||||||
} else {
|
_ => None,
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
TriggerPoint::InlayHint(highlight, lsp_location, server_id) => Some((
|
TriggerPoint::InlayHint(highlight, lsp_location, server_id) => Some((
|
||||||
Some(RangeInEditor::Inlay(highlight.clone())),
|
Some(RangeInEditor::Inlay(highlight.clone())),
|
||||||
vec![HoverLink::InlayHint(lsp_location.clone(), *server_id)],
|
vec![HoverLink::InlayHint(lsp_location.clone(), *server_id)],
|
||||||
@@ -606,7 +616,8 @@ pub fn show_link_definition(
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|(symbol_range, _)| symbol_range.clone());
|
.and_then(|(symbol_range, _)| symbol_range.clone());
|
||||||
|
|
||||||
if let Some((symbol_range, definitions)) = result {
|
match result {
|
||||||
|
Some((symbol_range, definitions)) => {
|
||||||
hovered_link_state.links = definitions;
|
hovered_link_state.links = definitions;
|
||||||
|
|
||||||
let underline_hovered_link = !hovered_link_state.links.is_empty()
|
let underline_hovered_link = !hovered_link_state.links.is_empty()
|
||||||
@@ -639,14 +650,24 @@ pub fn show_link_definition(
|
|||||||
|
|
||||||
match highlight_range {
|
match highlight_range {
|
||||||
RangeInEditor::Text(text_range) => editor
|
RangeInEditor::Text(text_range) => editor
|
||||||
.highlight_text::<HoveredLinkState>(vec![text_range], style, cx),
|
.highlight_text::<HoveredLinkState>(
|
||||||
|
vec![text_range],
|
||||||
|
style,
|
||||||
|
cx,
|
||||||
|
),
|
||||||
RangeInEditor::Inlay(highlight) => editor
|
RangeInEditor::Inlay(highlight) => editor
|
||||||
.highlight_inlays::<HoveredLinkState>(vec![highlight], style, cx),
|
.highlight_inlays::<HoveredLinkState>(
|
||||||
|
vec![highlight],
|
||||||
|
style,
|
||||||
|
cx,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
editor.hide_hovered_link(cx);
|
editor.hide_hovered_link(cx);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok::<_, anyhow::Error>(())
|
Ok::<_, anyhow::Error>(())
|
||||||
|
|||||||
@@ -289,7 +289,8 @@ fn show_hover(
|
|||||||
// Find the entry with the most specific range
|
// Find the entry with the most specific range
|
||||||
.min_by_key(|entry| entry.range.len());
|
.min_by_key(|entry| entry.range.len());
|
||||||
|
|
||||||
let diagnostic_popover = if let Some(local_diagnostic) = local_diagnostic {
|
let diagnostic_popover = match local_diagnostic {
|
||||||
|
Some(local_diagnostic) => {
|
||||||
let text = match local_diagnostic.diagnostic.source {
|
let text = match local_diagnostic.diagnostic.source {
|
||||||
Some(ref source) => {
|
Some(ref source) => {
|
||||||
format!("{source}: {}", local_diagnostic.diagnostic.message)
|
format!("{source}: {}", local_diagnostic.diagnostic.message)
|
||||||
@@ -348,7 +349,9 @@ fn show_hover(
|
|||||||
});
|
});
|
||||||
let markdown_style = MarkdownStyle {
|
let markdown_style = MarkdownStyle {
|
||||||
base_text_style,
|
base_text_style,
|
||||||
selection_background_color: { cx.theme().players().local().selection },
|
selection_background_color: {
|
||||||
|
cx.theme().players().local().selection
|
||||||
|
},
|
||||||
link: TextStyleRefinement {
|
link: TextStyleRefinement {
|
||||||
underline: Some(gpui::UnderlineStyle {
|
underline: Some(gpui::UnderlineStyle {
|
||||||
thickness: px(1.),
|
thickness: px(1.),
|
||||||
@@ -372,43 +375,48 @@ fn show_hover(
|
|||||||
keyboard_grace: Rc::new(RefCell::new(ignore_timeout)),
|
keyboard_grace: Rc::new(RefCell::new(ignore_timeout)),
|
||||||
anchor: Some(anchor),
|
anchor: Some(anchor),
|
||||||
})
|
})
|
||||||
} else {
|
}
|
||||||
None
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.update(cx, |this, _| {
|
this.update(cx, |this, _| {
|
||||||
this.hover_state.diagnostic_popover = diagnostic_popover;
|
this.hover_state.diagnostic_popover = diagnostic_popover;
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let invisible_char = if let Some(invisible) = snapshot
|
let invisible_char = match snapshot
|
||||||
.buffer_snapshot
|
.buffer_snapshot
|
||||||
.chars_at(anchor)
|
.chars_at(anchor)
|
||||||
.next()
|
.next()
|
||||||
.filter(|&c| is_invisible(c))
|
.filter(|&c| is_invisible(c))
|
||||||
{
|
{
|
||||||
|
Some(invisible) => {
|
||||||
let after = snapshot.buffer_snapshot.anchor_after(
|
let after = snapshot.buffer_snapshot.anchor_after(
|
||||||
anchor.to_offset(&snapshot.buffer_snapshot) + invisible.len_utf8(),
|
anchor.to_offset(&snapshot.buffer_snapshot) + invisible.len_utf8(),
|
||||||
);
|
);
|
||||||
Some((invisible, anchor..after))
|
Some((invisible, anchor..after))
|
||||||
} else if let Some(invisible) = snapshot
|
}
|
||||||
|
_ => {
|
||||||
|
match snapshot
|
||||||
.buffer_snapshot
|
.buffer_snapshot
|
||||||
.reversed_chars_at(anchor)
|
.reversed_chars_at(anchor)
|
||||||
.next()
|
.next()
|
||||||
.filter(|&c| is_invisible(c))
|
.filter(|&c| is_invisible(c))
|
||||||
{
|
{
|
||||||
|
Some(invisible) => {
|
||||||
let before = snapshot.buffer_snapshot.anchor_before(
|
let before = snapshot.buffer_snapshot.anchor_before(
|
||||||
anchor.to_offset(&snapshot.buffer_snapshot) - invisible.len_utf8(),
|
anchor.to_offset(&snapshot.buffer_snapshot) - invisible.len_utf8(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Some((invisible, before..anchor))
|
Some((invisible, before..anchor))
|
||||||
} else {
|
}
|
||||||
None
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let hovers_response = if let Some(hover_request) = hover_request {
|
let hovers_response = match hover_request {
|
||||||
hover_request.await
|
Some(hover_request) => hover_request.await,
|
||||||
} else {
|
_ => Vec::new(),
|
||||||
Vec::new()
|
|
||||||
};
|
};
|
||||||
let snapshot = this.update_in(cx, |this, window, cx| this.snapshot(window, cx))?;
|
let snapshot = this.update_in(cx, |this, window, cx| this.snapshot(window, cx))?;
|
||||||
let mut hover_highlights = Vec::with_capacity(hovers_response.len());
|
let mut hover_highlights = Vec::with_capacity(hovers_response.len());
|
||||||
@@ -543,11 +551,12 @@ async fn parse_blocks(
|
|||||||
language: Option<Arc<Language>>,
|
language: Option<Arc<Language>>,
|
||||||
cx: &mut AsyncWindowContext,
|
cx: &mut AsyncWindowContext,
|
||||||
) -> Option<Entity<Markdown>> {
|
) -> Option<Entity<Markdown>> {
|
||||||
let fallback_language_name = if let Some(ref l) = language {
|
let fallback_language_name = match language {
|
||||||
|
Some(ref l) => {
|
||||||
let l = Arc::clone(l);
|
let l = Arc::clone(l);
|
||||||
Some(l.lsp_id().clone())
|
Some(l.lsp_id().clone())
|
||||||
} else {
|
}
|
||||||
None
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let combined_text = blocks
|
let combined_text = blocks
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ impl Editor {
|
|||||||
cx: &mut Context<Editor>,
|
cx: &mut Context<Editor>,
|
||||||
) -> Option<Vec<IndentGuide>> {
|
) -> Option<Vec<IndentGuide>> {
|
||||||
let show_indent_guides = self.should_show_indent_guides().unwrap_or_else(|| {
|
let show_indent_guides = self.should_show_indent_guides().unwrap_or_else(|| {
|
||||||
if let Some(buffer) = self.buffer().read(cx).as_singleton() {
|
match self.buffer().read(cx).as_singleton() {
|
||||||
|
Some(buffer) => {
|
||||||
language_settings(
|
language_settings(
|
||||||
buffer.read(cx).language().map(|l| l.name()),
|
buffer.read(cx).language().map(|l| l.name()),
|
||||||
buffer.read(cx).file(),
|
buffer.read(cx).file(),
|
||||||
@@ -44,8 +45,8 @@ impl Editor {
|
|||||||
)
|
)
|
||||||
.indent_guides
|
.indent_guides
|
||||||
.enabled
|
.enabled
|
||||||
} else {
|
}
|
||||||
true
|
_ => true,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user