Merge branch 'main' into edit-prediction-syntax-highlighting

This commit is contained in:
Agus Zubiaga 2025-01-22 16:00:46 -03:00
commit 96ed0f4432
15 changed files with 327 additions and 264 deletions

2
Cargo.lock generated
View file

@ -16267,7 +16267,7 @@ dependencies = [
[[package]]
name = "zed"
version = "0.171.0"
version = "0.172.0"
dependencies = [
"activity_indicator",
"anyhow",

View file

@ -1,5 +1,6 @@
#![cfg_attr(target_os = "windows", allow(unused, dead_code))]
mod assistant_configuration;
pub mod assistant_panel;
mod inline_assistant;
pub mod slash_command_settings;

View file

@ -0,0 +1,197 @@
use std::sync::Arc;
use collections::HashMap;
use gpui::{canvas, AnyView, AppContext, EventEmitter, FocusHandle, FocusableView, Subscription};
use language_model::{LanguageModelProvider, LanguageModelProviderId, LanguageModelRegistry};
use ui::{prelude::*, ElevationIndex};
use workspace::Item;
pub struct ConfigurationView {
focus_handle: FocusHandle,
configuration_views: HashMap<LanguageModelProviderId, AnyView>,
_registry_subscription: Subscription,
}
impl ConfigurationView {
pub fn new(cx: &mut ViewContext<Self>) -> Self {
let focus_handle = cx.focus_handle();
let registry_subscription = cx.subscribe(
&LanguageModelRegistry::global(cx),
|this, _, event: &language_model::Event, cx| match event {
language_model::Event::AddedProvider(provider_id) => {
let provider = LanguageModelRegistry::read_global(cx).provider(provider_id);
if let Some(provider) = provider {
this.add_configuration_view(&provider, cx);
}
}
language_model::Event::RemovedProvider(provider_id) => {
this.remove_configuration_view(provider_id);
}
_ => {}
},
);
let mut this = Self {
focus_handle,
configuration_views: HashMap::default(),
_registry_subscription: registry_subscription,
};
this.build_configuration_views(cx);
this
}
fn build_configuration_views(&mut self, cx: &mut ViewContext<Self>) {
let providers = LanguageModelRegistry::read_global(cx).providers();
for provider in providers {
self.add_configuration_view(&provider, cx);
}
}
fn remove_configuration_view(&mut self, provider_id: &LanguageModelProviderId) {
self.configuration_views.remove(provider_id);
}
fn add_configuration_view(
&mut self,
provider: &Arc<dyn LanguageModelProvider>,
cx: &mut ViewContext<Self>,
) {
let configuration_view = provider.configuration_view(cx);
self.configuration_views
.insert(provider.id(), configuration_view);
}
fn render_provider_view(
&mut self,
provider: &Arc<dyn LanguageModelProvider>,
cx: &mut ViewContext<Self>,
) -> Div {
let provider_id = provider.id().0.clone();
let provider_name = provider.name().0.clone();
let configuration_view = self.configuration_views.get(&provider.id()).cloned();
let open_new_context = cx.listener({
let provider = provider.clone();
move |_, _, cx| {
cx.emit(ConfigurationViewEvent::NewProviderContextEditor(
provider.clone(),
))
}
});
v_flex()
.gap_2()
.child(
h_flex()
.justify_between()
.child(Headline::new(provider_name.clone()).size(HeadlineSize::Small))
.when(provider.is_authenticated(cx), move |this| {
this.child(
h_flex().justify_end().child(
Button::new(
SharedString::from(format!("new-context-{provider_id}")),
"Open New Chat",
)
.icon_position(IconPosition::Start)
.icon(IconName::Plus)
.style(ButtonStyle::Filled)
.layer(ElevationIndex::ModalSurface)
.on_click(open_new_context),
),
)
}),
)
.child(
div()
.p(DynamicSpacing::Base08.rems(cx))
.bg(cx.theme().colors().surface_background)
.border_1()
.border_color(cx.theme().colors().border_variant)
.rounded_md()
.when(configuration_view.is_none(), |this| {
this.child(div().child(Label::new(format!(
"No configuration view for {}",
provider_name
))))
})
.when_some(configuration_view, |this, configuration_view| {
this.child(configuration_view)
}),
)
}
}
impl Render for ConfigurationView {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let providers = LanguageModelRegistry::read_global(cx).providers();
let provider_views = providers
.into_iter()
.map(|provider| self.render_provider_view(&provider, cx))
.collect::<Vec<_>>();
let mut element = v_flex()
.id("assistant-configuration-view")
.track_focus(&self.focus_handle(cx))
.bg(cx.theme().colors().editor_background)
.size_full()
.overflow_y_scroll()
.child(
v_flex()
.p(DynamicSpacing::Base16.rems(cx))
.border_b_1()
.border_color(cx.theme().colors().border)
.gap_1()
.child(Headline::new("Configure your Assistant").size(HeadlineSize::Medium))
.child(
Label::new(
"At least one LLM provider must be configured to use the Assistant.",
)
.color(Color::Muted),
),
)
.child(
v_flex()
.p(DynamicSpacing::Base16.rems(cx))
.mt_1()
.gap_6()
.flex_1()
.children(provider_views),
)
.into_any();
// We use a canvas here to get scrolling to work in the ConfigurationView. It's a workaround
// because we couldn't the element to take up the size of the parent.
canvas(
move |bounds, cx| {
element.prepaint_as_root(bounds.origin, bounds.size.into(), cx);
element
},
|_, mut element, cx| {
element.paint(cx);
},
)
.flex_1()
.w_full()
}
}
pub enum ConfigurationViewEvent {
NewProviderContextEditor(Arc<dyn LanguageModelProvider>),
}
impl EventEmitter<ConfigurationViewEvent> for ConfigurationView {}
impl FocusableView for ConfigurationView {
fn focus_handle(&self, _: &AppContext) -> FocusHandle {
self.focus_handle.clone()
}
}
impl Item for ConfigurationView {
type Event = ConfigurationViewEvent;
fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
Some("Configuration".into())
}
}

