Open files with drag and drop, escape filenames

This commit is contained in:
Christopher Lübbemeier 2018-03-19 19:00:40 +01:00
parent fdaff9748a
commit 0c398c893a
6 changed files with 84 additions and 1 deletions

9
Cargo.lock generated
View File

@ -350,14 +350,17 @@ dependencies = [
"gtk 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "gtk 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gtk-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "gtk-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"neovim-lib 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "neovim-lib 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"pango 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "pango 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pango-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "pango-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pangocairo 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "pangocairo 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pangocairo-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "pangocairo-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", "phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
"phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", "phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
@ -419,6 +422,11 @@ dependencies = [
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "percent-encoding"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "phf" name = "phf"
version = "0.7.21" version = "0.7.21"
@ -750,6 +758,7 @@ dependencies = [
"checksum pango-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94039b3921a4af4058a3e4335e5d15099101f298a92f5afc40bab3a3027594a1" "checksum pango-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94039b3921a4af4058a3e4335e5d15099101f298a92f5afc40bab3a3027594a1"
"checksum pangocairo 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "db4130d0e0567b177178c9568466277ff167cb43a245b2881e203509ea5fbd84" "checksum pangocairo 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "db4130d0e0567b177178c9568466277ff167cb43a245b2881e203509ea5fbd84"
"checksum pangocairo-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "decc9523cd5bbba49a67ed8d2ef7dfca957f4760bf420a1ea4a82634da26381c" "checksum pangocairo-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "decc9523cd5bbba49a67ed8d2ef7dfca957f4760bf420a1ea4a82634da26381c"
"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
"checksum phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "cb325642290f28ee14d8c6201159949a872f220c62af6e110a56ea914fbe42fc" "checksum phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "cb325642290f28ee14d8c6201159949a872f220c62af6e110a56ea914fbe42fc"
"checksum phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "d62594c0bb54c464f633175d502038177e90309daf2e0158be42ed5f023ce88f" "checksum phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "d62594c0bb54c464f633175d502038177e90309daf2e0158be42ed5f023ce88f"
"checksum phf_generator 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "6b07ffcc532ccc85e3afc45865469bf5d9e4ef5bfcf9622e3cfe80c2d275ec03" "checksum phf_generator 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "6b07ffcc532ccc85e3afc45865469bf5d9e4ef5bfcf9622e3cfe80c2d275ec03"

View File

@ -31,6 +31,9 @@ phf = "0.7"
log = "0.4" log = "0.4"
env_logger = "0.5" env_logger = "0.5"
htmlescape = "0.3" htmlescape = "0.3"
percent-encoding = "1.0"
regex = "0.2"
lazy_static = "1.0"
serde = "1.0" serde = "1.0"
serde_derive = "1.0" serde_derive = "1.0"

View File

@ -15,6 +15,7 @@ use gtk::prelude::*;
use neovim_lib::{NeovimApi, NeovimApiAsync}; use neovim_lib::{NeovimApi, NeovimApiAsync};
use misc::escape_filename;
use nvim::{ErrorReport, NeovimClient, NeovimRef}; use nvim::{ErrorReport, NeovimClient, NeovimRef};
use shell; use shell;
@ -267,6 +268,7 @@ impl FileBrowserWidget {
} else { } else {
&file_path &file_path
}; };
let file_path = escape_filename(file_path);
nvim_ref.nvim().unwrap().command_async(&format!(":e {}", file_path)) nvim_ref.nvim().unwrap().command_async(&format!(":e {}", file_path))
.cb(|r| r.report_err()) .cb(|r| r.report_err())
.call(); .call();

View File

@ -11,13 +11,17 @@ extern crate gtk;
extern crate gtk_sys; extern crate gtk_sys;
extern crate htmlescape; extern crate htmlescape;
#[macro_use] #[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log; extern crate log;
extern crate neovim_lib; extern crate neovim_lib;
extern crate pango; extern crate pango;
extern crate pango_cairo_sys; extern crate pango_cairo_sys;
extern crate pango_sys; extern crate pango_sys;
extern crate pangocairo; extern crate pangocairo;
extern crate percent_encoding;
extern crate phf; extern crate phf;
extern crate regex;
extern crate serde; extern crate serde;
#[macro_use] #[macro_use]
@ -50,6 +54,7 @@ mod tabline;
mod error; mod error;
mod file_browser; mod file_browser;
mod subscriptions; mod subscriptions;
mod misc;
use std::env; use std::env;
use std::time::Duration; use std::time::Duration;
@ -58,6 +63,7 @@ use gio::prelude::*;
use ui::Ui; use ui::Ui;
use misc::escape_filename;
use shell::ShellOptions; use shell::ShellOptions;
const BIN_PATH_ARG: &str = "--nvim-bin-path"; const BIN_PATH_ARG: &str = "--nvim-bin-path";
@ -101,7 +107,9 @@ fn open(app: &gtk::Application, files: &[gio::File], _: &str) {
for f in files { for f in files {
let mut ui = Ui::new(ShellOptions::new( let mut ui = Ui::new(ShellOptions::new(
nvim_bin_path(std::env::args()), nvim_bin_path(std::env::args()),
f.get_path().and_then(|p| p.to_str().map(str::to_owned)), f.get_path().and_then(|p| {
p.to_str().map(|path| escape_filename(path).to_string())
}),
nvim_timeout(std::env::args()), nvim_timeout(std::env::args()),
)); ));

38
src/misc.rs Normal file
View File

@ -0,0 +1,38 @@
use std::borrow::Cow;
use regex::Regex;
use percent_encoding::percent_decode;
/// Escape special ASCII characters with a backslash.
pub fn escape_filename<'t>(filename: &'t str) -> Cow<'t, str> {
lazy_static! {
static ref SPECIAL_CHARS: Regex = if cfg!(target_os = "windows") {
// On Windows, don't escape `:` and `\`, as these are valid components of the path.
Regex::new(r"[[:ascii:]&&[^0-9a-zA-Z._:\\-]]").unwrap()
} else {
// Similarly, don't escape `/` on other platforms.
Regex::new(r"[[:ascii:]&&[^0-9a-zA-Z._/-]]").unwrap()
};
}
SPECIAL_CHARS.replace_all(&*filename, r"\$0")
}
/// Decode a file URI.
///
/// - On UNIX: `file:///path/to/a%20file.ext` -> `/path/to/a file.ext`
/// - On Windows: `file:///C:/path/to/a%20file.ext` -> `C:\path\to\a file.ext`
pub fn decode_uri(uri: &str) -> Option<String> {
let path = match uri.split_at(8) {
("file:///", path) => path,
_ => return None,
};
let path = percent_decode(path.as_bytes()).decode_utf8().ok()?;
if cfg!(target_os = "windows") {
lazy_static! {
static ref SLASH: Regex = Regex::new(r"/").unwrap();
}
Some(String::from(SLASH.replace_all(&*path, r"\")))
} else {
Some("/".to_owned() + &path)
}
}

View File

@ -18,6 +18,7 @@ use pangocairo;
use neovim_lib::{Neovim, NeovimApi, NeovimApiAsync, Value}; use neovim_lib::{Neovim, NeovimApi, NeovimApiAsync, Value};
use neovim_lib::neovim_api::Tabpage; use neovim_lib::neovim_api::Tabpage;
use misc::{decode_uri, escape_filename};
use settings::{FontSource, Settings}; use settings::{FontSource, Settings};
use ui_model::{Attrs, ModelRect, UiModel}; use ui_model::{Attrs, ModelRect, UiModel};
use color::{Color, ColorModel, COLOR_BLACK, COLOR_RED, COLOR_WHITE}; use color::{Color, ColorModel, COLOR_BLACK, COLOR_RED, COLOR_WHITE};
@ -567,6 +568,28 @@ impl Shell {
state.drawing_area.connect_size_allocate(move |_, _| { state.drawing_area.connect_size_allocate(move |_, _| {
init_nvim(&ref_state); init_nvim(&ref_state);
}); });
let ref_state = self.state.clone();
let targets = vec![
gtk::TargetEntry::new("text/uri-list", gtk::TargetFlags::OTHER_APP, 0),
];
state
.drawing_area
.drag_dest_set(gtk::DestDefaults::ALL, &targets, gdk::DragAction::COPY);
state
.drawing_area
.connect_drag_data_received(move |_, _, _, _, s, _, _| {
let uris = s.get_uris();
let command = uris.iter()
.filter_map(|uri| decode_uri(uri))
.fold(":ar".to_owned(), |command, filename| {
let filename = escape_filename(&filename);
command + " " + &filename
});
let state = ref_state.borrow_mut();
let mut nvim = state.nvim().unwrap();
nvim.command_async(&command).cb(|r| r.report_err()).call()
});
} }
#[cfg(unix)] #[cfg(unix)]