1#![allow(unused_variables, unused_mut)]
2//todo!()
3
4mod assets;
5pub mod languages;
6mod only_instance;
7mod open_listener;
8
9pub use assets::*;
10use breadcrumbs::Breadcrumbs;
11use collections::VecDeque;
12use editor::{Editor, MultiBuffer};
13use gpui::{
14 actions, point, px, AppContext, Context, FocusableView, PromptLevel, TitlebarOptions,
15 ViewContext, VisualContext, WindowBounds, WindowKind, WindowOptions,
16};
17pub use only_instance::*;
18pub use open_listener::*;
19
20use anyhow::{anyhow, Context as _};
21use project_panel::ProjectPanel;
22use settings::{initial_local_settings_content, Settings};
23use std::{borrow::Cow, ops::Deref, sync::Arc};
24use terminal_view::terminal_panel::TerminalPanel;
25use util::{
26 asset_str,
27 channel::{AppCommitSha, ReleaseChannel},
28 paths::{self, LOCAL_SETTINGS_RELATIVE_PATH},
29 ResultExt,
30};
31use uuid::Uuid;
32use workspace::{
33 create_and_open_local_file, dock::PanelHandle,
34 notifications::simple_message_notification::MessageNotification, open_new, AppState, NewFile,
35 NewWindow, Workspace, WorkspaceSettings,
36};
37use zed_actions::{OpenBrowser, OpenZedURL};
38
39actions!(
40 About,
41 DebugElements,
42 DecreaseBufferFontSize,
43 Hide,
44 HideOthers,
45 IncreaseBufferFontSize,
46 Minimize,
47 OpenDefaultKeymap,
48 OpenDefaultSettings,
49 OpenKeymap,
50 OpenLicenses,
51 OpenLocalSettings,
52 OpenLog,
53 OpenSettings,
54 OpenTelemetryLog,
55 Quit,
56 ResetBufferFontSize,
57 ResetDatabase,
58 ShowAll,
59 ToggleFullScreen,
60 Zoom,
61);
62
63pub fn build_window_options(
64 bounds: Option<WindowBounds>,
65 display_uuid: Option<Uuid>,
66 cx: &mut AppContext,
67) -> WindowOptions {
68 let bounds = bounds.unwrap_or(WindowBounds::Maximized);
69 let display = display_uuid.and_then(|uuid| {
70 cx.displays()
71 .into_iter()
72 .find(|display| display.uuid().ok() == Some(uuid))
73 });
74
75 WindowOptions {
76 bounds,
77 titlebar: Some(TitlebarOptions {
78 title: None,
79 appears_transparent: true,
80 traffic_light_position: Some(point(px(8.), px(8.))),
81 }),
82 center: false,
83 focus: false,
84 show: false,
85 kind: WindowKind::Normal,
86 is_movable: true,
87 display_id: display.map(|display| display.id()),
88 }
89}
90
91pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
92 cx.observe_new_views(move |workspace: &mut Workspace, cx| {
93 let workspace_handle = cx.view().clone();
94 cx.subscribe(&workspace_handle, {
95 move |workspace, _, event, cx| {
96 if let workspace::Event::PaneAdded(pane) = event {
97 pane.update(cx, |pane, cx| {
98 pane.toolbar().update(cx, |toolbar, cx| {
99 let breadcrumbs = cx.build_view(|_| Breadcrumbs::new(workspace));
100 toolbar.add_item(breadcrumbs, cx);
101 let buffer_search_bar = cx.build_view(search::BufferSearchBar::new);
102 toolbar.add_item(buffer_search_bar.clone(), cx);
103 // todo!()
104 // let quick_action_bar = cx.add_view(|_| {
105 // QuickActionBar::new(buffer_search_bar, workspace)
106 // });
107 // toolbar.add_item(quick_action_bar, cx);
108 let diagnostic_editor_controls =
109 cx.build_view(|_| diagnostics::ToolbarControls::new());
110 // toolbar.add_item(diagnostic_editor_controls, cx);
111 // let project_search_bar = cx.add_view(|_| ProjectSearchBar::new());
112 // toolbar.add_item(project_search_bar, cx);
113 // let submit_feedback_button =
114 // cx.add_view(|_| SubmitFeedbackButton::new());
115 // toolbar.add_item(submit_feedback_button, cx);
116 // let feedback_info_text = cx.add_view(|_| FeedbackInfoText::new());
117 // toolbar.add_item(feedback_info_text, cx);
118 // let lsp_log_item =
119 // cx.add_view(|_| language_tools::LspLogToolbarItemView::new());
120 // toolbar.add_item(lsp_log_item, cx);
121 // let syntax_tree_item = cx
122 // .add_view(|_| language_tools::SyntaxTreeToolbarItemView::new());
123 // toolbar.add_item(syntax_tree_item, cx);
124 })
125 });
126 }
127 }
128 })
129 .detach();
130
131 // cx.emit(workspace2::Event::PaneAdded(
132 // workspace.active_pane().clone(),
133 // ));
134
135 // let collab_titlebar_item =
136 // cx.add_view(|cx| CollabTitlebarItem::new(workspace, &workspace_handle, cx));
137 // workspace.set_titlebar_item(collab_titlebar_item.into_any(), cx);
138
139 // let copilot =
140 // cx.add_view(|cx| copilot_button::CopilotButton::new(app_state.fs.clone(), cx));
141 let diagnostic_summary =
142 cx.build_view(|cx| diagnostics::items::DiagnosticIndicator::new(workspace, cx));
143 let activity_indicator =
144 activity_indicator::ActivityIndicator::new(workspace, app_state.languages.clone(), cx);
145 // let active_buffer_language =
146 // cx.add_view(|_| language_selector::ActiveBufferLanguage::new(workspace));
147 // let vim_mode_indicator = cx.add_view(|cx| vim::ModeIndicator::new(cx));
148 // let feedback_button = cx.add_view(|_| {
149 // feedback::deploy_feedback_button::DeployFeedbackButton::new(workspace)
150 // });
151 let cursor_position = cx.build_view(|_| editor::items::CursorPosition::new());
152 workspace.status_bar().update(cx, |status_bar, cx| {
153 status_bar.add_left_item(diagnostic_summary, cx);
154 status_bar.add_left_item(activity_indicator, cx);
155
156 // status_bar.add_right_item(feedback_button, cx);
157 // status_bar.add_right_item(copilot, cx);
158 // status_bar.add_right_item(active_buffer_language, cx);
159 // status_bar.add_right_item(vim_mode_indicator, cx);
160 status_bar.add_right_item(cursor_position, cx);
161 });
162
163 auto_update::notify_of_any_new_update(cx);
164
165 // vim::observe_keystrokes(cx);
166
167 let handle = cx.view().downgrade();
168 cx.on_window_should_close(move |cx| {
169 handle
170 .update(cx, |workspace, cx| {
171 if let Some(task) = workspace.close(&Default::default(), cx) {
172 task.detach_and_log_err(cx);
173 }
174 false
175 })
176 .unwrap_or(true)
177 });
178
179 cx.spawn(|workspace_handle, mut cx| async move {
180 let project_panel = ProjectPanel::load(workspace_handle.clone(), cx.clone());
181 let terminal_panel = TerminalPanel::load(workspace_handle.clone(), cx.clone());
182 // let assistant_panel = AssistantPanel::load(workspace_handle.clone(), cx.clone());
183 let channels_panel =
184 collab_ui::collab_panel::CollabPanel::load(workspace_handle.clone(), cx.clone());
185 // let chat_panel =
186 // collab_ui::chat_panel::ChatPanel::load(workspace_handle.clone(), cx.clone());
187 // let notification_panel = collab_ui::notification_panel::NotificationPanel::load(
188 // workspace_handle.clone(),
189 // cx.clone(),
190 // );
191 let (
192 project_panel,
193 terminal_panel,
194 // assistant_panel,
195 channels_panel,
196 // chat_panel,
197 // notification_panel,
198 ) = futures::try_join!(
199 project_panel,
200 terminal_panel,
201 // assistant_panel,
202 channels_panel,
203 // chat_panel,
204 // notification_panel,
205 )?;
206
207 workspace_handle.update(&mut cx, |workspace, cx| {
208 let project_panel_position = project_panel.position(cx);
209 workspace.add_panel(project_panel, cx);
210 workspace.add_panel(terminal_panel, cx);
211 // workspace.add_panel(assistant_panel, cx);
212 workspace.add_panel(channels_panel, cx);
213 // workspace.add_panel(chat_panel, cx);
214 // workspace.add_panel(notification_panel, cx);
215
216 // if !was_deserialized
217 // && workspace
218 // .project()
219 // .read(cx)
220 // .visible_worktrees(cx)
221 // .any(|tree| {
222 // tree.read(cx)
223 // .root_entry()
224 // .map_or(false, |entry| entry.is_dir())
225 // })
226 // {
227 // workspace.toggle_dock(project_panel_position, cx);
228 // }
229 // cx.focus_self();
230 })
231 })
232 .detach();
233
234 workspace
235 .register_action(about)
236 .register_action(|_, _: &Hide, cx| {
237 cx.hide();
238 })
239 .register_action(|_, _: &HideOthers, cx| {
240 cx.hide_other_apps();
241 })
242 .register_action(|_, _: &ShowAll, cx| {
243 cx.unhide_other_apps();
244 })
245 .register_action(|_, _: &Minimize, cx| {
246 cx.minimize_window();
247 })
248 .register_action(|_, _: &Zoom, cx| {
249 cx.zoom_window();
250 })
251 .register_action(|_, _: &ToggleFullScreen, cx| {
252 cx.toggle_full_screen();
253 })
254 .register_action(quit)
255 .register_action(|_, action: &OpenZedURL, cx| {
256 cx.global::<Arc<OpenListener>>()
257 .open_urls(&[action.url.clone()])
258 })
259 .register_action(|_, action: &OpenBrowser, cx| cx.open_url(&action.url))
260 //todo!(buffer font size)
261 // cx.add_global_action(move |_: &IncreaseBufferFontSize, cx| {
262 // theme::adjust_font_size(cx, |size| *size += 1.0)
263 // });
264 // cx.add_global_action(move |_: &DecreaseBufferFontSize, cx| {
265 // theme::adjust_font_size(cx, |size| *size -= 1.0)
266 // });
267 // cx.add_global_action(move |_: &ResetBufferFontSize, cx| theme::reset_font_size(cx));
268 .register_action(|_, _: &install_cli::Install, cx| {
269 cx.spawn(|_, cx| async move {
270 install_cli::install_cli(cx.deref())
271 .await
272 .context("error creating CLI symlink")
273 })
274 .detach_and_log_err(cx);
275 })
276 .register_action(|workspace, _: &OpenLog, cx| {
277 open_log_file(workspace, cx);
278 })
279 .register_action(|workspace, _: &OpenLicenses, cx| {
280 open_bundled_file(
281 workspace,
282 asset_str::<Assets>("licenses.md"),
283 "Open Source License Attribution",
284 "Markdown",
285 cx,
286 );
287 })
288 .register_action(
289 move |workspace: &mut Workspace,
290 _: &OpenTelemetryLog,
291 cx: &mut ViewContext<Workspace>| {
292 open_telemetry_log_file(workspace, cx);
293 },
294 )
295 .register_action(
296 move |_: &mut Workspace, _: &OpenKeymap, cx: &mut ViewContext<Workspace>| {
297 create_and_open_local_file(&paths::KEYMAP, cx, Default::default)
298 .detach_and_log_err(cx);
299 },
300 )
301 .register_action(
302 move |_: &mut Workspace, _: &OpenSettings, cx: &mut ViewContext<Workspace>| {
303 create_and_open_local_file(&paths::SETTINGS, cx, || {
304 settings::initial_user_settings_content().as_ref().into()
305 })
306 .detach_and_log_err(cx);
307 },
308 )
309 .register_action(open_local_settings_file)
310 .register_action(
311 move |workspace: &mut Workspace,
312 _: &OpenDefaultKeymap,
313 cx: &mut ViewContext<Workspace>| {
314 open_bundled_file(
315 workspace,
316 settings::default_keymap(),
317 "Default Key Bindings",
318 "JSON",
319 cx,
320 );
321 },
322 )
323 .register_action(
324 move |workspace: &mut Workspace,
325 _: &OpenDefaultSettings,
326 cx: &mut ViewContext<Workspace>| {
327 open_bundled_file(
328 workspace,
329 settings::default_settings(),
330 "Default Settings",
331 "JSON",
332 cx,
333 );
334 },
335 )
336 //todo!()
337 // cx.add_action({
338 // move |workspace: &mut Workspace, _: &DebugElements, cx: &mut ViewContext<Workspace>| {
339 // let app_state = workspace.app_state().clone();
340 // let markdown = app_state.languages.language_for_name("JSON");
341 // let window = cx.window();
342 // cx.spawn(|workspace, mut cx| async move {
343 // let markdown = markdown.await.log_err();
344 // let content = to_string_pretty(&window.debug_elements(&cx).ok_or_else(|| {
345 // anyhow!("could not debug elements for window {}", window.id())
346 // })?)
347 // .unwrap();
348 // workspace
349 // .update(&mut cx, |workspace, cx| {
350 // workspace.with_local_workspace(cx, move |workspace, cx| {
351 // let project = workspace.project().clone();
352 // let buffer = project
353 // .update(cx, |project, cx| {
354 // project.create_buffer(&content, markdown, cx)
355 // })
356 // .expect("creating buffers on a local workspace always succeeds");
357 // let buffer = cx.add_model(|cx| {
358 // MultiBuffer::singleton(buffer, cx)
359 // .with_title("Debug Elements".into())
360 // });
361 // workspace.add_item(
362 // Box::new(cx.add_view(|cx| {
363 // Editor::for_multibuffer(buffer, Some(project.clone()), cx)
364 // })),
365 // cx,
366 // );
367 // })
368 // })?
369 // .await
370 // })
371 // .detach_and_log_err(cx);
372 // }
373 // });
374 // .register_action(
375 // |workspace: &mut Workspace,
376 // _: &project_panel::ToggleFocus,
377 // cx: &mut ViewContext<Workspace>| {
378 // workspace.toggle_panel_focus::<ProjectPanel>(cx);
379 // },
380 // );
381 // cx.add_action(
382 // |workspace: &mut Workspace,
383 // _: &collab_ui::collab_panel::ToggleFocus,
384 // cx: &mut ViewContext<Workspace>| {
385 // workspace.toggle_panel_focus::<collab_ui::collab_panel::CollabPanel>(cx);
386 // },
387 // );
388 // cx.add_action(
389 // |workspace: &mut Workspace,
390 // _: &collab_ui::chat_panel::ToggleFocus,
391 // cx: &mut ViewContext<Workspace>| {
392 // workspace.toggle_panel_focus::<collab_ui::chat_panel::ChatPanel>(cx);
393 // },
394 // );
395 // cx.add_action(
396 // |workspace: &mut Workspace,
397 // _: &collab_ui::notification_panel::ToggleFocus,
398 // cx: &mut ViewContext<Workspace>| {
399 // workspace.toggle_panel_focus::<collab_ui::notification_panel::NotificationPanel>(cx);
400 // },
401 // );
402 // cx.add_action(
403 // |workspace: &mut Workspace,
404 // _: &terminal_panel::ToggleFocus,
405 // cx: &mut ViewContext<Workspace>| {
406 // workspace.toggle_panel_focus::<TerminalPanel>(cx);
407 // },
408 // );
409 .register_action({
410 let app_state = Arc::downgrade(&app_state);
411 move |_, _: &NewWindow, cx| {
412 if let Some(app_state) = app_state.upgrade() {
413 open_new(&app_state, cx, |workspace, cx| {
414 Editor::new_file(workspace, &Default::default(), cx)
415 })
416 .detach();
417 }
418 }
419 })
420 .register_action({
421 let app_state = Arc::downgrade(&app_state);
422 move |_, _: &NewFile, cx| {
423 if let Some(app_state) = app_state.upgrade() {
424 open_new(&app_state, cx, |workspace, cx| {
425 Editor::new_file(workspace, &Default::default(), cx)
426 })
427 .detach();
428 }
429 }
430 });
431
432 workspace.focus_handle(cx).focus(cx);
433 //todo!()
434 // load_default_keymap(cx);
435 })
436 .detach();
437}
438
439fn about(_: &mut Workspace, _: &About, cx: &mut gpui::ViewContext<Workspace>) {
440 use std::fmt::Write as _;
441
442 let app_name = cx.global::<ReleaseChannel>().display_name();
443 let version = env!("CARGO_PKG_VERSION");
444 let mut message = format!("{app_name} {version}");
445 if let Some(sha) = cx.try_global::<AppCommitSha>() {
446 write!(&mut message, "\n\n{}", sha.0).unwrap();
447 }
448
449 let prompt = cx.prompt(PromptLevel::Info, &message, &["OK"]);
450 cx.foreground_executor()
451 .spawn(async {
452 prompt.await.ok();
453 })
454 .detach();
455}
456
457fn quit(_: &mut Workspace, _: &Quit, cx: &mut gpui::ViewContext<Workspace>) {
458 let should_confirm = WorkspaceSettings::get_global(cx).confirm_quit;
459 cx.spawn(|_, mut cx| async move {
460 let mut workspace_windows = cx.update(|_, cx| {
461 cx.windows()
462 .into_iter()
463 .filter_map(|window| window.downcast::<Workspace>())
464 .collect::<Vec<_>>()
465 })?;
466
467 // If multiple windows have unsaved changes, and need a save prompt,
468 // prompt in the active window before switching to a different window.
469 cx.update(|_, cx| {
470 workspace_windows.sort_by_key(|window| window.is_active(&cx) == Some(false));
471 })
472 .log_err();
473
474 if let (true, Some(window)) = (should_confirm, workspace_windows.first().copied()) {
475 let answer = cx
476 .update(|_, cx| {
477 cx.prompt(
478 PromptLevel::Info,
479 "Are you sure you want to quit?",
480 &["Quit", "Cancel"],
481 )
482 })
483 .log_err();
484
485 if let Some(mut answer) = answer {
486 let answer = answer.await.ok();
487 if answer != Some(0) {
488 return Ok(());
489 }
490 }
491 }
492
493 // If the user cancels any save prompt, then keep the app open.
494 for window in workspace_windows {
495 if let Some(should_close) = window
496 .update(&mut cx, |workspace, cx| {
497 workspace.prepare_to_close(true, cx)
498 })
499 .log_err()
500 {
501 if !should_close.await? {
502 return Ok(());
503 }
504 }
505 }
506 cx.update(|_, cx| {
507 cx.quit();
508 })?;
509 anyhow::Ok(())
510 })
511 .detach_and_log_err(cx);
512}
513
514fn open_log_file(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
515 const MAX_LINES: usize = 1000;
516 workspace
517 .with_local_workspace(cx, move |workspace, cx| {
518 let fs = workspace.app_state().fs.clone();
519 cx.spawn(|workspace, mut cx| async move {
520 let (old_log, new_log) =
521 futures::join!(fs.load(&paths::OLD_LOG), fs.load(&paths::LOG));
522
523 let mut lines = VecDeque::with_capacity(MAX_LINES);
524 for line in old_log
525 .iter()
526 .flat_map(|log| log.lines())
527 .chain(new_log.iter().flat_map(|log| log.lines()))
528 {
529 if lines.len() == MAX_LINES {
530 lines.pop_front();
531 }
532 lines.push_back(line);
533 }
534 let log = lines
535 .into_iter()
536 .flat_map(|line| [line, "\n"])
537 .collect::<String>();
538
539 workspace
540 .update(&mut cx, |workspace, cx| {
541 let project = workspace.project().clone();
542 let buffer = project
543 .update(cx, |project, cx| project.create_buffer("", None, cx))
544 .expect("creating buffers on a local workspace always succeeds");
545 buffer.update(cx, |buffer, cx| buffer.edit([(0..0, log)], None, cx));
546
547 let buffer = cx.build_model(|cx| {
548 MultiBuffer::singleton(buffer, cx).with_title("Log".into())
549 });
550 workspace.add_item(
551 Box::new(cx.build_view(|cx| {
552 Editor::for_multibuffer(buffer, Some(project), cx)
553 })),
554 cx,
555 );
556 })
557 .log_err();
558 })
559 .detach();
560 })
561 .detach();
562}
563
564fn open_local_settings_file(
565 workspace: &mut Workspace,
566 _: &OpenLocalSettings,
567 cx: &mut ViewContext<Workspace>,
568) {
569 let project = workspace.project().clone();
570 let worktree = project
571 .read(cx)
572 .visible_worktrees(cx)
573 .find_map(|tree| tree.read(cx).root_entry()?.is_dir().then_some(tree));
574 if let Some(worktree) = worktree {
575 let tree_id = worktree.read(cx).id();
576 cx.spawn(|workspace, mut cx| async move {
577 let file_path = &*LOCAL_SETTINGS_RELATIVE_PATH;
578
579 if let Some(dir_path) = file_path.parent() {
580 if worktree.update(&mut cx, |tree, _| tree.entry_for_path(dir_path).is_none())? {
581 project
582 .update(&mut cx, |project, cx| {
583 project.create_entry((tree_id, dir_path), true, cx)
584 })?
585 .ok_or_else(|| anyhow!("worktree was removed"))?
586 .await?;
587 }
588 }
589
590 if worktree.update(&mut cx, |tree, _| tree.entry_for_path(file_path).is_none())? {
591 project
592 .update(&mut cx, |project, cx| {
593 project.create_entry((tree_id, file_path), false, cx)
594 })?
595 .ok_or_else(|| anyhow!("worktree was removed"))?
596 .await?;
597 }
598
599 let editor = workspace
600 .update(&mut cx, |workspace, cx| {
601 workspace.open_path((tree_id, file_path), None, true, cx)
602 })?
603 .await?
604 .downcast::<Editor>()
605 .ok_or_else(|| anyhow!("unexpected item type"))?;
606
607 editor
608 .downgrade()
609 .update(&mut cx, |editor, cx| {
610 if let Some(buffer) = editor.buffer().read(cx).as_singleton() {
611 if buffer.read(cx).is_empty() {
612 buffer.update(cx, |buffer, cx| {
613 buffer.edit([(0..0, initial_local_settings_content())], None, cx)
614 });
615 }
616 }
617 })
618 .ok();
619
620 anyhow::Ok(())
621 })
622 .detach();
623 } else {
624 workspace.show_notification(0, cx, |cx| {
625 cx.build_view(|_| MessageNotification::new("This project has no folders open."))
626 })
627 }
628}
629
630fn open_telemetry_log_file(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
631 workspace.with_local_workspace(cx, move |workspace, cx| {
632 let app_state = workspace.app_state().clone();
633 cx.spawn(|workspace, mut cx| async move {
634 async fn fetch_log_string(app_state: &Arc<AppState>) -> Option<String> {
635 let path = app_state.client.telemetry().log_file_path()?;
636 app_state.fs.load(&path).await.log_err()
637 }
638
639 let log = fetch_log_string(&app_state).await.unwrap_or_else(|| "// No data has been collected yet".to_string());
640
641 const MAX_TELEMETRY_LOG_LEN: usize = 5 * 1024 * 1024;
642 let mut start_offset = log.len().saturating_sub(MAX_TELEMETRY_LOG_LEN);
643 if let Some(newline_offset) = log[start_offset..].find('\n') {
644 start_offset += newline_offset + 1;
645 }
646 let log_suffix = &log[start_offset..];
647 let json = app_state.languages.language_for_name("JSON").await.log_err();
648
649 workspace.update(&mut cx, |workspace, cx| {
650 let project = workspace.project().clone();
651 let buffer = project
652 .update(cx, |project, cx| project.create_buffer("", None, cx))
653 .expect("creating buffers on a local workspace always succeeds");
654 buffer.update(cx, |buffer, cx| {
655 buffer.set_language(json, cx);
656 buffer.edit(
657 [(
658 0..0,
659 concat!(
660 "// Zed collects anonymous usage data to help us understand how people are using the app.\n",
661 "// Telemetry can be disabled via the `settings.json` file.\n",
662 "// Here is the data that has been reported for the current session:\n",
663 "\n"
664 ),
665 )],
666 None,
667 cx,
668 );
669 buffer.edit([(buffer.len()..buffer.len(), log_suffix)], None, cx);
670 });
671
672 let buffer = cx.build_model(|cx| {
673 MultiBuffer::singleton(buffer, cx).with_title("Telemetry Log".into())
674 });
675 workspace.add_item(
676 Box::new(cx.build_view(|cx| Editor::for_multibuffer(buffer, Some(project), cx))),
677 cx,
678 );
679 }).log_err()?;
680
681 Some(())
682 })
683 .detach();
684 }).detach();
685}
686
687fn open_bundled_file(
688 workspace: &mut Workspace,
689 text: Cow<'static, str>,
690 title: &'static str,
691 language: &'static str,
692 cx: &mut ViewContext<Workspace>,
693) {
694 let language = workspace.app_state().languages.language_for_name(language);
695 cx.spawn(|workspace, mut cx| async move {
696 let language = language.await.log_err();
697 workspace
698 .update(&mut cx, |workspace, cx| {
699 workspace.with_local_workspace(cx, |workspace, cx| {
700 let project = workspace.project();
701 let buffer = project.update(cx, move |project, cx| {
702 project
703 .create_buffer(text.as_ref(), language, cx)
704 .expect("creating buffers on a local workspace always succeeds")
705 });
706 let buffer = cx.build_model(|cx| {
707 MultiBuffer::singleton(buffer, cx).with_title(title.into())
708 });
709 workspace.add_item(
710 Box::new(cx.build_view(|cx| {
711 Editor::for_multibuffer(buffer, Some(project.clone()), cx)
712 })),
713 cx,
714 );
715 })
716 })?
717 .await
718 })
719 .detach_and_log_err(cx);
720}