Open files with drag and drop, escape filenames
This commit is contained in:
@@ -15,6 +15,7 @@ use gtk::prelude::*;
|
||||
|
||||
use neovim_lib::{NeovimApi, NeovimApiAsync};
|
||||
|
||||
use misc::escape_filename;
|
||||
use nvim::{ErrorReport, NeovimClient, NeovimRef};
|
||||
use shell;
|
||||
|
||||
@@ -267,6 +268,7 @@ impl FileBrowserWidget {
|
||||
} else {
|
||||
&file_path
|
||||
};
|
||||
let file_path = escape_filename(file_path);
|
||||
nvim_ref.nvim().unwrap().command_async(&format!(":e {}", file_path))
|
||||
.cb(|r| r.report_err())
|
||||
.call();
|
||||
|
||||
10
src/main.rs
10
src/main.rs
@@ -11,13 +11,17 @@ extern crate gtk;
|
||||
extern crate gtk_sys;
|
||||
extern crate htmlescape;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate neovim_lib;
|
||||
extern crate pango;
|
||||
extern crate pango_cairo_sys;
|
||||
extern crate pango_sys;
|
||||
extern crate pangocairo;
|
||||
extern crate percent_encoding;
|
||||
extern crate phf;
|
||||
extern crate regex;
|
||||
|
||||
extern crate serde;
|
||||
#[macro_use]
|
||||
@@ -50,6 +54,7 @@ mod tabline;
|
||||
mod error;
|
||||
mod file_browser;
|
||||
mod subscriptions;
|
||||
mod misc;
|
||||
|
||||
use std::env;
|
||||
use std::time::Duration;
|
||||
@@ -58,6 +63,7 @@ use gio::prelude::*;
|
||||
|
||||
use ui::Ui;
|
||||
|
||||
use misc::escape_filename;
|
||||
use shell::ShellOptions;
|
||||
|
||||
const BIN_PATH_ARG: &str = "--nvim-bin-path";
|
||||
@@ -101,7 +107,9 @@ fn open(app: >k::Application, files: &[gio::File], _: &str) {
|
||||
for f in files {
|
||||
let mut ui = Ui::new(ShellOptions::new(
|
||||
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()),
|
||||
));
|
||||
|
||||
|
||||
38
src/misc.rs
Normal file
38
src/misc.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
23
src/shell.rs
23
src/shell.rs
@@ -18,6 +18,7 @@ use pangocairo;
|
||||
use neovim_lib::{Neovim, NeovimApi, NeovimApiAsync, Value};
|
||||
use neovim_lib::neovim_api::Tabpage;
|
||||
|
||||
use misc::{decode_uri, escape_filename};
|
||||
use settings::{FontSource, Settings};
|
||||
use ui_model::{Attrs, ModelRect, UiModel};
|
||||
use color::{Color, ColorModel, COLOR_BLACK, COLOR_RED, COLOR_WHITE};
|
||||
@@ -567,6 +568,28 @@ impl Shell {
|
||||
state.drawing_area.connect_size_allocate(move |_, _| {
|
||||
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)]
|
||||
|
||||
Reference in New Issue
Block a user