Compare commits
10 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e4d5b63426 | ||
![]() |
51db8e0423 | ||
![]() |
4558b504fc | ||
![]() |
49230304bc | ||
![]() |
f47fa88f5f | ||
![]() |
0230e0ecd9 | ||
![]() |
7d3fe56723 | ||
![]() |
4841d85da7 | ||
![]() |
b60aed8037 | ||
![]() |
ab6255def4 |
22 changed files with 299 additions and 99 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -8345,7 +8345,7 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
|
|||
|
||||
[[package]]
|
||||
name = "zed"
|
||||
version = "0.73.0"
|
||||
version = "0.73.3"
|
||||
dependencies = [
|
||||
"activity_indicator",
|
||||
"anyhow",
|
||||
|
|
|
@ -63,6 +63,7 @@ pub struct ContextMenu {
|
|||
visible: bool,
|
||||
previously_focused_view_id: Option<usize>,
|
||||
clicked: bool,
|
||||
parent_view_id: usize,
|
||||
_actions_observation: Subscription,
|
||||
}
|
||||
|
||||
|
@ -114,6 +115,8 @@ impl View for ContextMenu {
|
|||
|
||||
impl ContextMenu {
|
||||
pub fn new(cx: &mut ViewContext<Self>) -> Self {
|
||||
let parent_view_id = cx.parent().unwrap();
|
||||
|
||||
Self {
|
||||
show_count: 0,
|
||||
anchor_position: Default::default(),
|
||||
|
@ -123,6 +126,7 @@ impl ContextMenu {
|
|||
visible: Default::default(),
|
||||
previously_focused_view_id: Default::default(),
|
||||
clicked: false,
|
||||
parent_view_id,
|
||||
_actions_observation: cx.observe_actions(Self::action_dispatched),
|
||||
}
|
||||
}
|
||||
|
@ -251,6 +255,7 @@ impl ContextMenu {
|
|||
}
|
||||
|
||||
fn render_menu_for_measurement(&self, cx: &mut RenderContext<Self>) -> impl Element {
|
||||
let window_id = cx.window_id();
|
||||
let style = cx.global::<Settings>().theme.context_menu.clone();
|
||||
Flex::row()
|
||||
.with_child(
|
||||
|
@ -289,6 +294,8 @@ impl ContextMenu {
|
|||
Some(ix) == self.selected_index,
|
||||
);
|
||||
KeystrokeLabel::new(
|
||||
window_id,
|
||||
self.parent_view_id,
|
||||
action.boxed_clone(),
|
||||
style.keystroke.container,
|
||||
style.keystroke.text.clone(),
|
||||
|
@ -318,6 +325,7 @@ impl ContextMenu {
|
|||
|
||||
let style = cx.global::<Settings>().theme.context_menu.clone();
|
||||
|
||||
let window_id = cx.window_id();
|
||||
MouseEventHandler::<Menu>::new(0, cx, |_, cx| {
|
||||
Flex::column()
|
||||
.with_children(self.items.iter().enumerate().map(|(ix, item)| {
|
||||
|
@ -337,6 +345,8 @@ impl ContextMenu {
|
|||
)
|
||||
.with_child({
|
||||
KeystrokeLabel::new(
|
||||
window_id,
|
||||
self.parent_view_id,
|
||||
action.boxed_clone(),
|
||||
style.keystroke.container,
|
||||
style.keystroke.text.clone(),
|
||||
|
|
|
@ -31,7 +31,6 @@ use workspace::{
|
|||
use crate::system_specs::SystemSpecs;
|
||||
|
||||
const FEEDBACK_CHAR_LIMIT: RangeInclusive<usize> = 10..=5000;
|
||||
const FEEDBACK_PLACEHOLDER_TEXT: &str = "Save to submit feedback as Markdown.";
|
||||
const FEEDBACK_SUBMISSION_ERROR_TEXT: &str =
|
||||
"Feedback failed to submit, see error log for details.";
|
||||
|
||||
|
@ -117,7 +116,6 @@ impl FeedbackEditor {
|
|||
let editor = cx.add_view(|cx| {
|
||||
let mut editor = Editor::for_buffer(buffer, Some(project.clone()), cx);
|
||||
editor.set_vertical_scroll_margin(5, cx);
|
||||
editor.set_placeholder_text(FEEDBACK_PLACEHOLDER_TEXT, cx);
|
||||
editor
|
||||
});
|
||||
|
||||
|
@ -483,6 +481,13 @@ impl View for SubmitFeedbackButton {
|
|||
.aligned()
|
||||
.contained()
|
||||
.with_margin_left(theme.feedback.button_margin)
|
||||
.with_tooltip::<Self, _>(
|
||||
0,
|
||||
"cmd-s".into(),
|
||||
Some(Box::new(SubmitFeedback)),
|
||||
theme.tooltip.clone(),
|
||||
cx,
|
||||
)
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
@ -504,3 +509,56 @@ impl ToolbarItemView for SubmitFeedbackButton {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FeedbackInfoText {
|
||||
active_item: Option<ViewHandle<FeedbackEditor>>,
|
||||
}
|
||||
|
||||
impl FeedbackInfoText {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
active_item: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Entity for FeedbackInfoText {
|
||||
type Event = ();
|
||||
}
|
||||
|
||||
impl View for FeedbackInfoText {
|
||||
fn ui_name() -> &'static str {
|
||||
"FeedbackInfoText"
|
||||
}
|
||||
|
||||
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
|
||||
let theme = cx.global::<Settings>().theme.clone();
|
||||
let text = "We read whatever you submit here. For issues and discussions, visit the community repo on GitHub.";
|
||||
Label::new(text.to_string(), theme.feedback.info_text.text.clone())
|
||||
.contained()
|
||||
.aligned()
|
||||
.left()
|
||||
.clipped()
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToolbarItemView for FeedbackInfoText {
|
||||
fn set_active_pane_item(
|
||||
&mut self,
|
||||
active_pane_item: Option<&dyn ItemHandle>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> workspace::ToolbarItemLocation {
|
||||
cx.notify();
|
||||
if let Some(feedback_editor) = active_pane_item.and_then(|i| i.downcast::<FeedbackEditor>())
|
||||
{
|
||||
self.active_item = Some(feedback_editor);
|
||||
ToolbarItemLocation::PrimaryLeft {
|
||||
flex: Some((1., false)),
|
||||
}
|
||||
} else {
|
||||
self.active_item = None;
|
||||
ToolbarItemLocation::Hidden
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1332,6 +1332,31 @@ impl MutableAppContext {
|
|||
self.action_deserializers.keys().copied()
|
||||
}
|
||||
|
||||
/// Return keystrokes that would dispatch the given action on the given view.
|
||||
pub(crate) fn keystrokes_for_action(
|
||||
&mut self,
|
||||
window_id: usize,
|
||||
view_id: usize,
|
||||
action: &dyn Action,
|
||||
) -> Option<SmallVec<[Keystroke; 2]>> {
|
||||
let mut contexts = Vec::new();
|
||||
for view_id in self.ancestors(window_id, view_id) {
|
||||
if let Some(view) = self.views.get(&(window_id, view_id)) {
|
||||
contexts.push(view.keymap_context(self));
|
||||
}
|
||||
}
|
||||
|
||||
self.keystroke_matcher
|
||||
.bindings_for_action_type(action.as_any().type_id())
|
||||
.find_map(|b| {
|
||||
if b.match_context(&contexts) {
|
||||
b.keystrokes().map(|s| s.into())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn available_actions(
|
||||
&self,
|
||||
window_id: usize,
|
||||
|
@ -1339,8 +1364,10 @@ impl MutableAppContext {
|
|||
) -> impl Iterator<Item = (&'static str, Box<dyn Action>, SmallVec<[&Binding; 1]>)> {
|
||||
let mut action_types: HashSet<_> = self.global_actions.keys().copied().collect();
|
||||
|
||||
let mut contexts = Vec::new();
|
||||
for view_id in self.ancestors(window_id, view_id) {
|
||||
if let Some(view) = self.views.get(&(window_id, view_id)) {
|
||||
contexts.push(view.keymap_context(self));
|
||||
let view_type = view.as_any().type_id();
|
||||
if let Some(actions) = self.actions.get(&view_type) {
|
||||
action_types.extend(actions.keys().copied());
|
||||
|
@ -1357,6 +1384,7 @@ impl MutableAppContext {
|
|||
deserialize("{}").ok()?,
|
||||
self.keystroke_matcher
|
||||
.bindings_for_action_type(*type_id)
|
||||
.filter(|b| b.match_context(&contexts))
|
||||
.collect(),
|
||||
))
|
||||
} else {
|
||||
|
@ -1384,34 +1412,6 @@ impl MutableAppContext {
|
|||
self.global_actions.contains_key(&action_type)
|
||||
}
|
||||
|
||||
/// Return keystrokes that would dispatch the given action closest to the focused view, if there are any.
|
||||
pub(crate) fn keystrokes_for_action(
|
||||
&mut self,
|
||||
window_id: usize,
|
||||
view_stack: &[usize],
|
||||
action: &dyn Action,
|
||||
) -> Option<SmallVec<[Keystroke; 2]>> {
|
||||
self.keystroke_matcher.contexts.clear();
|
||||
for view_id in view_stack.iter().rev() {
|
||||
let view = self
|
||||
.cx
|
||||
.views
|
||||
.get(&(window_id, *view_id))
|
||||
.expect("view in responder chain does not exist");
|
||||
self.keystroke_matcher
|
||||
.contexts
|
||||
.push(view.keymap_context(self.as_ref()));
|
||||
let keystrokes = self
|
||||
.keystroke_matcher
|
||||
.keystrokes_for_action(action, &self.keystroke_matcher.contexts);
|
||||
if keystrokes.is_some() {
|
||||
return keystrokes;
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
// Traverses the parent tree. Walks down the tree toward the passed
|
||||
// view calling visit with true. Then walks back up the tree calling visit with false.
|
||||
// If `visit` returns false this function will immediately return.
|
||||
|
@ -1915,10 +1915,11 @@ impl MutableAppContext {
|
|||
{
|
||||
self.update(|this| {
|
||||
let view_id = post_inc(&mut this.next_entity_id);
|
||||
// Make sure we can tell child views about their parent
|
||||
this.cx.parents.insert((window_id, view_id), parent_id);
|
||||
let mut cx = ViewContext::new(this, window_id, view_id);
|
||||
let handle = if let Some(view) = build_view(&mut cx) {
|
||||
this.cx.views.insert((window_id, view_id), Box::new(view));
|
||||
this.cx.parents.insert((window_id, view_id), parent_id);
|
||||
if let Some(window) = this.cx.windows.get_mut(&window_id) {
|
||||
window
|
||||
.invalidation
|
||||
|
@ -1928,6 +1929,7 @@ impl MutableAppContext {
|
|||
}
|
||||
Some(ViewHandle::new(window_id, view_id, &this.cx.ref_counts))
|
||||
} else {
|
||||
this.cx.parents.remove(&(window_id, view_id));
|
||||
None
|
||||
};
|
||||
handle
|
||||
|
@ -2813,6 +2815,16 @@ impl AppContext {
|
|||
}))
|
||||
}
|
||||
|
||||
/// Returns the id of the parent of the given view, or none if the given
|
||||
/// view is the root.
|
||||
fn parent(&self, window_id: usize, view_id: usize) -> Option<usize> {
|
||||
if let Some(ParentId::View(view_id)) = self.parents.get(&(window_id, view_id)) {
|
||||
Some(*view_id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_child_focused(&self, view: impl Into<AnyViewHandle>) -> bool {
|
||||
let view = view.into();
|
||||
if let Some(focused_view_id) = self.focused_view_id(view.window_id) {
|
||||
|
@ -3852,6 +3864,10 @@ impl<'a, T: View> ViewContext<'a, T> {
|
|||
.build_and_insert_view(self.window_id, ParentId::View(self.view_id), build_view)
|
||||
}
|
||||
|
||||
pub fn parent(&mut self) -> Option<usize> {
|
||||
self.cx.parent(self.window_id, self.view_id)
|
||||
}
|
||||
|
||||
pub fn reparent(&mut self, view_handle: impl Into<AnyViewHandle>) {
|
||||
let view_handle = view_handle.into();
|
||||
if self.window_id != view_handle.window_id {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
mod align;
|
||||
mod canvas;
|
||||
mod clipped;
|
||||
mod constrained_box;
|
||||
mod container;
|
||||
mod empty;
|
||||
|
@ -19,12 +20,12 @@ mod text;
|
|||
mod tooltip;
|
||||
mod uniform_list;
|
||||
|
||||
use self::expanded::Expanded;
|
||||
pub use self::{
|
||||
align::*, canvas::*, constrained_box::*, container::*, empty::*, flex::*, hook::*, image::*,
|
||||
keystroke_label::*, label::*, list::*, mouse_event_handler::*, overlay::*, resizable::*,
|
||||
stack::*, svg::*, text::*, tooltip::*, uniform_list::*,
|
||||
};
|
||||
use self::{clipped::Clipped, expanded::Expanded};
|
||||
pub use crate::presenter::ChildView;
|
||||
use crate::{
|
||||
geometry::{
|
||||
|
@ -135,6 +136,13 @@ pub trait Element {
|
|||
Align::new(self.boxed())
|
||||
}
|
||||
|
||||
fn clipped(self) -> Clipped
|
||||
where
|
||||
Self: 'static + Sized,
|
||||
{
|
||||
Clipped::new(self.boxed())
|
||||
}
|
||||
|
||||
fn contained(self) -> Container
|
||||
where
|
||||
Self: 'static + Sized,
|
||||
|
|
69
crates/gpui/src/elements/clipped.rs
Normal file
69
crates/gpui/src/elements/clipped.rs
Normal file
|
@ -0,0 +1,69 @@
|
|||
use std::ops::Range;
|
||||
|
||||
use pathfinder_geometry::{rect::RectF, vector::Vector2F};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::{
|
||||
json, DebugContext, Element, ElementBox, LayoutContext, MeasurementContext, PaintContext,
|
||||
SizeConstraint,
|
||||
};
|
||||
|
||||
pub struct Clipped {
|
||||
child: ElementBox,
|
||||
}
|
||||
|
||||
impl Clipped {
|
||||
pub fn new(child: ElementBox) -> Self {
|
||||
Self { child }
|
||||
}
|
||||
}
|
||||
|
||||
impl Element for Clipped {
|
||||
type LayoutState = ();
|
||||
type PaintState = ();
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
constraint: SizeConstraint,
|
||||
cx: &mut LayoutContext,
|
||||
) -> (Vector2F, Self::LayoutState) {
|
||||
(self.child.layout(constraint, cx), ())
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
bounds: RectF,
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
cx: &mut PaintContext,
|
||||
) -> Self::PaintState {
|
||||
cx.scene.push_layer(Some(bounds));
|
||||
self.child.paint(bounds.origin(), visible_bounds, cx);
|
||||
cx.scene.pop_layer();
|
||||
}
|
||||
|
||||
fn rect_for_text_range(
|
||||
&self,
|
||||
range_utf16: Range<usize>,
|
||||
_: RectF,
|
||||
_: RectF,
|
||||
_: &Self::LayoutState,
|
||||
_: &Self::PaintState,
|
||||
cx: &MeasurementContext,
|
||||
) -> Option<RectF> {
|
||||
self.child.rect_for_text_range(range_utf16, cx)
|
||||
}
|
||||
|
||||
fn debug(
|
||||
&self,
|
||||
_: RectF,
|
||||
_: &Self::LayoutState,
|
||||
_: &Self::PaintState,
|
||||
cx: &DebugContext,
|
||||
) -> json::Value {
|
||||
json!({
|
||||
"type": "Clipped",
|
||||
"child": self.child.debug(cx)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -12,15 +12,21 @@ pub struct KeystrokeLabel {
|
|||
action: Box<dyn Action>,
|
||||
container_style: ContainerStyle,
|
||||
text_style: TextStyle,
|
||||
window_id: usize,
|
||||
view_id: usize,
|
||||
}
|
||||
|
||||
impl KeystrokeLabel {
|
||||
pub fn new(
|
||||
window_id: usize,
|
||||
view_id: usize,
|
||||
action: Box<dyn Action>,
|
||||
container_style: ContainerStyle,
|
||||
text_style: TextStyle,
|
||||
) -> Self {
|
||||
Self {
|
||||
window_id,
|
||||
view_id,
|
||||
action,
|
||||
container_style,
|
||||
text_style,
|
||||
|
@ -37,7 +43,10 @@ impl Element for KeystrokeLabel {
|
|||
constraint: SizeConstraint,
|
||||
cx: &mut LayoutContext,
|
||||
) -> (Vector2F, ElementBox) {
|
||||
let mut element = if let Some(keystrokes) = cx.keystrokes_for_action(self.action.as_ref()) {
|
||||
let mut element = if let Some(keystrokes) =
|
||||
cx.app
|
||||
.keystrokes_for_action(self.window_id, self.view_id, self.action.as_ref())
|
||||
{
|
||||
Flex::row()
|
||||
.with_children(keystrokes.iter().map(|keystroke| {
|
||||
Label::new(keystroke.to_string(), self.text_style.clone())
|
||||
|
|
|
@ -61,11 +61,14 @@ impl Tooltip {
|
|||
) -> Self {
|
||||
struct ElementState<Tag>(Tag);
|
||||
struct MouseEventHandlerState<Tag>(Tag);
|
||||
let focused_view_id = cx.focused_view_id(cx.window_id);
|
||||
|
||||
let state_handle = cx.default_element_state::<ElementState<Tag>, Rc<TooltipState>>(id);
|
||||
let state = state_handle.read(cx).clone();
|
||||
let tooltip = if state.visible.get() {
|
||||
let mut collapsed_tooltip = Self::render_tooltip(
|
||||
cx.window_id,
|
||||
focused_view_id,
|
||||
text.clone(),
|
||||
style.clone(),
|
||||
action.as_ref().map(|a| a.boxed_clone()),
|
||||
|
@ -74,7 +77,7 @@ impl Tooltip {
|
|||
.boxed();
|
||||
Some(
|
||||
Overlay::new(
|
||||
Self::render_tooltip(text, style, action, false)
|
||||
Self::render_tooltip(cx.window_id, focused_view_id, text, style, action, false)
|
||||
.constrained()
|
||||
.dynamically(move |constraint, cx| {
|
||||
SizeConstraint::strict_along(
|
||||
|
@ -128,6 +131,8 @@ impl Tooltip {
|
|||
}
|
||||
|
||||
pub fn render_tooltip(
|
||||
window_id: usize,
|
||||
focused_view_id: Option<usize>,
|
||||
text: String,
|
||||
style: TooltipStyle,
|
||||
action: Option<Box<dyn Action>>,
|
||||
|
@ -144,13 +149,18 @@ impl Tooltip {
|
|||
text.flex(1., false).aligned().boxed()
|
||||
}
|
||||
})
|
||||
.with_children(action.map(|action| {
|
||||
let keystroke_label =
|
||||
KeystrokeLabel::new(action, style.keystroke.container, style.keystroke.text);
|
||||
.with_children(action.and_then(|action| {
|
||||
let keystroke_label = KeystrokeLabel::new(
|
||||
window_id,
|
||||
focused_view_id?,
|
||||
action,
|
||||
style.keystroke.container,
|
||||
style.keystroke.text,
|
||||
);
|
||||
if measure {
|
||||
keystroke_label.boxed()
|
||||
Some(keystroke_label.boxed())
|
||||
} else {
|
||||
keystroke_label.aligned().boxed()
|
||||
Some(keystroke_label.aligned().boxed())
|
||||
}
|
||||
}))
|
||||
.contained()
|
||||
|
|
|
@ -41,7 +41,7 @@ impl Binding {
|
|||
})
|
||||
}
|
||||
|
||||
fn match_context(&self, contexts: &[KeymapContext]) -> bool {
|
||||
pub fn match_context(&self, contexts: &[KeymapContext]) -> bool {
|
||||
self.context_predicate
|
||||
.as_ref()
|
||||
.map(|predicate| predicate.eval(contexts))
|
||||
|
|
|
@ -43,7 +43,7 @@ impl KeymapContextPredicate {
|
|||
pub fn eval(&self, contexts: &[KeymapContext]) -> bool {
|
||||
let Some(context) = contexts.first() else { return false };
|
||||
match self {
|
||||
Self::Identifier(name) => context.set.contains(name.as_str()),
|
||||
Self::Identifier(name) => (&context.set).contains(name.as_str()),
|
||||
Self::Equal(left, right) => context
|
||||
.map
|
||||
.get(left)
|
||||
|
|
|
@ -14,12 +14,12 @@ use pathfinder_geometry::{
|
|||
|
||||
pub trait Vector2FExt {
|
||||
/// Converts self to an NSPoint with y axis pointing up.
|
||||
fn to_screen_ns_point(&self, native_window: id) -> NSPoint;
|
||||
fn to_screen_ns_point(&self, native_window: id, window_height: f64) -> NSPoint;
|
||||
}
|
||||
impl Vector2FExt for Vector2F {
|
||||
fn to_screen_ns_point(&self, native_window: id) -> NSPoint {
|
||||
fn to_screen_ns_point(&self, native_window: id, window_height: f64) -> NSPoint {
|
||||
unsafe {
|
||||
let point = NSPoint::new(self.x() as f64, -self.y() as f64);
|
||||
let point = NSPoint::new(self.x() as f64, window_height - self.y() as f64);
|
||||
msg_send![native_window, convertPointToScreen: point]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -483,6 +483,7 @@ impl Window {
|
|||
|
||||
let native_view: id = msg_send![VIEW_CLASS, alloc];
|
||||
let native_view = NSView::init(native_view);
|
||||
|
||||
assert!(!native_view.is_null());
|
||||
|
||||
let window = Self(Rc::new(RefCell::new(WindowState {
|
||||
|
@ -828,12 +829,14 @@ impl platform::Window for Window {
|
|||
let self_id = self_borrow.id;
|
||||
|
||||
unsafe {
|
||||
let window_frame = self_borrow.frame();
|
||||
let app = NSApplication::sharedApplication(nil);
|
||||
|
||||
// Convert back to screen coordinates
|
||||
let screen_point =
|
||||
(position + window_frame.origin()).to_screen_ns_point(self_borrow.native_window);
|
||||
let screen_point = position.to_screen_ns_point(
|
||||
self_borrow.native_window,
|
||||
self_borrow.content_size().y() as f64,
|
||||
);
|
||||
|
||||
let window_number: NSInteger = msg_send![class!(NSWindow), windowNumberAtPoint:screen_point belowWindowWithWindowNumber:0];
|
||||
let top_most_window: id = msg_send![app, windowWithWindowNumber: window_number];
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ use crate::{
|
|||
font_cache::FontCache,
|
||||
geometry::rect::RectF,
|
||||
json::{self, ToJson},
|
||||
keymap_matcher::Keystroke,
|
||||
platform::{CursorStyle, Event},
|
||||
scene::{
|
||||
CursorRegion, MouseClick, MouseDown, MouseDownOut, MouseDrag, MouseEvent, MouseHover,
|
||||
|
@ -604,14 +603,6 @@ pub struct LayoutContext<'a> {
|
|||
}
|
||||
|
||||
impl<'a> LayoutContext<'a> {
|
||||
pub(crate) fn keystrokes_for_action(
|
||||
&mut self,
|
||||
action: &dyn Action,
|
||||
) -> Option<SmallVec<[Keystroke; 2]>> {
|
||||
self.app
|
||||
.keystrokes_for_action(self.window_id, &self.view_stack, action)
|
||||
}
|
||||
|
||||
fn layout(&mut self, view_id: usize, constraint: SizeConstraint) -> Vector2F {
|
||||
let print_error = |view_id| {
|
||||
format!(
|
||||
|
|
|
@ -65,6 +65,7 @@ struct Request<'a, T> {
|
|||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct AnyResponse<'a> {
|
||||
jsonrpc: &'a str,
|
||||
id: usize,
|
||||
#[serde(default)]
|
||||
error: Option<Error>,
|
||||
|
@ -204,8 +205,9 @@ impl LanguageServer {
|
|||
} else {
|
||||
on_unhandled_notification(msg);
|
||||
}
|
||||
} else if let Ok(AnyResponse { id, error, result }) =
|
||||
serde_json::from_slice(&buffer)
|
||||
} else if let Ok(AnyResponse {
|
||||
id, error, result, ..
|
||||
}) = serde_json::from_slice(&buffer)
|
||||
{
|
||||
if let Some(handler) = response_handlers
|
||||
.lock()
|
||||
|
@ -461,35 +463,57 @@ impl LanguageServer {
|
|||
method,
|
||||
Box::new(move |id, params, cx| {
|
||||
if let Some(id) = id {
|
||||
if let Some(params) = serde_json::from_str(params).log_err() {
|
||||
let response = f(params, cx.clone());
|
||||
cx.foreground()
|
||||
.spawn({
|
||||
let outbound_tx = outbound_tx.clone();
|
||||
async move {
|
||||
let response = match response.await {
|
||||
Ok(result) => Response {
|
||||
jsonrpc: JSON_RPC_VERSION,
|
||||
id,
|
||||
result: Some(result),
|
||||
error: None,
|
||||
},
|
||||
Err(error) => Response {
|
||||
jsonrpc: JSON_RPC_VERSION,
|
||||
id,
|
||||
result: None,
|
||||
error: Some(Error {
|
||||
message: error.to_string(),
|
||||
}),
|
||||
},
|
||||
};
|
||||
if let Some(response) = serde_json::to_vec(&response).log_err()
|
||||
{
|
||||
outbound_tx.try_send(response).ok();
|
||||
match serde_json::from_str(params) {
|
||||
Ok(params) => {
|
||||
let response = f(params, cx.clone());
|
||||
cx.foreground()
|
||||
.spawn({
|
||||
let outbound_tx = outbound_tx.clone();
|
||||
async move {
|
||||
let response = match response.await {
|
||||
Ok(result) => Response {
|
||||
jsonrpc: JSON_RPC_VERSION,
|
||||
id,
|
||||
result: Some(result),
|
||||
error: None,
|
||||
},
|
||||
Err(error) => Response {
|
||||
jsonrpc: JSON_RPC_VERSION,
|
||||
id,
|
||||
result: None,
|
||||
error: Some(Error {
|
||||
message: error.to_string(),
|
||||
}),
|
||||
},
|
||||
};
|
||||
if let Some(response) =
|
||||
serde_json::to_vec(&response).log_err()
|
||||
{
|
||||
outbound_tx.try_send(response).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
Err(error) => {
|
||||
log::error!(
|
||||
"error deserializing {} request: {:?}, message: {:?}",
|
||||
method,
|
||||
error,
|
||||
params
|
||||
);
|
||||
let response = AnyResponse {
|
||||
jsonrpc: JSON_RPC_VERSION,
|
||||
id,
|
||||
result: None,
|
||||
error: Some(Error {
|
||||
message: error.to_string(),
|
||||
}),
|
||||
};
|
||||
if let Some(response) = serde_json::to_vec(&response).log_err() {
|
||||
outbound_tx.try_send(response).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
|
|
@ -811,6 +811,7 @@ pub struct TerminalStyle {
|
|||
pub struct FeedbackStyle {
|
||||
pub submit_button: Interactive<ContainedText>,
|
||||
pub button_margin: f32,
|
||||
pub info_text: ContainedText,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Default)]
|
||||
|
|
|
@ -3,7 +3,7 @@ authors = ["Nathan Sobo <nathansobo@gmail.com>"]
|
|||
description = "The fast, collaborative code editor."
|
||||
edition = "2021"
|
||||
name = "zed"
|
||||
version = "0.73.0"
|
||||
version = "0.73.3"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
|
|
|
@ -1 +1 @@
|
|||
dev
|
||||
stable
|
|
@ -11,7 +11,7 @@ use collections::VecDeque;
|
|||
pub use editor;
|
||||
use editor::{Editor, MultiBuffer};
|
||||
|
||||
use feedback::feedback_editor::SubmitFeedbackButton;
|
||||
use feedback::feedback_editor::{FeedbackInfoText, SubmitFeedbackButton};
|
||||
use futures::StreamExt;
|
||||
use gpui::{
|
||||
actions,
|
||||
|
@ -290,6 +290,8 @@ pub fn initialize_workspace(
|
|||
toolbar.add_item(project_search_bar, cx);
|
||||
let submit_feedback_button = cx.add_view(|_| SubmitFeedbackButton::new());
|
||||
toolbar.add_item(submit_feedback_button, cx);
|
||||
let feedback_info_text = cx.add_view(|_| FeedbackInfoText::new());
|
||||
toolbar.add_item(feedback_info_text, cx);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
|
|
@ -41,8 +41,7 @@ function getLicenseText(schemeMeta: Meta[], callback: (meta: Meta, license_text:
|
|||
const { statusCode } = res;
|
||||
|
||||
if (statusCode < 200 || statusCode >= 300) {
|
||||
throw new Error('Failed to fetch license file.\n' +
|
||||
`Status Code: ${statusCode}`);
|
||||
throw new Error(`Failed to fetch license for: ${meta.name}, Status Code: ${statusCode}`);
|
||||
}
|
||||
|
||||
res.setEncoding('utf8');
|
||||
|
|
|
@ -5,7 +5,6 @@ import { background, border, text } from "./components";
|
|||
export default function feedback(colorScheme: ColorScheme) {
|
||||
let layer = colorScheme.highest;
|
||||
|
||||
// Currently feedback only needs style for the submit feedback button
|
||||
return {
|
||||
submit_button: {
|
||||
...text(layer, "mono", "on"),
|
||||
|
@ -32,6 +31,7 @@ export default function feedback(colorScheme: ColorScheme) {
|
|||
border: border(layer, "on", "hovered"),
|
||||
},
|
||||
},
|
||||
button_margin: 8
|
||||
button_margin: 8,
|
||||
info_text: text(layer, "sans", "default", { size: "xs" }),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -56,8 +56,8 @@ export const meta: Meta = {
|
|||
author: "atelierbram",
|
||||
license: {
|
||||
SPDX: "MIT",
|
||||
https_url: "https://raw.githubusercontent.com/atelierbram/syntax-highlighting/master/LICENSE",
|
||||
license_checksum: "6c2353bb9dd0b7b211364d98184ab482e54f40f611eda0c02974c3a1f9e6193c"
|
||||
https_url: "https://atelierbram.mit-license.org/license.txt",
|
||||
license_checksum: "f95ce526ef4e7eecf7a832bba0e3451cc1000f9ce63eb01ed6f64f8109f5d0a5"
|
||||
},
|
||||
url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/cave/"
|
||||
}
|
|
@ -35,8 +35,8 @@ export const meta: Meta = {
|
|||
author: "atelierbram",
|
||||
license: {
|
||||
SPDX: "MIT",
|
||||
https_url: "https://raw.githubusercontent.com/atelierbram/syntax-highlighting/master/LICENSE",
|
||||
license_checksum: "6c2353bb9dd0b7b211364d98184ab482e54f40f611eda0c02974c3a1f9e6193c"
|
||||
https_url: "https://atelierbram.mit-license.org/license.txt",
|
||||
license_checksum: "f95ce526ef4e7eecf7a832bba0e3451cc1000f9ce63eb01ed6f64f8109f5d0a5"
|
||||
},
|
||||
url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune/"
|
||||
url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/sulphurpool/"
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue