Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(plugins): allow loading relative urls #2539

Merged
merged 2 commits into from
Jun 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
---
source: zellij-server/src/plugins/./unit/plugin_tests.rs
assertion_line: 2496
assertion_line: 2889
expression: "format!(\"{:#?}\", new_tab_event)"
---
Some(
StartOrReloadPluginPane(
File(
"/path/to/my/plugin.wasm",
),
RunPlugin {
_allow_exec_host_cmd: false,
location: File(
"/path/to/my/plugin.wasm",
),
},
None,
),
)
13 changes: 11 additions & 2 deletions zellij-server/src/plugins/zellij_exports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use zellij_utils::{
input::{
actions::Action,
command::{RunCommand, RunCommandAction, TerminalAction},
layout::Layout,
layout::{Layout, RunPlugin, RunPluginLocation},
plugins::PluginType,
},
serde,
Expand Down Expand Up @@ -946,10 +946,19 @@ fn host_start_or_reload_plugin(env: &ForeignFunctionEnv) {
env.plugin_env.name()
)
};
let cwd = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
wasi_read_string(&env.plugin_env.wasi_env)
.and_then(|url| Url::parse(&url).map_err(|e| anyhow!("Failed to parse url: {}", e)))
.and_then(|url| {
let action = Action::StartOrReloadPlugin(url);
RunPluginLocation::parse(url.as_str(), Some(cwd))
.map_err(|e| anyhow!("Failed to parse plugin location: {}", e))
})
.and_then(|run_plugin_location| {
let run_plugin = RunPlugin {
location: run_plugin_location,
_allow_exec_host_cmd: false,
};
let action = Action::StartOrReloadPlugin(run_plugin);
apply_action!(action, error_msg, env);
Ok(())
})
Expand Down
11 changes: 3 additions & 8 deletions zellij-server/src/route.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use zellij_utils::{
actions::{Action, SearchDirection, SearchOption},
command::TerminalAction,
get_mode_info,
layout::{Layout, RunPluginLocation},
layout::Layout,
},
ipc::{
ClientAttributes, ClientToServerMsg, ExitReason, IpcReceiverWithContext, ServerToClientMsg,
Expand Down Expand Up @@ -615,14 +615,9 @@ pub(crate) fn route_action(
))
.with_context(err_context)?;
},
Action::StartOrReloadPlugin(url) => {
let run_plugin_location =
RunPluginLocation::parse(url.as_str()).with_context(err_context)?;
Action::StartOrReloadPlugin(run_plugin) => {
senders
.send_to_screen(ScreenInstruction::StartOrReloadPluginPane(
run_plugin_location,
None,
))
.send_to_screen(ScreenInstruction::StartOrReloadPluginPane(run_plugin, None))
.with_context(err_context)?;
},
Action::LaunchOrFocusPlugin(run_plugin, should_float) => {
Expand Down
8 changes: 2 additions & 6 deletions zellij-server/src/screen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ pub enum ScreenInstruction {
NewTiledPluginPane(RunPluginLocation, Option<String>, ClientId), // Option<String> is
// optional pane title
NewFloatingPluginPane(RunPluginLocation, Option<String>, ClientId), // Option<String> is an
StartOrReloadPluginPane(RunPluginLocation, Option<String>),
StartOrReloadPluginPane(RunPlugin, Option<String>),
// optional pane title
AddPlugin(
Option<bool>, // should_float
Expand Down Expand Up @@ -2573,14 +2573,10 @@ pub(crate) fn screen_thread_main(
size,
))?;
},
ScreenInstruction::StartOrReloadPluginPane(run_plugin_location, pane_title) => {
ScreenInstruction::StartOrReloadPluginPane(run_plugin, pane_title) => {
let tab_index = screen.active_tab_indices.values().next().unwrap_or(&1);
let size = Size::default();
let should_float = Some(false);
let run_plugin = RunPlugin {
_allow_exec_host_cmd: false,
location: run_plugin_location,
};
screen
.bus
.senders
Expand Down
2 changes: 1 addition & 1 deletion zellij-utils/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ pub enum CliAction {
/// Query all tab names
QueryTabNames,
StartOrReloadPlugin {
url: Url,
url: String,
},
LaunchOrFocusPlugin {
#[clap(short, long, value_parser)]
Expand Down
29 changes: 19 additions & 10 deletions zellij-utils/src/input/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ use serde::{Deserialize, Serialize};

use std::path::PathBuf;
use std::str::FromStr;
use url::Url;

use crate::position::Position;

Expand Down Expand Up @@ -233,7 +232,7 @@ pub enum Action {
/// Open a new tiled (embedded, non-floating) plugin pane
NewTiledPluginPane(RunPluginLocation, Option<String>), // String is an optional name
NewFloatingPluginPane(RunPluginLocation, Option<String>), // String is an optional name
StartOrReloadPlugin(Url),
StartOrReloadPlugin(RunPlugin),
}

impl Action {
Expand Down Expand Up @@ -287,14 +286,18 @@ impl Action {
close_on_exit,
start_suspended,
} => {
let current_dir = get_current_dir();
let cwd = cwd
.map(|cwd| current_dir.join(cwd))
.or_else(|| Some(current_dir));
if let Some(plugin) = plugin {
if floating {
let plugin = RunPluginLocation::parse(&plugin).map_err(|e| {
let plugin = RunPluginLocation::parse(&plugin, cwd).map_err(|e| {
format!("Failed to parse plugin loction {plugin}: {}", e)
})?;
Ok(vec![Action::NewFloatingPluginPane(plugin, name)])
} else {
let plugin = RunPluginLocation::parse(&plugin).map_err(|e| {
let plugin = RunPluginLocation::parse(&plugin, cwd).map_err(|e| {
format!("Failed to parse plugin location {plugin}: {}", e)
})?;
// it is intentional that a new tiled plugin pane cannot include a
Expand All @@ -310,10 +313,6 @@ impl Action {
} else if !command.is_empty() {
let mut command = command.clone();
let (command, args) = (PathBuf::from(command.remove(0)), command);
let current_dir = get_current_dir();
let cwd = cwd
.map(|cwd| current_dir.join(cwd))
.or_else(|| Some(current_dir));
let hold_on_start = start_suspended;
let hold_on_close = !close_on_exit;
let run_command_action = RunCommandAction {
Expand Down Expand Up @@ -474,9 +473,19 @@ impl Action {
CliAction::PreviousSwapLayout => Ok(vec![Action::PreviousSwapLayout]),
CliAction::NextSwapLayout => Ok(vec![Action::NextSwapLayout]),
CliAction::QueryTabNames => Ok(vec![Action::QueryTabNames]),
CliAction::StartOrReloadPlugin { url } => Ok(vec![Action::StartOrReloadPlugin(url)]),
CliAction::StartOrReloadPlugin { url } => {
let current_dir = get_current_dir();
let run_plugin_location = RunPluginLocation::parse(&url, Some(current_dir))
.map_err(|e| format!("Failed to parse plugin location: {}", e))?;
let run_plugin = RunPlugin {
location: run_plugin_location,
_allow_exec_host_cmd: false,
};
Ok(vec![Action::StartOrReloadPlugin(run_plugin)])
},
CliAction::LaunchOrFocusPlugin { url, floating } => {
let run_plugin_location = RunPluginLocation::parse(url.as_str())
let current_dir = get_current_dir();
let run_plugin_location = RunPluginLocation::parse(url.as_str(), Some(current_dir))
.map_err(|e| format!("Failed to parse plugin location: {}", e))?;
let run_plugin = RunPlugin {
location: run_plugin_location,
Expand Down
23 changes: 18 additions & 5 deletions zellij-utils/src/input/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ pub enum RunPluginLocation {
}

impl RunPluginLocation {
pub fn parse(location: &str) -> Result<Self, PluginsConfigError> {
pub fn parse(location: &str, cwd: Option<PathBuf>) -> Result<Self, PluginsConfigError> {
let url = Url::parse(location)?;

let decoded_path = percent_encoding::percent_decode_str(url.path()).decode_utf8_lossy();
Expand All @@ -233,16 +233,29 @@ impl RunPluginLocation {
// Path is absolute, its safe to use URL path.
//
// This is the case if the scheme and : delimiter are followed by a / slash
decoded_path
PathBuf::from(decoded_path.as_ref())
} else if location.starts_with("file:~") {
// Unwrap is safe here since location is a valid URL
PathBuf::from(location.strip_prefix("file:").unwrap())
} else {
// URL dep doesn't handle relative paths with `file` schema properly,
// it always makes them absolute. Use raw location string instead.
//
// Unwrap is safe here since location is a valid URL
location.strip_prefix("file:").unwrap().into()
let stripped = location.strip_prefix("file:").unwrap();
match cwd {
Some(cwd) => cwd.join(stripped),
None => PathBuf::from(stripped),
}
};

Ok(Self::File(PathBuf::from(path.as_ref())))
let path = match shellexpand::full(&path.to_string_lossy().to_string()) {
Ok(s) => PathBuf::from(s.as_ref()),
Err(e) => {
log::error!("Failed to shell expand plugin path: {}", e);
path
},
};
Ok(Self::File(path))
},
_ => Err(PluginsConfigError::InvalidUrlScheme(url)),
}
Expand Down
15 changes: 8 additions & 7 deletions zellij-utils/src/kdl/kdl_layout_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,13 +297,14 @@ impl<'a> KdlLayoutParser<'a> {
plugin_block.span().len(),
),
)?;
let location = RunPluginLocation::parse(&string_url).map_err(|e| {
ConfigError::new_layout_kdl_error(
e.to_string(),
url_node.span().offset(),
url_node.span().len(),
)
})?;
let location =
RunPluginLocation::parse(&string_url, self.cwd_prefix(None)?).map_err(|e| {
ConfigError::new_layout_kdl_error(
e.to_string(),
url_node.span().offset(),
url_node.span().len(),
)
})?;
Ok(Some(Run::Plugin(RunPlugin {
_allow_exec_host_cmd,
location,
Expand Down
7 changes: 4 additions & 3 deletions zellij-utils/src/kdl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -892,7 +892,8 @@ impl TryFrom<(&KdlNode, &Options)> for Action {
let should_float = command_metadata
.and_then(|c_m| kdl_child_bool_value_for_entry(c_m, "floating"))
.unwrap_or(false);
let location = RunPluginLocation::parse(&plugin_path)?;
let current_dir = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
let location = RunPluginLocation::parse(&plugin_path, Some(current_dir))?;
let run_plugin = RunPlugin {
location,
_allow_exec_host_cmd: false,
Expand Down Expand Up @@ -1402,7 +1403,7 @@ impl Options {
}

impl RunPlugin {
pub fn from_kdl(kdl_node: &KdlNode) -> Result<Self, ConfigError> {
pub fn from_kdl(kdl_node: &KdlNode, cwd: Option<PathBuf>) -> Result<Self, ConfigError> {
let _allow_exec_host_cmd =
kdl_get_child_entry_bool_value!(kdl_node, "_allow_exec_host_cmd").unwrap_or(false);
let string_url = kdl_get_child_entry_string_value!(kdl_node, "location").ok_or(
Expand All @@ -1412,7 +1413,7 @@ impl RunPlugin {
kdl_node.span().len(),
),
)?;
let location = RunPluginLocation::parse(string_url).map_err(|e| {
let location = RunPluginLocation::parse(string_url, cwd).map_err(|e| {
ConfigError::new_layout_kdl_error(
e.to_string(),
kdl_node.span().offset(),
Expand Down