Skip to content

Commit

Permalink
Add config option to escape path of dropped files
Browse files Browse the repository at this point in the history
  • Loading branch information
dsmatter committed Oct 3, 2021
1 parent 8cda3d1 commit bf76c7a
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 1 deletion.
14 changes: 14 additions & 0 deletions alacritty.yml
Expand Up @@ -452,6 +452,20 @@
# If this is `true`, the cursor is temporarily hidden when typing.
#hide_when_typing: false

# Drag & dropping a file into alacritty's window will insert its path at
# cursor position. This setting controls how the path is escaped before
# insertion.
#
# Values for `escape_dropped_path`:
# - None
# Don't escape the file path.
# - SingleQuotes
# Wrap the file path in single quotes and escape single quotes in
# the file path.
# - Backslashes
# Backlash-escape spaces and shell control characters.
#escape_dropped_path: None

# Regex hints
#
# Terminal hints can be used to find text in the visible part of the terminal
Expand Down
73 changes: 73 additions & 0 deletions alacritty/src/config/mouse.rs
Expand Up @@ -9,6 +9,7 @@ pub struct Mouse {
pub hide_when_typing: bool,
#[config(deprecated = "use `hints` section instead")]
pub url: Option<serde_yaml::Value>,
pub escape_dropped_path: Option<ShellEscapeMode>,
}

#[derive(ConfigDeserialize, Clone, Debug, PartialEq, Eq)]
Expand All @@ -27,3 +28,75 @@ impl ClickHandler {
Duration::from_millis(self.threshold as u64)
}
}

#[derive(ConfigDeserialize, Clone, Copy, Debug, PartialEq, Eq)]
pub enum ShellEscapeMode {
SingleQuotes,
Backslashes,
}

impl ShellEscapeMode {
pub fn escape(self, input: &str) -> String {
match self {
ShellEscapeMode::SingleQuotes => format!("'{}'", input.replace('\'', "'\\''")),
ShellEscapeMode::Backslashes => input.chars().flat_map(ShellEscapeChar::from).collect(),
}
}
}

enum ShellEscapeChar {
Plain(char),
Backslash(char),
Empty,
}

impl From<char> for ShellEscapeChar {
fn from(c: char) -> Self {
static NON_ESCAPE_CHARS: &str = ",._+:@%/-";

if c.is_alphanumeric() || NON_ESCAPE_CHARS.contains(c) {
ShellEscapeChar::Plain(c)
} else {
ShellEscapeChar::Backslash(c)
}
}
}

impl Iterator for ShellEscapeChar {
type Item = char;

fn next(&mut self) -> Option<Self::Item> {
match *self {
ShellEscapeChar::Plain(c) => {
*self = ShellEscapeChar::Empty;
Some(c)
},
ShellEscapeChar::Backslash(c) => {
*self = ShellEscapeChar::Plain(c);
Some('\\')
},
ShellEscapeChar::Empty => None,
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_single_quote_escapes() {
let mode = ShellEscapeMode::SingleQuotes;
assert_eq!(mode.escape("alphanum42"), "'alphanum42'");
assert_eq!(mode.escape(r#"foo_+@bar\$baz"&"#), r#"'foo_+@bar\$baz"&'"#);
assert_eq!(mode.escape("foo'bar"), "'foo'\\''bar'");
}

#[test]
fn test_backslash_escapes() {
let mode = ShellEscapeMode::Backslashes;
assert_eq!(mode.escape("alphanum42"), "alphanum42");
assert_eq!(mode.escape(r#"foo_+@bar\$baz"&"#), r#"foo_+@bar\\\$baz\"\&"#);
assert_eq!(mode.escape("foo'bar"), "foo\\'bar");
}
}
7 changes: 6 additions & 1 deletion alacritty/src/event.rs
Expand Up @@ -1325,7 +1325,12 @@ impl<N: Notify + OnResize> Processor<N> {
},
WindowEvent::DroppedFile(path) => {
let path: String = path.to_string_lossy().into();
processor.ctx.write_to_pty((path + " ").into_bytes());
let ui_config = &processor.ctx.config.ui_config;
let escaped_path = match ui_config.mouse.escape_dropped_path {
Some(mode) => mode.escape(&path),
None => path,
};
processor.ctx.write_to_pty((escaped_path + " ").into_bytes());
},
WindowEvent::CursorLeft { .. } => {
processor.ctx.mouse.inside_text_area = false;
Expand Down

0 comments on commit bf76c7a

Please sign in to comment.