tabs_command.rs

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