main
1diff --git a/src/context/mod.rs b/src/context/mod.rs
2index 230e162..943d57b 100644
3--- a/src/context/mod.rs
4+++ b/src/context/mod.rs
5@@ -73,6 +73,9 @@ pub struct Context<'a> {
6 /// Which prompt to print (main, right, ...)
7 pub target: Target,
8
9+ /// Whether the right prompt is allowed to span multiple lines
10+ pub multiline: bool,
11+
12 /// Width of terminal, or zero if width cannot be detected.
13 pub width: usize,
14
15@@ -189,6 +192,7 @@ impl<'a> Context<'a> {
16 jj_repo: OnceLock::new(),
17 shell,
18 target,
19+ multiline: false,
20 width,
21 env,
22 #[cfg(test)]
23@@ -212,6 +216,12 @@ impl<'a> Context<'a> {
24 self
25 }
26
27+ /// Allow the right prompt to span multiple lines
28+ pub fn with_multiline(mut self, multiline: bool) -> Self {
29+ self.multiline = multiline;
30+ self
31+ }
32+
33 /// Sets the Claude Code session data
34 pub fn with_claude_code_data(mut self, data: ClaudeCodeData) -> Self {
35 self.claude_code_data = Some(Box::new(data));
36diff --git a/src/init/starship.bash b/src/init/starship.bash
37index 0355c60..e2e838a 100644
38--- a/src/init/starship.bash
39+++ b/src/init/starship.bash
40@@ -82,7 +82,10 @@ starship_precmd() {
41 if [[ ${BLE_ATTACHED-} ]]; then
42 bleopt prompt_rps1="$nlns$(::STARSHIP:: prompt --right "${ARGS[@]}")"
43 elif [[ ${FLYLINE_VERSION-} ]]; then
44- RPS1="$nlns$(::STARSHIP:: prompt --right "${ARGS[@]}")"
45+ local rps1 rnlns
46+ rps1="$(::STARSHIP:: prompt --right --multiline "${ARGS[@]}")"
47+ rnlns=${rps1//[!$'\n']}
48+ RPS1="${nlns:${#rnlns}}$rps1"
49 fi
50 STARSHIP_PREEXEC_READY=true # Signal that we can safely restart the timer
51 }
52diff --git a/src/main.rs b/src/main.rs
53index 204ee40..8d9e6a6 100644
54--- a/src/main.rs
55+++ b/src/main.rs
56@@ -127,6 +127,9 @@ enum Commands {
57 /// Print the right prompt (instead of the standard left prompt)
58 #[clap(long)]
59 right: bool,
60+ /// Allow the right prompt to span multiple lines
61+ #[clap(long, requires = "right")]
62+ multiline: bool,
63 /// Print the prompt with the specified profile name (instead of the standard left prompt)
64 #[clap(long, conflicts_with = "right")]
65 profile: Option<String>,
66@@ -227,6 +230,7 @@ fn main() {
67 Commands::Prompt {
68 properties,
69 right,
70+ multiline,
71 profile,
72 continuation,
73 } => {
74@@ -236,7 +240,7 @@ fn main() {
75 (_, _, true) => Target::Continuation,
76 (_, _, _) => Target::Main,
77 };
78- print::prompt(properties, target);
79+ print::prompt(properties, target, multiline);
80 }
81 Commands::Module {
82 name,
83diff --git a/src/print.rs b/src/print.rs
84index a173e71..5e41760 100644
85--- a/src/print.rs
86+++ b/src/print.rs
87@@ -69,8 +69,8 @@ fn test_grapheme_aware_width() {
88 assert_eq!(11, "\x1B[35;6mnormal text".width_graphemes());
89 }
90
91-pub fn prompt(args: Properties, target: Target) {
92- let context = Context::new(args, target);
93+pub fn prompt(args: Properties, target: Target, multiline: bool) {
94+ let context = Context::new(args, target).with_multiline(multiline);
95 let stdout = io::stdout();
96 let mut handle = stdout.lock();
97
98@@ -143,7 +143,7 @@ pub fn get_prompt(context: &Context) -> String {
99 );
100
101 let module_strings = root_module.ansi_strings_for_width(Some(context.width));
102- if config.add_newline && context.target != Target::Continuation {
103+ if config.add_newline && !matches!(context.target, Target::Continuation | Target::Right) {
104 // continuation prompts normally do not include newlines, but they can
105 writeln!(buf).unwrap();
106 }
107@@ -153,7 +153,7 @@ pub fn get_prompt(context: &Context) -> String {
108 wrap_colorseq_for_shell(AnsiStrings(&module_strings).to_string(), context.shell);
109 write!(buf, "{shell_wrapped_output}").unwrap();
110
111- if context.target == Target::Right {
112+ if context.target == Target::Right && !context.multiline {
113 // right prompts generally do not allow newlines
114 buf = buf.replace('\n', "");
115 }