diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs
index e75d46dc..f479a7a0 100644
--- a/helix-term/src/commands.rs
+++ b/helix-term/src/commands.rs
@@ -1079,7 +1079,7 @@ fn select_all(cx: &mut Context) {
 }
 
 fn select_regex(cx: &mut Context) {
-    let prompt = ui::regex_prompt(cx, "select:".to_string(), move |view, doc, _, regex| {
+    let prompt = ui::regex_prompt(cx, "select:".into(), move |view, doc, _, regex| {
         let text = doc.text().slice(..);
         if let Some(selection) = selection::select_on_matches(text, doc.selection(view.id), &regex)
         {
@@ -1091,7 +1091,7 @@ fn select_regex(cx: &mut Context) {
 }
 
 fn split_selection(cx: &mut Context) {
-    let prompt = ui::regex_prompt(cx, "split:".to_string(), move |view, doc, _, regex| {
+    let prompt = ui::regex_prompt(cx, "split:".into(), move |view, doc, _, regex| {
         let text = doc.text().slice(..);
         let selection = selection::split_on_matches(text, doc.selection(view.id), &regex);
         doc.set_selection(view.id, selection);
@@ -1157,15 +1157,11 @@ fn search(cx: &mut Context) {
     // feed chunks into the regex yet
     let contents = doc.text().slice(..).to_string();
 
-    let prompt = ui::regex_prompt(
-        cx,
-        "search:".to_string(),
-        move |view, doc, registers, regex| {
-            search_impl(doc, view, &contents, &regex, false);
-            // TODO: only store on enter (accept), not update
-            registers.write('/', vec![regex.as_str().to_string()]);
-        },
-    );
+    let prompt = ui::regex_prompt(cx, "search:".into(), move |view, doc, registers, regex| {
+        search_impl(doc, view, &contents, &regex, false);
+        // TODO: only store on enter (accept), not update
+        registers.write('/', vec![regex.as_str().to_string()]);
+    });
 
     cx.push_layer(Box::new(prompt));
 }
@@ -2210,7 +2206,7 @@ mod cmd {
 
 fn command_mode(cx: &mut Context) {
     let mut prompt = Prompt::new(
-        ":".to_owned(),
+        ":".into(),
         Some(':'),
         |input: &str| {
             // we use .this over split_whitespace() because we care about empty segments
@@ -3819,7 +3815,7 @@ fn join_selections(cx: &mut Context) {
 
 fn keep_selections(cx: &mut Context) {
     // keep selections matching regex
-    let prompt = ui::regex_prompt(cx, "keep:".to_string(), move |view, doc, _, regex| {
+    let prompt = ui::regex_prompt(cx, "keep:".into(), move |view, doc, _, regex| {
         let text = doc.text().slice(..);
 
         if let Some(selection) = selection::keep_matches(text, doc.selection(view.id), &regex) {
@@ -4307,26 +4303,26 @@ enum ShellBehavior {
 }
 
 fn shell_pipe(cx: &mut Context) {
-    shell(cx, "pipe:", ShellBehavior::Replace);
+    shell(cx, "pipe:".into(), ShellBehavior::Replace);
 }
 
 fn shell_pipe_to(cx: &mut Context) {
-    shell(cx, "pipe-to:", ShellBehavior::Ignore);
+    shell(cx, "pipe-to:".into(), ShellBehavior::Ignore);
 }
 
 fn shell_insert_output(cx: &mut Context) {
-    shell(cx, "insert-output:", ShellBehavior::Insert);
+    shell(cx, "insert-output:".into(), ShellBehavior::Insert);
 }
 
 fn shell_append_output(cx: &mut Context) {
-    shell(cx, "append-output:", ShellBehavior::Append);
+    shell(cx, "append-output:".into(), ShellBehavior::Append);
 }
 
 fn shell_keep_pipe(cx: &mut Context) {
-    shell(cx, "keep-pipe:", ShellBehavior::Filter);
+    shell(cx, "keep-pipe:".into(), ShellBehavior::Filter);
 }
 
-fn shell(cx: &mut Context, prompt: &str, behavior: ShellBehavior) {
+fn shell(cx: &mut Context, prompt: Cow<'static, str>, behavior: ShellBehavior) {
     use std::io::Write;
     use std::process::{Command, Stdio};
     if cx.editor.config.shell.is_empty() {
@@ -4338,7 +4334,7 @@ fn shell(cx: &mut Context, prompt: &str, behavior: ShellBehavior) {
         ShellBehavior::Insert | ShellBehavior::Append => false,
     };
     let prompt = Prompt::new(
-        prompt.to_owned(),
+        prompt,
         Some('|'),
         |_input: &str| Vec::new(),
         move |cx: &mut compositor::Context, input: &str, event: PromptEvent| {
@@ -4408,9 +4404,8 @@ fn shell(cx: &mut Context, prompt: &str, behavior: ShellBehavior) {
                 }
             }
 
-            let transaction = Transaction::change(doc.text(), changes.into_iter());
-
             if behavior != ShellBehavior::Ignore {
+                let transaction = Transaction::change(doc.text(), changes.into_iter());
                 doc.apply(&transaction, view.id);
                 doc.append_changes_to_history(view.id);
             }
diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs
index f3f8670e..0a1e24b5 100644
--- a/helix-term/src/ui/mod.rs
+++ b/helix-term/src/ui/mod.rs
@@ -27,7 +27,7 @@ use std::path::PathBuf;
 
 pub fn regex_prompt(
     cx: &mut crate::commands::Context,
-    prompt: String,
+    prompt: std::borrow::Cow<'static, str>,
     fun: impl Fn(&mut View, &mut Document, &mut Registers, Regex) + 'static,
 ) -> Prompt {
     let (view, doc) = current!(cx.editor);
diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs
index ef2c434c..06e424ea 100644
--- a/helix-term/src/ui/picker.rs
+++ b/helix-term/src/ui/picker.rs
@@ -202,7 +202,7 @@ impl<T> Picker<T> {
         callback_fn: impl Fn(&mut Editor, &T, Action) + 'static,
     ) -> Self {
         let prompt = Prompt::new(
-            "".to_string(),
+            "".into(),
             None,
             |_pattern: &str| Vec::new(),
             |_editor: &mut Context, _pattern: &str, _event: PromptEvent| {
diff --git a/helix-term/src/ui/prompt.rs b/helix-term/src/ui/prompt.rs
index 7197adea..1d512ad2 100644
--- a/helix-term/src/ui/prompt.rs
+++ b/helix-term/src/ui/prompt.rs
@@ -15,7 +15,7 @@ use helix_view::{
 pub type Completion = (RangeFrom<usize>, Cow<'static, str>);
 
 pub struct Prompt {
-    prompt: String,
+    prompt: Cow<'static, str>,
     pub line: String,
     cursor: usize,
     completion: Vec<Completion>,
@@ -55,7 +55,7 @@ pub enum Movement {
 
 impl Prompt {
     pub fn new(
-        prompt: String,
+        prompt: Cow<'static, str>,
         history_register: Option<char>,
         mut completion_fn: impl FnMut(&str) -> Vec<Completion> + 'static,
         callback_fn: impl FnMut(&mut Context, &str, PromptEvent) + 'static,