Compare commits

...
Sign in to create a new pull request.

19 commits

Author SHA1 Message Date
Mikayla Maki
db6e634277 zed 0.72.5 2023-02-10 12:09:39 -08:00
Mikayla Maki
7923584214 Merge pull request #2154 from zed-industries/fix-tooltip-crash
Don't render tooltip keystroke label if there's no focused view
2023-02-10 12:08:36 -08:00
Mikayla Maki
eba11e4718 Merge pull request #2156 from zed-industries/fix-atelier-cave-license
Update the atelier cave license file
2023-02-10 12:08:25 -08:00
Joseph Lyons
1cd4c5d99b zed 0.72.4 2023-02-09 10:09:06 -05:00
Antonio Scandurra
a08ee28279 Merge pull request #2147 from zed-industries/fix-pyright
Always respond to language server, even when its requests are malformed
2023-02-09 10:06:21 -05:00
Mikayla Maki
ab97a75156 zed 0.72.3 2023-02-08 16:15:14 -08:00
Mikayla Maki
58b819e9a4 Merge pull request #2134 from zed-industries/fix-action-keystroke-bugs
Fix several action dispatching bugs
2023-02-08 16:14:21 -08:00
Joseph Lyons
f753a83eb1 v0.72.x stable 2023-02-08 14:55:09 -05:00
Antonio Scandurra
f877d26c3a Merge pull request #2145 from zed-industries/fix-focus-in-project-search
Focus results editor only when starting a new project search
2023-02-08 13:19:56 -05:00
Max Brunsfeld
37ad025ccd Merge pull request #2140 from zed-industries/feedback/929-project-search-crashes
Feedback/929 project search crashes
2023-02-07 10:12:34 -08:00
Max Brunsfeld
e8ebd6d7a2 zed 0.72.2 2023-02-07 09:25:30 -08:00
Kay Simmons
bcc38a60bf Merge pull request #2136 from zed-industries/fix-recent-projects-panic
Fix Recent Project Panic
2023-02-07 09:24:59 -08:00
Max Brunsfeld
8944ca9236 Merge pull request #2135 from zed-industries/handle-window-moved-crash
Fix crash when unplugging display containing a zed window
2023-02-07 09:24:49 -08:00
Joseph Lyons
6ae297832f zed 0.72.1 2023-02-06 14:24:41 -05:00
Joseph T. Lyons
a7f788746c Merge pull request #2125 from zed-industries/trim-leading-and-trailing-whitespace-in-feedback
Trim leading and trailing whitespace in feedback
2023-02-06 14:19:33 -05:00
Kay Simmons
7ff55087f3 Merge pull request #2124 from zed-industries/fix-display-uuid-panic
Make display uuid optional if the display is disconnected
2023-02-06 14:15:58 -05:00
Joseph Lyons
41102751ef Reduce length of feedback placeholder text 2023-02-03 08:35:55 -05:00
Joseph T. Lyons
a25580fb7f Merge pull request #2127 from zed-industries/fix-discourse-release
Fix discourse release
2023-02-02 14:41:59 -05:00
Joseph Lyons
bc7bccf5f5 v0.72.x preview 2023-02-01 13:44:50 -05:00
25 changed files with 245 additions and 157 deletions

View file

