tabs_command.rs

  1use super::{file_command::FilePlaceholder, SlashCommand, SlashCommandOutput};
  2use anyhow::{anyhow, Result};
  3use assistant_slash_command::SlashCommandOutputSection;
  4use collections::HashMap;
  5use editor::Editor;
  6use gpui::{AppContext, Entity, Task, WeakView};
  7use language::LspAdapterDelegate;
  8use std::{fmt::Write, path::Path, sync::Arc};
  9use ui::{IntoElement, WindowContext};
 10use workspace::Workspace;
 11
 12pub(crate) struct TabsSlashCommand;
 13
 14impl SlashCommand for TabsSlashCommand {
 15    fn name(&self) -> String {
 16        "tabs".into()
 17    }
 18
 19    fn description(&self) -> String {
 20        "insert open tabs".into()
 21    }
 22
 23    fn menu_text(&self) -> String {
 24        "Insert Open Tabs".into()
 25    }
 26
 27    fn requires_argument(&self) -> bool {
 28        false
 29    }
 30
 31    fn complete_argument(
 32        &self,
 33        _query: String,
 34        _cancel: Arc<std::sync::atomic::AtomicBool>,
 35        _workspace: WeakView<Workspace>,
 36        _cx: &mut AppContext,
 37    ) -> Task<Result<Vec<String>>> {
 38        Task::ready(Err(anyhow!("this command does not require argument")))
 39    }
 40
 41    fn run(
 42        self: Arc<Self>,
 43        _argument: Option<&str>,
 44        workspace: WeakView<Workspace>,
 45        _delegate: Arc<dyn LspAdapterDelegate>,
 46        cx: &mut WindowContext,
 47    ) -> Task<Result<SlashCommandOutput>> {
 48        let open_buffers = workspace.update(cx, |workspace, cx| {
 49            let mut timestamps_by_entity_id = HashMap::default();
 50            let mut open_buffers = Vec::new();
 51
 52            for pane in workspace.panes() {
 53                let pane = pane.read(cx);
 54                for entry in pane.activation_history() {
 55                    timestamps_by_entity_id.insert(entry.entity_id, entry.timestamp);
 56                }
 57            }
 58
 59            for editor in workspace.items_of_type::<Editor>(cx) {
 60                if let Some(buffer) = editor.read(cx).buffer().read(cx).as_singleton() {
 61                    if let Some(timestamp) = timestamps_by_entity_id.get(&editor.entity_id()) {
 62                        let snapshot = buffer.read(cx).snapshot();
 63                        let full_path = snapshot.resolve_file_path(cx, true);
 64                        open_buffers.push((full_path, snapshot, *timestamp));
 65                    }
 66                }
 67            }
 68
 69            open_buffers
 70        });
 71
 72        match open_buffers {
 73            Ok(mut open_buffers) => cx.background_executor().spawn(async move {
 74                open_buffers.sort_by_key(|(_, _, timestamp)| *timestamp);
 75
 76                let mut sections = Vec::new();
 77                let mut text = String::new();
 78                for (full_path, buffer, _) in open_buffers {
 79                    let section_start_ix = text.len();
 80                    writeln!(
 81                        text,
 82                        "```{}\n",
 83                        full_path
 84                            .as_deref()
 85                            .unwrap_or(Path::new("untitled"))
 86                            .display()
 87                    )
 88                    .unwrap();
 89                    for chunk in buffer.as_rope().chunks() {
 90                        text.push_str(chunk);
 91                    }
 92                    if !text.ends_with('\n') {
 93                        text.push('\n');
 94                    }
 95                    writeln!(text, "```\n").unwrap();
 96                    let section_end_ix = text.len() - 1;
 97
 98                    sections.push(SlashCommandOutputSection {
 99                        range: section_start_ix..section_end_ix,
100                        render_placeholder: Arc::new(move |id, unfold, _| {
101                            FilePlaceholder {
102                                id,
103                                path: full_path.clone(),
104                                line_range: None,
105                                unfold,
106                            }
107                            .into_any_element()
108                        }),
109                    });
110                }
111
112                Ok(SlashCommandOutput { text, sections })
113            }),
114            Err(error) => Task::ready(Err(error)),
115        }
116    }
117}