use anyhow::{Result, anyhow};use collections::{HashMap, HashSet};use fs::Fs;use futures::StreamExt;use gpui::{Context, EventEmitter, Task};use std::borrow::Cow;use std::path::{Path, PathBuf};use std::sync::Arc;use std::time::Duration; /// An error that occurred while loading a command file.#[derive(Debug, Clone)]pub struct CommandLoadError { /// The path to the file that failed to load pub path: PathBuf, /// The base path of the commands directory (used to derive command name) pub base_path: PathBuf, /// A description of the error pub message: String,} impl CommandLoadError { /// Derives the command name from the file path, similar to how successful commands are named. /// Returns None if the command name cannot be determined (e.g., for directory errors). pub fn command_name(&self) -> Option<String> { let base_name = self.path.file_stem()?.to_string_lossy().into_owned(); // Only derive command name for .md files if self.path.extension().is_none_or(|ext| ext != "md") { return None; } let namespace = self .path .parent() .and_then(|parent| parent.strip_prefix(&self.base_path).ok()) .filter(|rel| !rel.as_os_str().is_empty()) .map(|rel| { rel.to_string_lossy() .replace(std::path::MAIN_SEPARATOR, "/") }); let name = match &namespace { Some(namespace) => format!("{}:{}", namespace.replace('/', ":"), base_name), None => base_name, }; Some(name) }} impl std::fmt::Display for CommandLoadError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "Failed to load {}: {}", self.path.display(), self.message ) }} /// Result of loading commands, including any errors encountered.#[derive(Debug, Default, Clone)]pub struct CommandLoadResult { /// Successfully loaded commands pub commands: Vec<UserSlashCommand>, /// Errors encountered while loading commands pub errors: Vec<CommandLoadError>,} /// The scope of a user-defined slash command.#[derive(Debug, Clone, Copy, PartialEq, Eq)]pub enum CommandScope { /// Project-specific command from .zed/commands/ Project, /// User-wide command from config_dir()/commands/ User,} /// A user-defined slash command loaded from a markdown file.#[derive(Debug, Clone, PartialEq)]pub struct UserSlashCommand { /// The command name for invocation. /// For commands in subdirectories, this is prefixed: "namespace:name" (e.g., "frontend:component") /// For commands in the root, this is just the filename without .md extension. pub name: Arc<str>, /// The template content from the file pub template: Arc<str>, /// The namespace (subdirectory path, if any), used for description display pub namespace: Option<Arc<str>>, /// The full path to the command file pub path: PathBuf, /// Whether this is a project or user command pub scope: CommandScope,} impl UserSlashCommand { /// Returns a description string for display in completions. pub fn description(&self) -> String { String::new() } /// Returns true if this command has any placeholders ($1, $2, etc. or $ARGUMENTS) pub fn requires_arguments(&self) -> bool { has_placeholders(&self.template) }} fn command_base_path(command: &UserSlashCommand) -> PathBuf { let mut base_path = command.path.clone(); base_path.pop(); if let Some(namespace) = &command.namespace { for segment in namespace.split('/') { if segment.is_empty() { continue; } if !base_path.pop() { break; } } } base_path} impl CommandLoadError { pub fn from_command(command: &UserSlashCommand, message: String) -> Self { Self { path: command.path.clone(), base_path: command_base_path(command), message, } }} /// Parsed user command from input text#[derive(Debug, Clone, PartialEq)]pub struct ParsedUserCommand<'a> { pub name: &'a str, pub raw_arguments: &'a str,} /// Returns the path to the user commands directory.pub fn user_commands_dir() -> PathBuf { paths::config_dir().join("commands")} /// Returns the path to the project commands directory for a given worktree root.pub fn project_commands_dir(worktree_root: &Path) -> PathBuf { worktree_root.join(".zed").join("commands")} /// Events emitted by SlashCommandRegistry#[derive(Debug, Clone)]#[allow(dead_code)] // Infrastructure for future caching implementationpub enum SlashCommandRegistryEvent { /// Commands have been reloaded CommandsChanged,} /// A registry that caches user-defined slash commands and watches for changes./// Currently used in tests; will be integrated into the UI layer for caching.#[allow(dead_code)]pub struct SlashCommandRegistry { fs: Arc<dyn Fs>, commands: HashMap<String, UserSlashCommand>, errors: Vec<CommandLoadError>, worktree_roots: Vec<PathBuf>, _watch_task: Option<Task<()>>,} impl EventEmitter<SlashCommandRegistryEvent> for SlashCommandRegistry {} #[allow(dead_code)]impl SlashCommandRegistry { /// Creates a new registry and starts loading commands. pub fn new(fs: Arc<dyn Fs>, worktree_roots: Vec<PathBuf>, cx: &mut Context<Self>) -> Self { let mut this = Self { fs, commands: HashMap::default(), errors: Vec::new(), worktree_roots, _watch_task: None, }; this.start_watching(cx); this.reload(cx); this } /// Returns all loaded commands. pub fn commands(&self) -> &HashMap<String, UserSlashCommand> { &self.commands } /// Returns any errors from the last load. pub fn errors(&self) -> &[CommandLoadError] { &self.errors } /// Updates the worktree roots and reloads commands. pub fn set_worktree_roots(&mut self, roots: Vec<PathBuf>, cx: &mut Context<Self>) { if self.worktree_roots != roots { self.worktree_roots = roots; self.start_watching(cx); self.reload(cx); } } /// Manually triggers a reload of all commands. pub fn reload(&mut self, cx: &mut Context<Self>) { let fs = self.fs.clone(); let worktree_roots = self.worktree_roots.clone(); cx.spawn(async move |this, cx| { let result = load_all_commands_async(&fs, &worktree_roots).await; this.update(cx, |this, cx| { this.commands = commands_to_map(&result.commands); this.errors = result.errors; cx.emit(SlashCommandRegistryEvent::CommandsChanged); }) }) .detach_and_log_err(cx); } fn start_watching(&mut self, cx: &mut Context<Self>) { let fs = self.fs.clone(); let worktree_roots = self.worktree_roots.clone(); let task = cx.spawn(async move |this, cx| { let user_dir = user_commands_dir(); let mut dirs_to_watch = vec![user_dir]; for root in &worktree_roots { dirs_to_watch.push(project_commands_dir(root)); } let mut watch_streams = Vec::new(); for dir in &dirs_to_watch { let (stream, _watcher) = fs.watch(dir, Duration::from_millis(100)).await; watch_streams.push(stream); } let mut combined = futures::stream::select_all(watch_streams); while let Some(events) = combined.next().await { let should_reload = events.iter().any(|event| { event.path.extension().is_some_and(|ext| ext == "md") || event.kind == Some(fs::PathEventKind::Created) || event.kind == Some(fs::PathEventKind::Removed) }); if should_reload { let result = load_all_commands_async(&fs, &worktree_roots).await; let _ = this.update(cx, |this, cx| { this.commands = commands_to_map(&result.commands); this.errors = result.errors; cx.emit(SlashCommandRegistryEvent::CommandsChanged); }); } } }); self._watch_task = Some(task); }} /// Loads all commands (both project and user) for given worktree roots asynchronously.pub async fn load_all_commands_async( fs: &Arc<dyn Fs>, worktree_roots: &[PathBuf],) -> CommandLoadResult { let mut result = CommandLoadResult::default(); let mut seen_commands: HashMap<String, PathBuf> = HashMap::default(); // Load project commands first for root in worktree_roots { let commands_path = project_commands_dir(root); let project_result = load_commands_from_path_async(fs, &commands_path, CommandScope::Project).await; result.errors.extend(project_result.errors); for cmd in project_result.commands { if let Some(existing_path) = seen_commands.get(&*cmd.name) { result.errors.push(CommandLoadError { path: cmd.path.clone(), base_path: commands_path.clone(), message: format!( "Command '{}' is ambiguous: also defined at {}", cmd.name, existing_path.display() ), }); } else { seen_commands.insert(cmd.name.to_string(), cmd.path.clone()); result.commands.push(cmd); } } } // Load user commands let user_commands_path = user_commands_dir(); let user_result = load_commands_from_path_async(fs, &user_commands_path, CommandScope::User).await; result.errors.extend(user_result.errors); for cmd in user_result.commands { if let Some(existing_path) = seen_commands.get(&*cmd.name) { result.errors.push(CommandLoadError { path: cmd.path.clone(), base_path: user_commands_path.clone(), message: format!( "Command '{}' is ambiguous: also defined at {}", cmd.name, existing_path.display() ), }); } else { seen_commands.insert(cmd.name.to_string(), cmd.path.clone()); result.commands.push(cmd); } } result} async fn load_commands_from_path_async( fs: &Arc<dyn Fs>, commands_path: &Path, scope: CommandScope,) -> CommandLoadResult { let mut result = CommandLoadResult::default(); if !fs.is_dir(commands_path).await { return result; } load_commands_from_dir_async(fs, commands_path, commands_path, scope, &mut result).await; result} fn load_commands_from_dir_async<'a>( fs: &'a Arc<dyn Fs>, base_path: &'a Path, current_path: &'a Path, scope: CommandScope, result: &'a mut CommandLoadResult,) -> futures::future::BoxFuture<'a, ()> { Box::pin(async move { let entries = match fs.read_dir(current_path).await { Ok(entries) => entries, Err(e) => { result.errors.push(CommandLoadError { path: current_path.to_path_buf(), base_path: base_path.to_path_buf(), message: format!("Failed to read directory: {}", e), }); return; } }; let entries: Vec<_> = entries.collect().await; for entry in entries { let path = match entry { Ok(path) => path, Err(e) => { result.errors.push(CommandLoadError { path: current_path.to_path_buf(), base_path: base_path.to_path_buf(), message: format!("Failed to read directory entry: {}", e), }); continue; } }; if fs.is_dir(&path).await { load_commands_from_dir_async(fs, base_path, &path, scope, result).await; } else if path.extension().is_some_and(|ext| ext == "md") { match load_command_file_async(fs, base_path, &path, scope).await { Ok(Some(command)) => result.commands.push(command), Ok(None) => {} // Empty file, skip silently Err(e) => { result.errors.push(CommandLoadError { path: path.clone(), base_path: base_path.to_path_buf(), message: e.to_string(), }); } } } } })} async fn load_command_file_async( fs: &Arc<dyn Fs>, base_path: &Path, file_path: &Path, scope: CommandScope,) -> Result<Option<UserSlashCommand>> { let base_name = match file_path.file_stem() { Some(stem) => stem.to_string_lossy().into_owned(), None => return Ok(None), }; let template = fs.load(file_path).await?; if template.is_empty() { return Ok(None); } if template.trim().is_empty() { return Err(anyhow!("Command file contains only whitespace")); } let namespace = file_path .parent() .and_then(|parent| parent.strip_prefix(base_path).ok()) .filter(|rel| !rel.as_os_str().is_empty()) .map(|rel| { rel.to_string_lossy() .replace(std::path::MAIN_SEPARATOR, "/") }); // Build the full command name: "namespace:basename" or just "basename" let name = match &namespace { Some(namespace) => format!("{}:{}", namespace.replace('/', ":"), base_name), None => base_name, }; Ok(Some(UserSlashCommand { name: name.into(), template: template.into(), namespace: namespace.map(|s| s.into()), path: file_path.to_path_buf(), scope, }))} /// Converts a list of UserSlashCommand to a HashMap for quick lookup./// The key is the command name.pub fn commands_to_map(commands: &[UserSlashCommand]) -> HashMap<String, UserSlashCommand> { let mut map = HashMap::default(); for cmd in commands { map.insert(cmd.name.to_string(), cmd.clone()); } map} fn has_error_for_command(errors: &[CommandLoadError], name: &str) -> bool { errors .iter() .any(|error| error.command_name().as_deref() == Some(name))} fn server_conflict_message(name: &str) -> String { format!( "Command '{}' conflicts with server-provided /{}", name, name )} pub fn apply_server_command_conflicts( commands: &mut Vec<UserSlashCommand>, errors: &mut Vec<CommandLoadError>, server_command_names: &HashSet<String>,) { commands.retain(|command| { if server_command_names.contains(command.name.as_ref()) { if !has_error_for_command(errors, command.name.as_ref()) { errors.push(CommandLoadError::from_command( command, server_conflict_message(command.name.as_ref()), )); } false } else { true } });} pub fn apply_server_command_conflicts_to_map( commands: &mut HashMap<String, UserSlashCommand>, errors: &mut Vec<CommandLoadError>, server_command_names: &HashSet<String>,) { commands.retain(|name, command| { if server_command_names.contains(name) { if !has_error_for_command(errors, name) { errors.push(CommandLoadError::from_command( command, server_conflict_message(name), )); } false } else { true } });} /// Parses a line of input to extract a user command invocation.Lines 1–500 of 2118. Load more
use anyhow::{Result, anyhow}; use collections::{HashMap, HashSet}; use fs::Fs; use futures::StreamExt; use gpui::{Context, EventEmitter, Task}; use std::borrow::Cow; use std::path::{Path, PathBuf}; use std::sync::Arc; use std::time::Duration; /// An error that occurred while loading a command file. #[derive(Debug, Clone)] pub struct CommandLoadError { /// The path to the file that failed to load pub path: PathBuf, /// The base path of the commands directory (used to derive command name) pub base_path: PathBuf, /// A description of the error pub message: String, } impl CommandLoadError { /// Derives the command name from the file path, similar to how successful commands are named. /// Returns None if the command name cannot be determined (e.g., for directory errors). pub fn command_name(&self) -> Option<String> { let base_name = self.path.file_stem()?.to_string_lossy().into_owned(); // Only derive command name for .md files if self.path.extension().is_none_or(|ext| ext != "md") { return None; } let namespace = self .path .parent() .and_then(|parent| parent.strip_prefix(&self.base_path).ok()) .filter(|rel| !rel.as_os_str().is_empty()) .map(|rel| { rel.to_string_lossy() .replace(std::path::MAIN_SEPARATOR, "/") }); let name = match &namespace { Some(namespace) => format!("{}:{}", namespace.replace('/', ":"), base_name), None => base_name, }; Some(name) } } impl std::fmt::Display for CommandLoadError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "Failed to load {}: {}", self.path.display(), self.message ) } } /// Result of loading commands, including any errors encountered. #[derive(Debug, Default, Clone)] pub struct CommandLoadResult { /// Successfully loaded commands pub commands: Vec<UserSlashCommand>, /// Errors encountered while loading commands pub errors: Vec<CommandLoadError>, } /// The scope of a user-defined slash command. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum CommandScope { /// Project-specific command from .zed/commands/ Project, /// User-wide command from config_dir()/commands/ User, } /// A user-defined slash command loaded from a markdown file. #[derive(Debug, Clone, PartialEq)] pub struct UserSlashCommand { /// The command name for invocation. /// For commands in subdirectories, this is prefixed: "namespace:name" (e.g., "frontend:component") /// For commands in the root, this is just the filename without .md extension. pub name: Arc<str>, /// The template content from the file pub template: Arc<str>, /// The namespace (subdirectory path, if any), used for description display pub namespace: Option<Arc<str>>, /// The full path to the command file pub path: PathBuf, /// Whether this is a project or user command pub scope: CommandScope, } impl UserSlashCommand { /// Returns a description string for display in completions. pub fn description(&self) -> String { String::new() } /// Returns true if this command has any placeholders ($1, $2, etc. or $ARGUMENTS) pub fn requires_arguments(&self) -> bool { has_placeholders(&self.template) } } fn command_base_path(command: &UserSlashCommand) -> PathBuf { let mut base_path = command.path.clone(); base_path.pop(); if let Some(namespace) = &command.namespace { for segment in namespace.split('/') { if segment.is_empty() { continue; } if !base_path.pop() { break; } } } base_path } impl CommandLoadError { pub fn from_command(command: &UserSlashCommand, message: String) -> Self { Self { path: command.path.clone(), base_path: command_base_path(command), message, } } } /// Parsed user command from input text #[derive(Debug, Clone, PartialEq)] pub struct ParsedUserCommand<'a> { pub name: &'a str, pub raw_arguments: &'a str, } /// Returns the path to the user commands directory. pub fn user_commands_dir() -> PathBuf { paths::config_dir().join("commands") } /// Returns the path to the project commands directory for a given worktree root. pub fn project_commands_dir(worktree_root: &Path) -> PathBuf { worktree_root.join(".zed").join("commands") } /// Events emitted by SlashCommandRegistry #[derive(Debug, Clone)] #[allow(dead_code)] // Infrastructure for future caching implementation pub enum SlashCommandRegistryEvent { /// Commands have been reloaded CommandsChanged, } /// A registry that caches user-defined slash commands and watches for changes. /// Currently used in tests; will be integrated into the UI layer for caching. #[allow(dead_code)] pub struct SlashCommandRegistry { fs: Arc<dyn Fs>, commands: HashMap<String, UserSlashCommand>, errors: Vec<CommandLoadError>, worktree_roots: Vec<PathBuf>, _watch_task: Option<Task<()>>, } impl EventEmitter<SlashCommandRegistryEvent> for SlashCommandRegistry {} #[allow(dead_code)] impl SlashCommandRegistry { /// Creates a new registry and starts loading commands. pub fn new(fs: Arc<dyn Fs>, worktree_roots: Vec<PathBuf>, cx: &mut Context<Self>) -> Self { let mut this = Self { fs, commands: HashMap::default(), errors: Vec::new(), worktree_roots, _watch_task: None, }; this.start_watching(cx); this.reload(cx); this } /// Returns all loaded commands. pub fn commands(&self) -> &HashMap<String, UserSlashCommand> { &self.commands } /// Returns any errors from the last load. pub fn errors(&self) -> &[CommandLoadError] { &self.errors } /// Updates the worktree roots and reloads commands. pub fn set_worktree_roots(&mut self, roots: Vec<PathBuf>, cx: &mut Context<Self>) { if self.worktree_roots != roots { self.worktree_roots = roots; self.start_watching(cx); self.reload(cx); } } /// Manually triggers a reload of all commands. pub fn reload(&mut self, cx: &mut Context<Self>) { let fs = self.fs.clone(); let worktree_roots = self.worktree_roots.clone(); cx.spawn(async move |this, cx| { let result = load_all_commands_async(&fs, &worktree_roots).await; this.update(cx, |this, cx| { this.commands = commands_to_map(&result.commands); this.errors = result.errors; cx.emit(SlashCommandRegistryEvent::CommandsChanged); }) }) .detach_and_log_err(cx); } fn start_watching(&mut self, cx: &mut Context<Self>) { let fs = self.fs.clone(); let worktree_roots = self.worktree_roots.clone(); let task = cx.spawn(async move |this, cx| { let user_dir = user_commands_dir(); let mut dirs_to_watch = vec![user_dir]; for root in &worktree_roots { dirs_to_watch.push(project_commands_dir(root)); } let mut watch_streams = Vec::new(); for dir in &dirs_to_watch { let (stream, _watcher) = fs.watch(dir, Duration::from_millis(100)).await; watch_streams.push(stream); } let mut combined = futures::stream::select_all(watch_streams); while let Some(events) = combined.next().await { let should_reload = events.iter().any(|event| { event.path.extension().is_some_and(|ext| ext == "md") || event.kind == Some(fs::PathEventKind::Created) || event.kind == Some(fs::PathEventKind::Removed) }); if should_reload { let result = load_all_commands_async(&fs, &worktree_roots).await; let _ = this.update(cx, |this, cx| { this.commands = commands_to_map(&result.commands); this.errors = result.errors; cx.emit(SlashCommandRegistryEvent::CommandsChanged); }); } } }); self._watch_task = Some(task); } } /// Loads all commands (both project and user) for given worktree roots asynchronously. pub async fn load_all_commands_async( fs: &Arc<dyn Fs>, worktree_roots: &[PathBuf], ) -> CommandLoadResult { let mut result = CommandLoadResult::default(); let mut seen_commands: HashMap<String, PathBuf> = HashMap::default(); // Load project commands first for root in worktree_roots { let commands_path = project_commands_dir(root); let project_result = load_commands_from_path_async(fs, &commands_path, CommandScope::Project).await; result.errors.extend(project_result.errors); for cmd in project_result.commands { if let Some(existing_path) = seen_commands.get(&*cmd.name) { result.errors.push(CommandLoadError { path: cmd.path.clone(), base_path: commands_path.clone(), message: format!( "Command '{}' is ambiguous: also defined at {}", cmd.name, existing_path.display() ), }); } else { seen_commands.insert(cmd.name.to_string(), cmd.path.clone()); result.commands.push(cmd); } } } // Load user commands let user_commands_path = user_commands_dir(); let user_result = load_commands_from_path_async(fs, &user_commands_path, CommandScope::User).await; result.errors.extend(user_result.errors); for cmd in user_result.commands { if let Some(existing_path) = seen_commands.get(&*cmd.name) { result.errors.push(CommandLoadError { path: cmd.path.clone(), base_path: user_commands_path.clone(), message: format!( "Command '{}' is ambiguous: also defined at {}", cmd.name, existing_path.display() ), }); } else { seen_commands.insert(cmd.name.to_string(), cmd.path.clone()); result.commands.push(cmd); } } result } async fn load_commands_from_path_async( fs: &Arc<dyn Fs>, commands_path: &Path, scope: CommandScope, ) -> CommandLoadResult { let mut result = CommandLoadResult::default(); if !fs.is_dir(commands_path).await { return result; } load_commands_from_dir_async(fs, commands_path, commands_path, scope, &mut result).await; result } fn load_commands_from_dir_async<'a>( fs: &'a Arc<dyn Fs>, base_path: &'a Path, current_path: &'a Path, scope: CommandScope, result: &'a mut CommandLoadResult, ) -> futures::future::BoxFuture<'a, ()> { Box::pin(async move { let entries = match fs.read_dir(current_path).await { Ok(entries) => entries, Err(e) => { result.errors.push(CommandLoadError { path: current_path.to_path_buf(), base_path: base_path.to_path_buf(), message: format!("Failed to read directory: {}", e), }); return; } }; let entries: Vec<_> = entries.collect().await; for entry in entries { let path = match entry { Ok(path) => path, Err(e) => { result.errors.push(CommandLoadError { path: current_path.to_path_buf(), base_path: base_path.to_path_buf(), message: format!("Failed to read directory entry: {}", e), }); continue; } }; if fs.is_dir(&path).await { load_commands_from_dir_async(fs, base_path, &path, scope, result).await; } else if path.extension().is_some_and(|ext| ext == "md") { match load_command_file_async(fs, base_path, &path, scope).await { Ok(Some(command)) => result.commands.push(command), Ok(None) => {} // Empty file, skip silently Err(e) => { result.errors.push(CommandLoadError { path: path.clone(), base_path: base_path.to_path_buf(), message: e.to_string(), }); } } } } }) } async fn load_command_file_async( fs: &Arc<dyn Fs>, base_path: &Path, file_path: &Path, scope: CommandScope, ) -> Result<Option<UserSlashCommand>> { let base_name = match file_path.file_stem() { Some(stem) => stem.to_string_lossy().into_owned(), None => return Ok(None), }; let template = fs.load(file_path).await?; if template.is_empty() { return Ok(None); } if template.trim().is_empty() { return Err(anyhow!("Command file contains only whitespace")); } let namespace = file_path .parent() .and_then(|parent| parent.strip_prefix(base_path).ok()) .filter(|rel| !rel.as_os_str().is_empty()) .map(|rel| { rel.to_string_lossy() .replace(std::path::MAIN_SEPARATOR, "/") }); // Build the full command name: "namespace:basename" or just "basename" let name = match &namespace { Some(namespace) => format!("{}:{}", namespace.replace('/', ":"), base_name), None => base_name, }; Ok(Some(UserSlashCommand { name: name.into(), template: template.into(), namespace: namespace.map(|s| s.into()), path: file_path.to_path_buf(), scope, })) } /// Converts a list of UserSlashCommand to a HashMap for quick lookup. /// The key is the command name. pub fn commands_to_map(commands: &[UserSlashCommand]) -> HashMap<String, UserSlashCommand> { let mut map = HashMap::default(); for cmd in commands { map.insert(cmd.name.to_string(), cmd.clone()); } map } fn has_error_for_command(errors: &[CommandLoadError], name: &str) -> bool { errors .iter() .any(|error| error.command_name().as_deref() == Some(name)) } fn server_conflict_message(name: &str) -> String { format!( "Command '{}' conflicts with server-provided /{}", name, name ) } pub fn apply_server_command_conflicts( commands: &mut Vec<UserSlashCommand>, errors: &mut Vec<CommandLoadError>, server_command_names: &HashSet<String>, ) { commands.retain(|command| { if server_command_names.contains(command.name.as_ref()) { if !has_error_for_command(errors, command.name.as_ref()) { errors.push(CommandLoadError::from_command( command, server_conflict_message(command.name.as_ref()), )); } false } else { true } }); } pub fn apply_server_command_conflicts_to_map( commands: &mut HashMap<String, UserSlashCommand>, errors: &mut Vec<CommandLoadError>, server_command_names: &HashSet<String>, ) { commands.retain(|name, command| { if server_command_names.contains(name) { if !has_error_for_command(errors, name) { errors.push(CommandLoadError::from_command( command, server_conflict_message(name), )); } false } else { true } }); } /// Parses a line of input to extract a user command invocation.