
This PR fixes an issue where slash commands were not able to run when Zed did not have any worktrees opened. This requirement was only necessary for slash commands originating from extensions, and we can enforce the presence of a worktree just for those: <img width="378" alt="Screenshot 2024-08-01 at 5 01 58 PM" src="https://github.com/user-attachments/assets/38bea947-e33b-4c64-853c-c1f36c63d779"> Release Notes: - N/A
113 lines
3.3 KiB
Rust
113 lines
3.3 KiB
Rust
use std::sync::atomic::AtomicBool;
|
|
use std::sync::Arc;
|
|
|
|
use anyhow::Result;
|
|
use assistant_slash_command::{
|
|
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
|
|
};
|
|
use gpui::{AppContext, Task, WeakView};
|
|
use language::{CodeLabel, LspAdapterDelegate};
|
|
use terminal_view::{terminal_panel::TerminalPanel, TerminalView};
|
|
use ui::prelude::*;
|
|
use workspace::{dock::Panel, Workspace};
|
|
|
|
use crate::DEFAULT_CONTEXT_LINES;
|
|
|
|
use super::create_label_for_command;
|
|
|
|
pub(crate) struct TermSlashCommand;
|
|
|
|
const LINE_COUNT_ARG: &str = "--line-count";
|
|
|
|
impl SlashCommand for TermSlashCommand {
|
|
fn name(&self) -> String {
|
|
"term".into()
|
|
}
|
|
|
|
fn label(&self, cx: &AppContext) -> CodeLabel {
|
|
create_label_for_command("term", &[LINE_COUNT_ARG], cx)
|
|
}
|
|
|
|
fn description(&self) -> String {
|
|
"insert terminal output".into()
|
|
}
|
|
|
|
fn menu_text(&self) -> String {
|
|
"Insert Terminal Output".into()
|
|
}
|
|
|
|
fn requires_argument(&self) -> bool {
|
|
false
|
|
}
|
|
|
|
fn complete_argument(
|
|
self: Arc<Self>,
|
|
_query: String,
|
|
_cancel: Arc<AtomicBool>,
|
|
_workspace: Option<WeakView<Workspace>>,
|
|
_cx: &mut AppContext,
|
|
) -> Task<Result<Vec<ArgumentCompletion>>> {
|
|
Task::ready(Ok(vec![ArgumentCompletion {
|
|
label: LINE_COUNT_ARG.to_string(),
|
|
new_text: LINE_COUNT_ARG.to_string(),
|
|
run_command: true,
|
|
}]))
|
|
}
|
|
|
|
fn run(
|
|
self: Arc<Self>,
|
|
argument: Option<&str>,
|
|
workspace: WeakView<Workspace>,
|
|
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
|
cx: &mut WindowContext,
|
|
) -> Task<Result<SlashCommandOutput>> {
|
|
let Some(workspace) = workspace.upgrade() else {
|
|
return Task::ready(Err(anyhow::anyhow!("workspace was dropped")));
|
|
};
|
|
let Some(terminal_panel) = workspace.read(cx).panel::<TerminalPanel>(cx) else {
|
|
return Task::ready(Err(anyhow::anyhow!("no terminal panel open")));
|
|
};
|
|
let Some(active_terminal) = terminal_panel.read(cx).pane().and_then(|pane| {
|
|
pane.read(cx)
|
|
.active_item()
|
|
.and_then(|t| t.downcast::<TerminalView>())
|
|
}) else {
|
|
return Task::ready(Err(anyhow::anyhow!("no active terminal")));
|
|
};
|
|
|
|
let line_count = argument
|
|
.and_then(|a| parse_argument(a))
|
|
.unwrap_or(DEFAULT_CONTEXT_LINES);
|
|
|
|
let lines = active_terminal
|
|
.read(cx)
|
|
.model()
|
|
.read(cx)
|
|
.last_n_non_empty_lines(line_count);
|
|
|
|
let mut text = String::new();
|
|
text.push_str("Terminal output:\n");
|
|
text.push_str(&lines.join("\n"));
|
|
let range = 0..text.len();
|
|
|
|
Task::ready(Ok(SlashCommandOutput {
|
|
text,
|
|
sections: vec![SlashCommandOutputSection {
|
|
range,
|
|
icon: IconName::Terminal,
|
|
label: "Terminal".into(),
|
|
}],
|
|
run_commands_in_text: false,
|
|
}))
|
|
}
|
|
}
|
|
|
|
fn parse_argument(argument: &str) -> Option<usize> {
|
|
let mut args = argument.split(' ');
|
|
if args.next() == Some(LINE_COUNT_ARG) {
|
|
if let Some(line_count) = args.next().and_then(|s| s.parse::<usize>().ok()) {
|
|
return Some(line_count);
|
|
}
|
|
}
|
|
None
|
|
}
|