Skip to content

Commit

Permalink
feat(layouts): edit panes (#1799)
Browse files Browse the repository at this point in the history
* feat(layouts): edit panes

* style(fmt): rustfmt
  • Loading branch information
imsnif authored Oct 14, 2022
1 parent 8c2b576 commit efceb56
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 13 deletions.
22 changes: 22 additions & 0 deletions zellij-server/src/pty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
match self.bus.os_input.as_mut().unwrap().spawn_terminal(
default_shell.clone(),
Expand Down
14 changes: 14 additions & 0 deletions zellij-utils/src/input/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,15 @@ pub enum Run {
Plugin(RunPlugin),
#[serde(rename = "command")]
Command(RunCommand),
EditFile(PathBuf, Option<usize>), // TODO: merge this with TerminalAction::OpenFile
Cwd(PathBuf),
}

impl 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
match (base, other) {
(Some(Run::Command(base_run_command)), Some(Run::Command(other_run_command))) => {
Expand All @@ -91,6 +95,16 @@ impl Run {
}
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(base.clone()),
(None, Some(other)) => Some(other.clone()),
Expand Down
32 changes: 32 additions & 0 deletions zellij-utils/src/input/unit/layout_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1282,6 +1282,38 @@ fn pane_template_with_bare_propagated_to_its_consumer_command_with_cwd() {
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]
fn global_cwd_given_to_panes_without_cwd() {
let kdl_layout = r#"
Expand Down
Original file line number Diff line number Diff line change
@@ -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,
},
),
}
Original file line number Diff line number Diff line change
@@ -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,
},
),
}
40 changes: 27 additions & 13 deletions zellij-utils/src/kdl/kdl_layout_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ impl<'a> KdlLayoutParser<'a> {
|| word == "tab_template"
|| word == "default_tab_template"
|| word == "command"
|| word == "edit"
|| word == "plugin"
|| word == "children"
|| word == "tab"
Expand All @@ -66,6 +67,7 @@ impl<'a> KdlLayoutParser<'a> {
|| property_name == "size"
|| property_name == "plugin"
|| property_name == "command"
|| property_name == "edit"
|| property_name == "cwd"
|| property_name == "args"
|| property_name == "split_direction"
Expand Down Expand Up @@ -192,6 +194,8 @@ impl<'a> KdlLayoutParser<'a> {
) -> Result<Option<Run>, ConfigError> {
let command = kdl_get_string_property_or_child_value_with_error!(pane_node, "command")
.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 {
// we fill the global_cwd for templates later
kdl_get_string_property_or_child_value_with_error!(pane_node, "cwd")
Expand All @@ -200,31 +204,38 @@ impl<'a> KdlLayoutParser<'a> {
self.parse_cwd(pane_node)?
};
let args = self.parse_args(pane_node)?;
match (command, cwd, args, is_template) {
(None, Some(cwd), _, _) => Ok(Some(Run::Cwd(cwd))),
(None, _, Some(_args), false) => Err(ConfigError::new_kdl_error(
match (command, edit, cwd, args, is_template) {
(None, None, Some(cwd), _, _) => Ok(Some(Run::Cwd(cwd))),
(None, _, _, Some(_args), false) => Err(ConfigError::new_kdl_error(
"args can only be set if a command was specified".into(),
pane_node.span().offset(),
pane_node.span().len(),
)),
(Some(command), cwd, args, _) => Ok(Some(Run::Command(RunCommand {
(Some(command), None, cwd, args, _) => Ok(Some(Run::Command(RunCommand {
command,
args: args.unwrap_or_else(|| vec![]),
cwd,
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),
}
}
fn parse_command_or_plugin_block(
fn parse_command_plugin_or_edit_block(
&self,
kdl_node: &KdlNode,
) -> Result<Option<Run>, ConfigError> {
let mut run = self.parse_pane_command(kdl_node, false)?;
if let Some(plugin_block) = kdl_get_child!(kdl_node, "plugin") {
if run.is_some() {
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().len(),
));
Expand All @@ -233,15 +244,15 @@ impl<'a> KdlLayoutParser<'a> {
}
Ok(run)
}
fn parse_command_or_plugin_block_for_template(
fn parse_command_plugin_or_edit_block_for_template(
&self,
kdl_node: &KdlNode,
) -> Result<Option<Run>, ConfigError> {
let mut run = self.parse_pane_command(kdl_node, true)?;
if let Some(plugin_block) = kdl_get_child!(kdl_node, "plugin") {
if run.is_some() {
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().len(),
));
Expand All @@ -257,7 +268,7 @@ impl<'a> KdlLayoutParser<'a> {
let name = kdl_get_string_property_or_child_value_with_error!(kdl_node, "name")
.map(|name| name.to_string());
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 (external_children_index, children) = match kdl_children_nodes!(kdl_node) {
Some(children) => self.parse_child_pane_nodes_for_pane(&children)?,
Expand Down Expand Up @@ -315,7 +326,7 @@ impl<'a> KdlLayoutParser<'a> {
.map(|name| name.to_string());
let args = self.parse_args(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(
&run,
&pane_template.run,
Expand Down Expand Up @@ -370,6 +381,9 @@ impl<'a> KdlLayoutParser<'a> {
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)) => {
*pane_template_cwd = global_cwd.join(&pane_template_cwd);
},
Expand Down Expand Up @@ -406,7 +420,7 @@ impl<'a> KdlLayoutParser<'a> {
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 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 (external_children_index, pane_parts) = match kdl_children_nodes!(kdl_node) {
Some(children) => self.parse_child_pane_nodes_for_pane(&children)?,
Expand Down Expand Up @@ -625,7 +639,7 @@ impl<'a> KdlLayoutParser<'a> {
let has_cwd_prop =
kdl_get_string_property_or_child_value_with_error!(kdl_node, "cwd").is_some();
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 {
Run::Cwd(_) => false,
_ => true,
Expand All @@ -643,7 +657,7 @@ impl<'a> KdlLayoutParser<'a> {
offending_nodes.push("focus");
}
if has_non_cwd_run_prop {
offending_nodes.push("command/plugin");
offending_nodes.push("command/edit/plugin");
}
if has_cwd_prop {
offending_nodes.push("cwd");
Expand Down

0 comments on commit efceb56

Please sign in to comment.