feat(layouts): edit panes (#1799)
* feat(layouts): edit panes * style(fmt): rustfmt
This commit is contained in:
parent
8c2b576b67
commit
efceb562de
6 changed files with 169 additions and 13 deletions
|
|
@ -536,6 +536,28 @@ impl Pty {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Some(Run::EditFile(path_to_file, line_number)) => {
|
||||||
|
match self.bus.os_input.as_mut().unwrap().spawn_terminal(
|
||||||
|
TerminalAction::OpenFile(path_to_file, line_number),
|
||||||
|
quit_cb,
|
||||||
|
self.default_editor.clone(),
|
||||||
|
) {
|
||||||
|
Ok((terminal_id, pid_primary, child_fd)) => {
|
||||||
|
self.id_to_child_pid.insert(terminal_id, child_fd);
|
||||||
|
new_pane_pids.push((terminal_id, None, Ok(pid_primary)));
|
||||||
|
},
|
||||||
|
Err(SpawnTerminalError::CommandNotFound(terminal_id)) => {
|
||||||
|
new_pane_pids.push((
|
||||||
|
terminal_id,
|
||||||
|
None,
|
||||||
|
Err(SpawnTerminalError::CommandNotFound(terminal_id)),
|
||||||
|
));
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Failed to spawn terminal: {}", e);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
None => {
|
None => {
|
||||||
match self.bus.os_input.as_mut().unwrap().spawn_terminal(
|
match self.bus.os_input.as_mut().unwrap().spawn_terminal(
|
||||||
default_shell.clone(),
|
default_shell.clone(),
|
||||||
|
|
|
||||||
|
|
@ -62,11 +62,15 @@ pub enum Run {
|
||||||
Plugin(RunPlugin),
|
Plugin(RunPlugin),
|
||||||
#[serde(rename = "command")]
|
#[serde(rename = "command")]
|
||||||
Command(RunCommand),
|
Command(RunCommand),
|
||||||
|
EditFile(PathBuf, Option<usize>), // TODO: merge this with TerminalAction::OpenFile
|
||||||
Cwd(PathBuf),
|
Cwd(PathBuf),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Run {
|
impl Run {
|
||||||
pub fn merge(base: &Option<Run>, other: &Option<Run>) -> Option<Run> {
|
pub fn merge(base: &Option<Run>, other: &Option<Run>) -> Option<Run> {
|
||||||
|
// This method is necessary to merge between pane_templates and their consumers
|
||||||
|
// TODO: reconsider the way we parse command/edit/plugin pane_templates from layouts to prevent this
|
||||||
|
// madness
|
||||||
// TODO: handle Plugin variants once there's a need
|
// TODO: handle Plugin variants once there's a need
|
||||||
match (base, other) {
|
match (base, other) {
|
||||||
(Some(Run::Command(base_run_command)), Some(Run::Command(other_run_command))) => {
|
(Some(Run::Command(base_run_command)), Some(Run::Command(other_run_command))) => {
|
||||||
|
|
@ -91,6 +95,16 @@ impl Run {
|
||||||
}
|
}
|
||||||
Some(Run::Command(merged))
|
Some(Run::Command(merged))
|
||||||
},
|
},
|
||||||
|
(
|
||||||
|
Some(Run::Command(base_run_command)),
|
||||||
|
Some(Run::EditFile(file_to_edit, line_number)),
|
||||||
|
) => match &base_run_command.cwd {
|
||||||
|
Some(cwd) => Some(Run::EditFile(cwd.join(&file_to_edit), *line_number)),
|
||||||
|
None => Some(Run::EditFile(file_to_edit.clone(), *line_number)),
|
||||||
|
},
|
||||||
|
(Some(Run::Cwd(cwd)), Some(Run::EditFile(file_to_edit, line_number))) => {
|
||||||
|
Some(Run::EditFile(cwd.join(&file_to_edit), *line_number))
|
||||||
|
},
|
||||||
(Some(_base), Some(other)) => Some(other.clone()),
|
(Some(_base), Some(other)) => Some(other.clone()),
|
||||||
(Some(base), _) => Some(base.clone()),
|
(Some(base), _) => Some(base.clone()),
|
||||||
(None, Some(other)) => Some(other.clone()),
|
(None, Some(other)) => Some(other.clone()),
|
||||||
|
|
|
||||||
|
|
@ -1282,6 +1282,38 @@ fn pane_template_with_bare_propagated_to_its_consumer_command_with_cwd() {
|
||||||
assert_snapshot!(format!("{:#?}", layout));
|
assert_snapshot!(format!("{:#?}", layout));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pane_template_with_bare_propagated_to_its_consumer_edit() {
|
||||||
|
let kdl_layout = r#"
|
||||||
|
layout {
|
||||||
|
cwd "/tmp"
|
||||||
|
pane_template name="tail" {
|
||||||
|
cwd "foo"
|
||||||
|
}
|
||||||
|
tail edit="bar"
|
||||||
|
// pane should have /tmp/foo/bar with the edit file variant
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
|
||||||
|
assert_snapshot!(format!("{:#?}", layout));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pane_template_with_command_propagated_to_its_consumer_edit() {
|
||||||
|
let kdl_layout = r#"
|
||||||
|
layout {
|
||||||
|
cwd "/tmp"
|
||||||
|
pane_template name="tail" command="not-vim" {
|
||||||
|
cwd "foo"
|
||||||
|
}
|
||||||
|
tail edit="bar"
|
||||||
|
// pane should have /tmp/foo/bar with the edit file variant
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
|
||||||
|
assert_snapshot!(format!("{:#?}", layout));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn global_cwd_given_to_panes_without_cwd() {
|
fn global_cwd_given_to_panes_without_cwd() {
|
||||||
let kdl_layout = r#"
|
let kdl_layout = r#"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
---
|
||||||
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
|
assertion_line: 1298
|
||||||
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
|
---
|
||||||
|
Layout {
|
||||||
|
tabs: [],
|
||||||
|
focused_tab_index: None,
|
||||||
|
template: Some(
|
||||||
|
PaneLayout {
|
||||||
|
children_split_direction: Horizontal,
|
||||||
|
name: None,
|
||||||
|
children: [
|
||||||
|
PaneLayout {
|
||||||
|
children_split_direction: Horizontal,
|
||||||
|
name: None,
|
||||||
|
children: [],
|
||||||
|
split_size: None,
|
||||||
|
run: Some(
|
||||||
|
EditFile(
|
||||||
|
"/tmp/foo/bar",
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
borderless: false,
|
||||||
|
focus: None,
|
||||||
|
external_children_index: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
split_size: None,
|
||||||
|
run: None,
|
||||||
|
borderless: false,
|
||||||
|
focus: None,
|
||||||
|
external_children_index: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
---
|
||||||
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
|
assertion_line: 1314
|
||||||
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
|
---
|
||||||
|
Layout {
|
||||||
|
tabs: [],
|
||||||
|
focused_tab_index: None,
|
||||||
|
template: Some(
|
||||||
|
PaneLayout {
|
||||||
|
children_split_direction: Horizontal,
|
||||||
|
name: None,
|
||||||
|
children: [
|
||||||
|
PaneLayout {
|
||||||
|
children_split_direction: Horizontal,
|
||||||
|
name: None,
|
||||||
|
children: [],
|
||||||
|
split_size: None,
|
||||||
|
run: Some(
|
||||||
|
EditFile(
|
||||||
|
"/tmp/foo/bar",
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
borderless: false,
|
||||||
|
focus: None,
|
||||||
|
external_children_index: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
split_size: None,
|
||||||
|
run: None,
|
||||||
|
borderless: false,
|
||||||
|
focus: None,
|
||||||
|
external_children_index: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
@ -48,6 +48,7 @@ impl<'a> KdlLayoutParser<'a> {
|
||||||
|| word == "tab_template"
|
|| word == "tab_template"
|
||||||
|| word == "default_tab_template"
|
|| word == "default_tab_template"
|
||||||
|| word == "command"
|
|| word == "command"
|
||||||
|
|| word == "edit"
|
||||||
|| word == "plugin"
|
|| word == "plugin"
|
||||||
|| word == "children"
|
|| word == "children"
|
||||||
|| word == "tab"
|
|| word == "tab"
|
||||||
|
|
@ -66,6 +67,7 @@ impl<'a> KdlLayoutParser<'a> {
|
||||||
|| property_name == "size"
|
|| property_name == "size"
|
||||||
|| property_name == "plugin"
|
|| property_name == "plugin"
|
||||||
|| property_name == "command"
|
|| property_name == "command"
|
||||||
|
|| property_name == "edit"
|
||||||
|| property_name == "cwd"
|
|| property_name == "cwd"
|
||||||
|| property_name == "args"
|
|| property_name == "args"
|
||||||
|| property_name == "split_direction"
|
|| property_name == "split_direction"
|
||||||
|
|
@ -192,6 +194,8 @@ impl<'a> KdlLayoutParser<'a> {
|
||||||
) -> Result<Option<Run>, ConfigError> {
|
) -> Result<Option<Run>, ConfigError> {
|
||||||
let command = kdl_get_string_property_or_child_value_with_error!(pane_node, "command")
|
let command = kdl_get_string_property_or_child_value_with_error!(pane_node, "command")
|
||||||
.map(|c| PathBuf::from(c));
|
.map(|c| PathBuf::from(c));
|
||||||
|
let edit = kdl_get_string_property_or_child_value_with_error!(pane_node, "edit")
|
||||||
|
.map(|c| PathBuf::from(c));
|
||||||
let cwd = if is_template {
|
let cwd = if is_template {
|
||||||
// we fill the global_cwd for templates later
|
// we fill the global_cwd for templates later
|
||||||
kdl_get_string_property_or_child_value_with_error!(pane_node, "cwd")
|
kdl_get_string_property_or_child_value_with_error!(pane_node, "cwd")
|
||||||
|
|
@ -200,23 +204,30 @@ impl<'a> KdlLayoutParser<'a> {
|
||||||
self.parse_cwd(pane_node)?
|
self.parse_cwd(pane_node)?
|
||||||
};
|
};
|
||||||
let args = self.parse_args(pane_node)?;
|
let args = self.parse_args(pane_node)?;
|
||||||
match (command, cwd, args, is_template) {
|
match (command, edit, cwd, args, is_template) {
|
||||||
(None, Some(cwd), _, _) => Ok(Some(Run::Cwd(cwd))),
|
(None, None, Some(cwd), _, _) => Ok(Some(Run::Cwd(cwd))),
|
||||||
(None, _, Some(_args), false) => Err(ConfigError::new_kdl_error(
|
(None, _, _, Some(_args), false) => Err(ConfigError::new_kdl_error(
|
||||||
"args can only be set if a command was specified".into(),
|
"args can only be set if a command was specified".into(),
|
||||||
pane_node.span().offset(),
|
pane_node.span().offset(),
|
||||||
pane_node.span().len(),
|
pane_node.span().len(),
|
||||||
)),
|
)),
|
||||||
(Some(command), cwd, args, _) => Ok(Some(Run::Command(RunCommand {
|
(Some(command), None, cwd, args, _) => Ok(Some(Run::Command(RunCommand {
|
||||||
command,
|
command,
|
||||||
args: args.unwrap_or_else(|| vec![]),
|
args: args.unwrap_or_else(|| vec![]),
|
||||||
cwd,
|
cwd,
|
||||||
hold_on_close: true,
|
hold_on_close: true,
|
||||||
}))),
|
}))),
|
||||||
|
(None, Some(edit), Some(cwd), _, _) => Ok(Some(Run::EditFile(cwd.join(edit), None))),
|
||||||
|
(None, Some(edit), None, _, _) => Ok(Some(Run::EditFile(edit, None))),
|
||||||
|
(Some(_command), Some(_edit), _, _, _) => Err(ConfigError::new_kdl_error(
|
||||||
|
"cannot have both a command and an edit instruction for the same pane".into(),
|
||||||
|
pane_node.span().offset(),
|
||||||
|
pane_node.span().len(),
|
||||||
|
)),
|
||||||
_ => Ok(None),
|
_ => Ok(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn parse_command_or_plugin_block(
|
fn parse_command_plugin_or_edit_block(
|
||||||
&self,
|
&self,
|
||||||
kdl_node: &KdlNode,
|
kdl_node: &KdlNode,
|
||||||
) -> Result<Option<Run>, ConfigError> {
|
) -> Result<Option<Run>, ConfigError> {
|
||||||
|
|
@ -224,7 +235,7 @@ impl<'a> KdlLayoutParser<'a> {
|
||||||
if let Some(plugin_block) = kdl_get_child!(kdl_node, "plugin") {
|
if let Some(plugin_block) = kdl_get_child!(kdl_node, "plugin") {
|
||||||
if run.is_some() {
|
if run.is_some() {
|
||||||
return Err(ConfigError::new_kdl_error(
|
return Err(ConfigError::new_kdl_error(
|
||||||
"Cannot have both a command and a plugin block for a single pane".into(),
|
"Cannot have both a command/edit and a plugin block for a single pane".into(),
|
||||||
plugin_block.span().offset(),
|
plugin_block.span().offset(),
|
||||||
plugin_block.span().len(),
|
plugin_block.span().len(),
|
||||||
));
|
));
|
||||||
|
|
@ -233,7 +244,7 @@ impl<'a> KdlLayoutParser<'a> {
|
||||||
}
|
}
|
||||||
Ok(run)
|
Ok(run)
|
||||||
}
|
}
|
||||||
fn parse_command_or_plugin_block_for_template(
|
fn parse_command_plugin_or_edit_block_for_template(
|
||||||
&self,
|
&self,
|
||||||
kdl_node: &KdlNode,
|
kdl_node: &KdlNode,
|
||||||
) -> Result<Option<Run>, ConfigError> {
|
) -> Result<Option<Run>, ConfigError> {
|
||||||
|
|
@ -241,7 +252,7 @@ impl<'a> KdlLayoutParser<'a> {
|
||||||
if let Some(plugin_block) = kdl_get_child!(kdl_node, "plugin") {
|
if let Some(plugin_block) = kdl_get_child!(kdl_node, "plugin") {
|
||||||
if run.is_some() {
|
if run.is_some() {
|
||||||
return Err(ConfigError::new_kdl_error(
|
return Err(ConfigError::new_kdl_error(
|
||||||
"Cannot have both a command and a plugin block for a single pane".into(),
|
"Cannot have both a command/edit and a plugin block for a single pane".into(),
|
||||||
plugin_block.span().offset(),
|
plugin_block.span().offset(),
|
||||||
plugin_block.span().len(),
|
plugin_block.span().len(),
|
||||||
));
|
));
|
||||||
|
|
@ -257,7 +268,7 @@ impl<'a> KdlLayoutParser<'a> {
|
||||||
let name = kdl_get_string_property_or_child_value_with_error!(kdl_node, "name")
|
let name = kdl_get_string_property_or_child_value_with_error!(kdl_node, "name")
|
||||||
.map(|name| name.to_string());
|
.map(|name| name.to_string());
|
||||||
let split_size = self.parse_split_size(kdl_node)?;
|
let split_size = self.parse_split_size(kdl_node)?;
|
||||||
let run = self.parse_command_or_plugin_block(kdl_node)?;
|
let run = self.parse_command_plugin_or_edit_block(kdl_node)?;
|
||||||
let children_split_direction = self.parse_split_direction(kdl_node)?;
|
let children_split_direction = self.parse_split_direction(kdl_node)?;
|
||||||
let (external_children_index, children) = match kdl_children_nodes!(kdl_node) {
|
let (external_children_index, children) = match kdl_children_nodes!(kdl_node) {
|
||||||
Some(children) => self.parse_child_pane_nodes_for_pane(&children)?,
|
Some(children) => self.parse_child_pane_nodes_for_pane(&children)?,
|
||||||
|
|
@ -315,7 +326,7 @@ impl<'a> KdlLayoutParser<'a> {
|
||||||
.map(|name| name.to_string());
|
.map(|name| name.to_string());
|
||||||
let args = self.parse_args(kdl_node)?;
|
let args = self.parse_args(kdl_node)?;
|
||||||
let split_size = self.parse_split_size(kdl_node)?;
|
let split_size = self.parse_split_size(kdl_node)?;
|
||||||
let run = self.parse_command_or_plugin_block_for_template(kdl_node)?;
|
let run = self.parse_command_plugin_or_edit_block_for_template(kdl_node)?;
|
||||||
self.assert_no_bare_args_in_pane_node_with_template(
|
self.assert_no_bare_args_in_pane_node_with_template(
|
||||||
&run,
|
&run,
|
||||||
&pane_template.run,
|
&pane_template.run,
|
||||||
|
|
@ -370,6 +381,9 @@ impl<'a> KdlLayoutParser<'a> {
|
||||||
run_command.cwd = Some(global_cwd.clone());
|
run_command.cwd = Some(global_cwd.clone());
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Some(Run::EditFile(path_to_file, _line_number)) => {
|
||||||
|
*path_to_file = global_cwd.join(&path_to_file);
|
||||||
|
},
|
||||||
Some(Run::Cwd(pane_template_cwd)) => {
|
Some(Run::Cwd(pane_template_cwd)) => {
|
||||||
*pane_template_cwd = global_cwd.join(&pane_template_cwd);
|
*pane_template_cwd = global_cwd.join(&pane_template_cwd);
|
||||||
},
|
},
|
||||||
|
|
@ -406,7 +420,7 @@ impl<'a> KdlLayoutParser<'a> {
|
||||||
let borderless = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "borderless");
|
let borderless = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "borderless");
|
||||||
let focus = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "focus");
|
let focus = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "focus");
|
||||||
let split_size = self.parse_split_size(kdl_node)?;
|
let split_size = self.parse_split_size(kdl_node)?;
|
||||||
let run = self.parse_command_or_plugin_block(kdl_node)?;
|
let run = self.parse_command_plugin_or_edit_block(kdl_node)?;
|
||||||
let children_split_direction = self.parse_split_direction(kdl_node)?;
|
let children_split_direction = self.parse_split_direction(kdl_node)?;
|
||||||
let (external_children_index, pane_parts) = match kdl_children_nodes!(kdl_node) {
|
let (external_children_index, pane_parts) = match kdl_children_nodes!(kdl_node) {
|
||||||
Some(children) => self.parse_child_pane_nodes_for_pane(&children)?,
|
Some(children) => self.parse_child_pane_nodes_for_pane(&children)?,
|
||||||
|
|
@ -625,7 +639,7 @@ impl<'a> KdlLayoutParser<'a> {
|
||||||
let has_cwd_prop =
|
let has_cwd_prop =
|
||||||
kdl_get_string_property_or_child_value_with_error!(kdl_node, "cwd").is_some();
|
kdl_get_string_property_or_child_value_with_error!(kdl_node, "cwd").is_some();
|
||||||
let has_non_cwd_run_prop = self
|
let has_non_cwd_run_prop = self
|
||||||
.parse_command_or_plugin_block(kdl_node)?
|
.parse_command_plugin_or_edit_block(kdl_node)?
|
||||||
.map(|r| match r {
|
.map(|r| match r {
|
||||||
Run::Cwd(_) => false,
|
Run::Cwd(_) => false,
|
||||||
_ => true,
|
_ => true,
|
||||||
|
|
@ -643,7 +657,7 @@ impl<'a> KdlLayoutParser<'a> {
|
||||||
offending_nodes.push("focus");
|
offending_nodes.push("focus");
|
||||||
}
|
}
|
||||||
if has_non_cwd_run_prop {
|
if has_non_cwd_run_prop {
|
||||||
offending_nodes.push("command/plugin");
|
offending_nodes.push("command/edit/plugin");
|
||||||
}
|
}
|
||||||
if has_cwd_prop {
|
if has_cwd_prop {
|
||||||
offending_nodes.push("cwd");
|
offending_nodes.push("cwd");
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue