mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-10 14:06:11 +03:00
Overhaul assistant panel (#2610)
Closes https://linear.app/zed-industries/issue/Z-2368/use-a-different-icon-for-the-assistant-panel Closes https://linear.app/zed-industries/issue/Z-2363/ship-the-assistant-only-on-preview Closes https://linear.app/zed-industries/issue/Z-2331/scrolling-makes-it-hard-to-read Closes https://linear.app/zed-industries/issue/Z-2306/allow-undo-and-collaboration-in-assistant This pull request is a significant overhaul of the assistant panel, which now uses a simple `Buffer` as opposed to a `MultiBuffer` to show messages. Specifically, we track the start of each message with an anchor located right after the newline (or `Anchor::MIN` for the first message). When the anchor becomes invalid (that is, the newline is deleted), we merge the message with the preceding ones. Crucially, messages don't actually get deleted so that, if the newline anchor becomes valid again (such as when undoing/redoing), we can restore the messages as well. As part of this overhaul, we are also improving the scrolling behavior to maintain the viewport stable only when editing or moving the cursor, but otherwise leave the scroll position unchanged when manually scrolling up or down. Note that with these changes, we are limiting access to the assistant to users on preview (and dev), as we want to polish the behavior a little more before shipping to the general public. Users on stable will still be able to see the default settings/keybindings of the assistant, but I think that's okay, as they won't be able to do anything with them. Release Notes: - Added support for undo/redo in the assistant (preview-only) - Improved the scrolling behavior of the assistant when it was generating responses. Now Zed will keep the viewport stable only when editing or moving the cursor, but otherwise leave the scroll position unchanged when manually scrolling up or down (preview-only) - Changed the icon of the assistant panel (preview-only) **Note for @JosephTLyons: given that we're feature flagging this, let's make sure things on stable look reasonable and work correctly. Things to look out for: ensure a stock installation works, changing the settings on stable works, changing the keybinding on stable works.**
This commit is contained in:
commit
2b8b954c3e
4
assets/icons/robot_14.svg
Normal file
4
assets/icons/robot_14.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<svg width="14" height="16" viewBox="0 0 14 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M2.5 4C2.5 2.89531 3.39688 2 4.5 2H9.5C10.6031 2 11.5 2.89531 11.5 4V8C11.5 9.10312 10.6031 10 9.5 10H4.5C3.39688 10 2.5 9.10312 2.5 8V4ZM5 4C4.44687 4 4 4.44687 4 5C4 5.55313 4.44687 6 5 6C5.55313 6 6 5.55313 6 5C6 4.44687 5.55313 4 5 4ZM9 6C9.55313 6 10 5.55313 10 5C10 4.44687 9.55313 4 9 4C8.44687 4 8 4.44687 8 5C8 5.55313 8.44687 6 9 6ZM5 8.5C5.275 8.5 5.5 8.275 5.5 8C5.5 7.725 5.275 7.5 5 7.5C4.725 7.5 4.5 7.725 4.5 8C4.5 8.275 4.725 8.5 5 8.5ZM7 7.5C6.725 7.5 6.5 7.725 6.5 8C6.5 8.275 6.725 8.5 7 8.5C7.275 8.5 7.5 8.275 7.5 8C7.5 7.725 7.275 7.5 7 7.5ZM9 8.5C9.275 8.5 9.5 8.275 9.5 8C9.5 7.725 9.275 7.5 9 7.5C8.725 7.5 8.5 7.725 8.5 8C8.5 8.275 8.725 8.5 9 8.5ZM0 14C0 12.3156 1.34312 11 3 11H11C12.6562 11 14 12.3156 14 14V15C14 15.5531 13.5531 16 13 16H11V14C11 13.4469 10.5531 13 10 13H4C3.44687 13 3 13.4469 3 14V16H1C0.447812 16 0 15.5531 0 15V14Z" fill="#808080"/>
|
||||||
|
<path d="M7.5 2H6.5V0.5C6.5 0.22375 6.725 0 7 0C7.275 0 7.5 0.22375 7.5 0.5V2ZM1.5 4.5V7.5C1.5 7.775 1.27625 8 1 8C0.72375 8 0.5 7.775 0.5 7.5V4.5C0.5 4.225 0.72375 4 1 4C1.27625 4 1.5 4.225 1.5 4.5ZM5.5 16H4.5V14.5C4.5 14.225 4.725 14 5 14C5.275 14 5.5 14.225 5.5 14.5V16ZM7.5 16H6.5V14.5C6.5 14.225 6.725 14 7 14C7.275 14 7.5 14.225 7.5 14.5V16ZM9 14C9.275 14 9.5 14.225 9.5 14.5V16H8.5V14.5C8.5 14.225 8.725 14 9 14ZM13.5 7.5C13.5 7.775 13.275 8 13 8C12.725 8 12.5 7.775 12.5 7.5V4.5C12.5 4.225 12.725 4 13 4C13.275 4 13.5 4.225 13.5 4.5V7.5Z" fill="#808080"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
File diff suppressed because it is too large
Load Diff
@ -430,7 +430,7 @@ impl ProjectDiagnosticsEditor {
|
|||||||
});
|
});
|
||||||
|
|
||||||
self.editor.update(cx, |editor, cx| {
|
self.editor.update(cx, |editor, cx| {
|
||||||
editor.remove_blocks(blocks_to_remove, cx);
|
editor.remove_blocks(blocks_to_remove, None, cx);
|
||||||
let block_ids = editor.insert_blocks(
|
let block_ids = editor.insert_blocks(
|
||||||
blocks_to_add.into_iter().map(|block| {
|
blocks_to_add.into_iter().map(|block| {
|
||||||
let (excerpt_id, text_anchor) = block.position;
|
let (excerpt_id, text_anchor) = block.position;
|
||||||
@ -442,6 +442,7 @@ impl ProjectDiagnosticsEditor {
|
|||||||
disposition: block.disposition,
|
disposition: block.disposition,
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
Some(Autoscroll::fit()),
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -31,13 +31,11 @@ use copilot::Copilot;
|
|||||||
pub use display_map::DisplayPoint;
|
pub use display_map::DisplayPoint;
|
||||||
use display_map::*;
|
use display_map::*;
|
||||||
pub use editor_settings::EditorSettings;
|
pub use editor_settings::EditorSettings;
|
||||||
pub use element::RenderExcerptHeaderParams;
|
|
||||||
pub use element::{
|
pub use element::{
|
||||||
Cursor, EditorElement, HighlightedRange, HighlightedRangeLine, LineWithInvisibles,
|
Cursor, EditorElement, HighlightedRange, HighlightedRangeLine, LineWithInvisibles,
|
||||||
};
|
};
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
use fuzzy::{StringMatch, StringMatchCandidate};
|
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||||
use gpui::LayoutContext;
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions,
|
actions,
|
||||||
color::Color,
|
color::Color,
|
||||||
@ -511,7 +509,6 @@ pub struct Editor {
|
|||||||
mode: EditorMode,
|
mode: EditorMode,
|
||||||
show_gutter: bool,
|
show_gutter: bool,
|
||||||
placeholder_text: Option<Arc<str>>,
|
placeholder_text: Option<Arc<str>>,
|
||||||
render_excerpt_header: Option<element::RenderExcerptHeader>,
|
|
||||||
highlighted_rows: Option<Range<u32>>,
|
highlighted_rows: Option<Range<u32>>,
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
background_highlights: BTreeMap<TypeId, (fn(&Theme) -> Color, Vec<Range<Anchor>>)>,
|
background_highlights: BTreeMap<TypeId, (fn(&Theme) -> Color, Vec<Range<Anchor>>)>,
|
||||||
@ -1317,7 +1314,6 @@ impl Editor {
|
|||||||
mode,
|
mode,
|
||||||
show_gutter: mode == EditorMode::Full,
|
show_gutter: mode == EditorMode::Full,
|
||||||
placeholder_text: None,
|
placeholder_text: None,
|
||||||
render_excerpt_header: None,
|
|
||||||
highlighted_rows: None,
|
highlighted_rows: None,
|
||||||
background_highlights: Default::default(),
|
background_highlights: Default::default(),
|
||||||
nav_history: None,
|
nav_history: None,
|
||||||
@ -6272,6 +6268,7 @@ impl Editor {
|
|||||||
}),
|
}),
|
||||||
disposition: BlockDisposition::Below,
|
disposition: BlockDisposition::Below,
|
||||||
}],
|
}],
|
||||||
|
Some(Autoscroll::fit()),
|
||||||
cx,
|
cx,
|
||||||
)[0];
|
)[0];
|
||||||
this.pending_rename = Some(RenameState {
|
this.pending_rename = Some(RenameState {
|
||||||
@ -6338,7 +6335,11 @@ impl Editor {
|
|||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Option<RenameState> {
|
) -> Option<RenameState> {
|
||||||
let rename = self.pending_rename.take()?;
|
let rename = self.pending_rename.take()?;
|
||||||
self.remove_blocks([rename.block_id].into_iter().collect(), cx);
|
self.remove_blocks(
|
||||||
|
[rename.block_id].into_iter().collect(),
|
||||||
|
Some(Autoscroll::fit()),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
self.clear_text_highlights::<Rename>(cx);
|
self.clear_text_highlights::<Rename>(cx);
|
||||||
self.show_local_selections = true;
|
self.show_local_selections = true;
|
||||||
|
|
||||||
@ -6724,29 +6725,43 @@ impl Editor {
|
|||||||
pub fn insert_blocks(
|
pub fn insert_blocks(
|
||||||
&mut self,
|
&mut self,
|
||||||
blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
|
blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
|
||||||
|
autoscroll: Option<Autoscroll>,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Vec<BlockId> {
|
) -> Vec<BlockId> {
|
||||||
let blocks = self
|
let blocks = self
|
||||||
.display_map
|
.display_map
|
||||||
.update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
|
.update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
|
||||||
self.request_autoscroll(Autoscroll::fit(), cx);
|
if let Some(autoscroll) = autoscroll {
|
||||||
|
self.request_autoscroll(autoscroll, cx);
|
||||||
|
}
|
||||||
blocks
|
blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn replace_blocks(
|
pub fn replace_blocks(
|
||||||
&mut self,
|
&mut self,
|
||||||
blocks: HashMap<BlockId, RenderBlock>,
|
blocks: HashMap<BlockId, RenderBlock>,
|
||||||
|
autoscroll: Option<Autoscroll>,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) {
|
) {
|
||||||
self.display_map
|
self.display_map
|
||||||
.update(cx, |display_map, _| display_map.replace_blocks(blocks));
|
.update(cx, |display_map, _| display_map.replace_blocks(blocks));
|
||||||
self.request_autoscroll(Autoscroll::fit(), cx);
|
if let Some(autoscroll) = autoscroll {
|
||||||
|
self.request_autoscroll(autoscroll, cx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_blocks(&mut self, block_ids: HashSet<BlockId>, cx: &mut ViewContext<Self>) {
|
pub fn remove_blocks(
|
||||||
|
&mut self,
|
||||||
|
block_ids: HashSet<BlockId>,
|
||||||
|
autoscroll: Option<Autoscroll>,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) {
|
||||||
self.display_map.update(cx, |display_map, cx| {
|
self.display_map.update(cx, |display_map, cx| {
|
||||||
display_map.remove_blocks(block_ids, cx)
|
display_map.remove_blocks(block_ids, cx)
|
||||||
});
|
});
|
||||||
|
if let Some(autoscroll) = autoscroll {
|
||||||
|
self.request_autoscroll(autoscroll, cx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
|
pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
|
||||||
@ -6827,20 +6842,6 @@ impl Editor {
|
|||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_render_excerpt_header(
|
|
||||||
&mut self,
|
|
||||||
render_excerpt_header: impl 'static
|
|
||||||
+ Fn(
|
|
||||||
&mut Editor,
|
|
||||||
RenderExcerptHeaderParams,
|
|
||||||
&mut LayoutContext<Editor>,
|
|
||||||
) -> AnyElement<Editor>,
|
|
||||||
cx: &mut ViewContext<Self>,
|
|
||||||
) {
|
|
||||||
self.render_excerpt_header = Some(Arc::new(render_excerpt_header));
|
|
||||||
cx.notify();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
|
pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
|
||||||
if let Some(buffer) = self.buffer().read(cx).as_singleton() {
|
if let Some(buffer) = self.buffer().read(cx).as_singleton() {
|
||||||
if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
|
if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
|
||||||
@ -7448,6 +7449,7 @@ pub enum Event {
|
|||||||
},
|
},
|
||||||
ScrollPositionChanged {
|
ScrollPositionChanged {
|
||||||
local: bool,
|
local: bool,
|
||||||
|
autoscroll: bool,
|
||||||
},
|
},
|
||||||
Closed,
|
Closed,
|
||||||
}
|
}
|
||||||
@ -7479,12 +7481,8 @@ impl View for Editor {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut editor = EditorElement::new(style.clone());
|
|
||||||
if let Some(render_excerpt_header) = self.render_excerpt_header.clone() {
|
|
||||||
editor = editor.with_render_excerpt_header(render_excerpt_header);
|
|
||||||
}
|
|
||||||
Stack::new()
|
Stack::new()
|
||||||
.with_child(editor)
|
.with_child(EditorElement::new(style.clone()))
|
||||||
.with_child(ChildView::new(&self.mouse_context_menu, cx))
|
.with_child(ChildView::new(&self.mouse_context_menu, cx))
|
||||||
.into_any()
|
.into_any()
|
||||||
}
|
}
|
||||||
|
@ -2495,6 +2495,7 @@ fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
|
|||||||
height: 1,
|
height: 1,
|
||||||
render: Arc::new(|_| Empty::new().into_any()),
|
render: Arc::new(|_| Empty::new().into_any()),
|
||||||
}],
|
}],
|
||||||
|
Some(Autoscroll::fit()),
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
editor.change_selections(None, cx, |s| {
|
editor.change_selections(None, cx, |s| {
|
||||||
|
@ -91,41 +91,17 @@ impl SelectionLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RenderExcerptHeaderParams<'a> {
|
|
||||||
pub id: crate::ExcerptId,
|
|
||||||
pub buffer: &'a language::BufferSnapshot,
|
|
||||||
pub range: &'a crate::ExcerptRange<text::Anchor>,
|
|
||||||
pub starts_new_buffer: bool,
|
|
||||||
pub gutter_padding: f32,
|
|
||||||
pub editor_style: &'a EditorStyle,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type RenderExcerptHeader = Arc<
|
|
||||||
dyn Fn(
|
|
||||||
&mut Editor,
|
|
||||||
RenderExcerptHeaderParams,
|
|
||||||
&mut LayoutContext<Editor>,
|
|
||||||
) -> AnyElement<Editor>,
|
|
||||||
>;
|
|
||||||
|
|
||||||
pub struct EditorElement {
|
pub struct EditorElement {
|
||||||
style: Arc<EditorStyle>,
|
style: Arc<EditorStyle>,
|
||||||
render_excerpt_header: RenderExcerptHeader,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EditorElement {
|
impl EditorElement {
|
||||||
pub fn new(style: EditorStyle) -> Self {
|
pub fn new(style: EditorStyle) -> Self {
|
||||||
Self {
|
Self {
|
||||||
style: Arc::new(style),
|
style: Arc::new(style),
|
||||||
render_excerpt_header: Arc::new(render_excerpt_header),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_render_excerpt_header(mut self, render: RenderExcerptHeader) -> Self {
|
|
||||||
self.render_excerpt_header = render;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn attach_mouse_handlers(
|
fn attach_mouse_handlers(
|
||||||
scene: &mut SceneBuilder,
|
scene: &mut SceneBuilder,
|
||||||
position_map: &Arc<PositionMap>,
|
position_map: &Arc<PositionMap>,
|
||||||
@ -1531,18 +1507,117 @@ impl EditorElement {
|
|||||||
range,
|
range,
|
||||||
starts_new_buffer,
|
starts_new_buffer,
|
||||||
..
|
..
|
||||||
} => (self.render_excerpt_header)(
|
} => {
|
||||||
editor,
|
let tooltip_style = theme::current(cx).tooltip.clone();
|
||||||
RenderExcerptHeaderParams {
|
let include_root = editor
|
||||||
id: *id,
|
.project
|
||||||
buffer,
|
.as_ref()
|
||||||
range,
|
.map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
|
||||||
starts_new_buffer: *starts_new_buffer,
|
.unwrap_or_default();
|
||||||
gutter_padding,
|
let jump_icon = project::File::from_dyn(buffer.file()).map(|file| {
|
||||||
editor_style: style,
|
let jump_path = ProjectPath {
|
||||||
},
|
worktree_id: file.worktree_id(cx),
|
||||||
cx,
|
path: file.path.clone(),
|
||||||
),
|
};
|
||||||
|
let jump_anchor = range
|
||||||
|
.primary
|
||||||
|
.as_ref()
|
||||||
|
.map_or(range.context.start, |primary| primary.start);
|
||||||
|
let jump_position = language::ToPoint::to_point(&jump_anchor, buffer);
|
||||||
|
|
||||||
|
enum JumpIcon {}
|
||||||
|
MouseEventHandler::<JumpIcon, _>::new((*id).into(), cx, |state, _| {
|
||||||
|
let style = style.jump_icon.style_for(state, false);
|
||||||
|
Svg::new("icons/arrow_up_right_8.svg")
|
||||||
|
.with_color(style.color)
|
||||||
|
.constrained()
|
||||||
|
.with_width(style.icon_width)
|
||||||
|
.aligned()
|
||||||
|
.contained()
|
||||||
|
.with_style(style.container)
|
||||||
|
.constrained()
|
||||||
|
.with_width(style.button_width)
|
||||||
|
.with_height(style.button_width)
|
||||||
|
})
|
||||||
|
.with_cursor_style(CursorStyle::PointingHand)
|
||||||
|
.on_click(MouseButton::Left, move |_, editor, cx| {
|
||||||
|
if let Some(workspace) = editor
|
||||||
|
.workspace
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|(workspace, _)| workspace.upgrade(cx))
|
||||||
|
{
|
||||||
|
workspace.update(cx, |workspace, cx| {
|
||||||
|
Editor::jump(
|
||||||
|
workspace,
|
||||||
|
jump_path.clone(),
|
||||||
|
jump_position,
|
||||||
|
jump_anchor,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.with_tooltip::<JumpIcon>(
|
||||||
|
(*id).into(),
|
||||||
|
"Jump to Buffer".to_string(),
|
||||||
|
Some(Box::new(crate::OpenExcerpts)),
|
||||||
|
tooltip_style.clone(),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
.aligned()
|
||||||
|
.flex_float()
|
||||||
|
});
|
||||||
|
|
||||||
|
if *starts_new_buffer {
|
||||||
|
let editor_font_size = style.text.font_size;
|
||||||
|
let style = &style.diagnostic_path_header;
|
||||||
|
let font_size = (style.text_scale_factor * editor_font_size).round();
|
||||||
|
|
||||||
|
let path = buffer.resolve_file_path(cx, include_root);
|
||||||
|
let mut filename = None;
|
||||||
|
let mut parent_path = None;
|
||||||
|
// Can't use .and_then() because `.file_name()` and `.parent()` return references :(
|
||||||
|
if let Some(path) = path {
|
||||||
|
filename = path.file_name().map(|f| f.to_string_lossy().to_string());
|
||||||
|
parent_path =
|
||||||
|
path.parent().map(|p| p.to_string_lossy().to_string() + "/");
|
||||||
|
}
|
||||||
|
|
||||||
|
Flex::row()
|
||||||
|
.with_child(
|
||||||
|
Label::new(
|
||||||
|
filename.unwrap_or_else(|| "untitled".to_string()),
|
||||||
|
style.filename.text.clone().with_font_size(font_size),
|
||||||
|
)
|
||||||
|
.contained()
|
||||||
|
.with_style(style.filename.container)
|
||||||
|
.aligned(),
|
||||||
|
)
|
||||||
|
.with_children(parent_path.map(|path| {
|
||||||
|
Label::new(path, style.path.text.clone().with_font_size(font_size))
|
||||||
|
.contained()
|
||||||
|
.with_style(style.path.container)
|
||||||
|
.aligned()
|
||||||
|
}))
|
||||||
|
.with_children(jump_icon)
|
||||||
|
.contained()
|
||||||
|
.with_style(style.container)
|
||||||
|
.with_padding_left(gutter_padding)
|
||||||
|
.with_padding_right(gutter_padding)
|
||||||
|
.expanded()
|
||||||
|
.into_any_named("path header block")
|
||||||
|
} else {
|
||||||
|
let text_style = style.text.clone();
|
||||||
|
Flex::row()
|
||||||
|
.with_child(Label::new("⋯", text_style))
|
||||||
|
.with_children(jump_icon)
|
||||||
|
.contained()
|
||||||
|
.with_padding_left(gutter_padding)
|
||||||
|
.with_padding_right(gutter_padding)
|
||||||
|
.expanded()
|
||||||
|
.into_any_named("collapsed context")
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
element.layout(
|
element.layout(
|
||||||
@ -2679,121 +2754,6 @@ impl HighlightedRange {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_excerpt_header(
|
|
||||||
editor: &mut Editor,
|
|
||||||
RenderExcerptHeaderParams {
|
|
||||||
id,
|
|
||||||
buffer,
|
|
||||||
range,
|
|
||||||
starts_new_buffer,
|
|
||||||
gutter_padding,
|
|
||||||
editor_style,
|
|
||||||
}: RenderExcerptHeaderParams,
|
|
||||||
cx: &mut LayoutContext<Editor>,
|
|
||||||
) -> AnyElement<Editor> {
|
|
||||||
let tooltip_style = theme::current(cx).tooltip.clone();
|
|
||||||
let include_root = editor
|
|
||||||
.project
|
|
||||||
.as_ref()
|
|
||||||
.map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
|
|
||||||
.unwrap_or_default();
|
|
||||||
let jump_icon = project::File::from_dyn(buffer.file()).map(|file| {
|
|
||||||
let jump_path = ProjectPath {
|
|
||||||
worktree_id: file.worktree_id(cx),
|
|
||||||
path: file.path.clone(),
|
|
||||||
};
|
|
||||||
let jump_anchor = range
|
|
||||||
.primary
|
|
||||||
.as_ref()
|
|
||||||
.map_or(range.context.start, |primary| primary.start);
|
|
||||||
let jump_position = language::ToPoint::to_point(&jump_anchor, buffer);
|
|
||||||
|
|
||||||
enum JumpIcon {}
|
|
||||||
MouseEventHandler::<JumpIcon, _>::new(id.into(), cx, |state, _| {
|
|
||||||
let style = editor_style.jump_icon.style_for(state, false);
|
|
||||||
Svg::new("icons/arrow_up_right_8.svg")
|
|
||||||
.with_color(style.color)
|
|
||||||
.constrained()
|
|
||||||
.with_width(style.icon_width)
|
|
||||||
.aligned()
|
|
||||||
.contained()
|
|
||||||
.with_style(style.container)
|
|
||||||
.constrained()
|
|
||||||
.with_width(style.button_width)
|
|
||||||
.with_height(style.button_width)
|
|
||||||
})
|
|
||||||
.with_cursor_style(CursorStyle::PointingHand)
|
|
||||||
.on_click(MouseButton::Left, move |_, editor, cx| {
|
|
||||||
if let Some(workspace) = editor
|
|
||||||
.workspace
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|(workspace, _)| workspace.upgrade(cx))
|
|
||||||
{
|
|
||||||
workspace.update(cx, |workspace, cx| {
|
|
||||||
Editor::jump(workspace, jump_path.clone(), jump_position, jump_anchor, cx);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.with_tooltip::<JumpIcon>(
|
|
||||||
id.into(),
|
|
||||||
"Jump to Buffer".to_string(),
|
|
||||||
Some(Box::new(crate::OpenExcerpts)),
|
|
||||||
tooltip_style.clone(),
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
.aligned()
|
|
||||||
.flex_float()
|
|
||||||
});
|
|
||||||
|
|
||||||
if starts_new_buffer {
|
|
||||||
let style = &editor_style.diagnostic_path_header;
|
|
||||||
let font_size = (style.text_scale_factor * editor_style.text.font_size).round();
|
|
||||||
|
|
||||||
let path = buffer.resolve_file_path(cx, include_root);
|
|
||||||
let mut filename = None;
|
|
||||||
let mut parent_path = None;
|
|
||||||
// Can't use .and_then() because `.file_name()` and `.parent()` return references :(
|
|
||||||
if let Some(path) = path {
|
|
||||||
filename = path.file_name().map(|f| f.to_string_lossy().to_string());
|
|
||||||
parent_path = path.parent().map(|p| p.to_string_lossy().to_string() + "/");
|
|
||||||
}
|
|
||||||
|
|
||||||
Flex::row()
|
|
||||||
.with_child(
|
|
||||||
Label::new(
|
|
||||||
filename.unwrap_or_else(|| "untitled".to_string()),
|
|
||||||
style.filename.text.clone().with_font_size(font_size),
|
|
||||||
)
|
|
||||||
.contained()
|
|
||||||
.with_style(style.filename.container)
|
|
||||||
.aligned(),
|
|
||||||
)
|
|
||||||
.with_children(parent_path.map(|path| {
|
|
||||||
Label::new(path, style.path.text.clone().with_font_size(font_size))
|
|
||||||
.contained()
|
|
||||||
.with_style(style.path.container)
|
|
||||||
.aligned()
|
|
||||||
}))
|
|
||||||
.with_children(jump_icon)
|
|
||||||
.contained()
|
|
||||||
.with_style(style.container)
|
|
||||||
.with_padding_left(gutter_padding)
|
|
||||||
.with_padding_right(gutter_padding)
|
|
||||||
.expanded()
|
|
||||||
.into_any_named("path header block")
|
|
||||||
} else {
|
|
||||||
let text_style = editor_style.text.clone();
|
|
||||||
Flex::row()
|
|
||||||
.with_child(Label::new("⋯", text_style))
|
|
||||||
.with_children(jump_icon)
|
|
||||||
.contained()
|
|
||||||
.with_padding_left(gutter_padding)
|
|
||||||
.with_padding_right(gutter_padding)
|
|
||||||
.expanded()
|
|
||||||
.into_any_named("collapsed context")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn position_to_display_point(
|
fn position_to_display_point(
|
||||||
position: Vector2F,
|
position: Vector2F,
|
||||||
text_bounds: RectF,
|
text_bounds: RectF,
|
||||||
@ -2923,6 +2883,7 @@ mod tests {
|
|||||||
position: Anchor::min(),
|
position: Anchor::min(),
|
||||||
render: Arc::new(|_| Empty::new().into_any()),
|
render: Arc::new(|_| Empty::new().into_any()),
|
||||||
}],
|
}],
|
||||||
|
None,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -294,7 +294,7 @@ impl FollowableItem for Editor {
|
|||||||
match event {
|
match event {
|
||||||
Event::Edited => true,
|
Event::Edited => true,
|
||||||
Event::SelectionsChanged { local } => *local,
|
Event::SelectionsChanged { local } => *local,
|
||||||
Event::ScrollPositionChanged { local } => *local,
|
Event::ScrollPositionChanged { local, .. } => *local,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -173,6 +173,7 @@ impl ScrollManager {
|
|||||||
scroll_position: Vector2F,
|
scroll_position: Vector2F,
|
||||||
map: &DisplaySnapshot,
|
map: &DisplaySnapshot,
|
||||||
local: bool,
|
local: bool,
|
||||||
|
autoscroll: bool,
|
||||||
workspace_id: Option<i64>,
|
workspace_id: Option<i64>,
|
||||||
cx: &mut ViewContext<Editor>,
|
cx: &mut ViewContext<Editor>,
|
||||||
) {
|
) {
|
||||||
@ -203,7 +204,7 @@ impl ScrollManager {
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
self.set_anchor(new_anchor, top_row, local, workspace_id, cx);
|
self.set_anchor(new_anchor, top_row, local, autoscroll, workspace_id, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_anchor(
|
fn set_anchor(
|
||||||
@ -211,11 +212,12 @@ impl ScrollManager {
|
|||||||
anchor: ScrollAnchor,
|
anchor: ScrollAnchor,
|
||||||
top_row: u32,
|
top_row: u32,
|
||||||
local: bool,
|
local: bool,
|
||||||
|
autoscroll: bool,
|
||||||
workspace_id: Option<i64>,
|
workspace_id: Option<i64>,
|
||||||
cx: &mut ViewContext<Editor>,
|
cx: &mut ViewContext<Editor>,
|
||||||
) {
|
) {
|
||||||
self.anchor = anchor;
|
self.anchor = anchor;
|
||||||
cx.emit(Event::ScrollPositionChanged { local });
|
cx.emit(Event::ScrollPositionChanged { local, autoscroll });
|
||||||
self.show_scrollbar(cx);
|
self.show_scrollbar(cx);
|
||||||
self.autoscroll_request.take();
|
self.autoscroll_request.take();
|
||||||
if let Some(workspace_id) = workspace_id {
|
if let Some(workspace_id) = workspace_id {
|
||||||
@ -296,21 +298,28 @@ impl Editor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_scroll_position(&mut self, scroll_position: Vector2F, cx: &mut ViewContext<Self>) {
|
pub fn set_scroll_position(&mut self, scroll_position: Vector2F, cx: &mut ViewContext<Self>) {
|
||||||
self.set_scroll_position_internal(scroll_position, true, cx);
|
self.set_scroll_position_internal(scroll_position, true, false, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn set_scroll_position_internal(
|
pub(crate) fn set_scroll_position_internal(
|
||||||
&mut self,
|
&mut self,
|
||||||
scroll_position: Vector2F,
|
scroll_position: Vector2F,
|
||||||
local: bool,
|
local: bool,
|
||||||
|
autoscroll: bool,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) {
|
) {
|
||||||
let map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
|
let map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||||
|
|
||||||
hide_hover(self, cx);
|
hide_hover(self, cx);
|
||||||
let workspace_id = self.workspace.as_ref().map(|workspace| workspace.1);
|
let workspace_id = self.workspace.as_ref().map(|workspace| workspace.1);
|
||||||
self.scroll_manager
|
self.scroll_manager.set_scroll_position(
|
||||||
.set_scroll_position(scroll_position, &map, local, workspace_id, cx);
|
scroll_position,
|
||||||
|
&map,
|
||||||
|
local,
|
||||||
|
autoscroll,
|
||||||
|
workspace_id,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scroll_position(&self, cx: &mut ViewContext<Self>) -> Vector2F {
|
pub fn scroll_position(&self, cx: &mut ViewContext<Self>) -> Vector2F {
|
||||||
@ -326,7 +335,7 @@ impl Editor {
|
|||||||
.to_point(&self.buffer().read(cx).snapshot(cx))
|
.to_point(&self.buffer().read(cx).snapshot(cx))
|
||||||
.row;
|
.row;
|
||||||
self.scroll_manager
|
self.scroll_manager
|
||||||
.set_anchor(scroll_anchor, top_row, true, workspace_id, cx);
|
.set_anchor(scroll_anchor, top_row, true, false, workspace_id, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn set_scroll_anchor_remote(
|
pub(crate) fn set_scroll_anchor_remote(
|
||||||
@ -341,7 +350,7 @@ impl Editor {
|
|||||||
.to_point(&self.buffer().read(cx).snapshot(cx))
|
.to_point(&self.buffer().read(cx).snapshot(cx))
|
||||||
.row;
|
.row;
|
||||||
self.scroll_manager
|
self.scroll_manager
|
||||||
.set_anchor(scroll_anchor, top_row, false, workspace_id, cx);
|
.set_anchor(scroll_anchor, top_row, false, false, workspace_id, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scroll_screen(&mut self, amount: &ScrollAmount, cx: &mut ViewContext<Self>) {
|
pub fn scroll_screen(&mut self, amount: &ScrollAmount, cx: &mut ViewContext<Self>) {
|
||||||
|
@ -136,23 +136,23 @@ impl Editor {
|
|||||||
|
|
||||||
if target_top < start_row {
|
if target_top < start_row {
|
||||||
scroll_position.set_y(target_top);
|
scroll_position.set_y(target_top);
|
||||||
self.set_scroll_position_internal(scroll_position, local, cx);
|
self.set_scroll_position_internal(scroll_position, local, true, cx);
|
||||||
} else if target_bottom >= end_row {
|
} else if target_bottom >= end_row {
|
||||||
scroll_position.set_y(target_bottom - visible_lines);
|
scroll_position.set_y(target_bottom - visible_lines);
|
||||||
self.set_scroll_position_internal(scroll_position, local, cx);
|
self.set_scroll_position_internal(scroll_position, local, true, cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AutoscrollStrategy::Center => {
|
AutoscrollStrategy::Center => {
|
||||||
scroll_position.set_y((first_cursor_top - margin).max(0.0));
|
scroll_position.set_y((first_cursor_top - margin).max(0.0));
|
||||||
self.set_scroll_position_internal(scroll_position, local, cx);
|
self.set_scroll_position_internal(scroll_position, local, true, cx);
|
||||||
}
|
}
|
||||||
AutoscrollStrategy::Top => {
|
AutoscrollStrategy::Top => {
|
||||||
scroll_position.set_y((first_cursor_top).max(0.0));
|
scroll_position.set_y((first_cursor_top).max(0.0));
|
||||||
self.set_scroll_position_internal(scroll_position, local, cx);
|
self.set_scroll_position_internal(scroll_position, local, true, cx);
|
||||||
}
|
}
|
||||||
AutoscrollStrategy::Bottom => {
|
AutoscrollStrategy::Bottom => {
|
||||||
scroll_position.set_y((last_cursor_bottom - visible_lines).max(0.0));
|
scroll_position.set_y((last_cursor_bottom - visible_lines).max(0.0));
|
||||||
self.set_scroll_position_internal(scroll_position, local, cx);
|
self.set_scroll_position_internal(scroll_position, local, true, cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,13 +254,6 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::AppContext) {
|
|||||||
workspace.toggle_panel_focus::<TerminalPanel>(cx);
|
workspace.toggle_panel_focus::<TerminalPanel>(cx);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
cx.add_action(
|
|
||||||
|workspace: &mut Workspace,
|
|
||||||
_: &ai::assistant::ToggleFocus,
|
|
||||||
cx: &mut ViewContext<Workspace>| {
|
|
||||||
workspace.toggle_panel_focus::<AssistantPanel>(cx);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
cx.add_global_action({
|
cx.add_global_action({
|
||||||
let app_state = Arc::downgrade(&app_state);
|
let app_state = Arc::downgrade(&app_state);
|
||||||
move |_: &NewWindow, cx: &mut AppContext| {
|
move |_: &NewWindow, cx: &mut AppContext| {
|
||||||
@ -368,9 +361,12 @@ pub fn initialize_workspace(
|
|||||||
|
|
||||||
let project_panel = ProjectPanel::load(workspace_handle.clone(), cx.clone());
|
let project_panel = ProjectPanel::load(workspace_handle.clone(), cx.clone());
|
||||||
let terminal_panel = TerminalPanel::load(workspace_handle.clone(), cx.clone());
|
let terminal_panel = TerminalPanel::load(workspace_handle.clone(), cx.clone());
|
||||||
let assistant_panel = AssistantPanel::load(workspace_handle.clone(), cx.clone());
|
let assistant_panel = if *util::channel::RELEASE_CHANNEL == ReleaseChannel::Stable {
|
||||||
let (project_panel, terminal_panel, assistant_panel) =
|
None
|
||||||
futures::try_join!(project_panel, terminal_panel, assistant_panel)?;
|
} else {
|
||||||
|
Some(AssistantPanel::load(workspace_handle.clone(), cx.clone()).await?)
|
||||||
|
};
|
||||||
|
let (project_panel, terminal_panel) = futures::try_join!(project_panel, terminal_panel)?;
|
||||||
workspace_handle.update(&mut cx, |workspace, cx| {
|
workspace_handle.update(&mut cx, |workspace, cx| {
|
||||||
let project_panel_position = project_panel.position(cx);
|
let project_panel_position = project_panel.position(cx);
|
||||||
workspace.add_panel(project_panel, cx);
|
workspace.add_panel(project_panel, cx);
|
||||||
@ -389,7 +385,9 @@ pub fn initialize_workspace(
|
|||||||
}
|
}
|
||||||
|
|
||||||
workspace.add_panel(terminal_panel, cx);
|
workspace.add_panel(terminal_panel, cx);
|
||||||
workspace.add_panel(assistant_panel, cx);
|
if let Some(assistant_panel) = assistant_panel {
|
||||||
|
workspace.add_panel(assistant_panel, cx);
|
||||||
|
}
|
||||||
})?;
|
})?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user