Merge branch 'plug-manager'
This commit is contained in:
commit
6763512964
25
Cargo.lock
generated
25
Cargo.lock
generated
@ -22,6 +22,7 @@ dependencies = [
|
||||
"phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@ -106,6 +107,11 @@ name = "custom_derive"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "dtoa"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.4.3"
|
||||
@ -277,6 +283,11 @@ name = "htmlescape"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "0.2.8"
|
||||
@ -496,6 +507,17 @@ dependencies = [
|
||||
"synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.2.2"
|
||||
@ -585,6 +607,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
|
||||
"checksum conv 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "78ff10625fd0ac447827aa30ea8b861fead473bb60aeb73af6c1c58caf0d1299"
|
||||
"checksum custom_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9"
|
||||
"checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
|
||||
"checksum env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3ddf21e73e016298f5cb37d6ef8e8da8e39f91f9ec8b0df44b7deb16a9f8cd5b"
|
||||
"checksum gdk 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f30018ecbbb1e6f1d59c4024ec08675850744b799abc5420be0629ac9ba0abd2"
|
||||
"checksum gdk-pixbuf 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "caf05dab73febcc6e90abaff8f24cfe1cf1bd2222cd648ddfe337bf3b994489f"
|
||||
@ -598,6 +621,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum gtk 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce17f98e7dcdc9d06b3a5f7621d796a24937c04953481205b1be267c5a02697a"
|
||||
"checksum gtk-sys 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "854b56ce6d6b05945f7735651482835c5ac1f8582142ce67306726259a3dafb0"
|
||||
"checksum htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e9025058dae765dee5070ec375f591e2ba14638c63feff74f13805a72e523163"
|
||||
"checksum itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c"
|
||||
"checksum lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3b37545ab726dd833ec6420aaba8231c5b320814b9029ad585555d2a03e94fbf"
|
||||
"checksum libc 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)" = "8a014d9226c2cc402676fbe9ea2e15dd5222cd1dd57f576b5b283178c944a264"
|
||||
"checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b"
|
||||
@ -624,6 +648,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum serde 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "f7726f29ddf9731b17ff113c461e362c381d9d69433f79de4f3dd572488823e9"
|
||||
"checksum serde_derive 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cf823e706be268e73e7747b147aa31c8f633ab4ba31f115efb57e5047c3a76dd"
|
||||
"checksum serde_derive_internals 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)" = "37aee4e0da52d801acfbc0cc219eb1eda7142112339726e427926a6f6ee65d3a"
|
||||
"checksum serde_json 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ae1e67ce320daa7e494c578e34d4b00689f23bb94512fe0ca0dfaf02ea53fb67"
|
||||
"checksum siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0df90a788073e8d0235a67e50441d47db7c8ad9debd91cbf43736a2a92d36537"
|
||||
"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
|
||||
"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
|
||||
|
@ -34,6 +34,7 @@ htmlescape = "0.3"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
toml = "0.4"
|
||||
serde_json = "1.0"
|
||||
|
||||
#[dependencies.neovim-lib]
|
||||
#git = "https://github.com/daa84/neovim-lib"
|
||||
|
33
src/dirs.rs
Normal file
33
src/dirs.rs
Normal file
@ -0,0 +1,33 @@
|
||||
use std;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub fn get_app_config_dir_create() -> Result<PathBuf, String> {
|
||||
let config_dir = get_app_config_dir()?;
|
||||
|
||||
std::fs::create_dir_all(&config_dir).map_err(
|
||||
|e| format!("{}", e),
|
||||
)?;
|
||||
|
||||
Ok(config_dir)
|
||||
}
|
||||
|
||||
pub fn get_app_config_dir() -> Result<PathBuf, String> {
|
||||
let mut config_dir = get_xdg_config_dir()?;
|
||||
|
||||
config_dir.push("nvim-gtk");
|
||||
|
||||
Ok(config_dir)
|
||||
}
|
||||
|
||||
fn get_xdg_config_dir() -> Result<PathBuf, String> {
|
||||
if let Ok(config_path) = std::env::var("XDG_CONFIG_HOME") {
|
||||
return Ok(PathBuf::from(config_path));
|
||||
}
|
||||
|
||||
let mut home_dir = std::env::home_dir().ok_or(
|
||||
"Impossible to get your home dir!",
|
||||
)?;
|
||||
home_dir.push(".config");
|
||||
Ok(home_dir)
|
||||
}
|
||||
|
@ -18,18 +18,23 @@ extern crate log;
|
||||
extern crate env_logger;
|
||||
extern crate htmlescape;
|
||||
|
||||
extern crate serde;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
extern crate toml;
|
||||
extern crate serde_json;
|
||||
|
||||
mod sys;
|
||||
|
||||
mod nvim_config;
|
||||
mod dirs;
|
||||
mod color;
|
||||
mod value;
|
||||
mod mode;
|
||||
mod ui_model;
|
||||
#[macro_use]
|
||||
mod ui;
|
||||
mod plug_manager;
|
||||
mod nvim;
|
||||
mod render;
|
||||
mod shell;
|
||||
|
18
src/nvim.rs
18
src/nvim.rs
@ -13,6 +13,7 @@ use ui::UiMutex;
|
||||
use ui_model::{ModelRect, ModelRectVec};
|
||||
use shell;
|
||||
use glib;
|
||||
use nvim_config::NvimConfig;
|
||||
|
||||
use value::ValueMapExt;
|
||||
|
||||
@ -257,6 +258,12 @@ pub fn start(
|
||||
cmd.arg("--cmd").arg("let &rtp.=',runtime'");
|
||||
}
|
||||
|
||||
if let Some(nvim_config) = NvimConfig::config_path() {
|
||||
if let Some(path) = nvim_config.to_str() {
|
||||
cmd.arg("--cmd").arg(format!("source {}", path));
|
||||
}
|
||||
}
|
||||
|
||||
let session = Session::new_child_cmd(&mut cmd);
|
||||
|
||||
let session = match session {
|
||||
@ -505,17 +512,24 @@ fn call(
|
||||
Ok(repaint_mode)
|
||||
}
|
||||
|
||||
pub trait ErrorReport {
|
||||
pub trait ErrorReport<T> {
|
||||
fn report_err(&self, nvim: &mut NeovimApi);
|
||||
|
||||
fn ok_and_report(&self, nvim: &mut NeovimApi) -> Option<&T>;
|
||||
}
|
||||
|
||||
impl<T> ErrorReport for result::Result<T, CallError> {
|
||||
impl<T> ErrorReport<T> for result::Result<T, CallError> {
|
||||
fn report_err(&self, _: &mut NeovimApi) {
|
||||
if let Err(ref err) = *self {
|
||||
println!("{}", err);
|
||||
//nvim.report_error(&err_msg).expect("Error report error :)");
|
||||
}
|
||||
}
|
||||
|
||||
fn ok_and_report(&self, nvim: &mut NeovimApi) -> Option<&T> {
|
||||
self.report_err(nvim);
|
||||
self.as_ref().ok()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
68
src/nvim_config.rs
Normal file
68
src/nvim_config.rs
Normal file
@ -0,0 +1,68 @@
|
||||
use std::path::PathBuf;
|
||||
use std::fs::{remove_file, OpenOptions};
|
||||
use std::io::Write;
|
||||
|
||||
use dirs;
|
||||
use plug_manager;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct NvimConfig {
|
||||
plug_config: Option<plug_manager::PlugManagerConfigSource>,
|
||||
}
|
||||
|
||||
impl NvimConfig {
|
||||
const CONFIG_PATH: &'static str = "settings.vim";
|
||||
|
||||
pub fn new(plug_config: Option<plug_manager::PlugManagerConfigSource>) -> Self {
|
||||
NvimConfig { plug_config }
|
||||
}
|
||||
|
||||
pub fn generate_config(&self) -> Option<PathBuf> {
|
||||
if self.plug_config.is_some() {
|
||||
match self.write_file() {
|
||||
Err(err) => {
|
||||
error!("{}", err);
|
||||
None
|
||||
}
|
||||
Ok(file) => Some(file),
|
||||
}
|
||||
} else {
|
||||
NvimConfig::config_path().map(remove_file);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn config_path() -> Option<PathBuf> {
|
||||
if let Ok(mut path) = dirs::get_app_config_dir() {
|
||||
path.push(NvimConfig::CONFIG_PATH);
|
||||
if path.is_file() {
|
||||
return Some(path);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn write_file(&self) -> Result<PathBuf, String> {
|
||||
let mut config_dir = dirs::get_app_config_dir_create()?;
|
||||
config_dir.push(NvimConfig::CONFIG_PATH);
|
||||
|
||||
let mut file = OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.open(&config_dir)
|
||||
.map_err(|e| format!("{}", e))?;
|
||||
|
||||
let content = &self.plug_config.as_ref().unwrap().source;
|
||||
if !content.is_empty() {
|
||||
debug!("{}", content);
|
||||
file.write_all(content.as_bytes()).map_err(
|
||||
|e| format!("{}", e),
|
||||
)?;
|
||||
}
|
||||
|
||||
file.sync_all().map_err(|e| format!("{}", e))?;
|
||||
Ok(config_dir)
|
||||
}
|
||||
}
|
118
src/plug_manager/manager.rs
Normal file
118
src/plug_manager/manager.rs
Normal file
@ -0,0 +1,118 @@
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
|
||||
use super::vim_plug;
|
||||
use super::store::{Store, PlugInfo};
|
||||
|
||||
use nvim::NeovimClient;
|
||||
|
||||
pub struct Manager {
|
||||
pub vim_plug: vim_plug::Manager,
|
||||
pub store: Store,
|
||||
pub plug_manage_state: PlugManageState,
|
||||
}
|
||||
|
||||
impl Manager {
|
||||
pub fn new() -> Self {
|
||||
let (plug_manage_state, store) = if Store::is_config_exists() {
|
||||
(PlugManageState::NvimGtk, Store::load())
|
||||
} else {
|
||||
(PlugManageState::Unknown, Store::empty())
|
||||
};
|
||||
|
||||
Manager {
|
||||
vim_plug: vim_plug::Manager::new(),
|
||||
plug_manage_state,
|
||||
store,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_config(&self) -> Option<PlugManagerConfigSource> {
|
||||
if self.store.is_enabled() {
|
||||
Some(PlugManagerConfigSource::new(&self.store))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_nvim_client(&mut self, nvim: Rc<RefCell<NeovimClient>>) {
|
||||
self.vim_plug.initialize(nvim);
|
||||
}
|
||||
|
||||
pub fn reload_store(&mut self) {
|
||||
match self.plug_manage_state {
|
||||
PlugManageState::Unknown => {
|
||||
if self.vim_plug.is_loaded() {
|
||||
self.store = Store::load_from_plug(&self.vim_plug);
|
||||
self.plug_manage_state = PlugManageState::VimPlug;
|
||||
} else {
|
||||
self.store = Store::empty();
|
||||
}
|
||||
}
|
||||
PlugManageState::NvimGtk => {
|
||||
if Store::is_config_exists() {
|
||||
self.store = Store::load();
|
||||
} else {
|
||||
self.store = Store::empty();
|
||||
}
|
||||
}
|
||||
PlugManageState::VimPlug => {
|
||||
if Store::is_config_exists() {
|
||||
self.store = Store::load();
|
||||
self.plug_manage_state = PlugManageState::NvimGtk;
|
||||
} else {
|
||||
self.store = Store::empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
if let PlugManageState::Unknown = self.plug_manage_state {
|
||||
if self.vim_plug.is_loaded() {
|
||||
self.store = Store::load_from_plug(&self.vim_plug);
|
||||
self.plug_manage_state = PlugManageState::VimPlug;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn save(&self) {
|
||||
self.store.save();
|
||||
}
|
||||
|
||||
pub fn clear_removed(&mut self) {
|
||||
self.store.clear_removed();
|
||||
}
|
||||
|
||||
pub fn add_plug(&mut self, plug: PlugInfo) -> bool {
|
||||
self.store.add_plug(plug)
|
||||
}
|
||||
|
||||
pub fn move_item(&mut self, idx: usize, offset: i32) {
|
||||
self.store.move_item(idx, offset);
|
||||
}
|
||||
}
|
||||
|
||||
pub enum PlugManageState {
|
||||
NvimGtk,
|
||||
VimPlug,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PlugManagerConfigSource {
|
||||
pub source: String,
|
||||
}
|
||||
|
||||
impl PlugManagerConfigSource {
|
||||
pub fn new(store: &Store) -> Self {
|
||||
let mut builder = "call plug#begin()\n".to_owned();
|
||||
|
||||
for plug in store.get_plugs() {
|
||||
if !plug.removed {
|
||||
builder += &format!("Plug '{}', {{ 'as': '{}' }}\n", plug.get_plug_path(), plug.name);
|
||||
}
|
||||
}
|
||||
|
||||
builder += "call plug#end()\n";
|
||||
|
||||
PlugManagerConfigSource { source: builder }
|
||||
}
|
||||
}
|
9
src/plug_manager/mod.rs
Normal file
9
src/plug_manager/mod.rs
Normal file
@ -0,0 +1,9 @@
|
||||
mod ui;
|
||||
mod vim_plug;
|
||||
mod store;
|
||||
mod manager;
|
||||
mod plugin_settings_dlg;
|
||||
mod vimawesome;
|
||||
|
||||
pub use self::ui::Ui;
|
||||
pub use self::manager::{Manager, PlugManagerConfigSource};
|
114
src/plug_manager/plugin_settings_dlg.rs
Normal file
114
src/plug_manager/plugin_settings_dlg.rs
Normal file
@ -0,0 +1,114 @@
|
||||
use gtk;
|
||||
use gtk::prelude::*;
|
||||
|
||||
use super::store;
|
||||
|
||||
pub struct Builder<'a> {
|
||||
title: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> Builder<'a> {
|
||||
pub fn new(title: &'a str) -> Self {
|
||||
Builder { title }
|
||||
}
|
||||
|
||||
pub fn show<F: IsA<gtk::Window>>(&self, parent: &F) -> Option<store::PlugInfo> {
|
||||
let dlg = gtk::Dialog::new_with_buttons(
|
||||
Some(self.title),
|
||||
Some(parent),
|
||||
gtk::DIALOG_USE_HEADER_BAR | gtk::DIALOG_DESTROY_WITH_PARENT,
|
||||
&[
|
||||
("Cancel", gtk::ResponseType::Cancel.into()),
|
||||
("Ok", gtk::ResponseType::Ok.into()),
|
||||
],
|
||||
);
|
||||
|
||||
let content = dlg.get_content_area();
|
||||
let border = gtk::Box::new(gtk::Orientation::Horizontal, 0);
|
||||
border.set_border_width(12);
|
||||
|
||||
let list = gtk::ListBox::new();
|
||||
list.set_selection_mode(gtk::SelectionMode::None);
|
||||
|
||||
let path = gtk::Box::new(gtk::Orientation::Horizontal, 5);
|
||||
path.set_border_width(5);
|
||||
let path_lbl = gtk::Label::new("Repo");
|
||||
let path_e = gtk::Entry::new();
|
||||
path_e.set_placeholder_text("user_name/repo_name");
|
||||
|
||||
path.pack_start(&path_lbl, true, true, 0);
|
||||
path.pack_end(&path_e, false, true, 0);
|
||||
|
||||
list.add(&path);
|
||||
|
||||
|
||||
let name = gtk::Box::new(gtk::Orientation::Horizontal, 5);
|
||||
name.set_border_width(5);
|
||||
let name_lbl = gtk::Label::new("Name");
|
||||
let name_e = gtk::Entry::new();
|
||||
|
||||
name.pack_start(&name_lbl, true, true, 0);
|
||||
name.pack_end(&name_e, false, true, 0);
|
||||
|
||||
list.add(&name);
|
||||
|
||||
border.pack_start(&list, true, true, 0);
|
||||
content.add(&border);
|
||||
content.show_all();
|
||||
|
||||
path_e.connect_changed(clone!(name_e => move |p| {
|
||||
if let Some(name) = p.get_text().and_then(|t| extract_name(&t)) {
|
||||
name_e.set_text(&name);
|
||||
}
|
||||
}));
|
||||
|
||||
let ok: i32 = gtk::ResponseType::Ok.into();
|
||||
let res = if dlg.run() == ok {
|
||||
path_e.get_text().map(|path| {
|
||||
let name = name_e
|
||||
.get_text()
|
||||
.and_then(|name| if name.trim().is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(name)
|
||||
})
|
||||
.or_else(|| extract_name(&path))
|
||||
.unwrap_or_else(|| path.clone());
|
||||
|
||||
store::PlugInfo::new(name.to_owned(), path.to_owned())
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
dlg.destroy();
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_name(path: &str) -> Option<String> {
|
||||
if let Some(idx) = path.rfind(|c| c == '/' || c == '\\') {
|
||||
if idx < path.len() - 1 {
|
||||
let path = path.trim_right_matches(".git");
|
||||
Some(path[idx + 1..].to_owned())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_extract_name() {
|
||||
assert_eq!(
|
||||
Some("plugin_name".to_owned()),
|
||||
extract_name("http://github.com/somebody/plugin_name.git")
|
||||
);
|
||||
}
|
||||
}
|
155
src/plug_manager/store.rs
Normal file
155
src/plug_manager/store.rs
Normal file
@ -0,0 +1,155 @@
|
||||
use toml;
|
||||
|
||||
use settings::SettingsLoader;
|
||||
use super::vim_plug;
|
||||
|
||||
pub struct Store {
|
||||
settings: Settings,
|
||||
}
|
||||
|
||||
impl Store {
|
||||
pub fn is_config_exists() -> bool {
|
||||
Settings::is_file_exists()
|
||||
}
|
||||
|
||||
pub fn is_enabled(&self) -> bool {
|
||||
self.settings.enabled
|
||||
}
|
||||
|
||||
pub fn load() -> Self {
|
||||
Store { settings: Settings::load() }
|
||||
}
|
||||
|
||||
pub fn empty() -> Self {
|
||||
Store { settings: Settings::empty() }
|
||||
}
|
||||
|
||||
pub fn load_from_plug(vim_plug: &vim_plug::Manager) -> Self {
|
||||
let settings = match vim_plug.get_plugs() {
|
||||
Err(msg) => {
|
||||
error!("{}", msg);
|
||||
Settings::empty()
|
||||
}
|
||||
Ok(plugs) => {
|
||||
let plugs = plugs
|
||||
.iter()
|
||||
.map(|vpi| PlugInfo::new(vpi.name.to_owned(), vpi.uri.to_owned()))
|
||||
.collect();
|
||||
Settings::new(plugs)
|
||||
}
|
||||
};
|
||||
|
||||
Store { settings }
|
||||
}
|
||||
|
||||
pub fn get_plugs(&self) -> &[PlugInfo] {
|
||||
&self.settings.plugs
|
||||
}
|
||||
|
||||
pub fn set_enabled(&mut self, enabled: bool) {
|
||||
self.settings.enabled = enabled;
|
||||
}
|
||||
|
||||
pub fn clear_removed(&mut self) {
|
||||
self.settings.plugs.retain(|p| !p.removed);
|
||||
}
|
||||
|
||||
pub fn save(&self) {
|
||||
self.settings.save();
|
||||
}
|
||||
|
||||
pub fn remove_plug(&mut self, idx: usize) {
|
||||
self.settings.plugs[idx].removed = true;
|
||||
}
|
||||
|
||||
pub fn restore_plug(&mut self, idx: usize) {
|
||||
self.settings.plugs[idx].removed = false;
|
||||
}
|
||||
|
||||
pub fn add_plug(&mut self, plug: PlugInfo) -> bool {
|
||||
let path = plug.get_plug_path();
|
||||
if self.settings.plugs.iter().any(|p| p.get_plug_path() == path || p.name == plug.name) {
|
||||
return false;
|
||||
}
|
||||
self.settings.plugs.push(plug);
|
||||
true
|
||||
}
|
||||
|
||||
pub fn plugs_count(&self) -> usize {
|
||||
self.settings.plugs.len()
|
||||
}
|
||||
|
||||
pub fn move_item(&mut self, idx: usize, offset: i32) {
|
||||
let plug = self.settings.plugs.remove(idx);
|
||||
self.settings.plugs.insert((idx as i32 + offset) as usize, plug);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct Settings {
|
||||
enabled: bool,
|
||||
plugs: Vec<PlugInfo>,
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
fn new(plugs: Vec<PlugInfo>) -> Self {
|
||||
Settings { plugs, enabled: false }
|
||||
}
|
||||
}
|
||||
|
||||
impl SettingsLoader for Settings {
|
||||
const SETTINGS_FILE: &'static str = "plugs.toml";
|
||||
|
||||
fn empty() -> Self {
|
||||
Settings { plugs: vec![], enabled: false }
|
||||
}
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, String> {
|
||||
toml::from_str(&s).map_err(|e| format!("{}", e))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct PlugInfo {
|
||||
pub name: String,
|
||||
pub url: String,
|
||||
pub removed: bool,
|
||||
}
|
||||
|
||||
impl PlugInfo {
|
||||
pub fn new(name: String, url: String) -> Self {
|
||||
PlugInfo {
|
||||
name,
|
||||
url,
|
||||
removed: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_plug_path(&self) -> String {
|
||||
if self.url.contains("github.com") {
|
||||
let mut path_comps: Vec<&str> = self.url
|
||||
.trim_right_matches(".git")
|
||||
.rsplit('/')
|
||||
.take(2)
|
||||
.collect();
|
||||
path_comps.reverse();
|
||||
path_comps.join("/")
|
||||
} else {
|
||||
self.url.clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_get_plug_path() {
|
||||
let plug = PlugInfo::new(
|
||||
"rust.vim".to_owned(),
|
||||
"https://git::@github.com/rust-lang/rust.vim.git".to_owned(),
|
||||
);
|
||||
assert_eq!("rust-lang/rust.vim".to_owned(), plug.get_plug_path());
|
||||
}
|
||||
}
|
441
src/plug_manager/ui.rs
Normal file
441
src/plug_manager/ui.rs
Normal file
@ -0,0 +1,441 @@
|
||||
use std::sync::Arc;
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
use std::ops::Deref;
|
||||
|
||||
use ui::UiMutex;
|
||||
|
||||
use gtk;
|
||||
use gtk::prelude::*;
|
||||
use gtk_sys;
|
||||
|
||||
use super::manager;
|
||||
use super::store::{Store, PlugInfo};
|
||||
use super::plugin_settings_dlg;
|
||||
use super::vimawesome;
|
||||
use nvim_config::NvimConfig;
|
||||
|
||||
pub struct Ui<'a> {
|
||||
manager: &'a Arc<UiMutex<manager::Manager>>,
|
||||
}
|
||||
|
||||
impl<'a> Ui<'a> {
|
||||
pub fn new(manager: &'a Arc<UiMutex<manager::Manager>>) -> Ui<'a> {
|
||||
manager.borrow_mut().reload_store();
|
||||
|
||||
Ui { manager }
|
||||
}
|
||||
|
||||
pub fn show<T: IsA<gtk::Window>>(&mut self, parent: &T) {
|
||||
let dlg = gtk::Dialog::new_with_buttons(
|
||||
Some("Plug"),
|
||||
Some(parent),
|
||||
gtk::DIALOG_DESTROY_WITH_PARENT,
|
||||
&[
|
||||
("Cancel", gtk::ResponseType::Cancel.into()),
|
||||
("Ok", gtk::ResponseType::Ok.into()),
|
||||
],
|
||||
);
|
||||
|
||||
dlg.set_default_size(800, 600);
|
||||
let content = dlg.get_content_area();
|
||||
|
||||
let header_bar = gtk::HeaderBar::new();
|
||||
|
||||
let add_plug_btn = gtk::Button::new_with_label("Add..");
|
||||
add_plug_btn.get_style_context().map(|c| c.add_class("suggested-action"));
|
||||
header_bar.pack_end(&add_plug_btn);
|
||||
|
||||
|
||||
let enable_swc = gtk::Switch::new();
|
||||
enable_swc.set_valign(gtk::Align::Center);
|
||||
enable_swc.show();
|
||||
|
||||
header_bar.pack_end(&enable_swc);
|
||||
|
||||
header_bar.set_title("Plug");
|
||||
header_bar.set_show_close_button(true);
|
||||
header_bar.show();
|
||||
|
||||
dlg.set_titlebar(&header_bar);
|
||||
|
||||
let pages = SettingsPages::new(
|
||||
clone!(add_plug_btn => move |row_name| if row_name == "plugins" {
|
||||
add_plug_btn.show();
|
||||
} else {
|
||||
add_plug_btn.hide();
|
||||
}),
|
||||
);
|
||||
|
||||
enable_swc.set_state(self.manager.borrow().store.is_enabled());
|
||||
|
||||
let plugins = gtk::Box::new(gtk::Orientation::Vertical, 3);
|
||||
let plugs_panel = self.fill_plugin_list(&plugins, &self.manager.borrow().store);
|
||||
|
||||
add_vimawesome_tab(&pages, &self.manager, &plugs_panel);
|
||||
|
||||
|
||||
let plugins_lbl = gtk::Label::new("Plugins");
|
||||
pages.add_page(&plugins_lbl, &plugins, "plugins");
|
||||
|
||||
add_help_tab(
|
||||
&pages,
|
||||
&format!(
|
||||
"NeovimGtk plugin manager is a GUI for vim-plug.\n\
|
||||
It can load plugins from vim-plug configuration if vim-plug sarted and NeovimGtk manager settings is empty.\n\
|
||||
When enabled it generate and load vim-plug as simple vim file at startup before init.vim is processed.\n\
|
||||
So <b>after</b> enabling this manager <b>you must disable vim-plug</b> configuration in init.vim.\n\
|
||||
This manager currently only manage vim-plug configuration and do not any actions on plugin management.\n\
|
||||
So you must call all vim-plug (PlugInstall, PlugUpdate, PlugClean) commands manually.\n\
|
||||
Current configuration source is <b>{}</b>",
|
||||
match self.manager.borrow().plug_manage_state {
|
||||
manager::PlugManageState::NvimGtk => "NeovimGtk config file",
|
||||
manager::PlugManageState::VimPlug => "loaded from vim-plug",
|
||||
manager::PlugManageState::Unknown => "Unknown",
|
||||
}
|
||||
),
|
||||
);
|
||||
|
||||
let manager_ref = self.manager.clone();
|
||||
enable_swc.connect_state_set(move |_, state| {
|
||||
manager_ref.borrow_mut().store.set_enabled(state);
|
||||
Inhibit(false)
|
||||
});
|
||||
|
||||
let manager_ref = self.manager.clone();
|
||||
add_plug_btn.connect_clicked(clone!(dlg => move |_| {
|
||||
show_add_plug_dlg(&dlg, &manager_ref, &plugs_panel);
|
||||
}));
|
||||
|
||||
content.pack_start(&*pages, true, true, 0);
|
||||
content.show_all();
|
||||
|
||||
|
||||
let ok: i32 = gtk::ResponseType::Ok.into();
|
||||
if dlg.run() == ok {
|
||||
let mut manager = self.manager.borrow_mut();
|
||||
manager.clear_removed();
|
||||
manager.save();
|
||||
if let Some(config_path) = NvimConfig::new(manager.generate_config()).generate_config() {
|
||||
if let Some(path) = config_path.to_str() {
|
||||
manager.vim_plug.reload(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dlg.destroy();
|
||||
}
|
||||
|
||||
fn fill_plugin_list(&self, panel: >k::Box, store: &Store) -> gtk::ListBox {
|
||||
let scroll = gtk::ScrolledWindow::new(None, None);
|
||||
scroll.get_style_context().map(|c| c.add_class("view"));
|
||||
let plugs_panel = gtk::ListBox::new();
|
||||
|
||||
for (idx, plug_info) in store.get_plugs().iter().enumerate() {
|
||||
let row = create_plug_row(idx, plug_info, &self.manager);
|
||||
|
||||
plugs_panel.add(&row);
|
||||
}
|
||||
|
||||
scroll.add(&plugs_panel);
|
||||
panel.pack_start(&scroll, true, true, 5);
|
||||
|
||||
panel.pack_start(
|
||||
&create_up_down_btns(&plugs_panel, &self.manager),
|
||||
false,
|
||||
true,
|
||||
0,
|
||||
);
|
||||
|
||||
plugs_panel
|
||||
}
|
||||
}
|
||||
|
||||
fn create_up_down_btns(
|
||||
plugs_panel: >k::ListBox,
|
||||
manager: &Arc<UiMutex<manager::Manager>>,
|
||||
) -> gtk::Box {
|
||||
let buttons_panel = gtk::Box::new(gtk::Orientation::Horizontal, 5);
|
||||
let up_btn = gtk::Button::new_from_icon_name("go-up-symbolic", gtk_sys::GTK_ICON_SIZE_BUTTON as i32);
|
||||
let down_btn = gtk::Button::new_from_icon_name("go-down-symbolic", gtk_sys::GTK_ICON_SIZE_BUTTON as i32);
|
||||
|
||||
up_btn.connect_clicked(clone!(plugs_panel, manager => move |_| {
|
||||
if let Some(row) = plugs_panel.get_selected_row() {
|
||||
let idx = row.get_index();
|
||||
if idx > 0 {
|
||||
plugs_panel.unselect_row(&row);
|
||||
plugs_panel.remove(&row);
|
||||
plugs_panel.insert(&row, idx - 1);
|
||||
plugs_panel.select_row(&row);
|
||||
manager.borrow_mut().move_item(idx as usize, -1);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
down_btn.connect_clicked(clone!(plugs_panel, manager => move |_| {
|
||||
if let Some(row) = plugs_panel.get_selected_row() {
|
||||
let idx = row.get_index();
|
||||
let mut manager = manager.borrow_mut();
|
||||
if idx >= 0 && idx < manager.store.plugs_count() as i32 {
|
||||
plugs_panel.unselect_row(&row);
|
||||
plugs_panel.remove(&row);
|
||||
plugs_panel.insert(&row, idx + 1);
|
||||
plugs_panel.select_row(&row);
|
||||
manager.move_item(idx as usize, 1);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
buttons_panel.pack_start(&up_btn, false, true, 0);
|
||||
buttons_panel.pack_start(&down_btn, false, true, 0);
|
||||
buttons_panel.set_halign(gtk::Align::Center);
|
||||
|
||||
buttons_panel
|
||||
}
|
||||
|
||||
fn populate_get_plugins(
|
||||
query: Option<String>,
|
||||
get_plugins: >k::Box,
|
||||
manager: Arc<UiMutex<manager::Manager>>,
|
||||
plugs_panel: gtk::ListBox,
|
||||
) {
|
||||
let plugs_panel = UiMutex::new(plugs_panel);
|
||||
let get_plugins = UiMutex::new(get_plugins.clone());
|
||||
vimawesome::call(query, move |res| {
|
||||
let panel = get_plugins.borrow();
|
||||
for child in panel.get_children() {
|
||||
panel.remove(&child);
|
||||
}
|
||||
match res {
|
||||
Ok(list) => {
|
||||
let result = vimawesome::build_result_panel(&list, move |new_plug| {
|
||||
add_plugin(&manager, &*plugs_panel.borrow(), new_plug);
|
||||
});
|
||||
panel.pack_start(&result, true, true, 0);
|
||||
}
|
||||
Err(e) => {
|
||||
panel.pack_start(>k::Label::new(format!("{}", e).as_str()), false, true, 0);
|
||||
error!("{}", e)
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn create_plug_row(
|
||||
plug_idx: usize,
|
||||
plug_info: &PlugInfo,
|
||||
manager: &Arc<UiMutex<manager::Manager>>,
|
||||
) -> gtk::ListBoxRow {
|
||||
let row = gtk::ListBoxRow::new();
|
||||
let row_container = gtk::Box::new(gtk::Orientation::Vertical, 5);
|
||||
row_container.set_border_width(5);
|
||||
let hbox = gtk::Box::new(gtk::Orientation::Horizontal, 5);
|
||||
let label_box = create_plug_label(plug_info);
|
||||
|
||||
|
||||
let button_box = gtk::Box::new(gtk::Orientation::Horizontal, 0);
|
||||
button_box.set_halign(gtk::Align::End);
|
||||
|
||||
let exists_button_box = gtk::Box::new(gtk::Orientation::Horizontal, 5);
|
||||
|
||||
let remove_btn = gtk::Button::new_with_label("Remove");
|
||||
exists_button_box.pack_start(&remove_btn, false, true, 0);
|
||||
|
||||
let undo_btn = gtk::Button::new_with_label("Undo");
|
||||
|
||||
|
||||
row_container.pack_start(&hbox, true, true, 0);
|
||||
hbox.pack_start(&label_box, true, true, 0);
|
||||
button_box.pack_start(&exists_button_box, false, true, 0);
|
||||
hbox.pack_start(&button_box, false, true, 0);
|
||||
|
||||
row.add(&row_container);
|
||||
|
||||
|
||||
remove_btn.connect_clicked(
|
||||
clone!(manager, label_box, button_box, exists_button_box, undo_btn => move |_| {
|
||||
label_box.set_sensitive(false);
|
||||
button_box.remove(&exists_button_box);
|
||||
button_box.pack_start(&undo_btn, false, true, 0);
|
||||
button_box.show_all();
|
||||
manager.borrow_mut().store.remove_plug(plug_idx);
|
||||
}),
|
||||
);
|
||||
|
||||
undo_btn.connect_clicked(
|
||||
clone!(manager, label_box, button_box, exists_button_box, undo_btn => move |_| {
|
||||
label_box.set_sensitive(true);
|
||||
button_box.remove(&undo_btn);
|
||||
button_box.pack_start(&exists_button_box, false, true, 0);
|
||||
button_box.show_all();
|
||||
manager.borrow_mut().store.restore_plug(plug_idx);
|
||||
}),
|
||||
);
|
||||
|
||||
row
|
||||
}
|
||||
|
||||
fn show_add_plug_dlg<F: IsA<gtk::Window>>(
|
||||
parent: &F,
|
||||
manager: &Arc<UiMutex<manager::Manager>>,
|
||||
plugs_panel: >k::ListBox,
|
||||
) {
|
||||
if let Some(new_plugin) = plugin_settings_dlg::Builder::new("Add plugin").show(parent) {
|
||||
add_plugin(manager, plugs_panel, new_plugin);
|
||||
}
|
||||
}
|
||||
|
||||
fn add_plugin(
|
||||
manager: &Arc<UiMutex<manager::Manager>>,
|
||||
plugs_panel: >k::ListBox,
|
||||
new_plugin: PlugInfo,
|
||||
) -> bool {
|
||||
let row = create_plug_row(manager.borrow().store.plugs_count(), &new_plugin, manager);
|
||||
|
||||
if manager.borrow_mut().add_plug(new_plugin) {
|
||||
row.show_all();
|
||||
plugs_panel.add(&row);
|
||||
true
|
||||
} else {
|
||||
let dlg = gtk::MessageDialog::new(
|
||||
None::<>k::Window>,
|
||||
gtk::DialogFlags::empty(),
|
||||
gtk::MessageType::Error,
|
||||
gtk::ButtonsType::Ok,
|
||||
"Plugin with this name or path already exists",
|
||||
);
|
||||
dlg.run();
|
||||
dlg.destroy();
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn create_plug_label(plug_info: &PlugInfo) -> gtk::Box {
|
||||
let label_box = gtk::Box::new(gtk::Orientation::Vertical, 5);
|
||||
|
||||
let name_lbl = gtk::Label::new(None);
|
||||
name_lbl.set_markup(&format!("<b>{}</b>", plug_info.name));
|
||||
name_lbl.set_halign(gtk::Align::Start);
|
||||
let url_lbl = gtk::Label::new(Some(plug_info.get_plug_path().as_str()));
|
||||
url_lbl.set_halign(gtk::Align::Start);
|
||||
|
||||
|
||||
label_box.pack_start(&name_lbl, true, true, 0);
|
||||
label_box.pack_start(&url_lbl, true, true, 0);
|
||||
label_box
|
||||
}
|
||||
|
||||
fn add_vimawesome_tab(
|
||||
pages: &SettingsPages,
|
||||
manager: &Arc<UiMutex<manager::Manager>>,
|
||||
plugs_panel: >k::ListBox,
|
||||
) {
|
||||
let get_plugins = gtk::Box::new(gtk::Orientation::Vertical, 0);
|
||||
let spinner = gtk::Spinner::new();
|
||||
let get_plugins_lbl = gtk::Label::new("Get Plugins");
|
||||
pages.add_page(&get_plugins_lbl, &get_plugins, "get_plugins");
|
||||
|
||||
let list_panel = gtk::Box::new(gtk::Orientation::Vertical, 0);
|
||||
let link_button = gtk::Label::new(None);
|
||||
link_button.set_markup(
|
||||
"Plugins are taken from: <a href=\"https://vimawesome.com\">https://vimawesome.com</a>",
|
||||
);
|
||||
let search_entry = gtk::SearchEntry::new();
|
||||
|
||||
get_plugins.pack_start(&link_button, false, true, 10);
|
||||
get_plugins.pack_start(&search_entry, false, true, 5);
|
||||
get_plugins.pack_start(&list_panel, true, true, 0);
|
||||
list_panel.pack_start(&spinner, true, true, 0);
|
||||
spinner.start();
|
||||
|
||||
search_entry.connect_activate(clone!(list_panel, manager, plugs_panel => move |se| {
|
||||
let spinner = gtk::Spinner::new();
|
||||
list_panel.pack_start(&spinner, false, true, 5);
|
||||
spinner.show();
|
||||
spinner.start();
|
||||
populate_get_plugins(se.get_text(), &list_panel, manager.clone(), plugs_panel.clone());
|
||||
}));
|
||||
|
||||
gtk::idle_add(clone!(manager, plugs_panel => move || {
|
||||
populate_get_plugins(None, &list_panel, manager.clone(), plugs_panel.clone());
|
||||
Continue(false)
|
||||
}));
|
||||
}
|
||||
|
||||
fn add_help_tab(pages: &SettingsPages, markup: &str) {
|
||||
let help = gtk::Box::new(gtk::Orientation::Vertical, 3);
|
||||
let label = gtk::Label::new(None);
|
||||
label.set_markup(markup);
|
||||
help.pack_start(&label, true, false, 0);
|
||||
|
||||
let help_lbl = gtk::Label::new("Help");
|
||||
pages.add_page(&help_lbl, &help, "help");
|
||||
}
|
||||
|
||||
struct SettingsPages {
|
||||
categories: gtk::ListBox,
|
||||
stack: gtk::Stack,
|
||||
content: gtk::Box,
|
||||
rows: Rc<RefCell<Vec<(gtk::ListBoxRow, &'static str)>>>,
|
||||
}
|
||||
|
||||
impl SettingsPages {
|
||||
pub fn new<F: Fn(&str) + 'static>(row_selected: F) -> Self {
|
||||
let content = gtk::Box::new(gtk::Orientation::Horizontal, 5);
|
||||
let categories = gtk::ListBox::new();
|
||||
categories.get_style_context().map(|c| c.add_class("view"));
|
||||
let stack = gtk::Stack::new();
|
||||
stack.set_transition_type(gtk::StackTransitionType::Crossfade);
|
||||
let rows: Rc<RefCell<Vec<(gtk::ListBoxRow, &'static str)>>> =
|
||||
Rc::new(RefCell::new(Vec::new()));
|
||||
|
||||
content.pack_start(&categories, false, true, 0);
|
||||
content.pack_start(&stack, true, true, 0);
|
||||
|
||||
categories.connect_row_selected(
|
||||
clone!(stack, rows => move |_, row| if let &Some(ref row) = row {
|
||||
if let Some(ref r) = rows.borrow().iter().find(|r| r.0 == *row) {
|
||||
if let Some(child) = stack.get_child_by_name(&r.1) {
|
||||
stack.set_visible_child(&child);
|
||||
row_selected(&r.1);
|
||||
}
|
||||
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
SettingsPages {
|
||||
categories,
|
||||
stack,
|
||||
content,
|
||||
rows,
|
||||
}
|
||||
}
|
||||
|
||||
fn add_page<W: gtk::IsA<gtk::Widget>>(
|
||||
&self,
|
||||
label: >k::Label,
|
||||
widget: &W,
|
||||
name: &'static str,
|
||||
) {
|
||||
let row = gtk::ListBoxRow::new();
|
||||
|
||||
let hbox = gtk::Box::new(gtk::Orientation::Horizontal, 0);
|
||||
hbox.set_border_width(12);
|
||||
hbox.pack_start(label, false, true, 0);
|
||||
row.add(&hbox);
|
||||
|
||||
self.categories.add(&row);
|
||||
self.stack.add_named(widget, name);
|
||||
self.rows.borrow_mut().push((row, name));
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for SettingsPages {
|
||||
type Target = gtk::Box;
|
||||
|
||||
fn deref(&self) -> >k::Box {
|
||||
&self.content
|
||||
}
|
||||
}
|
109
src/plug_manager/vim_plug.rs
Normal file
109
src/plug_manager/vim_plug.rs
Normal file
@ -0,0 +1,109 @@
|
||||
use std::rc::Rc;
|
||||
use std::cell::{RefCell, RefMut};
|
||||
|
||||
use neovim_lib::{Neovim, NeovimApi};
|
||||
|
||||
use nvim::{NeovimClient, ErrorReport};
|
||||
use value::ValueMapExt;
|
||||
|
||||
pub struct Manager {
|
||||
nvim: Option<Rc<RefCell<NeovimClient>>>,
|
||||
}
|
||||
|
||||
impl Manager {
|
||||
pub fn new() -> Self {
|
||||
Manager { nvim: None }
|
||||
}
|
||||
|
||||
pub fn initialize(&mut self, nvim: Rc<RefCell<NeovimClient>>) {
|
||||
self.nvim = Some(nvim);
|
||||
}
|
||||
|
||||
fn nvim(&self) -> Option<RefMut<Neovim>> {
|
||||
let nvim_client = self.nvim.as_ref().unwrap();
|
||||
if nvim_client.borrow().is_initialized() {
|
||||
Some(RefMut::map(nvim_client.borrow_mut(), |n| n.nvim_mut()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_plugs(&self) -> Result<Box<[VimPlugInfo]>, String> {
|
||||
if let Some(mut nvim) = self.nvim() {
|
||||
let g_plugs = nvim.eval("g:plugs").map_err(|e| {
|
||||
format!("Can't retrive g:plugs map: {}", e)
|
||||
})?;
|
||||
|
||||
let plugs_map = g_plugs
|
||||
.as_map()
|
||||
.ok_or("Can't retrive g:plugs map".to_owned())?
|
||||
.to_attrs_map()?;
|
||||
|
||||
let g_plugs_order = nvim.eval("g:plugs_order").map_err(|e| format!("{}", e))?;
|
||||
|
||||
let order_arr = g_plugs_order.as_array().ok_or(
|
||||
"Can't find g:plugs_order array"
|
||||
.to_owned(),
|
||||
)?;
|
||||
|
||||
let plugs_info: Vec<VimPlugInfo> = order_arr
|
||||
.iter()
|
||||
.map(|n| n.as_str())
|
||||
.filter_map(|name| if let Some(name) = name {
|
||||
plugs_map
|
||||
.get(name)
|
||||
.and_then(|desc| desc.as_map())
|
||||
.and_then(|desc| desc.to_attrs_map().ok())
|
||||
.and_then(|desc| {
|
||||
let uri = desc.get("uri").and_then(|uri| uri.as_str());
|
||||
if let Some(uri) = uri {
|
||||
Some(VimPlugInfo::new(name.to_owned(), uri.to_owned()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
} else {
|
||||
None
|
||||
})
|
||||
.collect();
|
||||
Ok(plugs_info.into_boxed_slice())
|
||||
} else {
|
||||
Err("Nvim not initialized".to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_loaded(&self) -> bool {
|
||||
if let Some(mut nvim) = self.nvim() {
|
||||
let loaded_plug = nvim.eval("exists('g:loaded_plug')");
|
||||
loaded_plug
|
||||
.ok_and_report(&mut *nvim)
|
||||
.and_then(|loaded_plug| loaded_plug.as_i64())
|
||||
.map_or(false, |loaded_plug| if loaded_plug > 0 {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
})
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reload(&self, path: &str) {
|
||||
if let Some(mut nvim) = self.nvim() {
|
||||
nvim.command(&format!("source {}", path)).report_err(&mut *nvim);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct VimPlugInfo {
|
||||
pub name: String,
|
||||
pub uri: String,
|
||||
}
|
||||
|
||||
impl VimPlugInfo {
|
||||
pub fn new(name: String, uri: String) -> Self {
|
||||
VimPlugInfo { name, uri }
|
||||
}
|
||||
}
|
||||
|
162
src/plug_manager/vimawesome.rs
Normal file
162
src/plug_manager/vimawesome.rs
Normal file
@ -0,0 +1,162 @@
|
||||
use std::io;
|
||||
use std::thread;
|
||||
use std::rc::Rc;
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
use serde_json;
|
||||
|
||||
use gtk;
|
||||
use gtk::prelude::*;
|
||||
use glib;
|
||||
|
||||
use super::store::PlugInfo;
|
||||
|
||||
pub fn call<F>(query: Option<String>, cb: F)
|
||||
where
|
||||
F: FnOnce(io::Result<DescriptionList>) + Send + 'static,
|
||||
{
|
||||
thread::spawn(move || {
|
||||
let mut result = Some(request(query.as_ref().map(|s| s.as_ref())));
|
||||
let mut cb = Some(cb);
|
||||
|
||||
glib::idle_add(move || {
|
||||
cb.take().unwrap()(result.take().unwrap());
|
||||
Continue(false)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn request(query: Option<&str>) -> io::Result<DescriptionList> {
|
||||
let child = Command::new("curl")
|
||||
.arg("-s")
|
||||
.arg(format!(
|
||||
"https://vimawesome.com/api/plugins?query={}&page=1",
|
||||
query.unwrap_or("")
|
||||
))
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()?;
|
||||
|
||||
let out = child.wait_with_output()?;
|
||||
|
||||
if out.status.success() {
|
||||
if out.stdout.is_empty() {
|
||||
Ok(DescriptionList::empty())
|
||||
} else {
|
||||
let description_list: DescriptionList = serde_json::from_slice(&out.stdout).map_err(|e| {
|
||||
io::Error::new(io::ErrorKind::Other, e)
|
||||
})?;
|
||||
Ok(description_list)
|
||||
}
|
||||
} else {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!(
|
||||
"curl exit with error:\n{}",
|
||||
match out.status.code() {
|
||||
Some(code) => format!("Exited with status code: {}", code),
|
||||
None => "Process terminated by signal".to_owned(),
|
||||
}
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_result_panel<F: Fn(PlugInfo) + 'static>(
|
||||
list: &DescriptionList,
|
||||
add_cb: F,
|
||||
) -> gtk::ScrolledWindow {
|
||||
let scroll = gtk::ScrolledWindow::new(None, None);
|
||||
scroll.get_style_context().map(|c| c.add_class("view"));
|
||||
let panel = gtk::ListBox::new();
|
||||
|
||||
let cb_ref = Rc::new(add_cb);
|
||||
for plug in list.plugins.iter() {
|
||||
let row = create_plug_row(plug, cb_ref.clone());
|
||||
|
||||
panel.add(&row);
|
||||
}
|
||||
|
||||
scroll.add(&panel);
|
||||
scroll.show_all();
|
||||
scroll
|
||||
}
|
||||
|
||||
fn create_plug_row<F: Fn(PlugInfo) + 'static>(
|
||||
plug: &Description,
|
||||
add_cb: Rc<F>,
|
||||
) -> gtk::ListBoxRow {
|
||||
let row = gtk::ListBoxRow::new();
|
||||
let row_container = gtk::Box::new(gtk::Orientation::Vertical, 5);
|
||||
row_container.set_border_width(5);
|
||||
let hbox = gtk::Box::new(gtk::Orientation::Horizontal, 5);
|
||||
let label_box = create_plug_label(plug);
|
||||
|
||||
|
||||
let button_box = gtk::Box::new(gtk::Orientation::Horizontal, 0);
|
||||
button_box.set_halign(gtk::Align::End);
|
||||
|
||||
let add_btn = gtk::Button::new_with_label("Install");
|
||||
button_box.pack_start(&add_btn, false, true, 0);
|
||||
|
||||
row_container.pack_start(&hbox, true, true, 0);
|
||||
hbox.pack_start(&label_box, true, true, 0);
|
||||
hbox.pack_start(&button_box, false, true, 0);
|
||||
|
||||
row.add(&row_container);
|
||||
|
||||
|
||||
add_btn.connect_clicked(clone!(plug => move |btn| {
|
||||
if let Some(ref github_url) = plug.github_url {
|
||||
btn.set_sensitive(false);
|
||||
add_cb(PlugInfo::new(plug.name.clone(), github_url.clone()));
|
||||
}
|
||||
}));
|
||||
|
||||
row
|
||||
}
|
||||
|
||||
|
||||
fn create_plug_label(plug: &Description) -> gtk::Box {
|
||||
let label_box = gtk::Box::new(gtk::Orientation::Vertical, 5);
|
||||
|
||||
let name_lbl = gtk::Label::new(None);
|
||||
name_lbl.set_markup(&format!(
|
||||
"<b>{}</b> by {}",
|
||||
plug.name,
|
||||
plug.author.as_ref().map(|s| s.as_ref()).unwrap_or(
|
||||
"unknown",
|
||||
)
|
||||
));
|
||||
name_lbl.set_halign(gtk::Align::Start);
|
||||
let url_lbl = gtk::Label::new(None);
|
||||
if let Some(url) = plug.github_url.as_ref() {
|
||||
url_lbl.set_markup(&format!("<a href=\"{}\">{}</a>", url, url));
|
||||
}
|
||||
url_lbl.set_halign(gtk::Align::Start);
|
||||
|
||||
|
||||
label_box.pack_start(&name_lbl, true, true, 0);
|
||||
label_box.pack_start(&url_lbl, true, true, 0);
|
||||
label_box
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct DescriptionList {
|
||||
pub plugins: Box<[Description]>,
|
||||
}
|
||||
|
||||
impl DescriptionList {
|
||||
fn empty() -> DescriptionList {
|
||||
DescriptionList {
|
||||
plugins: Box::new([]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub struct Description {
|
||||
pub name: String,
|
||||
pub github_url: Option<String>,
|
||||
pub author: Option<String>,
|
||||
pub github_stars: Option<i64>,
|
||||
}
|
@ -532,14 +532,9 @@ impl Entry {
|
||||
|
||||
// ----- Store / Load settings
|
||||
//
|
||||
use std::path::PathBuf;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std;
|
||||
use settings::SettingsLoader;
|
||||
use toml;
|
||||
|
||||
const PROJECTS_SETTINGS_FILE: &str = "projects.toml";
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct ProjectSettings {
|
||||
projects: Vec<ProjectEntrySettings>,
|
||||
@ -564,87 +559,21 @@ impl ProjectEntrySettings {
|
||||
}
|
||||
}
|
||||
|
||||
impl ProjectSettings {
|
||||
fn new(projects: Vec<ProjectEntrySettings>) -> ProjectSettings {
|
||||
ProjectSettings { projects }
|
||||
}
|
||||
impl SettingsLoader for ProjectSettings {
|
||||
const SETTINGS_FILE: &'static str = "projects.toml";
|
||||
|
||||
fn empty() -> ProjectSettings {
|
||||
ProjectSettings { projects: vec![] }
|
||||
}
|
||||
|
||||
fn load_from_file(path: &Path) -> Result<ProjectSettings, String> {
|
||||
if path.exists() {
|
||||
let mut file = File::open(path).map_err(|e| format!("{}", e))?;
|
||||
let mut contents = String::new();
|
||||
file.read_to_string(&mut contents)
|
||||
.map_err(|e| format!("{}", e))?;
|
||||
toml::from_str(&contents).map_err(|e| format!("{}", e))
|
||||
} else {
|
||||
Ok(ProjectSettings::empty())
|
||||
}
|
||||
}
|
||||
|
||||
fn load_err() -> Result<ProjectSettings, String> {
|
||||
let mut toml_path = get_app_config_dir_create()?;
|
||||
toml_path.push(PROJECTS_SETTINGS_FILE);
|
||||
ProjectSettings::load_from_file(&toml_path)
|
||||
}
|
||||
|
||||
fn load() -> ProjectSettings {
|
||||
match ProjectSettings::load_err() {
|
||||
Ok(settings) => settings,
|
||||
Err(e) => {
|
||||
println!("{}", e);
|
||||
ProjectSettings::empty()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn save_err(&self) -> Result<(), String> {
|
||||
let mut toml_path = get_app_config_dir_create()?;
|
||||
toml_path.push(PROJECTS_SETTINGS_FILE);
|
||||
let mut file = File::create(toml_path).map_err(|e| format!("{}", e))?;
|
||||
|
||||
let contents = toml::to_vec(self).map_err(|e| format!("{}", e))?;
|
||||
|
||||
file.write_all(&contents).map_err(|e| format!("{}", e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn save(&self) {
|
||||
match self.save_err() {
|
||||
Ok(()) => (),
|
||||
Err(e) => println!("{}", e),
|
||||
}
|
||||
fn from_str(s: &str) -> Result<Self, String> {
|
||||
toml::from_str(&s).map_err(|e| format!("{}", e))
|
||||
}
|
||||
}
|
||||
|
||||
fn get_app_config_dir_create() -> Result<PathBuf, String> {
|
||||
let config_dir = get_app_config_dir()?;
|
||||
|
||||
std::fs::create_dir_all(&config_dir)
|
||||
.map_err(|e| format!("{}", e))?;
|
||||
|
||||
Ok(config_dir)
|
||||
}
|
||||
|
||||
fn get_app_config_dir() -> Result<PathBuf, String> {
|
||||
let mut config_dir = get_xdg_config_dir()?;
|
||||
|
||||
config_dir.push("nvim-gtk");
|
||||
|
||||
Ok(config_dir)
|
||||
}
|
||||
|
||||
fn get_xdg_config_dir() -> Result<PathBuf, String> {
|
||||
if let Ok(config_path) = std::env::var("XDG_CONFIG_HOME") {
|
||||
return Ok(PathBuf::from(config_path));
|
||||
impl ProjectSettings {
|
||||
fn new(projects: Vec<ProjectEntrySettings>) -> ProjectSettings {
|
||||
ProjectSettings { projects }
|
||||
}
|
||||
|
||||
let mut home_dir = std::env::home_dir()
|
||||
.ok_or("Impossible to get your home dir!")?;
|
||||
home_dir.push(".config");
|
||||
Ok(home_dir)
|
||||
}
|
||||
|
||||
|
@ -101,3 +101,78 @@ fn monospace_font_changed(mut shell: &mut Shell, state: &mut State) {
|
||||
state.update_font(&mut shell);
|
||||
}
|
||||
}
|
||||
|
||||
use std::path::Path;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
|
||||
use toml;
|
||||
use serde;
|
||||
|
||||
use dirs;
|
||||
|
||||
pub trait SettingsLoader: Sized + serde::Serialize {
|
||||
const SETTINGS_FILE: &'static str;
|
||||
|
||||
fn empty() -> Self;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, String>;
|
||||
|
||||
fn load() -> Self {
|
||||
match load_err() {
|
||||
Ok(settings) => settings,
|
||||
Err(e) => {
|
||||
println!("{}", e);
|
||||
Self::empty()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_file_exists() -> bool {
|
||||
if let Ok(mut toml_path) = dirs::get_app_config_dir() {
|
||||
toml_path.push(Self::SETTINGS_FILE);
|
||||
toml_path.is_file()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn save(&self) {
|
||||
match save_err(self) {
|
||||
Ok(()) => (),
|
||||
Err(e) => error!("{}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn load_from_file<T: SettingsLoader>(path: &Path) -> Result<T, String> {
|
||||
if path.exists() {
|
||||
let mut file = File::open(path).map_err(|e| format!("{}", e))?;
|
||||
let mut contents = String::new();
|
||||
file.read_to_string(&mut contents).map_err(
|
||||
|e| format!("{}", e),
|
||||
)?;
|
||||
T::from_str(&contents)
|
||||
} else {
|
||||
Ok(T::empty())
|
||||
}
|
||||
}
|
||||
|
||||
fn load_err<T: SettingsLoader>() -> Result<T, String> {
|
||||
let mut toml_path = dirs::get_app_config_dir_create()?;
|
||||
toml_path.push(T::SETTINGS_FILE);
|
||||
load_from_file(&toml_path)
|
||||
}
|
||||
|
||||
|
||||
fn save_err<T: SettingsLoader>(sl: &T) -> Result<(), String> {
|
||||
let mut toml_path = dirs::get_app_config_dir_create()?;
|
||||
toml_path.push(T::SETTINGS_FILE);
|
||||
let mut file = File::create(toml_path).map_err(|e| format!("{}", e))?;
|
||||
|
||||
let contents = toml::to_vec::<T>(sl).map_err(|e| format!("{}", e))?;
|
||||
|
||||
file.write_all(&contents).map_err(|e| format!("{}", e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
30
src/shell.rs
30
src/shell.rs
@ -79,6 +79,7 @@ pub struct State {
|
||||
options: ShellOptions,
|
||||
|
||||
detach_cb: Option<Box<RefCell<FnMut() + Send + 'static>>>,
|
||||
nvim_started_cb: Option<Box<RefCell<FnMut() + Send + 'static>>>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
@ -112,6 +113,7 @@ impl State {
|
||||
options,
|
||||
|
||||
detach_cb: None,
|
||||
nvim_started_cb: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -156,6 +158,17 @@ impl State {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_nvim_started_cb<F>(&mut self, cb: Option<F>)
|
||||
where
|
||||
F: FnMut() + Send + 'static,
|
||||
{
|
||||
if cb.is_some() {
|
||||
self.nvim_started_cb = Some(Box::new(RefCell::new(cb.unwrap())));
|
||||
} else {
|
||||
self.nvim_started_cb = None;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_font_desc(&self) -> &FontDescription {
|
||||
self.font_ctx.font_description()
|
||||
}
|
||||
@ -581,6 +594,14 @@ impl Shell {
|
||||
let mut state = self.state.borrow_mut();
|
||||
state.set_detach_cb(cb);
|
||||
}
|
||||
|
||||
pub fn set_nvim_started_cb<F>(&self, cb: Option<F>)
|
||||
where
|
||||
F: FnMut() + Send + 'static,
|
||||
{
|
||||
let mut state = self.state.borrow_mut();
|
||||
state.set_nvim_started_cb(cb);
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Shell {
|
||||
@ -763,9 +784,11 @@ fn init_nvim_async(
|
||||
});
|
||||
|
||||
// attach ui
|
||||
let state_ref = state_arc.clone();
|
||||
let mut nvim = Some(nvim);
|
||||
glib::idle_add(move || {
|
||||
let mut nvim = nvim.take().unwrap();
|
||||
|
||||
if let Err(err) = nvim::post_start_init(
|
||||
&mut nvim,
|
||||
options.open_path.as_ref(),
|
||||
@ -773,15 +796,18 @@ fn init_nvim_async(
|
||||
rows as u64,
|
||||
)
|
||||
{
|
||||
show_nvim_init_error(&err, state_arc.clone());
|
||||
show_nvim_init_error(&err, state_ref.clone());
|
||||
} else {
|
||||
let mut state = state_arc.borrow_mut();
|
||||
let mut state = state_ref.borrow_mut();
|
||||
state.nvim.borrow_mut().set_initialized(nvim);
|
||||
state.cursor.as_mut().unwrap().start();
|
||||
}
|
||||
|
||||
Continue(false)
|
||||
});
|
||||
|
||||
|
||||
idle_cb_call!(state_arc.nvim_started_cb());
|
||||
}
|
||||
|
||||
fn draw_initializing(state: &State, ctx: &cairo::Context) {
|
||||
|
91
src/ui.rs
91
src/ui.rs
@ -14,6 +14,24 @@ use settings::Settings;
|
||||
use shell::{Shell, ShellOptions};
|
||||
use shell_dlg;
|
||||
use project::Projects;
|
||||
use plug_manager;
|
||||
|
||||
macro_rules! clone {
|
||||
(@param _) => ( _ );
|
||||
(@param $x:ident) => ( $x );
|
||||
($($n:ident),+ => move || $body:expr) => (
|
||||
{
|
||||
$( let $n = $n.clone(); )+
|
||||
move || $body
|
||||
}
|
||||
);
|
||||
($($n:ident),+ => move |$($p:tt),+| $body:expr) => (
|
||||
{
|
||||
$( let $n = $n.clone(); )+
|
||||
move |$(clone!(@param $p),)+| $body
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
pub struct Ui {
|
||||
initialized: bool,
|
||||
@ -21,6 +39,7 @@ pub struct Ui {
|
||||
settings: Rc<RefCell<Settings>>,
|
||||
shell: Rc<RefCell<Shell>>,
|
||||
projects: Rc<RefCell<Projects>>,
|
||||
plug_manager: Arc<UiMutex<plug_manager::Manager>>,
|
||||
}
|
||||
|
||||
pub struct Components {
|
||||
@ -30,8 +49,8 @@ pub struct Components {
|
||||
|
||||
impl Components {
|
||||
fn new() -> Components {
|
||||
let save_image = Image::new_from_icon_name("document-open",
|
||||
gtk_sys::GTK_ICON_SIZE_SMALL_TOOLBAR as i32);
|
||||
let save_image =
|
||||
Image::new_from_icon_name("document-open", gtk_sys::GTK_ICON_SIZE_SMALL_TOOLBAR as i32);
|
||||
|
||||
Components {
|
||||
open_btn: ToolButton::new(Some(&save_image), "Open"),
|
||||
@ -50,6 +69,9 @@ impl Components {
|
||||
|
||||
impl Ui {
|
||||
pub fn new(options: ShellOptions) -> Ui {
|
||||
let plug_manager = plug_manager::Manager::new();
|
||||
|
||||
let plug_manager = Arc::new(UiMutex::new(plug_manager));
|
||||
let comps = Arc::new(UiMutex::new(Components::new()));
|
||||
let settings = Rc::new(RefCell::new(Settings::new()));
|
||||
let shell = Rc::new(RefCell::new(Shell::new(settings.clone(), options)));
|
||||
@ -63,6 +85,7 @@ impl Ui {
|
||||
shell,
|
||||
settings,
|
||||
projects,
|
||||
plug_manager,
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,24 +119,29 @@ impl Ui {
|
||||
|
||||
let projects = self.projects.clone();
|
||||
header_bar.pack_start(&comps.open_btn);
|
||||
comps
|
||||
.open_btn
|
||||
.connect_clicked(move |_| projects.borrow_mut().show());
|
||||
comps.open_btn.connect_clicked(
|
||||
move |_| projects.borrow_mut().show(),
|
||||
);
|
||||
|
||||
let save_image = Image::new_from_icon_name("document-save",
|
||||
gtk_sys::GTK_ICON_SIZE_SMALL_TOOLBAR as i32);
|
||||
let save_image = Image::new_from_icon_name(
|
||||
"document-save",
|
||||
gtk_sys::GTK_ICON_SIZE_SMALL_TOOLBAR as i32,
|
||||
);
|
||||
let save_btn = ToolButton::new(Some(&save_image), "Save");
|
||||
|
||||
let shell = self.shell.clone();
|
||||
save_btn.connect_clicked(move |_| shell.borrow_mut().edit_save_all());
|
||||
header_bar.pack_start(&save_btn);
|
||||
|
||||
let paste_image = Image::new_from_icon_name("edit-paste",
|
||||
gtk_sys::GTK_ICON_SIZE_SMALL_TOOLBAR as i32);
|
||||
let paste_image = Image::new_from_icon_name(
|
||||
"edit-paste",
|
||||
gtk_sys::GTK_ICON_SIZE_SMALL_TOOLBAR as i32,
|
||||
);
|
||||
let paste_btn = ToolButton::new(Some(&paste_image), "Paste");
|
||||
let shell = self.shell.clone();
|
||||
paste_btn.connect_clicked(move |_| shell.borrow_mut().edit_paste());
|
||||
header_bar.pack_start(&paste_btn);
|
||||
|
||||
header_bar.set_show_close_button(true);
|
||||
|
||||
window.set_titlebar(Some(&header_bar));
|
||||
@ -134,29 +162,56 @@ impl Ui {
|
||||
shell.grab_focus();
|
||||
|
||||
let comps_ref = self.comps.clone();
|
||||
shell.set_detach_cb(Some(move || {
|
||||
shell.set_detach_cb(Some(move || {
|
||||
let comps_ref = comps_ref.clone();
|
||||
gtk::idle_add(move || {
|
||||
comps_ref.borrow().close_window();
|
||||
Continue(false)
|
||||
});
|
||||
}));
|
||||
|
||||
let state_ref = self.shell.borrow().state.clone();
|
||||
let plug_manager_ref = self.plug_manager.clone();
|
||||
shell.set_nvim_started_cb(Some(move || {
|
||||
plug_manager_ref.borrow_mut().init_nvim_client(
|
||||
state_ref.borrow().nvim_clone(),
|
||||
);
|
||||
}));
|
||||
}
|
||||
|
||||
fn create_main_menu(&self, app: >k::Application) {
|
||||
let comps = self.comps.clone();
|
||||
let plug_manager = self.plug_manager.clone();
|
||||
|
||||
let menu = Menu::new();
|
||||
|
||||
let plugs = MenuItem::new("Plugins", None);
|
||||
plugs.set_detailed_action("app.Plugins");
|
||||
menu.append_item(&plugs);
|
||||
|
||||
let about = MenuItem::new("About", None);
|
||||
about.set_detailed_action("app.HelpAbout");
|
||||
menu.append_item(&about);
|
||||
|
||||
app.set_app_menu(Some(&menu));
|
||||
|
||||
let plugs_action = SimpleAction::new("Plugins", None);
|
||||
plugs_action.connect_activate(
|
||||
clone!(comps => move |_, _| plug_manager::Ui::new(&plug_manager).show(
|
||||
comps
|
||||
.borrow()
|
||||
.window
|
||||
.as_ref()
|
||||
.unwrap(),
|
||||
)),
|
||||
);
|
||||
|
||||
let about_action = SimpleAction::new("HelpAbout", None);
|
||||
let comps = self.comps.clone();
|
||||
about_action.connect_activate(move |_, _| on_help_about(&*comps.borrow()));
|
||||
about_action.set_enabled(true);
|
||||
|
||||
app.add_action(&about_action);
|
||||
app.add_action(&plugs_action);
|
||||
}
|
||||
}
|
||||
|
||||
@ -178,13 +233,13 @@ fn gtk_delete(comps: &UiMutex<Components>, shell: &RefCell<Shell>) -> Inhibit {
|
||||
}
|
||||
|
||||
Inhibit(if shell_dlg::can_close_window(comps, shell) {
|
||||
let comps = comps.borrow();
|
||||
comps.close_window();
|
||||
shell.borrow_mut().detach_ui();
|
||||
false
|
||||
} else {
|
||||
true
|
||||
})
|
||||
let comps = comps.borrow();
|
||||
comps.close_window();
|
||||
shell.borrow_mut().detach_ui();
|
||||
false
|
||||
} else {
|
||||
true
|
||||
})
|
||||
}
|
||||
|
||||
pub struct UiMutex<T: ?Sized> {
|
||||
|
Loading…
Reference in New Issue
Block a user