1use super::{Item, ItemView};
2use crate::Settings;
3use anyhow::Result;
4use editor::{Editor, EditorSettings, Event};
5use gpui::{fonts::TextStyle, AppContext, ModelHandle, Task, ViewContext};
6use language::{Buffer, File as _};
7use postage::watch;
8use project::{ProjectPath, Worktree};
9use std::path::Path;
10
11impl Item for Buffer {
12 type View = Editor;
13
14 fn build_view(
15 handle: ModelHandle<Self>,
16 settings: watch::Receiver<Settings>,
17 cx: &mut ViewContext<Self::View>,
18 ) -> Self::View {
19 Editor::for_buffer(
20 handle,
21 move |cx| {
22 let settings = settings.borrow();
23 let font_cache = cx.font_cache();
24 let font_family_id = settings.buffer_font_family;
25 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
26 let font_properties = Default::default();
27 let font_id = font_cache
28 .select_font(font_family_id, &font_properties)
29 .unwrap();
30 let font_size = settings.buffer_font_size;
31
32 let mut theme = settings.theme.editor.clone();
33 theme.text = TextStyle {
34 color: theme.text.color,
35 font_family_name,
36 font_family_id,
37 font_id,
38 font_size,
39 font_properties,
40 underline: None,
41 };
42 EditorSettings {
43 tab_size: settings.tab_size,
44 style: theme,
45 }
46 },
47 cx,
48 )
49 }
50
51 fn project_path(&self) -> Option<ProjectPath> {
52 self.file().map(|f| ProjectPath {
53 worktree_id: f.worktree_id(),
54 path: f.path().clone(),
55 })
56 }
57}
58
59impl ItemView for Editor {
60 fn should_activate_item_on_event(event: &Event) -> bool {
61 matches!(event, Event::Activate)
62 }
63
64 fn should_close_item_on_event(event: &Event) -> bool {
65 matches!(event, Event::Closed)
66 }
67
68 fn should_update_tab_on_event(event: &Event) -> bool {
69 matches!(
70 event,
71 Event::Saved | Event::Dirtied | Event::FileHandleChanged
72 )
73 }
74
75 fn title(&self, cx: &AppContext) -> String {
76 let filename = self
77 .buffer()
78 .read(cx)
79 .file()
80 .and_then(|file| file.file_name());
81 if let Some(name) = filename {
82 name.to_string_lossy().into()
83 } else {
84 "untitled".into()
85 }
86 }
87
88 fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
89 self.buffer().read(cx).file().map(|file| ProjectPath {
90 worktree_id: file.worktree_id(),
91 path: file.path().clone(),
92 })
93 }
94
95 fn clone_on_split(&self, cx: &mut ViewContext<Self>) -> Option<Self>
96 where
97 Self: Sized,
98 {
99 Some(self.clone(cx))
100 }
101
102 fn save(&mut self, cx: &mut ViewContext<Self>) -> Result<Task<Result<()>>> {
103 let save = self.buffer().update(cx, |b, cx| b.save(cx))?;
104 Ok(cx.spawn(|_, _| async move {
105 save.await?;
106 Ok(())
107 }))
108 }
109
110 fn save_as(
111 &mut self,
112 worktree: ModelHandle<Worktree>,
113 path: &Path,
114 cx: &mut ViewContext<Self>,
115 ) -> Task<Result<()>> {
116 self.buffer().update(cx, |buffer, cx| {
117 let handle = cx.handle();
118 let text = buffer.as_rope().clone();
119 let version = buffer.version();
120
121 let save_as = worktree.update(cx, |worktree, cx| {
122 worktree
123 .as_local_mut()
124 .unwrap()
125 .save_buffer_as(handle, path, text, cx)
126 });
127
128 cx.spawn(|buffer, mut cx| async move {
129 save_as.await.map(|new_file| {
130 let (language, language_server) = worktree.read_with(&cx, |worktree, _| {
131 let language = worktree.languages().select_language(new_file.full_path());
132 let language_server = worktree.language_server();
133 (language.cloned(), language_server.cloned())
134 });
135
136 buffer.update(&mut cx, |buffer, cx| {
137 buffer.did_save(version, new_file.mtime, Some(Box::new(new_file)), cx);
138 buffer.set_language(language, language_server, cx);
139 });
140 })
141 })
142 })
143 }
144
145 fn is_dirty(&self, cx: &AppContext) -> bool {
146 self.buffer().read(cx).is_dirty()
147 }
148
149 fn has_conflict(&self, cx: &AppContext) -> bool {
150 self.buffer().read(cx).has_conflict()
151 }
152}