Merge branch 'main' into picker

This commit is contained in:
Marshall Bowers 2023-11-08 11:18:54 -05:00
commit fe28d8faea
31 changed files with 5218 additions and 4767 deletions

1
Cargo.lock generated
View file

@ -9028,6 +9028,7 @@ dependencies = [
"fs2", "fs2",
"gpui2", "gpui2",
"indexmap 1.9.3", "indexmap 1.9.3",
"itertools 0.11.0",
"parking_lot 0.11.2", "parking_lot 0.11.2",
"refineable", "refineable",
"schemars", "schemars",

View file

@ -80,7 +80,6 @@ pub fn init(client: &Arc<Client>, cx: &mut AppContext) {
init_settings(cx); init_settings(cx);
let client = Arc::downgrade(client); let client = Arc::downgrade(client);
cx.register_action_type::<SignIn>();
cx.on_action({ cx.on_action({
let client = client.clone(); let client = client.clone();
move |_: &SignIn, cx| { move |_: &SignIn, cx| {
@ -93,7 +92,6 @@ pub fn init(client: &Arc<Client>, cx: &mut AppContext) {
} }
}); });
cx.register_action_type::<SignOut>();
cx.on_action({ cx.on_action({
let client = client.clone(); let client = client.clone();
move |_: &SignOut, cx| { move |_: &SignOut, cx| {
@ -106,7 +104,6 @@ pub fn init(client: &Arc<Client>, cx: &mut AppContext) {
} }
}); });
cx.register_action_type::<Reconnect>();
cx.on_action({ cx.on_action({
let client = client.clone(); let client = client.clone();
move |_: &Reconnect, cx| { move |_: &Reconnect, cx| {

View file

@ -7,8 +7,8 @@ use async_tar::Archive;
use collections::{HashMap, HashSet}; use collections::{HashMap, HashSet};
use futures::{channel::oneshot, future::Shared, Future, FutureExt, TryFutureExt}; use futures::{channel::oneshot, future::Shared, Future, FutureExt, TryFutureExt};
use gpui::{ use gpui::{
AppContext, AsyncAppContext, Context, Entity, EntityId, EventEmitter, Model, ModelContext, actions, AppContext, AsyncAppContext, Context, Entity, EntityId, EventEmitter, Model,
Task, WeakModel, ModelContext, Task, WeakModel,
}; };
use language::{ use language::{
language_settings::{all_language_settings, language_settings}, language_settings::{all_language_settings, language_settings},
@ -34,19 +34,11 @@ use util::{
// todo!() // todo!()
// const COPILOT_AUTH_NAMESPACE: &'static str = "copilot_auth"; // const COPILOT_AUTH_NAMESPACE: &'static str = "copilot_auth";
// actions!(copilot_auth, [SignIn, SignOut]); actions!(SignIn, SignOut);
// todo!() // todo!()
// const COPILOT_NAMESPACE: &'static str = "copilot"; // const COPILOT_NAMESPACE: &'static str = "copilot";
// actions!( actions!(Suggest, NextSuggestion, PreviousSuggestion, Reinstall);
// copilot,
// [Suggest, NextSuggestion, PreviousSuggestion, Reinstall]
// );
//
pub struct Suggest;
pub struct NextSuggestion;
pub struct PreviousSuggestion;
pub struct Reinstall;
pub fn init( pub fn init(
new_server_id: LanguageServerId, new_server_id: LanguageServerId,

File diff suppressed because it is too large Load diff

View file

@ -2,16 +2,25 @@ use crate::{
display_map::{BlockStyle, DisplaySnapshot, FoldStatus, HighlightedChunk, ToDisplayPoint}, display_map::{BlockStyle, DisplaySnapshot, FoldStatus, HighlightedChunk, ToDisplayPoint},
editor_settings::ShowScrollbar, editor_settings::ShowScrollbar,
git::{diff_hunk_to_display, DisplayDiffHunk}, git::{diff_hunk_to_display, DisplayDiffHunk},
hover_popover::hover_at,
link_go_to_definition::{
go_to_fetched_definition, go_to_fetched_type_definition, update_go_to_definition_link,
update_inlay_link_and_hover_points, GoToDefinitionTrigger,
},
scroll::scroll_amount::ScrollAmount,
CursorShape, DisplayPoint, Editor, EditorMode, EditorSettings, EditorSnapshot, EditorStyle, CursorShape, DisplayPoint, Editor, EditorMode, EditorSettings, EditorSnapshot, EditorStyle,
MoveDown, Point, Selection, SoftWrap, ToPoint, MAX_LINE_LEN, HalfPageDown, HalfPageUp, LineDown, LineUp, MoveDown, PageDown, PageUp, Point, SelectPhase,
Selection, SoftWrap, ToPoint, MAX_LINE_LEN,
}; };
use anyhow::Result; use anyhow::Result;
use collections::{BTreeMap, HashMap}; use collections::{BTreeMap, HashMap};
use gpui::{ use gpui::{
black, hsla, point, px, relative, size, transparent_black, Action, AnyElement, BorrowWindow, black, hsla, point, px, relative, size, transparent_black, Action, AnyElement,
Bounds, ContentMask, Corners, DispatchContext, DispatchPhase, Edges, Element, ElementId, BorrowAppContext, BorrowWindow, Bounds, ContentMask, Corners, DispatchContext, DispatchPhase,
Entity, Hsla, KeyDownEvent, KeyListener, KeyMatch, Line, Pixels, ScrollWheelEvent, ShapedGlyph, Edges, Element, ElementId, Entity, GlobalElementId, Hsla, KeyDownEvent, KeyListener, KeyMatch,
Size, StatefulInteractivity, Style, TextRun, TextStyle, TextSystem, ViewContext, WindowContext, Line, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels,
ScrollWheelEvent, ShapedGlyph, Size, Style, TextRun, TextStyle, TextSystem, ViewContext,
WindowContext,
}; };
use itertools::Itertools; use itertools::Itertools;
use language::language_settings::ShowWhitespaceSetting; use language::language_settings::ShowWhitespaceSetting;
@ -30,6 +39,7 @@ use std::{
}; };
use sum_tree::Bias; use sum_tree::Bias;
use theme::{ActiveTheme, PlayerColor}; use theme::{ActiveTheme, PlayerColor};
use util::ResultExt;
use workspace::item::Item; use workspace::item::Item;
enum FoldMarkers {} enum FoldMarkers {}
@ -103,194 +113,54 @@ impl EditorElement {
Self { style } Self { style }
} }
// fn attach_mouse_handlers( fn mouse_down(
// position_map: &Arc<PositionMap>, editor: &mut Editor,
// has_popovers: bool, event: &MouseDownEvent,
// visible_bounds: Bounds<Pixels>, position_map: &PositionMap,
// text_bounds: Bounds<Pixels>, text_bounds: Bounds<Pixels>,
// gutter_bounds: Bounds<Pixels>, gutter_bounds: Bounds<Pixels>,
// bounds: Bounds<Pixels>, cx: &mut ViewContext<Editor>,
// cx: &mut ViewContext<Editor>, ) -> bool {
// ) { let mut click_count = event.click_count;
// enum EditorElementMouseHandlers {} let modifiers = event.modifiers;
// let view_id = cx.view_id();
// cx.scene().push_mouse_region(
// MouseRegion::new::<EditorElementMouseHandlers>(view_id, view_id, visible_bounds)
// .on_down(MouseButton::Left, {
// let position_map = position_map.clone();
// move |event, editor, cx| {
// if !Self::mouse_down(
// editor,
// event.platform_event,
// position_map.as_ref(),
// text_bounds,
// gutter_bounds,
// cx,
// ) {
// cx.propagate_event();
// }
// }
// })
// .on_down(MouseButton::Right, {
// let position_map = position_map.clone();
// move |event, editor, cx| {
// if !Self::mouse_right_down(
// editor,
// event.position,
// position_map.as_ref(),
// text_bounds,
// cx,
// ) {
// cx.propagate_event();
// }
// }
// })
// .on_up(MouseButton::Left, {
// let position_map = position_map.clone();
// move |event, editor, cx| {
// if !Self::mouse_up(
// editor,
// event.position,
// event.cmd,
// event.shift,
// event.alt,
// position_map.as_ref(),
// text_bounds,
// cx,
// ) {
// cx.propagate_event()
// }
// }
// })
// .on_drag(MouseButton::Left, {
// let position_map = position_map.clone();
// move |event, editor, cx| {
// if event.end {
// return;
// }
// if !Self::mouse_dragged( if gutter_bounds.contains_point(&event.position) {
// editor, click_count = 3; // Simulate triple-click when clicking the gutter to select lines
// event.platform_event, } else if !text_bounds.contains_point(&event.position) {
// position_map.as_ref(), return false;
// text_bounds, }
// cx,
// ) {
// cx.propagate_event()
// }
// }
// })
// .on_move({
// let position_map = position_map.clone();
// move |event, editor, cx| {
// if !Self::mouse_moved(
// editor,
// event.platform_event,
// &position_map,
// text_bounds,
// cx,
// ) {
// cx.propagate_event()
// }
// }
// })
// .on_move_out(move |_, editor: &mut Editor, cx| {
// if has_popovers {
// hide_hover(editor, cx);
// }
// })
// .on_scroll({
// let position_map = position_map.clone();
// move |event, editor, cx| {
// if !Self::scroll(
// editor,
// event.position,
// *event.delta.raw(),
// event.delta.precise(),
// &position_map,
// bounds,
// cx,
// ) {
// cx.propagate_event()
// }
// }
// }),
// );
// enum GutterHandlers {} let point_for_position = position_map.point_for_position(text_bounds, event.position);
// let view_id = cx.view_id(); let position = point_for_position.previous_valid;
// let region_id = cx.view_id() + 1; if modifiers.shift && modifiers.alt {
// cx.scene().push_mouse_region( editor.select(
// MouseRegion::new::<GutterHandlers>(view_id, region_id, gutter_bounds).on_hover( SelectPhase::BeginColumnar {
// |hover, editor: &mut Editor, cx| { position,
// editor.gutter_hover( goal_column: point_for_position.exact_unclipped.column(),
// &GutterHover { },
// hovered: hover.started, cx,
// }, );
// cx, } else if modifiers.shift && !modifiers.control && !modifiers.alt && !modifiers.command {
// ); editor.select(
// }, SelectPhase::Extend {
// ), position,
// ) click_count,
// } },
cx,
);
} else {
editor.select(
SelectPhase::Begin {
position,
add: modifiers.alt,
click_count,
},
cx,
);
}
// fn mouse_down( true
// editor: &mut Editor, }
// MouseButtonEvent {
// position,
// modifiers:
// Modifiers {
// shift,
// ctrl,
// alt,
// cmd,
// ..
// },
// mut click_count,
// ..
// }: MouseButtonEvent,
// position_map: &PositionMap,
// text_bounds: Bounds<Pixels>,
// gutter_bounds: Bounds<Pixels>,
// cx: &mut EventContext<Editor>,
// ) -> bool {
// if gutter_bounds.contains_point(position) {
// click_count = 3; // Simulate triple-click when clicking the gutter to select lines
// } else if !text_bounds.contains_point(position) {
// return false;
// }
// let point_for_position = position_map.point_for_position(text_bounds, position);
// let position = point_for_position.previous_valid;
// if shift && alt {
// editor.select(
// SelectPhase::BeginColumnar {
// position,
// goal_column: point_for_position.exact_unclipped.column(),
// },
// cx,
// );
// } else if shift && !ctrl && !alt && !cmd {
// editor.select(
// SelectPhase::Extend {
// position,
// click_count,
// },
// cx,
// );
// } else {
// editor.select(
// SelectPhase::Begin {
// position,
// add: alt,
// click_count,
// },
// cx,
// );
// }
// true
// }
// fn mouse_right_down( // fn mouse_right_down(
// editor: &mut Editor, // editor: &mut Editor,
@ -312,157 +182,120 @@ impl EditorElement {
// true // true
// } // }
// fn mouse_up( fn mouse_up(
// editor: &mut Editor, editor: &mut Editor,
// position: gpui::Point<Pixels>, event: &MouseUpEvent,
// cmd: bool, position_map: &PositionMap,
// shift: bool, text_bounds: Bounds<Pixels>,
// alt: bool, cx: &mut ViewContext<Editor>,
// position_map: &PositionMap, ) -> bool {
// text_bounds: Bounds<Pixels>, let end_selection = editor.has_pending_selection();
// cx: &mut EventContext<Editor>, let pending_nonempty_selections = editor.has_pending_nonempty_selection();
// ) -> bool {
// let end_selection = editor.has_pending_selection();
// let pending_nonempty_selections = editor.has_pending_nonempty_selection();
// if end_selection { if end_selection {
// editor.select(SelectPhase::End, cx); editor.select(SelectPhase::End, cx);
// } }
// if !pending_nonempty_selections && cmd && text_bounds.contains_point(position) { if !pending_nonempty_selections
// let point = position_map.point_for_position(text_bounds, position); && event.modifiers.command
// let could_be_inlay = point.as_valid().is_none(); && text_bounds.contains_point(&event.position)
// if shift || could_be_inlay { {
// go_to_fetched_type_definition(editor, point, alt, cx); let point = position_map.point_for_position(text_bounds, event.position);
// } else { let could_be_inlay = point.as_valid().is_none();
// go_to_fetched_definition(editor, point, alt, cx); let split = event.modifiers.alt;
// } if event.modifiers.shift || could_be_inlay {
go_to_fetched_type_definition(editor, point, split, cx);
} else {
go_to_fetched_definition(editor, point, split, cx);
}
// return true; return true;
// } }
// end_selection end_selection
// } }
// fn mouse_dragged( fn mouse_moved(
// editor: &mut Editor, editor: &mut Editor,
// MouseMovedEvent { event: &MouseMoveEvent,
// modifiers: Modifiers { cmd, shift, .. }, position_map: &PositionMap,
// position, text_bounds: Bounds<Pixels>,
// .. gutter_bounds: Bounds<Pixels>,
// }: MouseMovedEvent, cx: &mut ViewContext<Editor>,
// position_map: &PositionMap, ) -> bool {
// text_bounds: Bounds<Pixels>, let modifiers = event.modifiers;
// cx: &mut EventContext<Editor>, if editor.has_pending_selection() && event.pressed_button == Some(MouseButton::Left) {
// ) -> bool { let point_for_position = position_map.point_for_position(text_bounds, event.position);
// // This will be handled more correctly once https://github.com/zed-industries/zed/issues/1218 is completed let mut scroll_delta = gpui::Point::<f32>::zero();
// // Don't trigger hover popover if mouse is hovering over context menu let vertical_margin = position_map.line_height.min(text_bounds.size.height / 3.0);
// let point = if text_bounds.contains_point(position) { let top = text_bounds.origin.y + vertical_margin;
// position_map let bottom = text_bounds.lower_left().y - vertical_margin;
// .point_for_position(text_bounds, position) if event.position.y < top {
// .as_valid() scroll_delta.y = -scale_vertical_mouse_autoscroll_delta(top - event.position.y);
// } else { }
// None if event.position.y > bottom {
// }; scroll_delta.y = scale_vertical_mouse_autoscroll_delta(event.position.y - bottom);
}
// update_go_to_definition_link( let horizontal_margin = position_map.line_height.min(text_bounds.size.width / 3.0);
// editor, let left = text_bounds.origin.x + horizontal_margin;
// point.map(GoToDefinitionTrigger::Text), let right = text_bounds.upper_right().x - horizontal_margin;
// cmd, if event.position.x < left {
// shift, scroll_delta.x = -scale_horizontal_mouse_autoscroll_delta(left - event.position.x);
// cx, }
// ); if event.position.x > right {
scroll_delta.x = scale_horizontal_mouse_autoscroll_delta(event.position.x - right);
}
// if editor.has_pending_selection() { editor.select(
// let mut scroll_delta = gpui::Point::<Pixels>::zero(); SelectPhase::Update {
position: point_for_position.previous_valid,
goal_column: point_for_position.exact_unclipped.column(),
scroll_position: (position_map.snapshot.scroll_position() + scroll_delta)
.clamp(&gpui::Point::zero(), &position_map.scroll_max),
},
cx,
);
}
// let vertical_margin = position_map.line_height.min(text_bounds.height() / 3.0); let text_hovered = text_bounds.contains_point(&event.position);
// let top = text_bounds.origin.y + vertical_margin; let gutter_hovered = gutter_bounds.contains_point(&event.position);
// let bottom = text_bounds.lower_left().y - vertical_margin; editor.set_gutter_hovered(gutter_hovered, cx);
// if position.y < top {
// scroll_delta.set_y(-scale_vertical_mouse_autoscroll_delta(top - position.y))
// }
// if position.y > bottom {
// scroll_delta.set_y(scale_vertical_mouse_autoscroll_delta(position.y - bottom))
// }
// let horizontal_margin = position_map.line_height.min(text_bounds.width() / 3.0); // Don't trigger hover popover if mouse is hovering over context menu
// let left = text_bounds.origin.x + horizontal_margin; if text_hovered {
// let right = text_bounds.upper_right().x - horizontal_margin; let point_for_position = position_map.point_for_position(text_bounds, event.position);
// if position.x < left {
// scroll_delta.set_x(-scale_horizontal_mouse_autoscroll_delta(
// left - position.x,
// ))
// }
// if position.x > right {
// scroll_delta.set_x(scale_horizontal_mouse_autoscroll_delta(
// position.x - right,
// ))
// }
// let point_for_position = position_map.point_for_position(text_bounds, position); match point_for_position.as_valid() {
Some(point) => {
update_go_to_definition_link(
editor,
Some(GoToDefinitionTrigger::Text(point)),
modifiers.command,
modifiers.shift,
cx,
);
hover_at(editor, Some(point), cx);
}
None => {
update_inlay_link_and_hover_points(
&position_map.snapshot,
point_for_position,
editor,
modifiers.command,
modifiers.shift,
cx,
);
}
}
// editor.select( true
// SelectPhase::Update { } else {
// position: point_for_position.previous_valid, update_go_to_definition_link(editor, None, modifiers.command, modifiers.shift, cx);
// goal_column: point_for_position.exact_unclipped.column(), hover_at(editor, None, cx);
// scroll_position: (position_map.snapshot.scroll_position() + scroll_delta) gutter_hovered
// .clamp(gpui::Point::<Pixels>::zero(), position_map.scroll_max), }
// }, }
// cx,
// );
// hover_at(editor, point, cx);
// true
// } else {
// hover_at(editor, point, cx);
// false
// }
// }
// fn mouse_moved(
// editor: &mut Editor,
// MouseMovedEvent {
// modifiers: Modifiers { shift, cmd, .. },
// position,
// ..
// }: MouseMovedEvent,
// position_map: &PositionMap,
// text_bounds: Bounds<Pixels>,
// cx: &mut ViewContext<Editor>,
// ) -> bool {
// // This will be handled more correctly once https://github.com/zed-industries/zed/issues/1218 is completed
// // Don't trigger hover popover if mouse is hovering over context menu
// if text_bounds.contains_point(position) {
// let point_for_position = position_map.point_for_position(text_bounds, position);
// match point_for_position.as_valid() {
// Some(point) => {
// update_go_to_definition_link(
// editor,
// Some(GoToDefinitionTrigger::Text(point)),
// cmd,
// shift,
// cx,
// );
// hover_at(editor, Some(point), cx);
// }
// None => {
// update_inlay_link_and_hover_points(
// &position_map.snapshot,
// point_for_position,
// editor,
// cmd,
// shift,
// cx,
// );
// }
// }
// } else {
// update_go_to_definition_link(editor, None, cmd, shift, cx);
// hover_at(editor, None, cx);
// }
// true
// }
fn scroll( fn scroll(
editor: &mut Editor, editor: &mut Editor,
@ -2336,6 +2169,79 @@ impl EditorElement {
// blocks, // blocks,
// ) // )
// } // }
fn paint_mouse_listeners(
&mut self,
bounds: Bounds<Pixels>,
gutter_bounds: Bounds<Pixels>,
text_bounds: Bounds<Pixels>,
position_map: &Arc<PositionMap>,
cx: &mut ViewContext<Editor>,
) {
cx.on_mouse_event({
let position_map = position_map.clone();
move |editor, event: &ScrollWheelEvent, phase, cx| {
if phase != DispatchPhase::Bubble {
return;
}
if Self::scroll(editor, event, &position_map, bounds, cx) {
cx.stop_propagation();
}
}
});
cx.on_mouse_event({
let position_map = position_map.clone();
move |editor, event: &MouseDownEvent, phase, cx| {
if phase != DispatchPhase::Bubble {
return;
}
if Self::mouse_down(editor, event, &position_map, text_bounds, gutter_bounds, cx) {
cx.stop_propagation()
}
}
});
cx.on_mouse_event({
let position_map = position_map.clone();
move |editor, event: &MouseUpEvent, phase, cx| {
if phase != DispatchPhase::Bubble {
return;
}
if Self::mouse_up(editor, event, &position_map, text_bounds, cx) {
cx.stop_propagation()
}
}
});
// todo!()
// on_down(MouseButton::Right, {
// let position_map = position_map.clone();
// move |event, editor, cx| {
// if !Self::mouse_right_down(
// editor,
// event.position,
// position_map.as_ref(),
// text_bounds,
// cx,
// ) {
// cx.propagate_event();
// }
// }
// });
cx.on_mouse_event({
let position_map = position_map.clone();
move |editor, event: &MouseMoveEvent, phase, cx| {
if phase != DispatchPhase::Bubble {
return;
}
if Self::mouse_moved(editor, event, &position_map, text_bounds, gutter_bounds, cx) {
cx.stop_propagation()
}
}
});
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -2555,30 +2461,9 @@ impl Element<Editor> for EditorElement {
let dispatch_context = editor.dispatch_context(cx); let dispatch_context = editor.dispatch_context(cx);
cx.with_element_id(cx.view().entity_id(), |global_id, cx| { cx.with_element_id(cx.view().entity_id(), |global_id, cx| {
cx.with_key_dispatch_context(dispatch_context, |cx| { cx.with_key_dispatch_context(dispatch_context, |cx| {
cx.with_key_listeners( cx.with_key_listeners(build_key_listeners(global_id), |cx| {
[ cx.with_focus(editor.focus_handle.clone(), |_| {})
build_action_listener(Editor::move_left), });
build_action_listener(Editor::move_right),
build_action_listener(Editor::move_down),
build_action_listener(Editor::move_up),
build_key_listener(
move |editor, key_down: &KeyDownEvent, dispatch_context, phase, cx| {
if phase == DispatchPhase::Bubble {
if let KeyMatch::Some(action) = cx.match_keystroke(
&global_id,
&key_down.keystroke,
dispatch_context,
) {
return Some(action);
}
}
None
},
),
],
|cx| cx.with_focus(editor.focus_handle.clone(), |_| {}),
);
}) })
}); });
} }
@ -2608,25 +2493,6 @@ impl Element<Editor> for EditorElement {
cx: &mut gpui::ViewContext<Editor>, cx: &mut gpui::ViewContext<Editor>,
) { ) {
let layout = self.compute_layout(editor, cx, bounds); let layout = self.compute_layout(editor, cx, bounds);
cx.on_mouse_event({
let position_map = layout.position_map.clone();
move |editor, event: &ScrollWheelEvent, phase, cx| {
if phase != DispatchPhase::Bubble {
return;
}
if Self::scroll(editor, event, &position_map, bounds, cx) {
cx.stop_propagation();
}
}
});
if editor.focus_handle.is_focused(cx) {
cx.handle_text_input();
}
cx.with_content_mask(ContentMask { bounds }, |cx| {
let gutter_bounds = Bounds { let gutter_bounds = Bounds {
origin: bounds.origin, origin: bounds.origin,
size: layout.gutter_size, size: layout.gutter_size,
@ -2636,6 +2502,18 @@ impl Element<Editor> for EditorElement {
size: layout.text_size, size: layout.text_size,
}; };
if editor.focus_handle.is_focused(cx) {
cx.handle_text_input();
}
cx.with_content_mask(ContentMask { bounds }, |cx| {
self.paint_mouse_listeners(
bounds,
gutter_bounds,
text_bounds,
&layout.position_map,
cx,
);
self.paint_background(gutter_bounds, text_bounds, &layout, cx); self.paint_background(gutter_bounds, text_bounds, &layout, cx);
if layout.gutter_size.width > Pixels::ZERO { if layout.gutter_size.width > Pixels::ZERO {
self.paint_gutter(gutter_bounds, &layout, editor, cx); self.paint_gutter(gutter_bounds, &layout, editor, cx);
@ -3664,12 +3542,12 @@ impl HighlightedRange {
// bounds.into_iter() // bounds.into_iter()
// } // }
pub fn scale_vertical_mouse_autoscroll_delta(delta: f32) -> f32 { pub fn scale_vertical_mouse_autoscroll_delta(delta: Pixels) -> f32 {
delta.powf(1.5) / 100.0 (delta.pow(1.5) / 100.0).into()
} }
fn scale_horizontal_mouse_autoscroll_delta(delta: f32) -> f32 { fn scale_horizontal_mouse_autoscroll_delta(delta: Pixels) -> f32 {
delta.powf(1.2) / 300.0 (delta.pow(1.2) / 300.0).into()
} }
// #[cfg(test)] // #[cfg(test)]
@ -4115,6 +3993,178 @@ fn scale_horizontal_mouse_autoscroll_delta(delta: f32) -> f32 {
// } // }
// } // }
fn build_key_listeners(
global_element_id: GlobalElementId,
) -> impl IntoIterator<Item = (TypeId, KeyListener<Editor>)> {
[
build_action_listener(Editor::move_left),
build_action_listener(Editor::move_right),
build_action_listener(Editor::move_down),
build_action_listener(Editor::move_up),
// build_action_listener(Editor::new_file), todo!()
// build_action_listener(Editor::new_file_in_direction), todo!()
build_action_listener(Editor::cancel),
build_action_listener(Editor::newline),
build_action_listener(Editor::newline_above),
build_action_listener(Editor::newline_below),
build_action_listener(Editor::backspace),
build_action_listener(Editor::delete),
build_action_listener(Editor::tab),
build_action_listener(Editor::tab_prev),
build_action_listener(Editor::indent),
build_action_listener(Editor::outdent),
build_action_listener(Editor::delete_line),
build_action_listener(Editor::join_lines),
build_action_listener(Editor::sort_lines_case_sensitive),
build_action_listener(Editor::sort_lines_case_insensitive),
build_action_listener(Editor::reverse_lines),
build_action_listener(Editor::shuffle_lines),
build_action_listener(Editor::convert_to_upper_case),
build_action_listener(Editor::convert_to_lower_case),
build_action_listener(Editor::convert_to_title_case),
build_action_listener(Editor::convert_to_snake_case),
build_action_listener(Editor::convert_to_kebab_case),
build_action_listener(Editor::convert_to_upper_camel_case),
build_action_listener(Editor::convert_to_lower_camel_case),
build_action_listener(Editor::delete_to_previous_word_start),
build_action_listener(Editor::delete_to_previous_subword_start),
build_action_listener(Editor::delete_to_next_word_end),
build_action_listener(Editor::delete_to_next_subword_end),
build_action_listener(Editor::delete_to_beginning_of_line),
build_action_listener(Editor::delete_to_end_of_line),
build_action_listener(Editor::cut_to_end_of_line),
build_action_listener(Editor::duplicate_line),
build_action_listener(Editor::move_line_up),
build_action_listener(Editor::move_line_down),
build_action_listener(Editor::transpose),
build_action_listener(Editor::cut),
build_action_listener(Editor::copy),
build_action_listener(Editor::paste),
build_action_listener(Editor::undo),
build_action_listener(Editor::redo),
build_action_listener(Editor::move_page_up),
build_action_listener(Editor::move_page_down),
build_action_listener(Editor::next_screen),
build_action_listener(Editor::scroll_cursor_top),
build_action_listener(Editor::scroll_cursor_center),
build_action_listener(Editor::scroll_cursor_bottom),
build_action_listener(|editor, _: &LineDown, cx| {
editor.scroll_screen(&ScrollAmount::Line(1.), cx)
}),
build_action_listener(|editor, _: &LineUp, cx| {
editor.scroll_screen(&ScrollAmount::Line(-1.), cx)
}),
build_action_listener(|editor, _: &HalfPageDown, cx| {
editor.scroll_screen(&ScrollAmount::Page(0.5), cx)
}),
build_action_listener(|editor, _: &HalfPageUp, cx| {
editor.scroll_screen(&ScrollAmount::Page(-0.5), cx)
}),
build_action_listener(|editor, _: &PageDown, cx| {
editor.scroll_screen(&ScrollAmount::Page(1.), cx)
}),
build_action_listener(|editor, _: &PageUp, cx| {
editor.scroll_screen(&ScrollAmount::Page(-1.), cx)
}),
build_action_listener(Editor::move_to_previous_word_start),
build_action_listener(Editor::move_to_previous_subword_start),
build_action_listener(Editor::move_to_next_word_end),
build_action_listener(Editor::move_to_next_subword_end),
build_action_listener(Editor::move_to_beginning_of_line),
build_action_listener(Editor::move_to_end_of_line),
build_action_listener(Editor::move_to_start_of_paragraph),
build_action_listener(Editor::move_to_end_of_paragraph),
build_action_listener(Editor::move_to_beginning),
build_action_listener(Editor::move_to_end),
build_action_listener(Editor::select_up),
build_action_listener(Editor::select_down),
build_action_listener(Editor::select_left),
build_action_listener(Editor::select_right),
build_action_listener(Editor::select_to_previous_word_start),
build_action_listener(Editor::select_to_previous_subword_start),
build_action_listener(Editor::select_to_next_word_end),
build_action_listener(Editor::select_to_next_subword_end),
build_action_listener(Editor::select_to_beginning_of_line),
build_action_listener(Editor::select_to_end_of_line),
build_action_listener(Editor::select_to_start_of_paragraph),
build_action_listener(Editor::select_to_end_of_paragraph),
build_action_listener(Editor::select_to_beginning),
build_action_listener(Editor::select_to_end),
build_action_listener(Editor::select_all),
build_action_listener(|editor, action, cx| {
editor.select_all_matches(action, cx).log_err();
}),
build_action_listener(Editor::select_line),
build_action_listener(Editor::split_selection_into_lines),
build_action_listener(Editor::add_selection_above),
build_action_listener(Editor::add_selection_below),
build_action_listener(|editor, action, cx| {
editor.select_next(action, cx).log_err();
}),
build_action_listener(|editor, action, cx| {
editor.select_previous(action, cx).log_err();
}),
build_action_listener(Editor::toggle_comments),
build_action_listener(Editor::select_larger_syntax_node),
build_action_listener(Editor::select_smaller_syntax_node),
build_action_listener(Editor::move_to_enclosing_bracket),
build_action_listener(Editor::undo_selection),
build_action_listener(Editor::redo_selection),
build_action_listener(Editor::go_to_diagnostic),
build_action_listener(Editor::go_to_prev_diagnostic),
build_action_listener(Editor::go_to_hunk),
build_action_listener(Editor::go_to_prev_hunk),
build_action_listener(Editor::go_to_definition),
build_action_listener(Editor::go_to_definition_split),
build_action_listener(Editor::go_to_type_definition),
build_action_listener(Editor::go_to_type_definition_split),
build_action_listener(Editor::fold),
build_action_listener(Editor::fold_at),
build_action_listener(Editor::unfold_lines),
build_action_listener(Editor::unfold_at),
build_action_listener(Editor::fold_selected_ranges),
build_action_listener(Editor::show_completions),
// build_action_listener(Editor::toggle_code_actions), todo!()
// build_action_listener(Editor::open_excerpts), todo!()
build_action_listener(Editor::toggle_soft_wrap),
build_action_listener(Editor::toggle_inlay_hints),
build_action_listener(Editor::reveal_in_finder),
build_action_listener(Editor::copy_path),
build_action_listener(Editor::copy_relative_path),
build_action_listener(Editor::copy_highlight_json),
build_action_listener(|editor, action, cx| {
editor
.format(action, cx)
.map(|task| task.detach_and_log_err(cx));
}),
build_action_listener(Editor::restart_language_server),
build_action_listener(Editor::show_character_palette),
// build_action_listener(Editor::confirm_completion), todo!()
// build_action_listener(Editor::confirm_code_action), todo!()
// build_action_listener(Editor::rename), todo!()
// build_action_listener(Editor::confirm_rename), todo!()
// build_action_listener(Editor::find_all_references), todo!()
build_action_listener(Editor::next_copilot_suggestion),
build_action_listener(Editor::previous_copilot_suggestion),
build_action_listener(Editor::copilot_suggest),
build_key_listener(
move |editor, key_down: &KeyDownEvent, dispatch_context, phase, cx| {
if phase == DispatchPhase::Bubble {
if let KeyMatch::Some(action) = cx.match_keystroke(
&global_element_id,
&key_down.keystroke,
dispatch_context,
) {
return Some(action);
}
}
None
},
),
]
}
fn build_key_listener<T: 'static>( fn build_key_listener<T: 'static>(
listener: impl Fn( listener: impl Fn(
&mut Editor, &mut Editor,

View file

@ -144,8 +144,7 @@ pub fn hide_hover(editor: &mut Editor, cx: &mut ViewContext<Editor>) -> bool {
editor.hover_state.info_task = None; editor.hover_state.info_task = None;
editor.hover_state.triggered_from = None; editor.hover_state.triggered_from = None;
// todo!() editor.clear_background_highlights::<HoverState>(cx);
// editor.clear_background_highlights::<HoverState>(cx);
if did_hide { if did_hide {
cx.notify(); cx.notify();
@ -325,23 +324,22 @@ fn show_hover(
}; };
this.update(&mut cx, |this, cx| { this.update(&mut cx, |this, cx| {
todo!(); if let Some(symbol_range) = hover_popover
// if let Some(symbol_range) = hover_popover .as_ref()
// .as_ref() .and_then(|hover_popover| hover_popover.symbol_range.as_text_range())
// .and_then(|hover_popover| hover_popover.symbol_range.as_text_range()) {
// { // Highlight the selected symbol using a background highlight
// // Highlight the selected symbol using a background highlight this.highlight_background::<HoverState>(
// this.highlight_background::<HoverState>( vec![symbol_range],
// vec![symbol_range], |theme| theme.element_hover, // todo! update theme
// |theme| theme.editor.hover_popover.highlight, cx,
// cx, );
// ); } else {
// } else { this.clear_background_highlights::<HoverState>(cx);
// this.clear_background_highlights::<HoverState>(cx); }
// }
// this.hover_state.info_popover = hover_popover;
// this.hover_state.info_popover = hover_popover; cx.notify();
// cx.notify();
})?; })?;
Ok::<_, anyhow::Error>(()) Ok::<_, anyhow::Error>(())

View file

@ -171,173 +171,170 @@ pub fn update_inlay_link_and_hover_points(
shift_held: bool, shift_held: bool,
cx: &mut ViewContext<'_, Editor>, cx: &mut ViewContext<'_, Editor>,
) { ) {
todo!("old implementation below") let hovered_offset = if point_for_position.column_overshoot_after_line_end == 0 {
} Some(snapshot.display_point_to_inlay_offset(point_for_position.exact_unclipped, Bias::Left))
// ) { } else {
// let hovered_offset = if point_for_position.column_overshoot_after_line_end == 0 { None
// Some(snapshot.display_point_to_inlay_offset(point_for_position.exact_unclipped, Bias::Left)) };
// } else { let mut go_to_definition_updated = false;
// None let mut hover_updated = false;
// }; if let Some(hovered_offset) = hovered_offset {
// let mut go_to_definition_updated = false; let buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
// let mut hover_updated = false; let previous_valid_anchor = buffer_snapshot.anchor_at(
// if let Some(hovered_offset) = hovered_offset { point_for_position.previous_valid.to_point(snapshot),
// let buffer_snapshot = editor.buffer().read(cx).snapshot(cx); Bias::Left,
// let previous_valid_anchor = buffer_snapshot.anchor_at( );
// point_for_position.previous_valid.to_point(snapshot), let next_valid_anchor = buffer_snapshot.anchor_at(
// Bias::Left, point_for_position.next_valid.to_point(snapshot),
// ); Bias::Right,
// let next_valid_anchor = buffer_snapshot.anchor_at( );
// point_for_position.next_valid.to_point(snapshot), if let Some(hovered_hint) = editor
// Bias::Right, .visible_inlay_hints(cx)
// ); .into_iter()
// if let Some(hovered_hint) = editor .skip_while(|hint| {
// .visible_inlay_hints(cx) hint.position
// .into_iter() .cmp(&previous_valid_anchor, &buffer_snapshot)
// .skip_while(|hint| { .is_lt()
// hint.position })
// .cmp(&previous_valid_anchor, &buffer_snapshot) .take_while(|hint| {
// .is_lt() hint.position
// }) .cmp(&next_valid_anchor, &buffer_snapshot)
// .take_while(|hint| { .is_le()
// hint.position })
// .cmp(&next_valid_anchor, &buffer_snapshot) .max_by_key(|hint| hint.id)
// .is_le() {
// }) let inlay_hint_cache = editor.inlay_hint_cache();
// .max_by_key(|hint| hint.id) let excerpt_id = previous_valid_anchor.excerpt_id;
// { if let Some(cached_hint) = inlay_hint_cache.hint_by_id(excerpt_id, hovered_hint.id) {
// let inlay_hint_cache = editor.inlay_hint_cache(); match cached_hint.resolve_state {
// let excerpt_id = previous_valid_anchor.excerpt_id; ResolveState::CanResolve(_, _) => {
// if let Some(cached_hint) = inlay_hint_cache.hint_by_id(excerpt_id, hovered_hint.id) { if let Some(buffer_id) = previous_valid_anchor.buffer_id {
// match cached_hint.resolve_state { inlay_hint_cache.spawn_hint_resolve(
// ResolveState::CanResolve(_, _) => { buffer_id,
// if let Some(buffer_id) = previous_valid_anchor.buffer_id { excerpt_id,
// inlay_hint_cache.spawn_hint_resolve( hovered_hint.id,
// buffer_id, cx,
// excerpt_id, );
// hovered_hint.id, }
// cx, }
// ); ResolveState::Resolved => {
// } let mut extra_shift_left = 0;
// } let mut extra_shift_right = 0;
// ResolveState::Resolved => { if cached_hint.padding_left {
// let mut extra_shift_left = 0; extra_shift_left += 1;
// let mut extra_shift_right = 0; extra_shift_right += 1;
// if cached_hint.padding_left { }
// extra_shift_left += 1; if cached_hint.padding_right {
// extra_shift_right += 1; extra_shift_right += 1;
// } }
// if cached_hint.padding_right { match cached_hint.label {
// extra_shift_right += 1; project::InlayHintLabel::String(_) => {
// } if let Some(tooltip) = cached_hint.tooltip {
// match cached_hint.label { hover_popover::hover_at_inlay(
// project::InlayHintLabel::String(_) => { editor,
// if let Some(tooltip) = cached_hint.tooltip { InlayHover {
// hover_popover::hover_at_inlay( excerpt: excerpt_id,
// editor, tooltip: match tooltip {
// InlayHover { InlayHintTooltip::String(text) => HoverBlock {
// excerpt: excerpt_id, text,
// tooltip: match tooltip { kind: HoverBlockKind::PlainText,
// InlayHintTooltip::String(text) => HoverBlock { },
// text, InlayHintTooltip::MarkupContent(content) => {
// kind: HoverBlockKind::PlainText, HoverBlock {
// }, text: content.value,
// InlayHintTooltip::MarkupContent(content) => { kind: content.kind,
// HoverBlock { }
// text: content.value, }
// kind: content.kind, },
// } range: InlayHighlight {
// } inlay: hovered_hint.id,
// }, inlay_position: hovered_hint.position,
// range: InlayHighlight { range: extra_shift_left
// inlay: hovered_hint.id, ..hovered_hint.text.len() + extra_shift_right,
// inlay_position: hovered_hint.position, },
// range: extra_shift_left },
// ..hovered_hint.text.len() + extra_shift_right, cx,
// }, );
// }, hover_updated = true;
// cx, }
// ); }
// hover_updated = true; project::InlayHintLabel::LabelParts(label_parts) => {
// } let hint_start =
// } snapshot.anchor_to_inlay_offset(hovered_hint.position);
// project::InlayHintLabel::LabelParts(label_parts) => { if let Some((hovered_hint_part, part_range)) =
// let hint_start = hover_popover::find_hovered_hint_part(
// snapshot.anchor_to_inlay_offset(hovered_hint.position); label_parts,
// if let Some((hovered_hint_part, part_range)) = hint_start,
// hover_popover::find_hovered_hint_part( hovered_offset,
// label_parts, )
// hint_start, {
// hovered_offset, let highlight_start =
// ) (part_range.start - hint_start).0 + extra_shift_left;
// { let highlight_end =
// let highlight_start = (part_range.end - hint_start).0 + extra_shift_right;
// (part_range.start - hint_start).0 + extra_shift_left; let highlight = InlayHighlight {
// let highlight_end = inlay: hovered_hint.id,
// (part_range.end - hint_start).0 + extra_shift_right; inlay_position: hovered_hint.position,
// let highlight = InlayHighlight { range: highlight_start..highlight_end,
// inlay: hovered_hint.id, };
// inlay_position: hovered_hint.position, if let Some(tooltip) = hovered_hint_part.tooltip {
// range: highlight_start..highlight_end, hover_popover::hover_at_inlay(
// }; editor,
// if let Some(tooltip) = hovered_hint_part.tooltip { InlayHover {
// hover_popover::hover_at_inlay( excerpt: excerpt_id,
// editor, tooltip: match tooltip {
// InlayHover { InlayHintLabelPartTooltip::String(text) => {
// excerpt: excerpt_id, HoverBlock {
// tooltip: match tooltip { text,
// InlayHintLabelPartTooltip::String(text) => { kind: HoverBlockKind::PlainText,
// HoverBlock { }
// text, }
// kind: HoverBlockKind::PlainText, InlayHintLabelPartTooltip::MarkupContent(
// } content,
// } ) => HoverBlock {
// InlayHintLabelPartTooltip::MarkupContent( text: content.value,
// content, kind: content.kind,
// ) => HoverBlock { },
// text: content.value, },
// kind: content.kind, range: highlight.clone(),
// }, },
// }, cx,
// range: highlight.clone(), );
// }, hover_updated = true;
// cx, }
// ); if let Some((language_server_id, location)) =
// hover_updated = true; hovered_hint_part.location
// } {
// if let Some((language_server_id, location)) = go_to_definition_updated = true;
// hovered_hint_part.location update_go_to_definition_link(
// { editor,
// go_to_definition_updated = true; Some(GoToDefinitionTrigger::InlayHint(
// update_go_to_definition_link( highlight,
// editor, location,
// Some(GoToDefinitionTrigger::InlayHint( language_server_id,
// highlight, )),
// location, cmd_held,
// language_server_id, shift_held,
// )), cx,
// cmd_held, );
// shift_held, }
// cx, }
// ); }
// } };
// } }
// } ResolveState::Resolving => {}
// }; }
// } }
// ResolveState::Resolving => {} }
// } }
// }
// }
// }
// if !go_to_definition_updated { if !go_to_definition_updated {
// update_go_to_definition_link(editor, None, cmd_held, shift_held, cx); update_go_to_definition_link(editor, None, cmd_held, shift_held, cx);
// } }
// if !hover_updated { if !hover_updated {
// hover_popover::hover_at(editor, None, cx); hover_popover::hover_at(editor, None, cx);
// } }
// } }
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum LinkDefinitionKind { pub enum LinkDefinitionKind {

View file

@ -288,16 +288,15 @@ impl ScrollManager {
} }
} }
// todo!()
impl Editor { impl Editor {
// pub fn vertical_scroll_margin(&mut self) -> usize { pub fn vertical_scroll_margin(&mut self) -> usize {
// self.scroll_manager.vertical_scroll_margin as usize self.scroll_manager.vertical_scroll_margin as usize
// } }
// pub fn set_vertical_scroll_margin(&mut self, margin_rows: usize, cx: &mut ViewContext<Self>) { pub fn set_vertical_scroll_margin(&mut self, margin_rows: usize, cx: &mut ViewContext<Self>) {
// self.scroll_manager.vertical_scroll_margin = margin_rows as f32; self.scroll_manager.vertical_scroll_margin = margin_rows as f32;
// cx.notify(); cx.notify();
// } }
pub fn visible_line_count(&self) -> Option<f32> { pub fn visible_line_count(&self) -> Option<f32> {
self.scroll_manager.visible_line_count self.scroll_manager.visible_line_count
@ -349,11 +348,9 @@ impl Editor {
self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx); self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
} }
pub fn scroll_position(&self, cx: &mut ViewContext<Self>) -> gpui::Point<Pixels> { pub fn scroll_position(&self, cx: &mut ViewContext<Self>) -> gpui::Point<f32> {
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));
// todo!() Should `self.scroll_manager.anchor.scroll_position()` return `Pixels`? self.scroll_manager.anchor.scroll_position(&display_map)
// self.scroll_manager.anchor.scroll_position(&display_map)
todo!()
} }
pub fn set_scroll_anchor(&mut self, scroll_anchor: ScrollAnchor, cx: &mut ViewContext<Self>) { pub fn set_scroll_anchor(&mut self, scroll_anchor: ScrollAnchor, cx: &mut ViewContext<Self>) {
@ -382,50 +379,50 @@ impl Editor {
.set_anchor(scroll_anchor, top_row, false, 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>) {
// if matches!(self.mode, EditorMode::SingleLine) { if matches!(self.mode, EditorMode::SingleLine) {
// cx.propagate_action(); cx.propagate();
// return; return;
// } }
// if self.take_rename(true, cx).is_some() { if self.take_rename(true, cx).is_some() {
// return; return;
// } }
// let cur_position = self.scroll_position(cx); let cur_position = self.scroll_position(cx);
// let new_pos = cur_position + point(0., amount.lines(self)); let new_pos = cur_position + point(0., amount.lines(self));
// self.set_scroll_position(new_pos, cx); self.set_scroll_position(new_pos, cx);
// } }
// /// Returns an ordering. The newest selection is: /// Returns an ordering. The newest selection is:
// /// Ordering::Equal => on screen /// Ordering::Equal => on screen
// /// Ordering::Less => above the screen /// Ordering::Less => above the screen
// /// Ordering::Greater => below the screen /// Ordering::Greater => below the screen
// pub fn newest_selection_on_screen(&self, cx: &mut AppContext) -> Ordering { pub fn newest_selection_on_screen(&self, cx: &mut AppContext) -> Ordering {
// let snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
// let newest_head = self let newest_head = self
// .selections .selections
// .newest_anchor() .newest_anchor()
// .head() .head()
// .to_display_point(&snapshot); .to_display_point(&snapshot);
// let screen_top = self let screen_top = self
// .scroll_manager .scroll_manager
// .anchor .anchor
// .anchor .anchor
// .to_display_point(&snapshot); .to_display_point(&snapshot);
// if screen_top > newest_head { if screen_top > newest_head {
// return Ordering::Less; return Ordering::Less;
// } }
// if let Some(visible_lines) = self.visible_line_count() { if let Some(visible_lines) = self.visible_line_count() {
// if newest_head.row() < screen_top.row() + visible_lines as u32 { if newest_head.row() < screen_top.row() + visible_lines as u32 {
// return Ordering::Equal; return Ordering::Equal;
// } }
// } }
// Ordering::Greater Ordering::Greater
// } }
pub fn read_scroll_position_from_db( pub fn read_scroll_position_from_db(
&mut self, &mut self,

View file

@ -1,66 +1,27 @@
use super::Axis; use super::Axis;
use crate::Editor; use crate::{
use gpui::{AppContext, Point, ViewContext}; Autoscroll, Bias, Editor, EditorMode, NextScreen, ScrollAnchor, ScrollCursorBottom,
ScrollCursorCenter, ScrollCursorTop,
// actions!( };
// editor, use gpui::{actions, AppContext, Point, ViewContext};
// [
// LineDown,
// LineUp,
// HalfPageDown,
// HalfPageUp,
// PageDown,
// PageUp,
// NextScreen,
// ScrollCursorTop,
// ScrollCursorCenter,
// ScrollCursorBottom,
// ]
// );
pub fn init(cx: &mut AppContext) {
// todo!()
// cx.add_action(Editor::next_screen);
// cx.add_action(Editor::scroll_cursor_top);
// cx.add_action(Editor::scroll_cursor_center);
// cx.add_action(Editor::scroll_cursor_bottom);
// cx.add_action(|this: &mut Editor, _: &LineDown, cx| {
// this.scroll_screen(&ScrollAmount::Line(1.), cx)
// });
// cx.add_action(|this: &mut Editor, _: &LineUp, cx| {
// this.scroll_screen(&ScrollAmount::Line(-1.), cx)
// });
// cx.add_action(|this: &mut Editor, _: &HalfPageDown, cx| {
// this.scroll_screen(&ScrollAmount::Page(0.5), cx)
// });
// cx.add_action(|this: &mut Editor, _: &HalfPageUp, cx| {
// this.scroll_screen(&ScrollAmount::Page(-0.5), cx)
// });
// cx.add_action(|this: &mut Editor, _: &PageDown, cx| {
// this.scroll_screen(&ScrollAmount::Page(1.), cx)
// });
// cx.add_action(|this: &mut Editor, _: &PageUp, cx| {
// this.scroll_screen(&ScrollAmount::Page(-1.), cx)
// });
}
impl Editor { impl Editor {
// pub fn next_screen(&mut self, _: &NextScreen, cx: &mut ViewContext<Editor>) -> Option<()> { pub fn next_screen(&mut self, _: &NextScreen, cx: &mut ViewContext<Editor>) {
// if self.take_rename(true, cx).is_some() { if self.take_rename(true, cx).is_some() {
// return None; return;
// } }
// todo!()
// if self.mouse_context_menu.read(cx).visible() { // if self.mouse_context_menu.read(cx).visible() {
// return None; // return None;
// } // }
// if matches!(self.mode, EditorMode::SingleLine) { if matches!(self.mode, EditorMode::SingleLine) {
// cx.propagate_action(); cx.propagate();
// return None; return;
// } }
// self.request_autoscroll(Autoscroll::Next, cx); self.request_autoscroll(Autoscroll::Next, cx);
// Some(()) }
// }
pub fn scroll( pub fn scroll(
&mut self, &mut self,
@ -72,79 +33,71 @@ impl Editor {
self.set_scroll_position(scroll_position, cx); self.set_scroll_position(scroll_position, cx);
} }
// fn scroll_cursor_top(editor: &mut Editor, _: &ScrollCursorTop, cx: &mut ViewContext<Editor>) { pub fn scroll_cursor_top(&mut self, _: &ScrollCursorTop, cx: &mut ViewContext<Editor>) {
// let snapshot = editor.snapshot(cx).display_snapshot; let snapshot = self.snapshot(cx).display_snapshot;
// let scroll_margin_rows = editor.vertical_scroll_margin() as u32; let scroll_margin_rows = self.vertical_scroll_margin() as u32;
// let mut new_screen_top = editor.selections.newest_display(cx).head(); let mut new_screen_top = self.selections.newest_display(cx).head();
// *new_screen_top.row_mut() = new_screen_top.row().saturating_sub(scroll_margin_rows); *new_screen_top.row_mut() = new_screen_top.row().saturating_sub(scroll_margin_rows);
// *new_screen_top.column_mut() = 0; *new_screen_top.column_mut() = 0;
// let new_screen_top = new_screen_top.to_offset(&snapshot, Bias::Left); let new_screen_top = new_screen_top.to_offset(&snapshot, Bias::Left);
// let new_anchor = snapshot.buffer_snapshot.anchor_before(new_screen_top); let new_anchor = snapshot.buffer_snapshot.anchor_before(new_screen_top);
// editor.set_scroll_anchor( self.set_scroll_anchor(
// ScrollAnchor { ScrollAnchor {
// anchor: new_anchor, anchor: new_anchor,
// offset: Default::default(), offset: Default::default(),
// }, },
// cx, cx,
// ) )
// } }
// fn scroll_cursor_center( pub fn scroll_cursor_center(&mut self, _: &ScrollCursorCenter, cx: &mut ViewContext<Editor>) {
// editor: &mut Editor, let snapshot = self.snapshot(cx).display_snapshot;
// _: &ScrollCursorCenter, let visible_rows = if let Some(visible_rows) = self.visible_line_count() {
// cx: &mut ViewContext<Editor>, visible_rows as u32
// ) { } else {
// let snapshot = editor.snapshot(cx).display_snapshot; return;
// let visible_rows = if let Some(visible_rows) = editor.visible_line_count() { };
// visible_rows as u32
// } else {
// return;
// };
// let mut new_screen_top = editor.selections.newest_display(cx).head(); let mut new_screen_top = self.selections.newest_display(cx).head();
// *new_screen_top.row_mut() = new_screen_top.row().saturating_sub(visible_rows / 2); *new_screen_top.row_mut() = new_screen_top.row().saturating_sub(visible_rows / 2);
// *new_screen_top.column_mut() = 0; *new_screen_top.column_mut() = 0;
// let new_screen_top = new_screen_top.to_offset(&snapshot, Bias::Left); let new_screen_top = new_screen_top.to_offset(&snapshot, Bias::Left);
// let new_anchor = snapshot.buffer_snapshot.anchor_before(new_screen_top); let new_anchor = snapshot.buffer_snapshot.anchor_before(new_screen_top);
// editor.set_scroll_anchor( self.set_scroll_anchor(
// ScrollAnchor { ScrollAnchor {
// anchor: new_anchor, anchor: new_anchor,
// offset: Default::default(), offset: Default::default(),
// }, },
// cx, cx,
// ) )
// } }
// fn scroll_cursor_bottom( pub fn scroll_cursor_bottom(&mut self, _: &ScrollCursorBottom, cx: &mut ViewContext<Editor>) {
// editor: &mut Editor, let snapshot = self.snapshot(cx).display_snapshot;
// _: &ScrollCursorBottom, let scroll_margin_rows = self.vertical_scroll_margin() as u32;
// cx: &mut ViewContext<Editor>, let visible_rows = if let Some(visible_rows) = self.visible_line_count() {
// ) { visible_rows as u32
// let snapshot = editor.snapshot(cx).display_snapshot; } else {
// let scroll_margin_rows = editor.vertical_scroll_margin() as u32; return;
// let visible_rows = if let Some(visible_rows) = editor.visible_line_count() { };
// visible_rows as u32
// } else {
// return;
// };
// let mut new_screen_top = editor.selections.newest_display(cx).head(); let mut new_screen_top = self.selections.newest_display(cx).head();
// *new_screen_top.row_mut() = new_screen_top *new_screen_top.row_mut() = new_screen_top
// .row() .row()
// .saturating_sub(visible_rows.saturating_sub(scroll_margin_rows)); .saturating_sub(visible_rows.saturating_sub(scroll_margin_rows));
// *new_screen_top.column_mut() = 0; *new_screen_top.column_mut() = 0;
// let new_screen_top = new_screen_top.to_offset(&snapshot, Bias::Left); let new_screen_top = new_screen_top.to_offset(&snapshot, Bias::Left);
// let new_anchor = snapshot.buffer_snapshot.anchor_before(new_screen_top); let new_anchor = snapshot.buffer_snapshot.anchor_before(new_screen_top);
// editor.set_scroll_anchor( self.set_scroll_anchor(
// ScrollAnchor { ScrollAnchor {
// anchor: new_anchor, anchor: new_anchor,
// offset: Default::default(), offset: Default::default(),
// }, },
// cx, cx,
// ) )
// } }
} }

View file

@ -302,39 +302,39 @@ impl SelectionsCollection {
.collect() .collect()
} }
// pub fn build_columnar_selection( pub fn build_columnar_selection(
// &mut self, &mut self,
// display_map: &DisplaySnapshot, display_map: &DisplaySnapshot,
// row: u32, row: u32,
// positions: &Range<Pixels>, positions: &Range<Pixels>,
// reversed: bool, reversed: bool,
// text_layout_details: &TextLayoutDetails, text_layout_details: &TextLayoutDetails,
// ) -> Option<Selection<Point>> { ) -> Option<Selection<Point>> {
// let is_empty = positions.start == positions.end; let is_empty = positions.start == positions.end;
// let line_len = display_map.line_len(row); let line_len = display_map.line_len(row);
// let layed_out_line = display_map.lay_out_line_for_row(row, &text_layout_details); let layed_out_line = display_map.lay_out_line_for_row(row, &text_layout_details);
// let start_col = layed_out_line.closest_index_for_x(positions.start) as u32; let start_col = layed_out_line.closest_index_for_x(positions.start) as u32;
// if start_col < line_len || (is_empty && positions.start == layed_out_line.width()) { if start_col < line_len || (is_empty && positions.start == layed_out_line.width) {
// let start = DisplayPoint::new(row, start_col); let start = DisplayPoint::new(row, start_col);
// let end_col = layed_out_line.closest_index_for_x(positions.end) as u32; let end_col = layed_out_line.closest_index_for_x(positions.end) as u32;
// let end = DisplayPoint::new(row, end_col); let end = DisplayPoint::new(row, end_col);
// Some(Selection { Some(Selection {
// id: post_inc(&mut self.next_selection_id), id: post_inc(&mut self.next_selection_id),
// start: start.to_point(display_map), start: start.to_point(display_map),
// end: end.to_point(display_map), end: end.to_point(display_map),
// reversed, reversed,
// goal: SelectionGoal::HorizontalRange { goal: SelectionGoal::HorizontalRange {
// start: positions.start, start: positions.start.into(),
// end: positions.end, end: positions.end.into(),
// }, },
// }) })
// } else { } else {
// None None
// } }
// } }
pub(crate) fn change_with<R>( pub(crate) fn change_with<R>(
&mut self, &mut self,

View file

@ -4,7 +4,6 @@ use workspace::ModalRegistry;
actions!(Toggle); actions!(Toggle);
pub fn init(cx: &mut AppContext) { pub fn init(cx: &mut AppContext) {
cx.register_action_type::<Toggle>();
cx.global_mut::<ModalRegistry>() cx.global_mut::<ModalRegistry>()
.register_modal(Toggle, |_, cx| { .register_modal(Toggle, |_, cx| {
// if let Some(editor) = workspace // if let Some(editor) = workspace

View file

@ -1,9 +1,54 @@
use crate::SharedString; use crate::SharedString;
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use collections::{HashMap, HashSet}; use collections::{HashMap, HashSet};
use lazy_static::lazy_static;
use parking_lot::{MappedRwLockReadGuard, RwLock, RwLockReadGuard};
use serde::Deserialize; use serde::Deserialize;
use std::any::{type_name, Any}; use std::any::{type_name, Any};
/// Actions are used to implement keyboard-driven UI.
/// When you declare an action, you can bind keys to the action in the keymap and
/// listeners for that action in the element tree.
///
/// To declare a list of simple actions, you can use the actions! macro, which defines a simple unit struct
/// action for each listed action name.
/// ```rust
/// actions!(MoveUp, MoveDown, MoveLeft, MoveRight, Newline);
/// ```
/// More complex data types can also be actions. If you annotate your type with the `#[action]` proc macro,
/// it will automatically
/// ```
/// #[action]
/// pub struct SelectNext {
/// pub replace_newest: bool,
/// }
///
/// Any type A that satisfies the following bounds is automatically an action:
///
/// ```
/// A: for<'a> Deserialize<'a> + PartialEq + Clone + Default + std::fmt::Debug + 'static,
/// ```
///
/// The `#[action]` annotation will derive these implementations for your struct automatically. If you
/// want to control them manually, you can use the lower-level `#[register_action]` macro, which only
/// generates the code needed to register your action before `main`. Then you'll need to implement all
/// the traits manually.
///
/// ```
/// #[gpui::register_action]
/// #[derive(gpui::serde::Deserialize, std::cmp::PartialEq, std::clone::Clone, std::fmt::Debug)]
/// pub struct Paste {
/// pub content: SharedString,
/// }
///
/// impl std::default::Default for Paste {
/// fn default() -> Self {
/// Self {
/// content: SharedString::from("🍝"),
/// }
/// }
/// }
/// ```
pub trait Action: std::fmt::Debug + 'static { pub trait Action: std::fmt::Debug + 'static {
fn qualified_name() -> SharedString fn qualified_name() -> SharedString
where where
@ -17,32 +62,7 @@ pub trait Action: std::fmt::Debug + 'static {
fn as_any(&self) -> &dyn Any; fn as_any(&self) -> &dyn Any;
} }
// actions defines structs that can be used as actions. // Types become actions by satisfying a list of trait bounds.
#[macro_export]
macro_rules! actions {
() => {};
( $name:ident ) => {
#[derive(::std::clone::Clone, ::std::default::Default, ::std::fmt::Debug, ::std::cmp::PartialEq, $crate::serde::Deserialize)]
pub struct $name;
};
( $name:ident { $($token:tt)* } ) => {
#[derive(::std::clone::Clone, ::std::default::Default, ::std::fmt::Debug, ::std::cmp::PartialEq, $crate::serde::Deserialize)]
pub struct $name { $($token)* }
};
( $name:ident, $($rest:tt)* ) => {
actions!($name);
actions!($($rest)*);
};
( $name:ident { $($token:tt)* }, $($rest:tt)* ) => {
actions!($name { $($token)* });
actions!($($rest)*);
};
}
impl<A> Action for A impl<A> Action for A
where where
A: for<'a> Deserialize<'a> + PartialEq + Clone + Default + std::fmt::Debug + 'static, A: for<'a> Deserialize<'a> + PartialEq + Clone + Default + std::fmt::Debug + 'static,
@ -80,6 +100,61 @@ where
} }
} }
type ActionBuilder = fn(json: Option<serde_json::Value>) -> anyhow::Result<Box<dyn Action>>;
lazy_static! {
static ref ACTION_REGISTRY: RwLock<ActionRegistry> = RwLock::default();
}
#[derive(Default)]
struct ActionRegistry {
builders_by_name: HashMap<SharedString, ActionBuilder>,
all_names: Vec<SharedString>, // So we can return a static slice.
}
/// Register an action type to allow it to be referenced in keymaps.
pub fn register_action<A: Action>() {
let name = A::qualified_name();
let mut lock = ACTION_REGISTRY.write();
lock.builders_by_name.insert(name.clone(), A::build);
lock.all_names.push(name);
}
/// Construct an action based on its name and optional JSON parameters sourced from the keymap.
pub fn build_action(name: &str, params: Option<serde_json::Value>) -> Result<Box<dyn Action>> {
let lock = ACTION_REGISTRY.read();
let build_action = lock
.builders_by_name
.get(name)
.ok_or_else(|| anyhow!("no action type registered for {}", name))?;
(build_action)(params)
}
pub fn all_action_names() -> MappedRwLockReadGuard<'static, [SharedString]> {
let lock = ACTION_REGISTRY.read();
RwLockReadGuard::map(lock, |registry: &ActionRegistry| {
registry.all_names.as_slice()
})
}
/// Defines unit structs that can be used as actions.
/// To use more complex data types as actions, annotate your type with the #[action] macro.
#[macro_export]
macro_rules! actions {
() => {};
( $name:ident ) => {
#[gpui::register_action]
#[derive(::std::clone::Clone, ::std::default::Default, ::std::fmt::Debug, ::std::cmp::PartialEq, $crate::serde::Deserialize)]
pub struct $name;
};
( $name:ident, $($rest:tt)* ) => {
actions!($name);
actions!($($rest)*);
};
}
#[derive(Clone, Debug, Default, Eq, PartialEq)] #[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct DispatchContext { pub struct DispatchContext {
set: HashSet<SharedString>, set: HashSet<SharedString>,
@ -317,22 +392,23 @@ fn skip_whitespace(source: &str) -> &str {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate as gpui;
use DispatchContextPredicate::*; use DispatchContextPredicate::*;
#[test] #[test]
fn test_actions_definition() { fn test_actions_definition() {
{ {
actions!(A, B { field: i32 }, C, D, E, F {}, G); actions!(A, B, C, D, E, F, G);
} }
{ {
actions!( actions!(
A, A,
B { field: i32 }, B,
C, C,
D, D,
E, E,
F {}, F,
G, // Don't wrap, test the trailing comma G, // Don't wrap, test the trailing comma
); );
} }

View file

@ -17,9 +17,9 @@ use crate::{
current_platform, image_cache::ImageCache, Action, AnyBox, AnyView, AnyWindowHandle, current_platform, image_cache::ImageCache, Action, AnyBox, AnyView, AnyWindowHandle,
AppMetadata, AssetSource, BackgroundExecutor, ClipboardItem, Context, DispatchPhase, DisplayId, AppMetadata, AssetSource, BackgroundExecutor, ClipboardItem, Context, DispatchPhase, DisplayId,
Entity, FocusEvent, FocusHandle, FocusId, ForegroundExecutor, KeyBinding, Keymap, LayoutId, Entity, FocusEvent, FocusHandle, FocusId, ForegroundExecutor, KeyBinding, Keymap, LayoutId,
PathPromptOptions, Pixels, Platform, PlatformDisplay, Point, Render, SharedString, PathPromptOptions, Pixels, Platform, PlatformDisplay, Point, Render, SubscriberSet,
SubscriberSet, Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, View, Window,
View, Window, WindowContext, WindowHandle, WindowId, WindowContext, WindowHandle, WindowId,
}; };
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use collections::{HashMap, HashSet, VecDeque}; use collections::{HashMap, HashSet, VecDeque};
@ -140,7 +140,6 @@ impl App {
} }
} }
type ActionBuilder = fn(json: Option<serde_json::Value>) -> anyhow::Result<Box<dyn Action>>;
pub(crate) type FrameCallback = Box<dyn FnOnce(&mut AppContext)>; pub(crate) type FrameCallback = Box<dyn FnOnce(&mut AppContext)>;
type Handler = Box<dyn FnMut(&mut AppContext) -> bool + 'static>; type Handler = Box<dyn FnMut(&mut AppContext) -> bool + 'static>;
type Listener = Box<dyn FnMut(&dyn Any, &mut AppContext) -> bool + 'static>; type Listener = Box<dyn FnMut(&dyn Any, &mut AppContext) -> bool + 'static>;
@ -176,7 +175,6 @@ pub struct AppContext {
pub(crate) keymap: Arc<Mutex<Keymap>>, pub(crate) keymap: Arc<Mutex<Keymap>>,
pub(crate) global_action_listeners: pub(crate) global_action_listeners:
HashMap<TypeId, Vec<Box<dyn Fn(&dyn Action, DispatchPhase, &mut Self)>>>, HashMap<TypeId, Vec<Box<dyn Fn(&dyn Action, DispatchPhase, &mut Self)>>>,
action_builders: HashMap<SharedString, ActionBuilder>,
pending_effects: VecDeque<Effect>, pending_effects: VecDeque<Effect>,
pub(crate) pending_notifications: HashSet<EntityId>, pub(crate) pending_notifications: HashSet<EntityId>,
pub(crate) pending_global_notifications: HashSet<TypeId>, pub(crate) pending_global_notifications: HashSet<TypeId>,
@ -234,7 +232,6 @@ impl AppContext {
windows: SlotMap::with_key(), windows: SlotMap::with_key(),
keymap: Arc::new(Mutex::new(Keymap::default())), keymap: Arc::new(Mutex::new(Keymap::default())),
global_action_listeners: HashMap::default(), global_action_listeners: HashMap::default(),
action_builders: HashMap::default(),
pending_effects: VecDeque::new(), pending_effects: VecDeque::new(),
pending_notifications: HashSet::default(), pending_notifications: HashSet::default(),
pending_global_notifications: HashSet::default(), pending_global_notifications: HashSet::default(),
@ -695,10 +692,6 @@ impl AppContext {
) )
} }
pub fn all_action_names<'a>(&'a self) -> impl Iterator<Item = SharedString> + 'a {
self.action_builders.keys().cloned()
}
/// Move the global of the given type to the stack. /// Move the global of the given type to the stack.
pub(crate) fn lease_global<G: 'static>(&mut self) -> GlobalLease<G> { pub(crate) fn lease_global<G: 'static>(&mut self) -> GlobalLease<G> {
GlobalLease::new( GlobalLease::new(
@ -761,24 +754,6 @@ impl AppContext {
})); }));
} }
/// Register an action type to allow it to be referenced in keymaps.
pub fn register_action_type<A: Action>(&mut self) {
self.action_builders.insert(A::qualified_name(), A::build);
}
/// Construct an action based on its name and parameters.
pub fn build_action(
&mut self,
name: &str,
params: Option<serde_json::Value>,
) -> Result<Box<dyn Action>> {
let build = self
.action_builders
.get(name)
.ok_or_else(|| anyhow!("no action type registered for {}", name))?;
(build)(params)
}
/// Event handlers propagate events by default. Call this method to stop dispatching to /// Event handlers propagate events by default. Call this method to stop dispatching to
/// event handlers with a lower z-index (mouse) or higher in the tree (keyboard). This is /// event handlers with a lower z-index (mouse) or higher in the tree (keyboard). This is
/// the opposite of [propagate]. It's also possible to cancel a call to [propagate] by /// the opposite of [propagate]. It's also possible to cancel a call to [propagate] by

View file

@ -68,7 +68,8 @@ impl<T> Future for Task<T> {
} }
} }
} }
type AnyLocalFuture<R> = Pin<Box<dyn 'static + Future<Output = R>>>;
type AnyFuture<R> = Pin<Box<dyn 'static + Send + Future<Output = R>>>;
impl BackgroundExecutor { impl BackgroundExecutor {
pub fn new(dispatcher: Arc<dyn PlatformDispatcher>) -> Self { pub fn new(dispatcher: Arc<dyn PlatformDispatcher>) -> Self {
Self { dispatcher } Self { dispatcher }
@ -81,11 +82,17 @@ impl BackgroundExecutor {
R: Send + 'static, R: Send + 'static,
{ {
let dispatcher = self.dispatcher.clone(); let dispatcher = self.dispatcher.clone();
fn inner<R: Send + 'static>(
dispatcher: Arc<dyn PlatformDispatcher>,
future: AnyFuture<R>,
) -> Task<R> {
let (runnable, task) = let (runnable, task) =
async_task::spawn(future, move |runnable| dispatcher.dispatch(runnable)); async_task::spawn(future, move |runnable| dispatcher.dispatch(runnable));
runnable.schedule(); runnable.schedule();
Task::Spawned(task) Task::Spawned(task)
} }
inner::<R>(dispatcher, Box::pin(future))
}
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
pub fn block_test<R>(&self, future: impl Future<Output = R>) -> R { pub fn block_test<R>(&self, future: impl Future<Output = R>) -> R {
@ -243,12 +250,18 @@ impl ForegroundExecutor {
R: 'static, R: 'static,
{ {
let dispatcher = self.dispatcher.clone(); let dispatcher = self.dispatcher.clone();
fn inner<R: 'static>(
dispatcher: Arc<dyn PlatformDispatcher>,
future: AnyLocalFuture<R>,
) -> Task<R> {
let (runnable, task) = async_task::spawn_local(future, move |runnable| { let (runnable, task) = async_task::spawn_local(future, move |runnable| {
dispatcher.dispatch_on_main_thread(runnable) dispatcher.dispatch_on_main_thread(runnable)
}); });
runnable.schedule(); runnable.schedule();
Task::Spawned(task) Task::Spawned(task)
} }
inner::<R>(dispatcher, Box::pin(future))
}
} }
pub struct Scope<'a> { pub struct Scope<'a> {

View file

@ -37,6 +37,7 @@ pub use anyhow::Result;
pub use app::*; pub use app::*;
pub use assets::*; pub use assets::*;
pub use color::*; pub use color::*;
pub use ctor::ctor;
pub use element::*; pub use element::*;
pub use elements::*; pub use elements::*;
pub use executor::*; pub use executor::*;

View file

@ -87,7 +87,7 @@ impl MetalRenderer {
MTLResourceOptions::StorageModeManaged, MTLResourceOptions::StorageModeManaged,
); );
let paths_rasterization_pipeline_state = build_pipeline_state( let paths_rasterization_pipeline_state = build_path_rasterization_pipeline_state(
&device, &device,
&library, &library,
"paths_rasterization", "paths_rasterization",
@ -823,7 +823,40 @@ fn build_pipeline_state(
color_attachment.set_source_alpha_blend_factor(metal::MTLBlendFactor::One); color_attachment.set_source_alpha_blend_factor(metal::MTLBlendFactor::One);
color_attachment.set_destination_rgb_blend_factor(metal::MTLBlendFactor::OneMinusSourceAlpha); color_attachment.set_destination_rgb_blend_factor(metal::MTLBlendFactor::OneMinusSourceAlpha);
color_attachment.set_destination_alpha_blend_factor(metal::MTLBlendFactor::One); color_attachment.set_destination_alpha_blend_factor(metal::MTLBlendFactor::One);
descriptor.set_depth_attachment_pixel_format(MTLPixelFormat::Invalid);
device
.new_render_pipeline_state(&descriptor)
.expect("could not create render pipeline state")
}
fn build_path_rasterization_pipeline_state(
device: &metal::DeviceRef,
library: &metal::LibraryRef,
label: &str,
vertex_fn_name: &str,
fragment_fn_name: &str,
pixel_format: metal::MTLPixelFormat,
) -> metal::RenderPipelineState {
let vertex_fn = library
.get_function(vertex_fn_name, None)
.expect("error locating vertex function");
let fragment_fn = library
.get_function(fragment_fn_name, None)
.expect("error locating fragment function");
let descriptor = metal::RenderPipelineDescriptor::new();
descriptor.set_label(label);
descriptor.set_vertex_function(Some(vertex_fn.as_ref()));
descriptor.set_fragment_function(Some(fragment_fn.as_ref()));
let color_attachment = descriptor.color_attachments().object_at(0).unwrap();
color_attachment.set_pixel_format(pixel_format);
color_attachment.set_blending_enabled(true);
color_attachment.set_rgb_blend_operation(metal::MTLBlendOperation::Add);
color_attachment.set_alpha_blend_operation(metal::MTLBlendOperation::Add);
color_attachment.set_source_rgb_blend_factor(metal::MTLBlendFactor::One);
color_attachment.set_source_alpha_blend_factor(metal::MTLBlendFactor::One);
color_attachment.set_destination_rgb_blend_factor(metal::MTLBlendFactor::One);
color_attachment.set_destination_alpha_blend_factor(metal::MTLBlendFactor::One);
device device
.new_render_pipeline_state(&descriptor) .new_render_pipeline_state(&descriptor)

View file

@ -614,6 +614,10 @@ impl<'a> WindowContext<'a> {
.find(|display| display.id() == self.window.display_id) .find(|display| display.id() == self.window.display_id)
} }
pub fn show_character_palette(&self) {
self.window.platform_window.show_character_palette();
}
/// The scale factor of the display associated with the window. For example, it could /// The scale factor of the display associated with the window. For example, it could
/// return 2.0 for a "retina" display, indicating that each logical pixel should actually /// return 2.0 for a "retina" display, indicating that each logical pixel should actually
/// be rendered as two pixels on screen. /// be rendered as two pixels on screen.

View file

@ -0,0 +1,55 @@
// Input:
//
// #[action]
// struct Foo {
// bar: String,
// }
// Output:
//
// #[gpui::register_action]
// #[derive(gpui::serde::Deserialize, std::cmp::PartialEq, std::clone::Clone, std::default::Default, std::fmt::Debug)]
// struct Foo {
// bar: String,
// }
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};
pub fn action(_attr: TokenStream, item: TokenStream) -> TokenStream {
let input = parse_macro_input!(item as DeriveInput);
let name = &input.ident;
let attrs = input
.attrs
.into_iter()
.filter(|attr| !attr.path.is_ident("action"))
.collect::<Vec<_>>();
let attributes = quote! {
#[gpui::register_action]
#[derive(gpui::serde::Deserialize, std::cmp::PartialEq, std::clone::Clone, std::default::Default, std::fmt::Debug)]
#(#attrs)*
};
let visibility = input.vis;
let output = match input.data {
syn::Data::Struct(ref struct_data) => {
let fields = &struct_data.fields;
quote! {
#attributes
#visibility struct #name #fields
}
}
syn::Data::Enum(ref enum_data) => {
let variants = &enum_data.variants;
quote! {
#attributes
#visibility enum #name { #variants }
}
}
_ => panic!("Expected a struct or an enum."),
};
TokenStream::from(output)
}

View file

@ -1,14 +1,26 @@
use proc_macro::TokenStream; mod action;
mod derive_component; mod derive_component;
mod register_action;
mod style_helpers; mod style_helpers;
mod test; mod test;
use proc_macro::TokenStream;
#[proc_macro] #[proc_macro]
pub fn style_helpers(args: TokenStream) -> TokenStream { pub fn style_helpers(args: TokenStream) -> TokenStream {
style_helpers::style_helpers(args) style_helpers::style_helpers(args)
} }
#[proc_macro_attribute]
pub fn action(attr: TokenStream, item: TokenStream) -> TokenStream {
action::action(attr, item)
}
#[proc_macro_attribute]
pub fn register_action(attr: TokenStream, item: TokenStream) -> TokenStream {
register_action::register_action(attr, item)
}
#[proc_macro_derive(Component, attributes(component))] #[proc_macro_derive(Component, attributes(component))]
pub fn derive_component(input: TokenStream) -> TokenStream { pub fn derive_component(input: TokenStream) -> TokenStream {
derive_component::derive_component(input) derive_component::derive_component(input)

View file

@ -0,0 +1,33 @@
// Input:
//
// struct FooBar {}
// Output:
//
// struct FooBar {}
//
// #[allow(non_snake_case)]
// #[gpui2::ctor]
// fn register_foobar_builder() {
// gpui2::register_action_builder::<Foo>()
// }
use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::{parse_macro_input, DeriveInput};
pub fn register_action(_attr: TokenStream, item: TokenStream) -> TokenStream {
let input = parse_macro_input!(item as DeriveInput);
let type_name = &input.ident;
let ctor_fn_name = format_ident!("register_{}_builder", type_name.to_string().to_lowercase());
let expanded = quote! {
#input
#[allow(non_snake_case)]
#[gpui::ctor]
fn #ctor_fn_name() {
gpui::register_action::<#type_name>()
}
};
TokenStream::from(expanded)
}

View file

@ -73,9 +73,9 @@ impl KeymapFile {
"Expected first item in array to be a string." "Expected first item in array to be a string."
))); )));
}; };
cx.build_action(&name, Some(data)) gpui::build_action(&name, Some(data))
} }
Value::String(name) => cx.build_action(&name, None), Value::String(name) => gpui::build_action(&name, None),
Value::Null => Ok(no_action()), Value::Null => Ok(no_action()),
_ => { _ => {
return Some(Err(anyhow!("Expected two-element array, got {action:?}"))) return Some(Err(anyhow!("Expected two-element array, got {action:?}")))

View file

@ -15,8 +15,6 @@ impl FocusStory {
KeyBinding::new("cmd-a", ActionB, Some("child-1")), KeyBinding::new("cmd-a", ActionB, Some("child-1")),
KeyBinding::new("cmd-c", ActionC, None), KeyBinding::new("cmd-c", ActionC, None),
]); ]);
cx.register_action_type::<ActionA>();
cx.register_action_type::<ActionB>();
cx.build_view(move |cx| Self {}) cx.build_view(move |cx| Self {})
} }

View file

@ -38,6 +38,7 @@ pub enum ComponentStory {
Palette, Palette,
Panel, Panel,
ProjectPanel, ProjectPanel,
Players,
RecentProjects, RecentProjects,
Scroll, Scroll,
Tab, Tab,
@ -80,6 +81,7 @@ impl ComponentStory {
Self::MultiBuffer => cx.build_view(|_| ui::MultiBufferStory).into(), Self::MultiBuffer => cx.build_view(|_| ui::MultiBufferStory).into(),
Self::NotificationsPanel => cx.build_view(|cx| ui::NotificationsPanelStory).into(), Self::NotificationsPanel => cx.build_view(|cx| ui::NotificationsPanelStory).into(),
Self::Palette => cx.build_view(|cx| ui::PaletteStory).into(), Self::Palette => cx.build_view(|cx| ui::PaletteStory).into(),
Self::Players => cx.build_view(|_| theme2::PlayerStory).into(),
Self::Panel => cx.build_view(|cx| ui::PanelStory).into(), Self::Panel => cx.build_view(|cx| ui::PanelStory).into(),
Self::ProjectPanel => cx.build_view(|_| ui::ProjectPanelStory).into(), Self::ProjectPanel => cx.build_view(|_| ui::ProjectPanelStory).into(),
Self::RecentProjects => cx.build_view(|_| ui::RecentProjectsStory).into(), Self::RecentProjects => cx.build_view(|_| ui::RecentProjectsStory).into(),

View file

@ -5,6 +5,8 @@ edition = "2021"
publish = false publish = false
[features] [features]
default = ["stories"]
stories = ["dep:itertools"]
test-support = [ test-support = [
"gpui/test-support", "gpui/test-support",
"fs/test-support", "fs/test-support",
@ -30,6 +32,7 @@ settings = { package = "settings2", path = "../settings2" }
toml.workspace = true toml.workspace = true
uuid.workspace = true uuid.workspace = true
util = { path = "../util" } util = { path = "../util" }
itertools = { version = "0.11.0", optional = true }
[dev-dependencies] [dev-dependencies]
gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] } gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }

View file

@ -3,7 +3,7 @@ use std::sync::Arc;
use gpui::Hsla; use gpui::Hsla;
use refineable::Refineable; use refineable::Refineable;
use crate::SyntaxTheme; use crate::{PlayerColors, SyntaxTheme};
#[derive(Clone)] #[derive(Clone)]
pub struct SystemColors { pub struct SystemColors {
@ -13,33 +13,6 @@ pub struct SystemColors {
pub mac_os_traffic_light_green: Hsla, pub mac_os_traffic_light_green: Hsla,
} }
#[derive(Debug, Clone, Copy)]
pub struct PlayerColor {
pub cursor: Hsla,
pub background: Hsla,
pub selection: Hsla,
}
#[derive(Clone)]
pub struct PlayerColors(pub Vec<PlayerColor>);
impl PlayerColors {
pub fn local(&self) -> PlayerColor {
// todo!("use a valid color");
*self.0.first().unwrap()
}
pub fn absent(&self) -> PlayerColor {
// todo!("use a valid color");
*self.0.last().unwrap()
}
pub fn color_for_participant(&self, participant_index: u32) -> PlayerColor {
let len = self.0.len() - 1;
self.0[(participant_index as usize % len) + 1]
}
}
#[derive(Refineable, Clone, Debug)] #[derive(Refineable, Clone, Debug)]
#[refineable(debug)] #[refineable(debug)]
pub struct StatusColors { pub struct StatusColors {

View file

@ -3,12 +3,106 @@ use std::num::ParseIntError;
use gpui::{hsla, Hsla, Rgba}; use gpui::{hsla, Hsla, Rgba};
use crate::{ use crate::{
colors::{GitStatusColors, PlayerColor, PlayerColors, StatusColors, SystemColors, ThemeColors}, colors::{GitStatusColors, StatusColors, SystemColors, ThemeColors},
scale::{ColorScaleSet, ColorScales}, scale::{ColorScaleSet, ColorScales},
syntax::SyntaxTheme, syntax::SyntaxTheme,
ColorScale, ColorScale, PlayerColor, PlayerColors,
}; };
impl Default for PlayerColors {
fn default() -> Self {
Self(vec![
PlayerColor {
cursor: blue().dark().step_9(),
background: blue().dark().step_5(),
selection: blue().dark().step_3(),
},
PlayerColor {
cursor: orange().dark().step_9(),
background: orange().dark().step_5(),
selection: orange().dark().step_3(),
},
PlayerColor {
cursor: pink().dark().step_9(),
background: pink().dark().step_5(),
selection: pink().dark().step_3(),
},
PlayerColor {
cursor: lime().dark().step_9(),
background: lime().dark().step_5(),
selection: lime().dark().step_3(),
},
PlayerColor {
cursor: purple().dark().step_9(),
background: purple().dark().step_5(),
selection: purple().dark().step_3(),
},
PlayerColor {
cursor: amber().dark().step_9(),
background: amber().dark().step_5(),
selection: amber().dark().step_3(),
},
PlayerColor {
cursor: jade().dark().step_9(),
background: jade().dark().step_5(),
selection: jade().dark().step_3(),
},
PlayerColor {
cursor: red().dark().step_9(),
background: red().dark().step_5(),
selection: red().dark().step_3(),
},
])
}
}
impl PlayerColors {
pub fn default_light() -> Self {
Self(vec![
PlayerColor {
cursor: blue().light().step_9(),
background: blue().light().step_4(),
selection: blue().light().step_3(),
},
PlayerColor {
cursor: orange().light().step_9(),
background: orange().light().step_4(),
selection: orange().light().step_3(),
},
PlayerColor {
cursor: pink().light().step_9(),
background: pink().light().step_4(),
selection: pink().light().step_3(),
},
PlayerColor {
cursor: lime().light().step_9(),
background: lime().light().step_4(),
selection: lime().light().step_3(),
},
PlayerColor {
cursor: purple().light().step_9(),
background: purple().light().step_4(),
selection: purple().light().step_3(),
},
PlayerColor {
cursor: amber().light().step_9(),
background: amber().light().step_4(),
selection: amber().light().step_3(),
},
PlayerColor {
cursor: jade().light().step_9(),
background: jade().light().step_4(),
selection: jade().light().step_3(),
},
PlayerColor {
cursor: red().light().step_9(),
background: red().light().step_4(),
selection: red().light().step_3(),
},
])
}
}
fn neutral() -> ColorScaleSet { fn neutral() -> ColorScaleSet {
slate() slate()
} }
@ -27,17 +121,17 @@ impl Default for SystemColors {
impl Default for StatusColors { impl Default for StatusColors {
fn default() -> Self { fn default() -> Self {
Self { Self {
conflict: red().dark().step_11(), conflict: red().dark().step_9(),
created: grass().dark().step_11(), created: grass().dark().step_9(),
deleted: red().dark().step_11(), deleted: red().dark().step_9(),
error: red().dark().step_11(), error: red().dark().step_9(),
hidden: neutral().dark().step_11(), hidden: neutral().dark().step_9(),
ignored: neutral().dark().step_11(), ignored: neutral().dark().step_9(),
info: blue().dark().step_11(), info: blue().dark().step_9(),
modified: yellow().dark().step_11(), modified: yellow().dark().step_9(),
renamed: blue().dark().step_11(), renamed: blue().dark().step_9(),
success: grass().dark().step_11(), success: grass().dark().step_9(),
warning: yellow().dark().step_11(), warning: yellow().dark().step_9(),
} }
} }
} }
@ -45,43 +139,16 @@ impl Default for StatusColors {
impl Default for GitStatusColors { impl Default for GitStatusColors {
fn default() -> Self { fn default() -> Self {
Self { Self {
conflict: orange().dark().step_11(), conflict: orange().dark().step_9(),
created: grass().dark().step_11(), created: grass().dark().step_9(),
deleted: red().dark().step_11(), deleted: red().dark().step_9(),
ignored: neutral().dark().step_11(), ignored: neutral().dark().step_9(),
modified: yellow().dark().step_11(), modified: yellow().dark().step_9(),
renamed: blue().dark().step_11(), renamed: blue().dark().step_9(),
} }
} }
} }
impl Default for PlayerColors {
fn default() -> Self {
Self(vec![
PlayerColor {
cursor: hsla(0.0, 0.0, 0.0, 1.0),
background: hsla(0.0, 0.0, 0.0, 1.0),
selection: hsla(0.0, 0.0, 0.0, 1.0),
},
PlayerColor {
cursor: hsla(0.0, 0.0, 0.0, 1.0),
background: hsla(0.0, 0.0, 0.0, 1.0),
selection: hsla(0.0, 0.0, 0.0, 1.0),
},
PlayerColor {
cursor: hsla(0.0, 0.0, 0.0, 1.0),
background: hsla(0.0, 0.0, 0.0, 1.0),
selection: hsla(0.0, 0.0, 0.0, 1.0),
},
PlayerColor {
cursor: hsla(0.0, 0.0, 0.0, 1.0),
background: hsla(0.0, 0.0, 0.0, 1.0),
selection: hsla(0.0, 0.0, 0.0, 1.0),
},
])
}
}
impl SyntaxTheme { impl SyntaxTheme {
pub fn default_light() -> Self { pub fn default_light() -> Self {
Self { Self {

View file

@ -1,8 +1,8 @@
use std::sync::Arc; use std::sync::Arc;
use crate::{ use crate::{
colors::{GitStatusColors, PlayerColors, StatusColors, SystemColors, ThemeColors, ThemeStyles}, colors::{GitStatusColors, StatusColors, SystemColors, ThemeColors, ThemeStyles},
default_color_scales, Appearance, SyntaxTheme, Theme, ThemeFamily, default_color_scales, Appearance, PlayerColors, SyntaxTheme, Theme, ThemeFamily,
}; };
fn zed_pro_daylight() -> Theme { fn zed_pro_daylight() -> Theme {
@ -15,7 +15,7 @@ fn zed_pro_daylight() -> Theme {
colors: ThemeColors::default_light(), colors: ThemeColors::default_light(),
status: StatusColors::default(), status: StatusColors::default(),
git: GitStatusColors::default(), git: GitStatusColors::default(),
player: PlayerColors::default(), player: PlayerColors::default_light(),
syntax: Arc::new(SyntaxTheme::default_light()), syntax: Arc::new(SyntaxTheme::default_light()),
}, },
} }

View file

@ -0,0 +1,170 @@
use gpui::Hsla;
#[derive(Debug, Clone, Copy)]
pub struct PlayerColor {
pub cursor: Hsla,
pub background: Hsla,
pub selection: Hsla,
}
/// A collection of colors that are used to color players in the editor.
///
/// The first color is always the local player's color, usually a blue.
///
/// The rest of the default colors crisscross back and forth on the
/// color wheel so that the colors are as distinct as possible.
#[derive(Clone)]
pub struct PlayerColors(pub Vec<PlayerColor>);
impl PlayerColors {
pub fn local(&self) -> PlayerColor {
// todo!("use a valid color");
*self.0.first().unwrap()
}
pub fn absent(&self) -> PlayerColor {
// todo!("use a valid color");
*self.0.last().unwrap()
}
pub fn color_for_participant(&self, participant_index: u32) -> PlayerColor {
let len = self.0.len() - 1;
self.0[(participant_index as usize % len) + 1]
}
}
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use super::*;
use crate::{ActiveTheme, Story};
use gpui::{div, img, px, Div, ParentElement, Render, Styled, ViewContext};
pub struct PlayerStory;
impl Render for PlayerStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx).child(
div()
.flex()
.flex_col()
.gap_4()
.child(Story::title_for::<_, PlayerColors>(cx))
.child(Story::label(cx, "Player Colors"))
.child(
div()
.flex()
.flex_col()
.gap_1()
.child(
div().flex().gap_1().children(
cx.theme().players().0.clone().iter_mut().map(|player| {
div().w_8().h_8().rounded_md().bg(player.cursor)
}),
),
)
.child(div().flex().gap_1().children(
cx.theme().players().0.clone().iter_mut().map(|player| {
div().w_8().h_8().rounded_md().bg(player.background)
}),
))
.child(div().flex().gap_1().children(
cx.theme().players().0.clone().iter_mut().map(|player| {
div().w_8().h_8().rounded_md().bg(player.selection)
}),
)),
)
.child(Story::label(cx, "Avatar Rings"))
.child(div().flex().gap_1().children(
cx.theme().players().0.clone().iter_mut().map(|player| {
div()
.my_1()
.rounded_full()
.border_2()
.border_color(player.cursor)
.child(
img()
.rounded_full()
.uri("https://avatars.githubusercontent.com/u/1714999?v=4")
.size_6()
.bg(gpui::red()),
)
}),
))
.child(Story::label(cx, "Player Backgrounds"))
.child(div().flex().gap_1().children(
cx.theme().players().0.clone().iter_mut().map(|player| {
div()
.my_1()
.rounded_xl()
.flex()
.items_center()
.h_8()
.py_0p5()
.px_1p5()
.bg(player.background)
.child(
div().relative().neg_mx_1().rounded_full().z_index(3)
.border_2()
.border_color(player.background)
.size(px(28.))
.child(
img()
.rounded_full()
.uri("https://avatars.githubusercontent.com/u/1714999?v=4")
.size(px(24.))
.bg(gpui::red()),
),
).child(
div().relative().neg_mx_1().rounded_full().z_index(2)
.border_2()
.border_color(player.background)
.size(px(28.))
.child(
img()
.rounded_full()
.uri("https://avatars.githubusercontent.com/u/1714999?v=4")
.size(px(24.))
.bg(gpui::red()),
),
).child(
div().relative().neg_mx_1().rounded_full().z_index(1)
.border_2()
.border_color(player.background)
.size(px(28.))
.child(
img()
.rounded_full()
.uri("https://avatars.githubusercontent.com/u/1714999?v=4")
.size(px(24.))
.bg(gpui::red()),
),
)
}),
))
.child(Story::label(cx, "Player Selections"))
.child(div().flex().flex_col().gap_px().children(
cx.theme().players().0.clone().iter_mut().map(|player| {
div()
.flex()
.child(
div()
.flex()
.flex_none()
.rounded_sm()
.px_0p5()
.text_color(cx.theme().colors().text)
.bg(player.selection)
.child("The brown fox jumped over the lazy dog."),
)
.child(div().flex_1())
}),
)),
)
}
}
}

View file

@ -0,0 +1,38 @@
use gpui::{div, Component, Div, ParentElement, Styled, ViewContext};
use crate::ActiveTheme;
pub struct Story {}
impl Story {
pub fn container<V: 'static>(cx: &mut ViewContext<V>) -> Div<V> {
div()
.size_full()
.flex()
.flex_col()
.pt_2()
.px_4()
.font("Zed Mono")
.bg(cx.theme().colors().background)
}
pub fn title<V: 'static>(cx: &mut ViewContext<V>, title: &str) -> impl Component<V> {
div()
.text_xl()
.text_color(cx.theme().colors().text)
.child(title.to_owned())
}
pub fn title_for<V: 'static, T>(cx: &mut ViewContext<V>) -> impl Component<V> {
Self::title(cx, std::any::type_name::<T>())
}
pub fn label<V: 'static>(cx: &mut ViewContext<V>, label: &str) -> impl Component<V> {
div()
.mt_4()
.mb_2()
.text_xs()
.text_color(cx.theme().colors().text)
.child(label.to_owned())
}
}

View file

@ -1,6 +1,7 @@
mod colors; mod colors;
mod default_colors; mod default_colors;
mod default_theme; mod default_theme;
mod players;
mod registry; mod registry;
mod scale; mod scale;
mod settings; mod settings;
@ -14,6 +15,7 @@ use ::settings::Settings;
pub use colors::*; pub use colors::*;
pub use default_colors::*; pub use default_colors::*;
pub use default_theme::*; pub use default_theme::*;
pub use players::*;
pub use registry::*; pub use registry::*;
pub use scale::*; pub use scale::*;
pub use settings::*; pub use settings::*;
@ -120,3 +122,8 @@ pub struct DiagnosticStyle {
pub hint: Hsla, pub hint: Hsla,
pub ignored: Hsla, pub ignored: Hsla,
} }
#[cfg(feature = "stories")]
mod story;
#[cfg(feature = "stories")]
pub use story::*;

View file

@ -107,7 +107,7 @@ impl LspAdapter for JsonLspAdapter {
&self, &self,
cx: &mut AppContext, cx: &mut AppContext,
) -> BoxFuture<'static, serde_json::Value> { ) -> BoxFuture<'static, serde_json::Value> {
let action_names = cx.all_action_names().collect::<Vec<_>>(); let action_names = gpui::all_action_names();
let staff_mode = cx.is_staff(); let staff_mode = cx.is_staff();
let language_names = &self.languages.language_names(); let language_names = &self.languages.language_names();
let settings_schema = cx.global::<SettingsStore>().json_schema( let settings_schema = cx.global::<SettingsStore>().json_schema(