View file

@ -1,3 +1,4 @@
use crate::assistant_configuration::{ConfigurationView, ConfigurationViewEvent};
use crate::{
terminal_inline_assistant::TerminalInlineAssistant, DeployHistory, DeployPromptLibrary,
InlineAssistant, NewContext,
@ -13,19 +14,15 @@ use assistant_settings::{AssistantDockPosition, AssistantSettings};
use assistant_slash_command::SlashCommandWorkingSet;
use assistant_tool::ToolWorkingSet;
use client::{proto, Client, Status};
use collections::HashMap;
use editor::{Editor, EditorEvent};
use fs::Fs;
use gpui::{
canvas, div, prelude::*, Action, AnyView, AppContext, AsyncWindowContext, EventEmitter,
ExternalPaths, FocusHandle, FocusableView, InteractiveElement, IntoElement, Model,
ParentElement, Pixels, Render, SharedString, StatefulInteractiveElement, Styled, Subscription,
Task, UpdateGlobal, View, WeakView,
prelude::*, Action, AppContext, AsyncWindowContext, EventEmitter, ExternalPaths, FocusHandle,
FocusableView, InteractiveElement, IntoElement, Model, ParentElement, Pixels, Render, Styled,
Subscription, Task, UpdateGlobal, View, WeakView,
};
use language::LanguageRegistry;
use language_model::{
LanguageModelProvider, LanguageModelProviderId, LanguageModelRegistry, ZED_CLOUD_PROVIDER_ID,
};
use language_model::{LanguageModelProviderId, LanguageModelRegistry, ZED_CLOUD_PROVIDER_ID};
use language_model_selector::LanguageModelSelector;
use project::Project;
use prompt_library::{open_prompt_library, PromptBuilder, PromptLibrary};
@ -34,12 +31,11 @@ use settings::{update_settings_file, Settings};
use smol::stream::StreamExt;
use std::{ops::ControlFlow, path::PathBuf, sync::Arc};
use terminal_view::{terminal_panel::TerminalPanel, TerminalView};
use ui::{prelude::*, ContextMenu, ElevationIndex, PopoverMenu, PopoverMenuHandle, Tooltip};
use ui::{prelude::*, ContextMenu, PopoverMenu, PopoverMenuHandle, Tooltip};
use util::{maybe, ResultExt};
use workspace::DraggedTab;
use workspace::{
dock::{DockPosition, Panel, PanelEvent},
item::Item,
pane, DraggedSelection, Pane, ShowConfiguration, ToggleZoom, Workspace,
};
use zed_actions::assistant::{InlineAssist, ToggleFocus};
@ -1371,193 +1367,3 @@ pub enum WorkflowAssistStatus {
Done,
Idle,
}
pub struct ConfigurationView {
focus_handle: FocusHandle,
configuration_views: HashMap<LanguageModelProviderId, AnyView>,
_registry_subscription: Subscription,
}
impl ConfigurationView {
fn new(cx: &mut ViewContext<Self>) -> Self {
let focus_handle = cx.focus_handle();
let registry_subscription = cx.subscribe(
&LanguageModelRegistry::global(cx),
|this, _, event: &language_model::Event, cx| match event {
language_model::Event::AddedProvider(provider_id) => {
let provider = LanguageModelRegistry::read_global(cx).provider(provider_id);
if let Some(provider) = provider {
this.add_configuration_view(&provider, cx);
}
}
language_model::Event::RemovedProvider(provider_id) => {
this.remove_configuration_view(provider_id);
}
_ => {}
},
);
let mut this = Self {
focus_handle,
configuration_views: HashMap::default(),
_registry_subscription: registry_subscription,
};
this.build_configuration_views(cx);
this
}
fn build_configuration_views(&mut self, cx: &mut ViewContext<Self>) {
let providers = LanguageModelRegistry::read_global(cx).providers();
for provider in providers {
self.add_configuration_view(&provider, cx);
}
}
fn remove_configuration_view(&mut self, provider_id: &LanguageModelProviderId) {
self.configuration_views.remove(provider_id);
}
fn add_configuration_view(
&mut self,
provider: &Arc<dyn LanguageModelProvider>,
cx: &mut ViewContext<Self>,
) {
let configuration_view = provider.configuration_view(cx);
self.configuration_views
.insert(provider.id(), configuration_view);
}
fn render_provider_view(
&mut self,
provider: &Arc<dyn LanguageModelProvider>,
cx: &mut ViewContext<Self>,
) -> Div {
let provider_id = provider.id().0.clone();
let provider_name = provider.name().0.clone();
let configuration_view = self.configuration_views.get(&provider.id()).cloned();
let open_new_context = cx.listener({
let provider = provider.clone();
move |_, _, cx| {
cx.emit(ConfigurationViewEvent::NewProviderContextEditor(
provider.clone(),
))
}
});
v_flex()
.gap_2()
.child(
h_flex()
.justify_between()
.child(Headline::new(provider_name.clone()).size(HeadlineSize::Small))
.when(provider.is_authenticated(cx), move |this| {
this.child(
h_flex().justify_end().child(
Button::new(
SharedString::from(format!("new-context-{provider_id}")),
"Open New Chat",
)
.icon_position(IconPosition::Start)
.icon(IconName::Plus)
.style(ButtonStyle::Filled)
.layer(ElevationIndex::ModalSurface)
.on_click(open_new_context),
),
)
}),
)
.child(
div()
.p(DynamicSpacing::Base08.rems(cx))
.bg(cx.theme().colors().surface_background)
.border_1()
.border_color(cx.theme().colors().border_variant)
.rounded_md()
.when(configuration_view.is_none(), |this| {
this.child(div().child(Label::new(format!(
"No configuration view for {}",
provider_name
))))
})
.when_some(configuration_view, |this, configuration_view| {
this.child(configuration_view)
}),
)
}
}
impl Render for ConfigurationView {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let providers = LanguageModelRegistry::read_global(cx).providers();
let provider_views = providers
.into_iter()
.map(|provider| self.render_provider_view(&provider, cx))
.collect::<Vec<_>>();
let mut element = v_flex()
.id("assistant-configuration-view")
.track_focus(&self.focus_handle(cx))
.bg(cx.theme().colors().editor_background)
.size_full()
.overflow_y_scroll()
.child(
v_flex()
.p(DynamicSpacing::Base16.rems(cx))
.border_b_1()
.border_color(cx.theme().colors().border)
.gap_1()
.child(Headline::new("Configure your Assistant").size(HeadlineSize::Medium))
.child(
Label::new(
"At least one LLM provider must be configured to use the Assistant.",
)
.color(Color::Muted),
),
)
.child(
v_flex()
.p(DynamicSpacing::Base16.rems(cx))
.mt_1()
.gap_6()
.flex_1()
.children(provider_views),
)
.into_any();
// We use a canvas here to get scrolling to work in the ConfigurationView. It's a workaround
// because we couldn't the element to take up the size of the parent.
canvas(
move |bounds, cx| {
element.prepaint_as_root(bounds.origin, bounds.size.into(), cx);
element
},
|_, mut element, cx| {
element.paint(cx);
},
)
.flex_1()
.w_full()
}
}
pub enum ConfigurationViewEvent {
NewProviderContextEditor(Arc<dyn LanguageModelProvider>),
}
impl EventEmitter<ConfigurationViewEvent> for ConfigurationView {}
impl FocusableView for ConfigurationView {
fn focus_handle(&self, _: &AppContext) -> FocusHandle {
self.focus_handle.clone()
}
}
impl Item for ConfigurationView {
type Event = ConfigurationViewEvent;
fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
Some("Configuration".into())
}
}

View file

@ -397,8 +397,12 @@ impl Panel for AssistantPanel {
"AssistantPanel2"
}
fn position(&self, _cx: &WindowContext) -> DockPosition {
DockPosition::Right
fn position(&self, cx: &WindowContext) -> DockPosition {
match AssistantSettings::get_global(cx).dock {
AssistantDockPosition::Left => DockPosition::Left,
AssistantDockPosition::Bottom => DockPosition::Bottom,
AssistantDockPosition::Right => DockPosition::Right,
}
}
fn position_is_valid(&self, _: DockPosition) -> bool {

View file

@ -865,18 +865,22 @@ impl CompletionsMenu {
drop(completions);
let mut entries = self.entries.borrow_mut();
if let Some(CompletionEntry::InlineCompletionHint(_)) = entries.first() {
let new_selection = if let Some(CompletionEntry::InlineCompletionHint(_)) = entries.first()
{
entries.truncate(1);
if inline_completion_was_selected || matches.is_empty() {
self.selected_item = 0;
0
} else {
self.selected_item = 1;
1
}
} else {
entries.truncate(0);
self.selected_item = 0;
}
0
};
entries.extend(matches.into_iter().map(CompletionEntry::Match));
self.selected_item = new_selection;
self.scroll_handle
.scroll_to_item(new_selection, ScrollStrategy::Top);
}
}

View file

@ -482,11 +482,17 @@ enum InlineCompletionText {
Edit(HighlightedEdits),
}
pub(crate) enum EditDisplayMode {
TabAccept,
DiffPopover,
Inline,
}
enum InlineCompletion {
Edit {
edits: Vec<(Range<Anchor>, String)>,
edit_preview: Option<EditPreview>,
single_line: bool,
display_mode: EditDisplayMode,
},
Move(Anchor),
}
@ -4933,13 +4939,23 @@ impl Editor {
invalidation_row_range = edit_start_row..edit_end_row;
let single_line = first_edit_start_point.row == last_edit_end_point.row
&& !edits.iter().any(|(_, edit)| edit.contains('\n'));
let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
if provider.show_tab_accept_marker()
&& first_edit_start_point.row == last_edit_end_point.row
&& !edits.iter().any(|(_, edit)| edit.contains('\n'))
{
EditDisplayMode::TabAccept
} else {
EditDisplayMode::Inline
}
} else {
EditDisplayMode::DiffPopover
};
completion = InlineCompletion::Edit {
edits,
single_line,
edit_preview: inline_completion.edit_preview,
display_mode,
};
};
@ -4984,7 +5000,7 @@ impl Editor {
InlineCompletion::Edit {
edits,
edit_preview,
single_line: _,
display_mode: _,
} => edit_preview
.as_ref()
.and_then(|edit_preview| {
@ -15225,3 +15241,31 @@ pub struct KillRing(ClipboardItem);
impl Global for KillRing {}
const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
fn all_edits_insertions_or_deletions(
edits: &Vec<(Range<Anchor>, String)>,
snapshot: &MultiBufferSnapshot,
) -> bool {
let mut all_insertions = true;
let mut all_deletions = true;
for (range, new_text) in edits.iter() {
let range_is_empty = range.to_offset(&snapshot).is_empty();
let text_is_empty = new_text.is_empty();
if range_is_empty != text_is_empty {
if range_is_empty {
all_deletions = false;
} else {
all_insertions = false;
}
} else {
return false;
}
if !all_insertions && !all_deletions {
return false;
}
}
all_insertions || all_deletions
}

View file

@ -18,12 +18,13 @@ use crate::{
mouse_context_menu::{self, MenuPosition, MouseContextMenu},
scroll::{axis_pair, scroll_amount::ScrollAmount, AxisPair},
BlockId, ChunkReplacement, CursorShape, CustomBlockId, DisplayPoint, DisplayRow,
DocumentHighlightRead, DocumentHighlightWrite, Editor, EditorMode, EditorSettings,
EditorSnapshot, EditorStyle, ExpandExcerpts, FocusedBlock, GutterDimensions, HalfPageDown,
HalfPageUp, HandleInput, HoveredCursor, HoveredHunk, InlineCompletion, JumpData, LineDown,
LineUp, OpenExcerpts, PageDown, PageUp, Point, RowExt, RowRangeExt, SelectPhase, Selection,
SoftWrap, StickyHeaderExcerpt, ToPoint, ToggleFold, CURSORS_VISIBLE_FOR, FILE_HEADER_HEIGHT,
GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED, MAX_LINE_LEN, MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
DocumentHighlightRead, DocumentHighlightWrite, EditDisplayMode, Editor, EditorMode,
EditorSettings, EditorSnapshot, EditorStyle, ExpandExcerpts, FocusedBlock, GutterDimensions,
HalfPageDown, HalfPageUp, HandleInput, HoveredCursor, HoveredHunk, InlineCompletion, JumpData,
LineDown, LineUp, OpenExcerpts, PageDown, PageUp, Point, RowExt, RowRangeExt, SelectPhase,
Selection, SoftWrap, StickyHeaderExcerpt, ToPoint, ToggleFold, CURSORS_VISIBLE_FOR,
FILE_HEADER_HEIGHT, GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED, MAX_LINE_LEN,
MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
};
use client::ParticipantIndex;
use collections::{BTreeMap, HashMap, HashSet};
@ -50,7 +51,7 @@ use language::{
use lsp::DiagnosticSeverity;
use multi_buffer::{
Anchor, AnchorRangeExt, ExcerptId, ExcerptInfo, ExpandExcerptDirection, MultiBufferPoint,
MultiBufferRow, MultiBufferSnapshot, ToOffset,
MultiBufferRow, ToOffset,
};
use project::project_settings::{GitGutterSetting, ProjectSettings};
use settings::Settings;
@ -1628,7 +1629,8 @@ impl EditorElement {
if let Some(inline_completion) = editor.active_inline_completion.as_ref() {
match &inline_completion.completion {
InlineCompletion::Edit {
single_line: true, ..
display_mode: EditDisplayMode::TabAccept,
..
} => padding += INLINE_ACCEPT_SUGGESTION_EM_WIDTHS,
_ => {}
}
@ -3389,7 +3391,7 @@ impl EditorElement {
InlineCompletion::Edit {
edits,
edit_preview,
single_line,
display_mode,
} => {
if self.editor.read(cx).has_active_completions_menu() {
return None;
@ -3414,8 +3416,8 @@ impl EditorElement {
return None;
}
if all_edits_insertions_or_deletions(edits, &editor_snapshot.buffer_snapshot) {
if *single_line {
match display_mode {
EditDisplayMode::TabAccept => {
let range = &edits.first()?.0;
let target_display_point = range.end.to_display_point(editor_snapshot);
@ -3437,8 +3439,8 @@ impl EditorElement {
return Some(element);
}
return None;
EditDisplayMode::Inline => return None,
EditDisplayMode::DiffPopover => {}
}
let highlighted_edits = edit_preview.as_ref().and_then(|edit_preview| {
@ -5250,34 +5252,6 @@ fn inline_completion_tab_indicator(
.into_any()
}
fn all_edits_insertions_or_deletions(
edits: &Vec<(Range<Anchor>, String)>,
snapshot: &MultiBufferSnapshot,
) -> bool {
let mut all_insertions = true;
let mut all_deletions = true;
for (range, new_text) in edits.iter() {
let range_is_empty = range.to_offset(&snapshot).is_empty();
let text_is_empty = new_text.is_empty();
if range_is_empty != text_is_empty {
if range_is_empty {
all_deletions = false;
} else {
all_insertions = false;
}
} else {
return false;
}
if !all_insertions && !all_deletions {
return false;
}
}
all_insertions || all_deletions
}
#[allow(clippy::too_many_arguments)]
fn prepaint_gutter_button(
button: IconButton,

View file

@ -23,6 +23,9 @@ pub trait InlineCompletionProvider: 'static + Sized {
fn display_name() -> &'static str;
fn show_completions_in_menu() -> bool;
fn show_completions_in_normal_mode() -> bool;
fn show_tab_accept_marker() -> bool {
false
}
fn is_enabled(
&self,
buffer: &Model<Buffer>,
@ -68,6 +71,7 @@ pub trait InlineCompletionProviderHandle {
) -> bool;
fn show_completions_in_menu(&self) -> bool;
fn show_completions_in_normal_mode(&self) -> bool;
fn show_tab_accept_marker(&self) -> bool;
fn needs_terms_acceptance(&self, cx: &AppContext) -> bool;
fn is_refreshing(&self, cx: &AppContext) -> bool;
fn refresh(
@ -114,6 +118,10 @@ where
T::show_completions_in_normal_mode()
}
fn show_tab_accept_marker(&self) -> bool {
T::show_tab_accept_marker()
}
fn is_enabled(
&self,
buffer: &Model<Buffer>,

View file

@ -15,7 +15,7 @@ use crate::platforms::{platform_linux, platform_mac, platform_windows};
use auto_update::AutoUpdateStatus;
use call::ActiveCall;
use client::{Client, UserStore};
use feature_flags::{FeatureFlagAppExt, ZedPro};
use feature_flags::{FeatureFlagAppExt, GitUiFeatureFlag, ZedPro};
use git_ui::repository_selector::RepositorySelector;
use git_ui::repository_selector::RepositorySelectorPopoverMenu;
use gpui::{
@ -27,6 +27,7 @@ use project::Project;
use rpc::proto;
use settings::Settings as _;
use smallvec::SmallVec;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use theme::ActiveTheme;
use ui::{
@ -43,7 +44,7 @@ pub use stories::*;
const MAX_PROJECT_NAME_LENGTH: usize = 40;
const MAX_BRANCH_NAME_LENGTH: usize = 40;
const BOOK_ONBOARDING: &str = "https://dub.sh/zed-onboarding";
const BOOK_ONBOARDING: &str = "https://dub.sh/zed-c-onboarding";
actions!(
collab,
@ -108,6 +109,7 @@ pub struct TitleBar {
should_move: bool,
application_menu: Option<View<ApplicationMenu>>,
_subscriptions: Vec<Subscription>,
git_ui_enabled: Arc<AtomicBool>,
}
impl Render for TitleBar {
@ -289,6 +291,14 @@ impl TitleBar {
subscriptions.push(cx.observe_window_activation(Self::window_activation_changed));
subscriptions.push(cx.observe(&user_store, |_, _, cx| cx.notify()));
let is_git_ui_enabled = Arc::new(AtomicBool::new(false));
subscriptions.push(cx.observe_flag::<GitUiFeatureFlag, _>({
let is_git_ui_enabled = is_git_ui_enabled.clone();
move |enabled, _cx| {
is_git_ui_enabled.store(enabled, Ordering::SeqCst);
}
}));
Self {
platform_style,
content: div().id(id.into()),
@ -301,6 +311,7 @@ impl TitleBar {
user_store,
client,
_subscriptions: subscriptions,
git_ui_enabled: is_git_ui_enabled,
}
}
@ -484,9 +495,14 @@ impl TitleBar {
&self,
cx: &mut ViewContext<Self>,
) -> Option<impl IntoElement> {
// TODO what to render if no active repository?
if !self.git_ui_enabled.load(Ordering::SeqCst) {
return None;
}
let active_repository = self.project.read(cx).active_repository(cx)?;
let display_name = active_repository.display_name(self.project.read(cx), cx);
// TODO: what to render if no active repository?
Some(RepositorySelectorPopoverMenu::new(
self.repository_selector.clone(),
ButtonLike::new("active-repository")

View file

@ -2043,17 +2043,21 @@ fn matching(map: &DisplaySnapshot, display_point: DisplayPoint) -> DisplayPoint
}
}
if open_range.start >= offset && line_range.contains(&open_range.start) {
let distance = open_range.start - offset;
if (open_range.contains(&offset) || open_range.start >= offset)
&& line_range.contains(&open_range.start)
{
let distance = open_range.start.saturating_sub(offset);
if distance < closest_distance {
closest_pair_destination = Some(close_range.end - 1);
closest_pair_destination = Some(close_range.start);
closest_distance = distance;
continue;
}
}
if close_range.start >= offset && line_range.contains(&close_range.start) {
let distance = close_range.start - offset;
if (close_range.contains(&offset) || close_range.start >= offset)
&& line_range.contains(&close_range.start)
{
let distance = close_range.start.saturating_sub(offset);
if distance < closest_distance {
closest_pair_destination = Some(open_range.start);
closest_distance = distance;

View file

@ -26,7 +26,7 @@ actions!(welcome, [ResetHints]);
pub const FIRST_OPEN: &str = "first_open";
pub const DOCS_URL: &str = "https://zed.dev/docs/";
const BOOK_ONBOARDING: &str = "https://dub.sh/zed-onboarding";
const BOOK_ONBOARDING: &str = "https://dub.sh/zed-c-onboarding";
pub fn init(cx: &mut AppContext) {
BaseKeymap::register(cx);

View file

@ -2,7 +2,7 @@
description = "The fast, collaborative code editor."
edition.workspace = true
name = "zed"
version = "0.171.0"
version = "0.172.0"
publish.workspace = true
license = "GPL-3.0-or-later"
authors = ["Zed Team <hi@zed.dev>"]

View file

@ -399,6 +399,7 @@ fn initialize_panels(prompt_builder: Arc<PromptBuilder>, cx: &mut ViewContext<Wo
_ = timeout => false,
}
};
let git_panel = if git_ui_enabled {
Some(git_ui::git_panel::GitPanel::load(workspace_handle.clone(), cx.clone()).await?)
} else {

View file

@ -1059,6 +1059,10 @@ impl inline_completion::InlineCompletionProvider for ZetaInlineCompletionProvide
true
}
fn show_tab_accept_marker() -> bool {
true
}
fn is_enabled(
&self,
buffer: &Model<Buffer>,