diff --git a/src/launch.rs b/src/launch.rs index 712407c..20c9fd6 100644 --- a/src/launch.rs +++ b/src/launch.rs @@ -142,7 +142,11 @@ impl Setup { /// Launches vscode with the given configuration. /// Returns the dev container that was used, if any. - pub fn launch(self, config: Option) -> Result> { + pub fn launch( + self, + config: Option, + subfolder: Option, + ) -> Result> { let editor_name = format_editor_name(&self.behavior.command); match self.behavior.strategy { @@ -156,6 +160,7 @@ impl Setup { self.dry_run, dev_container, &self.behavior.command, + subfolder.as_deref(), )?; } else { info!("No dev container found, opening on host system with {editor_name}..."); @@ -177,6 +182,7 @@ impl Setup { self.dry_run, dev_container, &self.behavior.command, + subfolder.as_deref(), )?; } else { bail!( diff --git a/src/main.rs b/src/main.rs index 48aed19..3985fc8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -58,8 +58,56 @@ fn main() -> Result<()> { match opts.command { opts::Commands::Open { path, launch } => { // Get workspace from args - let path = path.as_path(); - let ws = Workspace::from_path(path)?; + // If a custom config is specified, derive workspace from config location + let (workspace_path, subfolder) = if let Some(ref config) = launch.config { + // Get the parent directory of the config file + let config_parent = config + .parent() + .ok_or_else(|| color_eyre::eyre::eyre!("Invalid config path"))?; + + // If config is in .devcontainer folder, use its parent as workspace + let workspace_root = if config_parent + .file_name() + .map(|n| n == ".devcontainer") + .unwrap_or(false) + { + config_parent + .parent() + .ok_or_else(|| { + color_eyre::eyre::eyre!("Invalid config path structure") + })? + } else { + config_parent + }; + + // Handle the path argument - it might be a container path or relative path + let path_str = path.to_string_lossy(); + let subfolder_path = if path_str.starts_with("/workspace/") || path_str.starts_with("/workspaces/") { + // Extract subfolder from container path + // e.g., "/workspace/vscli/tests" or "/workspaces/vscli/tests" + let parts: Vec<&str> = path_str.split('/').collect(); + if parts.len() > 3 { + // Skip empty, "workspace(s)", and workspace name + Some(std::path::PathBuf::from(parts[3..].join("/"))) + } else { + None + } + } else if path.is_relative() && path != std::path::Path::new(".") { + // Use relative path as-is + Some(path.to_path_buf()) + } else if path.starts_with(workspace_root) { + // Calculate relative path from workspace root + path.strip_prefix(workspace_root).ok().map(|p| p.to_path_buf()) + } else { + None + }; + + (workspace_root.to_path_buf(), subfolder_path) + } else { + (path.to_path_buf(), None) + }; + + let ws = Workspace::from_path(&workspace_path)?; let ws_name = ws.name.clone(); // Open the container @@ -69,7 +117,7 @@ fn main() -> Result<()> { command: launch.command.unwrap_or_else(|| "code".to_string()), }; let setup = Setup::new(ws, behavior.clone(), opts.dry_run); - let dev_container = setup.launch(launch.config)?; + let dev_container = setup.launch(launch.config, subfolder)?; // Store the workspace in the history tracker.history.upsert(Entry { @@ -114,7 +162,7 @@ fn main() -> Result<()> { // Open the container let setup = Setup::new(ws, entry.behavior.clone(), opts.dry_run); - let dev_container = setup.launch(entry.config_path)?; + let dev_container = setup.launch(entry.config_path, None)?; // Update the tracker entry tracker.history.update( diff --git a/src/workspace.rs b/src/workspace.rs index 00c8948..9e02132 100644 --- a/src/workspace.rs +++ b/src/workspace.rs @@ -192,6 +192,7 @@ impl Workspace { dry_run: bool, dev_container: &DevContainer, command: &str, + subfolder: Option<&Path>, ) -> Result<()> { // Checking if '--folder-uri' is present in the arguments if args.iter().any(|arg| arg == "--folder-uri") { @@ -199,7 +200,19 @@ impl Workspace { } // get the folder path from the selected dev container - let container_folder: String = dev_container.workspace_path_in_container.clone(); + let mut container_folder: String = dev_container.workspace_path_in_container.clone(); + + // Append subfolder if specified + if let Some(subfolder) = subfolder { + let subfolder_str = subfolder.to_string_lossy(); + if !subfolder_str.is_empty() && subfolder_str != "." { + // Ensure proper path separator + if !container_folder.ends_with('/') { + container_folder.push('/'); + } + container_folder.push_str(&subfolder_str); + } + } let mut ws_path: String = self.path.to_string_lossy().into_owned(); let mut dc_path: String = dev_container.config_path.to_string_lossy().into_owned();