Skip to content
Open
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
8 changes: 8 additions & 0 deletions app/src/terminal/model/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2219,6 +2219,14 @@ impl Block {
.contents_to_string_force_full_grid_contents(false, None)
}

pub fn command_and_output_to_string(&self) -> String {
if self.honor_ps1() {
self.bounds_to_string(self.start_point(), self.end_point())
} else {
format!("{}\n{}", self.command_to_string(), self.output_to_string())
}
}

pub fn output_with_secrets_unobfuscated(&self) -> String {
self.output_grid()
.contents_to_string_with_secrets_unobfuscated(false, None)
Expand Down
56 changes: 56 additions & 0 deletions app/src/terminal/model/block_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -839,6 +839,62 @@ fn test_selection_bounds_all_grids_single_line_lprompt_command() {
assert_eq!(all_grids, "lprompt%cmd1\nrprompt\noutput1\noutput2");
}

#[test]
fn test_command_and_output_to_string_includes_ps1_prompt_command_rprompt_and_output() {
let block_index = BlockIndex::zero();
let mut prompt_and_command_grid = mock_blockgrid("lprompt%cmd1");
prompt_and_command_grid.finish();
let mut rprompt_grid = mock_blockgrid("rprompt");
rprompt_grid.finish();
let mut output_grid = mock_blockgrid("output1\r\noutput2\r\n");
output_grid.finish();

let mut block = create_test_block_with_grids(
block_index,
prompt_and_command_grid,
rprompt_grid,
output_grid,
true, /* honor_ps1 */
);
block.set_raw_prompt_end_point(Some(PromptEndPoint::PromptEnd {
point: Point::new(0, 7),
has_extra_trailing_newline: false,
}));

assert_eq!(
block.command_and_output_to_string(),
"lprompt%cmd1\nrprompt\noutput1\noutput2"
);
}

#[test]
fn test_command_and_output_to_string_excludes_warp_prompt() {
let block_index = BlockIndex::zero();
let mut prompt_and_command_grid = mock_blockgrid("cmd1");
prompt_and_command_grid.finish();
let mut rprompt_grid = mock_blockgrid("rprompt");
rprompt_grid.finish();
let mut output_grid = mock_blockgrid("output1\r\noutput2\r\n");
output_grid.finish();

let mut block = create_test_block_with_grids(
block_index,
prompt_and_command_grid,
rprompt_grid,
output_grid,
false, /* honor_ps1 */
);
block.set_honor_ps1(false);
let mut prompt_grid = mock_blockgrid("warp_prompt");
prompt_grid.finish();
block.set_prompt_grid(prompt_grid);

assert_eq!(
block.command_and_output_to_string(),
"cmd1\noutput1\noutput2"
);
}

/// Tests the single line lprompt, with trailing newline, and command case for text selection across grids.
#[test]
fn test_selection_bounds_all_grids_single_line_lprompt_trailing_newline_command() {
Expand Down
6 changes: 1 addition & 5 deletions app/src/terminal/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20772,11 +20772,7 @@ impl TerminalView {
let block_str = match entity {
BlockEntity::Command => block.command_to_string(),
BlockEntity::Output => block.output_to_string_force_full_grid_contents(),
BlockEntity::CommandAndOutput => format!(
"{}\n{}",
block.command_to_string(),
block.output_to_string(),
),
BlockEntity::CommandAndOutput => block.command_and_output_to_string(),
BlockEntity::FilteredOutput => block.output_to_string(),
};

Expand Down
2 changes: 2 additions & 0 deletions crates/integration/src/bin/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,8 @@ fn register_tests() -> HashMap<&'static str, BoxedBuilderFn> {
register_test!(test_color_overrides_in_prompt_dont_crash);
register_test!(test_copy_prompt_from_block_honor_ps1_disabled);
register_test!(test_copy_prompt_from_block_honor_ps1_enabled);
register_test!(test_copy_block_command_and_output_honor_ps1_disabled);
register_test!(test_copy_block_command_and_output_honor_ps1_enabled);
register_test!(test_copy_prompt_from_input_honor_ps1_disabled);
register_test!(test_warp_prompt_unsets_zsh_rprompt);
register_test!(test_copy_prompt_from_input_honor_ps1_enabled);
Expand Down
95 changes: 95 additions & 0 deletions crates/integration/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5845,6 +5845,101 @@ function prompt {{
)
}

pub fn test_copy_block_command_and_output_honor_ps1_disabled() -> Builder {
let command = "echo WARP_COPY_E2E_OUTPUT";
new_builder()
.use_tmp_filesystem_for_test_root_directory()
.with_step(wait_until_bootstrapped_single_pane_for_tab(0))
.with_step(execute_command_for_single_terminal_in_tab(
0,
command.into(),
ExpectedExitStatus::Success,
(),
))
.with_step(
new_step_with_default_assertions("Select last block")
.with_keystrokes(&["cmdorctrl-up"])
.add_assertion(assert_selected_block_index_is_last_renderable()),
)
.with_steps(open_context_menu_for_selected_block())
.with_step(
new_step_with_default_assertions("Copy block copies command and output")
.with_click_on_saved_position("Copy")
.add_assertion(assert_clipboard_contains_string(format!(
"{command}\nWARP_COPY_E2E_OUTPUT"
))),
)
}

pub fn test_copy_block_command_and_output_honor_ps1_enabled() -> Builder {
let prompt_text = "this is my custom prompt";
let command = "echo WARP_PS1_COPY_E2E_OUTPUT";
new_builder()
// TODO(CORE-2732): Flakey on linux
.set_should_run_test(skip_if_powershell_core_2303)
.with_user_defaults(HashMap::from([(
HonorPS1::storage_key().to_owned(),
true.to_string(),
)]))
.with_setup(move |utils| {
let dir = utils.test_dir();
write_rc_files_for_test(
&dir,
format!(r#"export PS1="{prompt_text}""#),
[ShellRcType::Bash, ShellRcType::Zsh],
);
write_rc_files_for_test(
&dir,
format!(
r#"
function fish_prompt
echo -n "{prompt_text}"
end
"#
),
[ShellRcType::Fish],
);
write_rc_files_for_test(
&dir,
format!(
r#"
function prompt {{
"{prompt_text}"
}}
"#
),
[ShellRcType::PowerShell],
)
})
.with_step(
wait_until_bootstrapped_single_pane_for_tab(0).add_assertion(move |app, window_id| {
let input = single_input_view_for_tab(app, window_id, 0);
let input_text = input.read(app, |input, ctx| input.prompt_and_rprompt_text(ctx).0);

async_assert_eq!(input_text, prompt_text)
}),
)
.with_step(execute_command_for_single_terminal_in_tab(
0,
command.into(),
ExpectedExitStatus::Success,
(),
))
.with_step(
new_step_with_default_assertions("Select last block")
.with_keystrokes(&["cmdorctrl-up"])
.add_assertion(assert_selected_block_index_is_last_renderable()),
)
.with_steps(open_context_menu_for_selected_block())
.with_step(
new_step_with_default_assertions("Copy block includes PS1 prompt, command, and output")
.with_click_on_saved_position("Copy")
.add_assertion(assert_clipboard_contains_string(format!(
"{prompt_text}{command}\nWARP_PS1_COPY_E2E_OUTPUT"
))),
)
}

pub fn test_copy_prompt_from_input_honor_ps1_disabled() -> Builder {
new_builder()
.use_tmp_filesystem_for_test_root_directory()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ integration_tests! {
// Tests of custom prompt behavior.
test_copy_prompt_from_block_honor_ps1_enabled,
test_copy_prompt_from_input_honor_ps1_enabled,
test_copy_block_command_and_output_honor_ps1_disabled,
test_copy_block_command_and_output_honor_ps1_enabled,
// Tests zsh-specific right-prompt behavior in Warp prompt mode.
test_warp_prompt_unsets_zsh_rprompt,

Expand Down