tabs_command.rs

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