@ -13,23 +13,28 @@ jobs:
webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
content: |
📣 Zed ${{ github.event.release.tag_name }} was just released!
Restart your Zed or head to https://zed.dev/releases/latest to grab it.
```md
# Changelog
${{ github.event.release.body }}
```
discourse_release:
if: ${{ ! github.event.release.prerelease }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Node
uses: actions/setup-node@v2
if: ${{ ! github.event.release.prerelease }}
with:
node-version: '16'
- run: script/discourse_release ${{ secrets.DISCOURSE_RELEASES_API_KEY }} ${{ github.event.release.tag_name }} ${{ github.event.release.body }}
node-version: "19"
- run: >
node "./script/discourse_release"
${{ secrets.DISCOURSE_RELEASES_API_KEY }}
${{ github.event.release.tag_name }}
${{ github.event.release.body }}
mixpanel_release:
runs-on: ubuntu-latest
steps:
@ -40,7 +45,7 @@ jobs:
architecture: "x64"
cache: "pip"
- run: pip install -r script/mixpanel_release/requirements.txt
- run: >
- run: >
python script/mixpanel_release/main.py
${{ github.event.release.tag_name }}
${{ secrets.MIXPANEL_PROJECT_ID }}

2
Cargo.lock generated
View file

@ -8228,7 +8228,7 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
[[package]]
name = "zed"
version = "0.72.0"
version = "0.72.5"
dependencies = [
"activity_indicator",
"anyhow",

View file

@ -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(),

View file

@ -31,7 +31,7 @@ use workspace::{
use crate::system_specs::SystemSpecs;
const FEEDBACK_CHAR_LIMIT: RangeInclusive<usize> = 10..=5000;
const FEEDBACK_PLACEHOLDER_TEXT: &str = "Thanks for spending time with Zed. Enter your feedback here as Markdown. Save the tab to submit your feedback.";
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.";
@ -125,7 +125,9 @@ impl FeedbackEditor {
_: ModelHandle<Project>,
cx: &mut ViewContext<Self>,
) -> Task<anyhow::Result<()>> {
let feedback_char_count = self.editor.read(cx).text(cx).chars().count();
let feedback_text = self.editor.read(cx).text(cx);
let feedback_char_count = feedback_text.chars().count();
let feedback_text = feedback_text.trim().to_string();
let error = if feedback_char_count < *FEEDBACK_CHAR_LIMIT.start() {
Some(format!(
@ -154,7 +156,6 @@ impl FeedbackEditor {
let this = cx.handle();
let client = cx.global::<Arc<Client>>().clone();
let feedback_text = self.editor.read(cx).text(cx);
let specs = self.system_specs.clone();
cx.spawn(|_, mut cx| async move {

View file

@ -910,15 +910,14 @@ impl MutableAppContext {
.map_or(false, |window| window.is_fullscreen)
}
pub fn window_bounds(&self, window_id: usize) -> WindowBounds {
self.presenters_and_platform_windows[&window_id].1.bounds()
pub fn window_bounds(&self, window_id: usize) -> Option<WindowBounds> {
let (_, window) = self.presenters_and_platform_windows.get(&window_id)?;
Some(window.bounds())
}
pub fn window_display_uuid(&self, window_id: usize) -> Uuid {
self.presenters_and_platform_windows[&window_id]
.1
.screen()
.display_uuid()
pub fn window_display_uuid(&self, window_id: usize) -> Option<Uuid> {
let (_, window) = self.presenters_and_platform_windows.get(&window_id)?;
window.screen().display_uuid()
}
pub fn render_view(&mut self, params: RenderParams) -> Result<ElementBox> {
@ -1333,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,
@ -1340,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());
@ -1358,6 +1384,7 @@ impl MutableAppContext {
deserialize("{}").ok()?,
self.keystroke_matcher
.bindings_for_action_type(*type_id)
.filter(|b| b.match_context(&contexts))
.collect(),
))
} else {
@ -1385,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.
@ -1916,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
@ -1929,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
@ -2375,12 +2376,15 @@ impl MutableAppContext {
callback(is_fullscreen, this)
});
let bounds = this.window_bounds(window_id);
let uuid = this.window_display_uuid(window_id);
let mut bounds_observations = this.window_bounds_observations.clone();
bounds_observations.emit(window_id, this, |callback, this| {
callback(bounds, uuid, this)
});
if let Some((uuid, bounds)) = this
.window_display_uuid(window_id)
.zip(this.window_bounds(window_id))
{
let mut bounds_observations = this.window_bounds_observations.clone();
bounds_observations.emit(window_id, this, |callback, this| {
callback(bounds, uuid, this)
});
}
Some(())
});
@ -2559,14 +2563,17 @@ impl MutableAppContext {
}
fn handle_window_moved(&mut self, window_id: usize) {
let bounds = self.window_bounds(window_id);
let display = self.window_display_uuid(window_id);
self.window_bounds_observations
.clone()
.emit(window_id, self, move |callback, this| {
callback(bounds, display, this);
true
});
if let Some((display, bounds)) = self
.window_display_uuid(window_id)
.zip(self.window_bounds(window_id))
{
self.window_bounds_observations
.clone()
.emit(window_id, self, move |callback, this| {
callback(bounds, display, this);
true
});
}
}
pub fn focus(&mut self, window_id: usize, view_id: Option<usize>) {
@ -2808,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) {
@ -3731,10 +3748,6 @@ impl<'a, T: View> ViewContext<'a, T> {
self.app.toggle_window_full_screen(self.window_id)
}
pub fn window_bounds(&self) -> WindowBounds {
self.app.window_bounds(self.window_id)
}
pub fn prompt(
&self,
level: PromptLevel,
@ -3851,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 {

View file

@ -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())

View file

@ -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()

View file

@ -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))

View file

@ -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)

View file

@ -124,7 +124,7 @@ pub trait InputHandler {
pub trait Screen: Debug {
fn as_any(&self) -> &dyn Any;
fn bounds(&self) -> RectF;
fn display_uuid(&self) -> Uuid;
fn display_uuid(&self) -> Option<Uuid>;
}
pub trait Window {

View file

@ -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]
}
}

View file

@ -35,7 +35,7 @@ impl Screen {
.map(|ix| Screen {
native_screen: native_screens.objectAtIndex(ix),
})
.find(|screen| platform::Screen::display_uuid(screen) == uuid)
.find(|screen| platform::Screen::display_uuid(screen) == Some(uuid))
}
}
@ -58,7 +58,7 @@ impl platform::Screen for Screen {
self
}
fn display_uuid(&self) -> uuid::Uuid {
fn display_uuid(&self) -> Option<uuid::Uuid> {
unsafe {
// Screen ids are not stable. Further, the default device id is also unstable across restarts.
// CGDisplayCreateUUIDFromDisplayID is stable but not exposed in the bindings we use.
@ -74,8 +74,12 @@ impl platform::Screen for Screen {
(&mut device_id) as *mut _ as *mut c_void,
);
let cfuuid = CGDisplayCreateUUIDFromDisplayID(device_id as CGDirectDisplayID);
if cfuuid.is_null() {
return None;
}
let bytes = CFUUIDGetUUIDBytes(cfuuid);
Uuid::from_bytes([
Some(Uuid::from_bytes([
bytes.byte0,
bytes.byte1,
bytes.byte2,
@ -92,7 +96,7 @@ impl platform::Screen for Screen {
bytes.byte13,
bytes.byte14,
bytes.byte15,
])
]))
}
}

View file

@ -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];

View file

@ -238,8 +238,8 @@ impl super::Screen for Screen {
RectF::new(Vector2F::zero(), Vector2F::new(1920., 1080.))
}
fn display_uuid(&self) -> uuid::Uuid {
uuid::Uuid::new_v4()
fn display_uuid(&self) -> Option<uuid::Uuid> {
Some(uuid::Uuid::new_v4())
}
}

View file

@ -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!(

View file

@ -64,6 +64,7 @@ struct Request<'a, T> {
#[derive(Serialize, Deserialize)]
struct AnyResponse<'a> {
jsonrpc: &'a str,
id: usize,
#[serde(default)]
error: Option<Error>,
@ -203,8 +204,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()
@ -460,35 +462,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();
}
}
}
}
}),

View file

@ -11,7 +11,10 @@ use highlighted_workspace_location::HighlightedWorkspaceLocation;
use ordered_float::OrderedFloat;
use picker::{Picker, PickerDelegate};
use settings::Settings;
use workspace::{OpenPaths, Workspace, WorkspaceLocation, WORKSPACE_DB};
use workspace::{
notifications::simple_message_notification::MessageNotification, OpenPaths, Workspace,
WorkspaceLocation, WORKSPACE_DB,
};
actions!(projects, [OpenRecent]);
@ -42,7 +45,7 @@ impl RecentProjectsView {
fn toggle(_: &mut Workspace, _: &OpenRecent, cx: &mut ViewContext<Workspace>) {
cx.spawn(|workspace, mut cx| async move {
let workspace_locations = cx
let workspace_locations: Vec<_> = cx
.background()
.spawn(async {
WORKSPACE_DB
@ -56,12 +59,20 @@ impl RecentProjectsView {
.await;
workspace.update(&mut cx, |workspace, cx| {
workspace.toggle_modal(cx, |_, cx| {
let view = cx.add_view(|cx| Self::new(workspace_locations, cx));
cx.subscribe(&view, Self::on_event).detach();
view
});
})
if !workspace_locations.is_empty() {
workspace.toggle_modal(cx, |_, cx| {
let view = cx.add_view(|cx| Self::new(workspace_locations, cx));
cx.subscribe(&view, Self::on_event).detach();
view
});
} else {
workspace.show_notification(0, cx, |cx| {
cx.add_view(|_| {
MessageNotification::new_message("No recent projects to open.")
})
})
}
});
})
.detach();
}

View file

@ -259,11 +259,7 @@ impl Item for ProjectSearchView {
.boxed(),
)
.with_children(self.model.read(cx).active_query.as_ref().map(|query| {
let query_text = if query.as_str().len() > MAX_TAB_TITLE_LEN {
query.as_str()[..MAX_TAB_TITLE_LEN].to_string() + ""
} else {
query.as_str().to_string()
};
let query_text = util::truncate_and_trailoff(query.as_str(), MAX_TAB_TITLE_LEN);
Label::new(query_text, tab_theme.label.clone())
.aligned()
@ -575,9 +571,9 @@ impl ProjectSearchView {
self.active_match_index = None;
} else {
let prev_search_id = mem::replace(&mut self.search_id, self.model.read(cx).search_id);
let reset_selections = self.search_id != prev_search_id;
let is_new_search = self.search_id != prev_search_id;
self.results_editor.update(cx, |editor, cx| {
if reset_selections {
if is_new_search {
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.select_ranges(match_ranges.first().cloned())
});
@ -588,7 +584,7 @@ impl ProjectSearchView {
cx,
);
});
if self.query_editor.is_focused(cx) {
if is_new_search && self.query_editor.is_focused(cx) {
self.focus_results_editor(cx);
}
}

View file

@ -46,10 +46,10 @@ pub fn truncate(s: &str, max_chars: usize) -> &str {
pub fn truncate_and_trailoff(s: &str, max_chars: usize) -> String {
debug_assert!(max_chars >= 5);
if s.len() > max_chars {
format!("{}", truncate(s, max_chars.saturating_sub(3)))
} else {
s.to_string()
let truncation_ix = s.char_indices().map(|(i, _)| i).nth(max_chars);
match truncation_ix {
Some(length) => s[..length].to_string() + "",
None => s.to_string(),
}
}
@ -276,4 +276,12 @@ mod tests {
assert_eq!(foo, None);
}
#[test]
fn test_trancate_and_trailoff() {
assert_eq!(truncate_and_trailoff("", 5), "");
assert_eq!(truncate_and_trailoff("èèèèèè", 7), "èèèèèè");
assert_eq!(truncate_and_trailoff("èèèèèè", 6), "èèèèèè");
assert_eq!(truncate_and_trailoff("èèèèèè", 5), "èèèèè…");
}
}

View file

@ -174,7 +174,7 @@ pub mod simple_message_notification {
}
impl MessageNotification {
pub fn new_messsage<S: AsRef<str>>(message: S) -> MessageNotification {
pub fn new_message<S: AsRef<str>>(message: S) -> MessageNotification {
Self {
message: message.as_ref().to_string(),
click_action: None,
@ -320,7 +320,7 @@ where
Err(err) => {
workspace.show_notification(0, cx, |cx| {
cx.add_view(|_cx| {
simple_message_notification::MessageNotification::new_messsage(format!(
simple_message_notification::MessageNotification::new_message(format!(
"Error: {:?}",
err,
))

View file

@ -3,7 +3,7 @@ authors = ["Nathan Sobo <nathansobo@gmail.com>"]
description = "The fast, collaborative code editor."
edition = "2021"
name = "zed"
version = "0.72.0"
version = "0.72.5"
publish = false
[lib]

View file

@ -1 +1 @@
dev
stable

View file

@ -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');

View file

@ -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/"
}

View file

@ -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/"
}