summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md11
-rw-r--r--Cargo.lock2
-rw-r--r--Cargo.toml2
-rw-r--r--src/app/app.rs7
-rw-r--r--src/app/app_context.rs5
-rw-r--r--src/app/panel_state.rs50
-rw-r--r--src/app/state_type.rs4
-rw-r--r--src/browser/browser_state.rs2
-rw-r--r--src/cli/args.rs4
-rw-r--r--src/cli/mod.rs1
-rw-r--r--src/conf/conf.rs5
-rw-r--r--src/conf/verb_conf.rs12
-rw-r--r--src/filesystems/filesystems_state.rs2
-rw-r--r--src/help/help_state.rs2
-rw-r--r--src/image/image_view.rs2
-rw-r--r--src/kitty/image_renderer.rs17
-rw-r--r--src/kitty/mod.rs14
-rw-r--r--src/preview/preview_state.rs2
-rw-r--r--src/shell_install/mod.rs8
-rw-r--r--src/shell_install/nushell.rs10
-rw-r--r--src/shell_install/util.rs1
-rw-r--r--src/stage/stage_state.rs2
-rw-r--r--src/verb/exec_pattern.rs4
-rw-r--r--src/verb/execution_builder.rs70
-rw-r--r--src/verb/file_type_condition.rs7
-rw-r--r--src/verb/internal.rs4
-rw-r--r--src/verb/internal_focus.rs4
-rw-r--r--src/verb/internal_select.rs2
-rw-r--r--src/verb/mod.rs2
-rw-r--r--src/verb/verb.rs2
-rw-r--r--src/verb/verb_store.rs15
-rw-r--r--src/verb/write.rs42
-rw-r--r--website/docs/conf_file.md29
-rw-r--r--website/docs/conf_verbs.md3
34 files changed, 291 insertions, 58 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6b2ad25..b515dc6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,14 @@
+### v1.34.0 - 2024-02-24
+<a name="v1.34.0"></a>
+- new `--verb-output` launch argument, dedicated to the new `:clear_output` and `:write_output` internals - Fix #825
+- verb sequences (based on `cmd`) can take arguments from the verb invocation
+- don't fail launch in case of bad verb configuration, more helpful error message in such case
+- faster kitty image rendering by default - Fix #789
+- `{file-git-relative}` verb argument - Thanks @VasilisManol
+- modify nushell function import: `use` instead of `source` - Thanks @texastoland and @FrancescElies
+- fix some resizing and flickering problems on Windows (appeared with 1.33.0) - Fix #840
+- write `installed` flag file on `--install` - Fix #837
+
### v1.33.1 - 2024-02-03
<a name="v1.33.1"></a>
- fix the release's version
diff --git a/Cargo.lock b/Cargo.lock
index af8ebf6..9688f2f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -217,7 +217,7 @@ checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
[[package]]
name = "broot"
-version = "1.33.1"
+version = "1.34.0"
dependencies = [
"ahash 0.8.7",
"ansi_colours",
diff --git a/Cargo.toml b/Cargo.toml
index 07649d9..3a338bc 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "broot"
-version = "1.33.1"
+version = "1.34.0"
authors = ["dystroy <denys.seguret@gmail.com>"]
repository = "https://github.com/Canop/broot"
homepage = "https://dystroy.org/broot"
diff --git a/src/app/app.rs b/src/app/app.rs
index 998afa4..b9a6cd9 100644
--- a/src/app/app.rs
+++ b/src/app/app.rs
@@ -858,13 +858,6 @@ impl App {
handled = true;
}
} else if let Event::Resize(mut width, mut height) = event.event {
- // I don't know why but Crossterm seems to always report an
- // understimated size on Windows
- #[cfg(windows)]
- {
- width += 1;
- height += 1;
- }
self.screen.set_terminal_size(width, height, con);
Areas::resize_all(
self.panels.as_mut_slice(),
diff --git a/src/app/app_context.rs b/src/app/app_context.rs
index 8b56db4..35c1afa 100644
--- a/src/app/app_context.rs
+++ b/src/app/app_context.rs
@@ -8,6 +8,7 @@ use {
errors::*,
file_sum,
icon::*,
+ kitty::TransmissionMedium,
pattern::SearchModeMap,
path::SpecialPaths,
skin::ExtColorMap,
@@ -118,6 +119,8 @@ pub struct AppContext {
/// the execution of a terminal program.
/// This is determined by app::run on launching the event source.
pub keyboard_enhanced: bool,
+
+ pub kitty_graphics_transmission: TransmissionMedium,
}
impl AppContext {
@@ -216,6 +219,8 @@ impl AppContext {
terminal_title_pattern,
update_work_dir: config.update_work_dir.unwrap_or(true),
keyboard_enhanced: false,
+ kitty_graphics_transmission: config.kitty_graphics_transmission
+ .unwrap_or_default(),
})
}
/// Return the --cmd argument, coming from the launch arguments (prefered)
diff --git a/src/app/panel_state.rs b/src/app/panel_state.rs
index dfa97d9..fd00aef 100644
--- a/src/app/panel_state.rs
+++ b/src/app/panel_state.rs
@@ -79,9 +79,11 @@ pub trait PanelState {
/// The invocation comes from the input and may be related
/// to a different verb (the verb may have been triggered
/// by a key shortcut)
+ #[allow(clippy::too_many_arguments)]
fn on_internal(
&mut self,
w: &mut W,
+ invocation_parser: Option<&InvocationParser>,
internal_exec: &InternalExecution,
input_invocation: Option<&VerbInvocation>,
trigger_type: TriggerType,
@@ -92,9 +94,11 @@ pub trait PanelState {
/// a generic implementation of on_internal which may be
/// called by states when they don't have a specific
/// behavior to execute
+ #[allow(clippy::too_many_arguments)]
fn on_internal_generic(
&mut self,
_w: &mut W,
+ invocation_parser: Option<&InvocationParser>,
internal_exec: &InternalExecution,
input_invocation: Option<&VerbInvocation>,
_trigger_type: TriggerType,
@@ -571,6 +575,37 @@ pub trait PanelState {
Internal::print_relative_path => print::print_relative_paths(self.sel_info(app_state), con)?,
Internal::refresh => CmdResult::RefreshState { clear_cache: true },
Internal::quit => CmdResult::Quit,
+ Internal::clear_output => {
+ verb_clear_output(con)
+ .unwrap_or_else(|e| CmdResult::DisplayError(format!("{e}")))
+ }
+ Internal::write_output => {
+ let sel_info = self.sel_info(app_state);
+ let exec_builder = match input_invocation {
+ Some(inv) => {
+ ExecutionStringBuilder::with_invocation(
+ invocation_parser,
+ sel_info,
+ app_state,
+ inv.args.as_ref(),
+ )
+ }
+ None => {
+ ExecutionStringBuilder::without_invocation(sel_info, app_state)
+ }
+ };
+ if let Some(pattern) = internal_exec.arg.as_ref() {
+ let line = exec_builder.string(pattern);
+ verb_write(con, &line)?;
+ } else {
+ let line = input_invocation
+ .and_then(|inv| inv.args.as_ref())
+ .map(|s| s.as_str())
+ .unwrap_or("");
+ verb_write(con, line)?;
+ }
+ CmdResult::Keep
+ }
_ => CmdResult::Keep,
})
}
@@ -653,6 +688,7 @@ pub trait PanelState {
VerbExecution::Internal(internal_exec) => {
self.on_internal(
w,
+ verb.invocation_parser.as_ref(),
internal_exec,
invocation,
trigger_type,
@@ -700,7 +736,7 @@ pub trait PanelState {
}
}
let exec_builder = ExecutionStringBuilder::with_invocation(
- &verb.invocation_parser,
+ verb.invocation_parser.as_ref(),
sel_info,
app_state,
if let Some(inv) = invocation {
@@ -719,7 +755,7 @@ pub trait PanelState {
seq_ex: &SequenceExecution,
invocation: Option<&VerbInvocation>,
app_state: &mut AppState,
- _cc: &CmdContext,
+ cc: &CmdContext,
) -> Result<CmdResult, ProgramError> {
let sel_info = self.sel_info(app_state);
if matches!(sel_info, SelInfo::More(_)) {
@@ -729,7 +765,7 @@ pub trait PanelState {
return Ok(CmdResult::error("sequences can't be executed on multiple selections"));
}
let exec_builder = ExecutionStringBuilder::with_invocation(
- &verb.invocation_parser,
+ verb.invocation_parser.as_ref(),
sel_info,
app_state,
if let Some(inv) = invocation {
@@ -738,12 +774,7 @@ pub trait PanelState {
None
},
);
- // TODO what follows is dangerous: if an inserted group value contains the separator,
- // the parsing will cut on this separator
- let sequence = Sequence {
- raw: exec_builder.shell_exec_string(&ExecPattern::from_string(&seq_ex.sequence.raw)),
- separator: seq_ex.sequence.separator.clone(),
- };
+ let sequence = exec_builder.sequence(&seq_ex.sequence, &cc.app.con.verb_store);
Ok(CmdResult::ExecuteSequence { sequence })
}
@@ -782,6 +813,7 @@ pub trait PanelState {
input_invocation,
} => self.on_internal(
w,
+ None,
&InternalExecution::from_internal(*internal),
input_invocation.as_ref(),
TriggerType::Other,
diff --git a/src/app/state_type.rs b/src/app/state_type.rs
index 28eb85f..81a4961 100644
--- a/src/app/state_type.rs
+++ b/src/app/state_type.rs
@@ -1,10 +1,10 @@
use {
- serde::Deserialize,
+ serde::{Deserialize, Serialize},
};
/// one of the types of state that you could
/// find in a panel today
-#[derive(Debug, Clone, Copy, PartialEq, Deserialize)]
+#[derive(Debug, Clone, Copy, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum PanelStateType {
diff --git a/src/browser/browser_state.rs b/src/browser/browser_state.rs
index 1a6e6db..e84788e 100644
--- a/src/browser/browser_state.rs
+++ b/src/browser/browser_state.rs
@@ -315,6 +315,7 @@ impl PanelState for BrowserState {
fn on_internal(
&mut self,
w: &mut W,
+ invocation_parser: Option<&InvocationParser>,
internal_exec: &InternalExecution,
input_invocation: Option<&VerbInvocation>,
trigger_type: TriggerType,
@@ -612,6 +613,7 @@ impl PanelState for BrowserState {
Internal::quit => CmdResult::Quit,
_ => self.on_internal_generic(
w,
+ invocation_parser,
internal_exec,
input_invocation,
trigger_type,
diff --git a/src/cli/args.rs b/src/cli/args.rs
index 6b45cc1..0dd7e86 100644
--- a/src/cli/args.rs
+++ b/src/cli/args.rs
@@ -140,6 +140,10 @@ pub struct Args {
#[arg(long, value_name = "path")]
pub outcmd: Option<PathBuf>,
+ /// An optional path where to write when a verb uses `:write_output`
+ #[arg(long, value_name = "verb-output")]
+ pub verb_output: Option<PathBuf>,
+
/// Semicolon separated commands to execute
#[arg(short, long, value_name = "cmd")]
pub cmd: Option<String>,
diff --git a/src/cli/mod.rs b/src/cli/mod.rs
index 408ad2d..f7cfc22 100644
--- a/src/cli/mod.rs
+++ b/src/cli/mod.rs
@@ -158,6 +158,7 @@ pub fn run() -> Result<Option<Launchable>, ProgramError> {
w.queue(EnableMouseCapture)?;
}
let r = app.run(&mut w, &mut context, &config);
+ w.flush()?;
if context.capture_mouse {
w.queue(DisableMouseCapture)?;
}
diff --git a/src/conf/conf.rs b/src/conf/conf.rs
index 983058d..aaeb147 100644
--- a/src/conf/conf.rs
+++ b/src/conf/conf.rs
@@ -7,6 +7,7 @@ use {
app::Mode,
display::ColsConf,
errors::{ConfError, ProgramError},
+ kitty::TransmissionMedium,
path::{
path_from,
PathAnchor,
@@ -122,6 +123,9 @@ pub struct Conf {
#[serde(alias="enable-keyboard-enhancements")]
pub enable_kitty_keyboard: Option<bool>,
+ #[serde(alias="kitty-graphics-transmission")]
+ pub kitty_graphics_transmission: Option<TransmissionMedium>,
+
// BEWARE: entries added here won't be usable unless also
// added in read_file!
}
@@ -208,6 +212,7 @@ impl Conf {
overwrite!(self, terminal_title, conf);
overwrite!(self, update_work_dir, conf);
overwrite!(self, enable_kitty_keyboard, conf);
+ overwrite!(self, kitty_graphics_transmission, conf);
self.verbs.append(&mut conf.verbs);
// the following maps are "additive": we can add entries from several
// config files and they still make sense
diff --git a/src/conf/verb_conf.rs b/src/conf/verb_conf.rs
index 569b4fe..f8b7825 100644
--- a/src/conf/verb_conf.rs
+++ b/src/conf/verb_conf.rs
@@ -5,11 +5,11 @@ use {
},
verb::*,
},
- serde::Deserialize,
+ serde::{Deserialize, Serialize},
};
/// A deserializable verb entry in the configuration
-#[derive(Default, Debug, Clone, Deserialize)]
+#[derive(Default, Debug, Clone, Deserialize, Serialize)]
pub struct VerbConf {
pub invocation: Option<String>,
@@ -26,10 +26,10 @@ pub struct VerbConf {
pub key: Option<String>,
- #[serde(default)]
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
pub keys: Vec<String>,
- #[serde(default)]
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
pub extensions: Vec<String>,
pub shortcut: Option<String>,
@@ -38,7 +38,7 @@ pub struct VerbConf {
pub from_shell: Option<bool>,
- #[serde(default)]
+ #[serde(default, skip_serializing_if = "FileTypeCondition::is_default")]
pub apply_to: FileTypeCondition,
pub set_working_dir: Option<bool>,
@@ -51,7 +51,7 @@ pub struct VerbConf {
pub switch_terminal: Option<bool>,
- #[serde(default)]
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
pub panels: Vec<PanelStateType>,
}
diff --git a/src/filesystems/filesystems_state.rs b/src/filesystems/filesystems_state.rs
index bbeb26d..1a11582 100644
--- a/src/filesystems/filesystems_state.rs
+++ b/src/filesystems/filesystems_state.rs
@@ -474,6 +474,7 @@ impl PanelState for FilesystemState {
fn on_internal(
&mut self,
w: &mut W,
+ invocation_parser: Option<&InvocationParser>,
internal_exec: &InternalExecution,
input_invocation: Option<&VerbInvocation>,
trigger_type: TriggerType,
@@ -578,6 +579,7 @@ impl PanelState for FilesystemState {
open_leave => CmdResult::PopStateAndReapply,
_ => self.on_internal_generic(
w,
+ invocation_parser,
internal_exec,
input_invocation,
trigger_type,
diff --git a/src/help/help_state.rs b/src/help/help_state.rs
index ac50fdc..9425b57 100644
--- a/src/help/help_state.rs
+++ b/src/help/help_state.rs
@@ -202,6 +202,7 @@ impl PanelState for HelpState {
fn on_internal(
&mut self,
w: &mut W,
+ invocation_parser: Option<&InvocationParser>,
internal_exec: &InternalExecution,
input_invocation: Option<&VerbInvocation>,
trigger_type: TriggerType,
@@ -260,6 +261,7 @@ impl PanelState for HelpState {
}
_ => self.on_internal_generic(
w,
+ invocation_parser,
internal_exec,
input_invocation,
trigger_type,
diff --git a/src/image/image_view.rs b/src/image/image_view.rs
index eeae252..b274a8d 100644
--- a/src/image/image_view.rs
+++ b/src/image/image_view.rs
@@ -119,7 +119,7 @@ impl ImageView {
}
self.kitty_image_id = kitty_manager
- .try_print_image(w, &self.source_img, area, bg, disc.count)?;
+ .try_print_image(w, &self.source_img, area, bg, disc.count, disc.con)?;
if self.kitty_image_id.is_some() {
return Ok(());
diff --git a/src/kitty/image_renderer.rs b/src/kitty/image_renderer.rs
index cecaf3b..b959c1e 100644
--- a/src/kitty/image_renderer.rs
+++ b/src/kitty/image_renderer.rs
@@ -24,6 +24,7 @@ use {
RgbImage,
RgbaImage,
},
+ serde::Deserialize,
std::{
io::{self, Write},
},
@@ -35,12 +36,17 @@ use {
///
/// Note that I didn't test yet the named shared memory
/// solution offered by kitty.
-#[derive(Debug)]
+///
+/// Documentation:
+/// https://sw.kovidgoyal.net/kitty/graphics-protocol/#the-transmission-medium
+#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Deserialize)]
+#[serde(rename_all = "snake_case")]
pub enum TransmissionMedium {
/// write a temp file, then give its path to kitty
/// in the payload of the escape sequence. It's quite
/// fast on SSD but a big downside is that it doesn't
/// work if you're distant
+ #[default]
TempFile,
/// send the whole rgb or rgba data, encoded in base64,
/// in the payloads of several escape sequence (each one
@@ -49,6 +55,10 @@ pub enum TransmissionMedium {
Chunks,
}
+pub struct KittyImageRendererOptions {
+ pub transmission_medium: TransmissionMedium,
+}
+
enum ImageData<'i> {
RgbRef(&'i RgbImage),
RgbaRef(&'i RgbaImage),
@@ -203,7 +213,7 @@ impl<'i> KittyImage<'i> {
impl KittyImageRenderer {
/// Called only once (at most) by the KittyManager
- pub fn new() -> Option<Self> {
+ pub fn new(options: &KittyImageRendererOptions) -> Option<Self> {
if !is_kitty_graphics_protocol_supported() {
return None;
}
@@ -213,7 +223,7 @@ impl KittyImageRenderer {
cell_width,
cell_height,
next_id: 1,
- transmission_medium: TransmissionMedium::Chunks,
+ transmission_medium: options.transmission_medium,
})
}
/// return a new image id
@@ -240,6 +250,7 @@ impl KittyImageRenderer {
let img = KittyImage::new(src, area, self);
debug!("transmission medium: {:?}", self.transmission_medium);
+ w.flush()?;
match self.transmission_medium {
TransmissionMedium::TempFile => img.print_with_temp_file(w)?,
TransmissionMedium::Chunks => img.print_with_chunks(w)?,
diff --git a/src/kitty/mod.rs b/src/kitty/mod.rs
index 9fa3740..8c5360a 100644
--- a/src/kitty/mod.rs
+++ b/src/kitty/mod.rs
@@ -7,6 +7,7 @@ pub use {
use {
crate::{
+ app::AppContext,
display::W,
errors::ProgramError,
image::SourceImage,
@@ -64,15 +65,21 @@ impl KittyManager {
_ => None,
}
}
- pub fn renderer(&mut self) -> Option<&mut KittyImageRenderer> {
+ pub fn renderer(
+ &mut self,
+ con: &AppContext,
+ ) -> Option<&mut KittyImageRenderer> {
if matches!(self.renderer, MaybeRenderer::Disabled) {
return None;
}
if matches!(self.renderer, MaybeRenderer::Enabled { .. }) {
return self.renderer_if_tested();
}
+ let options = KittyImageRendererOptions {
+ transmission_medium: con.kitty_graphics_transmission,
+ };
// we're in the Untested branch
- match KittyImageRenderer::new() {
+ match KittyImageRenderer::new(&options) {
Some(renderer) => {
self.renderer = MaybeRenderer::Enabled { renderer };
self.renderer_if_tested()
@@ -101,8 +108,9 @@ impl KittyManager {
area: &Area,
bg: Color,
drawing_count: usize,
+ con: &AppContext,
) -> Result<Option<KittyImageId>, ProgramError> {
- if let Some(renderer) = self.renderer() {
+ if let Some(renderer) = self.renderer(con) {
let img = src.optimal()?;
let new_id = renderer.print(w, &img, area, bg)?;
self.rendered_images.push(RenderedImage {
diff --git a/src/preview/preview_state.rs b/src/preview/preview_state.rs
index 4640cf7..ca27d95 100644
--- a/src/preview/preview_state.rs
+++ b/src/preview/preview_state.rs
@@ -304,6 +304,7 @@ impl PanelState for PreviewState {
fn on_internal(
&mut self,
w: &mut W,
+ invocation_parser: Option<&InvocationParser>,
internal_exec: &InternalExecution,
input_invocation: Option<&VerbInvocation>,
trigger_type: TriggerType,
@@ -397,6 +398,7 @@ impl PanelState for PreviewState {
Internal::preview_binary => self.set_mode(PreviewMode::Hex, con),
_ => self.on_internal_generic(
w,
+ invocation_parser,
internal_exec,
input_invocation,
trigger_type,
diff --git a/src/shell_install/mod.rs b/src/shell_install/mod.rs
index 8b39143..c68ae58 100644
--- a/src/shell_install/mod.rs
+++ b/src/shell_install/mod.rs
@@ -127,11 +127,11 @@ impl ShellInstall {
}
}
}
- // even if the installation isn't really complete (for example
- // when no bash file was found), we don't want to ask the user
- // again, we'll assume it's done
- ShellInstallState::UpToDate.write(self)?;
}
+ // even if the installation isn't really complete (for example
+ // when no bash file was found), we don't want to ask the user
+ // again, we'll assume it's done
+ ShellInstallState::UpToDate.write(self)?;
debug!("Starting install");
bash::install(self)?;
fish::install(self)?;
diff --git a/src/shell_install/nushell.rs b/src/shell_install/nushell.rs
index 70c401c..9812315 100644
--- a/src/shell_install/nushell.rs
+++ b/src/shell_install/nushell.rs
@@ -6,7 +6,7 @@
//! In a correct installation, we have:
//! - a function declaration script in ~/.local/share/broot/launcher/nushell/br/1
//! - a link to that script in ~/.config/broot/launcher/nushell/br/1
-//! - a line to source the link in ~/.config/nushell/config.nu
+//! - a line to use the link in ~/.config/nushell/config.nu
//! (exact paths depend on XDG variables)
//!
//! Please note that this function doesn't allow other commands than cd,
@@ -21,7 +21,7 @@ use {
};
const NAME: &str = "nushell";
-const VERSION: &str = "5";
+const VERSION: &str = "6";
const NU_FUNC: &str = r#"
# Launch broot
@@ -33,7 +33,7 @@ const NU_FUNC: &str = r#"
# > br -hi -c "vacheblan.svg;:open_preview" ..
#
# See https://dystroy.org/broot/install-br/
-def --env br [
+export def --env br [
--cmd(-c): string # Semicolon separated commands to execute
--color: string = "auto" # Whether to have styles and colors (auto is default and usually OK) [possible values: auto, yes, no]
--conf: string # Semicolon separated paths to specific config files"),
@@ -212,7 +212,7 @@ pub fn install(si: &mut ShellInstall) -> Result<(), ShellInstallError> {
si.create_link(&link_path, &script_path)?;
let escaped_path = link_path.to_string_lossy().replace(' ', "\\ ");
- let source_line = format!("source {}", &escaped_path);
+ let source_line = format!("use '{}' *", &escaped_path);
let sourcing_path = nushell_dir.join("config.nu");
if !sourcing_path.exists() {
@@ -234,7 +234,7 @@ pub fn install(si: &mut ShellInstall) -> Result<(), ShellInstallError> {
util::append_to_file(&sourcing_path, format!("\n{source_line}\n"))?;
mad_print_inline!(
&si.skin,
- "`$0` successfully patched, you can make the function immediately available with `source $0`\n",
+ "`$0` successfully patched, you can make the function immediately available with `use '$0' *`\n",
&sourcing_path_str,
);
}
diff --git a/src/shell_install/util.rs b/src/shell_install/util.rs
index 34d11d1..3976f02 100644
--- a/src/shell_install/util.rs
+++ b/src/shell_install/util.rs
@@ -20,7 +20,6 @@ pub fn file_contains_line(path: &Path, searched_line: &str) -> Result<bool, Shel
pub fn append_to_file<S: AsRef<str>>(path: &Path, content: S) -> Result<(), ShellInstallError> {
let mut shellrc = OpenOptions::new()
- .write(true)
.append(true)
.open(path)
.context(&|| format!("opening {path:?} for append"))?;
diff --git a/src/stage/stage_state.rs b/src/stage/stage_state.rs
index 7c277a3..0c70418 100644
--- a/src/stage/stage_state.rs
+++ b/src/stage/stage_state.rs
@@ -443,6 +443,7 @@ impl PanelState for StageState {
fn on_internal(
&mut self,
w: &mut W,
+ invocation_parser: Option<&InvocationParser>,
internal_exec: &Internal