1use std::{path::Path, sync::Arc};
2
3use gpui::{EventEmitter, FocusHandle, Focusable};
4use ui::{
5 App, Button, ButtonCommon, ButtonStyle, Clickable, Context, FluentBuilder, InteractiveElement,
6 KeyBinding, Label, LabelCommon, LabelSize, ParentElement, Render, SharedString, Styled as _,
7 Window, h_flex, v_flex,
8};
9use zed_actions::workspace::OpenWithSystem;
10
11use crate::Item;
12
13/// A view to display when a certain buffer fails to open.
14#[derive(Debug)]
15pub struct InvalidItemView {
16 /// Which path was attempted to open.
17 pub abs_path: Arc<Path>,
18 /// An error message, happened when opening the buffer.
19 pub error: SharedString,
20 is_local: bool,
21 focus_handle: FocusHandle,
22}
23
24impl InvalidItemView {
25 pub fn new(
26 abs_path: &Path,
27 is_local: bool,
28 e: &anyhow::Error,
29 _: &mut Window,
30 cx: &mut App,
31 ) -> Self {
32 Self {
33 is_local,
34 abs_path: Arc::from(abs_path),
35 error: format!("{}", e.root_cause()).into(),
36 focus_handle: cx.focus_handle(),
37 }
38 }
39}
40
41impl Item for InvalidItemView {
42 type Event = ();
43
44 fn tab_content_text(&self, mut detail: usize, _: &App) -> SharedString {
45 // Ensure we always render at least the filename.
46 detail += 1;
47
48 let path = self.abs_path.as_ref();
49
50 let mut prefix = path;
51 while detail > 0 {
52 if let Some(parent) = prefix.parent() {
53 prefix = parent;
54 detail -= 1;
55 } else {
56 break;
57 }
58 }
59
60 let path = if detail > 0 {
61 path
62 } else {
63 path.strip_prefix(prefix).unwrap_or(path)
64 };
65
66 SharedString::new(path.to_string_lossy())
67 }
68}
69
70impl EventEmitter<()> for InvalidItemView {}
71
72impl Focusable for InvalidItemView {
73 fn focus_handle(&self, _: &App) -> FocusHandle {
74 self.focus_handle.clone()
75 }
76}
77
78impl Render for InvalidItemView {
79 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl gpui::IntoElement {
80 let abs_path = self.abs_path.clone();
81 v_flex()
82 .size_full()
83 .track_focus(&self.focus_handle(cx))
84 .flex_none()
85 .justify_center()
86 .overflow_hidden()
87 .key_context("InvalidBuffer")
88 .child(
89 h_flex().size_full().justify_center().child(
90 v_flex()
91 .justify_center()
92 .gap_2()
93 .child(h_flex().justify_center().child("Could not open file"))
94 .child(
95 h_flex()
96 .justify_center()
97 .child(Label::new(self.error.clone()).size(LabelSize::Small)),
98 )
99 .when(self.is_local, |contents| {
100 contents.child(
101 h_flex().justify_center().child(
102 Button::new("open-with-system", "Open in Default App")
103 .on_click(move |_, _, cx| {
104 cx.open_with_system(&abs_path);
105 })
106 .style(ButtonStyle::Outlined)
107 .key_binding(KeyBinding::for_action(
108 &OpenWithSystem,
109 window,
110 cx,
111 )),
112 ),
113 )
114 }),
115 ),
116 )
117 }
118}