tabs_command.rs

  1use super::{
  2    diagnostics_command::write_single_file_diagnostics,
  3    file_command::{build_entry_output_section, codeblock_fence_for_path},
  4    SlashCommand, SlashCommandOutput,
  5};
  6use anyhow::{anyhow, Result};
  7use collections::HashMap;
  8use editor::Editor;
  9use gpui::{AppContext, Entity, Task, WeakView};
 10use language::LspAdapterDelegate;
 11use std::{fmt::Write, sync::Arc};
 12use ui::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: Arc<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                let mut has_diagnostics = false;
 82                for (full_path, buffer, _) in open_buffers {
 83                    let section_start_ix = text.len();
 84                    text.push_str(&codeblock_fence_for_path(full_path.as_deref(), None));
 85                    for chunk in buffer.as_rope().chunks() {
 86                        text.push_str(chunk);
 87                    }
 88                    if !text.ends_with('\n') {
 89                        text.push('\n');
 90                    }
 91                    writeln!(text, "```").unwrap();
 92                    if write_single_file_diagnostics(&mut text, full_path.as_deref(), &buffer) {
 93                        has_diagnostics = true;
 94                    }
 95                    if !text.ends_with('\n') {
 96                        text.push('\n');
 97                    }
 98
 99                    let section_end_ix = text.len() - 1;
100                    sections.push(build_entry_output_section(
101                        section_start_ix..section_end_ix,
102                        full_path.as_deref(),
103                        false,
104                        None,
105                    ));
106                }
107
108                Ok(SlashCommandOutput {
109                    text,
110                    sections,
111                    run_commands_in_text: has_diagnostics,
112                })
113            }),
114            Err(error) => Task::ready(Err(error)),
115        }
116    }
117}