active_command.rs

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