Commit d0d8945

HPCesia <me@hpcesia.com>
2026-10-13 07:19:36
shell: use starship for prompt
Assisted-by: crush:deepseek-v4.1-flash
1 parent adf0a7a
Changed files (3)
modules/core/shell.nix → modules/core/shell/default.nix
@@ -1,41 +1,52 @@
 {den, ...}: {
   den.aspects.core.includes = [den.aspects.core.shell];
   den.aspects.core.shell = {
-    nixos = {pkgs, ...}: let
-    in {
-      environment.systemPackages = [pkgs.nur.repos.hpcesia.flyline];
+    nixos = {
+      pkgs,
+      lib,
+      ...
+    }: {
+      # Flyline right prompt support. RPS1 wiring is upstream PR #7673;
+      # multi-line right prompts are a local extension.
+      nixpkgs.overlays = [
+        (final: prev: {
+          starship = prev.starship.overrideAttrs (old: {
+            patches =
+              (old.patches or []) ++ [./starship-flyline-rps1.patch];
+          });
+        })
+      ];
+
+      environment.systemPackages = [
+        pkgs.starship
+        pkgs.nur.repos.hpcesia.flyline
+      ];
+
+      environment.etc."starship.toml".source = ./starship.toml;
+
+      environment.variables.STARSHIP_CONFIG = "/etc/starship.toml";
 
       programs.bash = {
         enable = true;
         completion.enable = true;
+        promptInit = "";
         interactiveShellInit = ''
           enable -f ${pkgs.nur.repos.hpcesia.flyline}/lib/libflyline.so flyline
           flyline mouse --mode disabled
           flyline set-cursor --backend terminal
-        '';
-        promptInit = ''
-          # Provide a nice prompt if the terminal supports it.
-          if [ "$TERM" != "dumb" ] || [ -n "$INSIDE_EMACS" ]; then
-            PROMPT_COLOR="1;31m"
-            ((UID)) && PROMPT_COLOR="1;32m"
-            # Show the hostname only when connected via SSH.
-            SSH_HOST=""
-            [ -n "$SSH_CONNECTION" ] && SSH_HOST="@$HOSTNAME"
-            if [ -n "$INSIDE_EMACS" ]; then
-              # Emacs term mode doesn't support xterm title escape sequence (\e]0;)
-              PS1="\[\033[$PROMPT_COLOR\]\u$SSH_HOST\[\033[0m\]: \[\033[1;34m\]\w\[\033[0m\]\n\[\033[$PROMPT_COLOR\]\\$\[\033[0m\] "
-            else
-              PS1="\[\033[$PROMPT_COLOR\]\u$SSH_HOST\[\033[0m\]: \[\033[1;34m\]\w\[\033[0m\]\n\[\033[$PROMPT_COLOR\]\\$\[\033[0m\] "
-              PS1="\[\e]0;\u@\h: \w\a\]$PS1"
-            fi
-            if test "$TERM" = "xterm"; then
-              PS1="\[\033]2;\h:\u:\w\007\]$PS1"
-            fi
-            # To separate previous command output
-            PROMPT_RULER="\e[90m─\e[0m"
 
-            PS1_FINAL="\[\033[$PROMPT_COLOR\]\u@\h\\$\[\033[0m\] "
-            PROMPT_RULER_FINAL="empty-line"
+          flyline create-prompt-widget custom \
+            --name STARSHIP_TRANSIENT \
+            --command '${lib.getExe pkgs.starship} prompt --profile transient' \
+            --block
+          PS1_FINAL=STARSHIP_TRANSIENT
+
+          RPS1_FINAL=""
+
+          PROMPT_RULER="empty-line"
+
+          if [ "$TERM" != "dumb" ]; then
+            eval "$(${lib.getExe pkgs.starship} init bash)"
           fi
         '';
       };
@@ -51,9 +62,9 @@
           }
           if [[ ";''${PROMPT_COMMAND[*]:-};" != *";__ix_history_flush;"* ]]; then
             if [[ "$(declare -p PROMPT_COMMAND 2>&1)" == "declare -a"* ]]; then
-              PROMPT_COMMAND=(__ix_history_flush "''${PROMPT_COMMAND[@]}")
+              PROMPT_COMMAND=("''${PROMPT_COMMAND[@]}" __ix_history_flush)
             else
-              PROMPT_COMMAND="__ix_history_flush''${PROMPT_COMMAND:+;$PROMPT_COMMAND}"
+              PROMPT_COMMAND="''${PROMPT_COMMAND:+$PROMPT_COMMAND;}__ix_history_flush"
             fi
           fi
 
@@ -71,9 +82,9 @@
           }
           if [[ ";''${PROMPT_COMMAND[*]:-};" != *";__ix_fix_term_size;"* ]]; then
             if [[ "$(declare -p PROMPT_COMMAND 2>&1)" == "declare -a"* ]]; then
-              PROMPT_COMMAND=(__ix_fix_term_size "''${PROMPT_COMMAND[@]}")
+              PROMPT_COMMAND=("''${PROMPT_COMMAND[@]}" __ix_fix_term_size)
             else
-              PROMPT_COMMAND="__ix_fix_term_size''${PROMPT_COMMAND:+;$PROMPT_COMMAND}"
+              PROMPT_COMMAND="''${PROMPT_COMMAND:+$PROMPT_COMMAND;}__ix_fix_term_size"
             fi
           fi
         '';
modules/core/shell/starship-flyline-rps1.patch
@@ -0,0 +1,115 @@
+diff --git a/src/context.rs b/src/context.rs
+--- a/src/context.rs
++++ b/src/context.rs
+@@ -63,6 +63,9 @@ pub struct Context<'a> {
+     /// Which prompt to print (main, right, ...)
+     pub target: Target,
+ 
++    /// Whether the right prompt is allowed to span multiple lines
++    pub multiline: bool,
++
+     /// Width of terminal, or zero if width cannot be detected.
+     pub width: usize,
+ 
+@@ -178,6 +181,7 @@ impl<'a> Context<'a> {
+             repo: OnceLock::new(),
+             shell,
+             target,
++            multiline: false,
+             width,
+             env,
+             #[cfg(test)]
+@@ -207,6 +211,12 @@ impl<'a> Context<'a> {
+         self
+     }
+ 
++    /// Allow the right prompt to span multiple lines
++    pub fn with_multiline(mut self, multiline: bool) -> Self {
++        self.multiline = multiline;
++        self
++    }
++
+     // Tries to retrieve home directory from a table in testing mode or else retrieves it from the os
+     pub fn get_home(&self) -> Option<PathBuf> {
+         home_dir(&self.env)
+diff --git a/src/init/starship.bash b/src/init/starship.bash
+--- a/src/init/starship.bash
++++ b/src/init/starship.bash
+@@ -78,9 +78,14 @@ starship_precmd() {
+         STARSHIP_START_TIME=""
+     fi
+     PS1="$(::STARSHIP:: prompt "${ARGS[@]}")"
++    local nlns=${PS1//[!$'\n']}
+     if [[ ${BLE_ATTACHED-} ]]; then
+-        local nlns=${PS1//[!$'\n']}
+         bleopt prompt_rps1="$nlns$(::STARSHIP:: prompt --right "${ARGS[@]}")"
++    elif [[ ${FLYLINE_VERSION-} ]]; then
++        local rps1 rnlns
++        rps1="$(::STARSHIP:: prompt --right --multiline "${ARGS[@]}")"
++        rnlns=${rps1//[!$'\n']}
++        RPS1="${nlns:${#rnlns}}$rps1"
+     fi
+     STARSHIP_PREEXEC_READY=true  # Signal that we can safely restart the timer
+ }
+diff --git a/src/main.rs b/src/main.rs
+--- a/src/main.rs
++++ b/src/main.rs
+@@ -127,6 +127,9 @@ enum Commands {
+         /// Print the right prompt (instead of the standard left prompt)
+         #[clap(long)]
+         right: bool,
++        /// Allow the right prompt to span multiple lines
++        #[clap(long, requires = "right")]
++        multiline: bool,
+         /// Print the prompt with the specified profile name (instead of the standard left prompt)
+         #[clap(long, conflicts_with = "right")]
+         profile: Option<String>,
+@@ -227,6 +230,7 @@ fn main() {
+         Commands::Prompt {
+             properties,
+             right,
++            multiline,
+             profile,
+             continuation,
+         } => {
+@@ -236,7 +240,7 @@ fn main() {
+                 (_, _, true) => Target::Continuation,
+                 (_, _, _) => Target::Main,
+             };
+-            print::prompt(properties, target);
++            print::prompt(properties, target, multiline);
+         }
+         Commands::Module {
+             name,
+diff --git a/src/print.rs b/src/print.rs
+--- a/src/print.rs
++++ b/src/print.rs
+@@ -69,8 +69,8 @@ fn test_grapheme_aware_width() {
+     assert_eq!(11, "\x1B[35;6mnormal text".width_graphemes());
+ }
+ 
+-pub fn prompt(args: Properties, target: Target) {
+-    let context = Context::new(args, target);
++pub fn prompt(args: Properties, target: Target, multiline: bool) {
++    let context = Context::new(args, target).with_multiline(multiline);
+     let stdout = io::stdout();
+     let mut handle = stdout.lock();
+ 
+@@ -143,7 +143,7 @@ pub fn get_prompt(context: &Context) -> String {
+     );
+ 
+     let module_strings = root_module.ansi_strings_for_width(Some(context.width));
+-    if config.add_newline && context.target != Target::Continuation {
++    if config.add_newline && !matches!(context.target, Target::Continuation | Target::Right) {
+         // continuation prompts normally do not include newlines, but they can
+         writeln!(buf).unwrap();
+     }
+@@ -153,7 +153,7 @@ pub fn get_prompt(context: &Context) -> String {
+         wrap_colorseq_for_shell(AnsiStrings(&module_strings).to_string(), context.shell);
+     write!(buf, "{shell_wrapped_output}").unwrap();
+ 
+-    if context.target == Target::Right {
++    if context.target == Target::Right && !context.multiline {
+         // right prompts generally do not allow newlines
+         buf = buf.replace('\n', "");
+     }
modules/core/shell/starship.toml
@@ -0,0 +1,61 @@
+"$schema" = 'https://starship.rs/config-schema.json'
+
+format = """
+$username\
+$hostname\
+[: ](white)\
+$directory\
+$line_break\
+$character
+"""
+
+right_format = """
+$nix_shell\
+$time\
+$line_break\
+[<](white)
+"""
+
+add_newline = false
+
+[profiles]
+transient = "$username$hostname$character"
+
+[username]
+disabled = false
+show_always = true
+style_user = "green bold"
+style_root = "red bold"
+format = '[$user]($style)'
+
+[hostname]
+disabled = false
+ssh_only = false
+style = "yellow bold"
+format = '[@$hostname]($style)'
+
+[directory]
+disabled = false
+style = "blue"
+read_only_style = "red"
+read_only = "[ro]"
+format = '[$path]($style)[$read_only]($read_only_style)'
+
+[character]
+disabled = false
+success_symbol = "[\\$](bold green)"
+error_symbol = "[\\$](bold red)"
+format = "$symbol "
+
+[nix_shell]
+disabled = false
+heuristic = true
+symbol = "nix"
+format = "[$symbol\\[$name\\]]($style) "
+
+[time]
+disabled = false
+format = "[ $time]($style)"
+time_format = "%R"
+utc_time_offset = "local"
+style = "dimmed white"