Merge branch 'master' into fix-async-startup
This commit is contained in:
commit
25cdf3ef8f
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)",
|
"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 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_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)",
|
"toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -106,6 +107,11 @@ name = "custom_derive"
|
|||||||
version = "0.1.7"
|
version = "0.1.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
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]]
|
[[package]]
|
||||||
name = "env_logger"
|
name = "env_logger"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
@ -277,6 +283,11 @@ name = "htmlescape"
|
|||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
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]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "0.2.8"
|
version = "0.2.8"
|
||||||
@ -496,6 +507,17 @@ dependencies = [
|
|||||||
"synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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]]
|
[[package]]
|
||||||
name = "siphasher"
|
name = "siphasher"
|
||||||
version = "0.2.2"
|
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 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 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 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 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 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"
|
"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 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 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 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 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 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"
|
"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 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 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_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 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 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"
|
"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 = "1.0"
|
||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
toml = "0.4"
|
toml = "0.4"
|
||||||
|
serde_json = "1.0"
|
||||||
|
|
||||||
#[dependencies.neovim-lib]
|
#[dependencies.neovim-lib]
|
||||||
#git = "https://github.com/daa84/neovim-lib"
|
#git = "https://github.com/daa84/neovim-lib"
|
||||||
|
@ -36,7 +36,8 @@ git clone https://aur.archlinux.org/neovim-gtk-git.git
|
|||||||
cd neovim-gtk-git
|
cd neovim-gtk-git
|
||||||
makepkg -si
|
makepkg -si
|
||||||
```
|
```
|
||||||
|
## flatpak
|
||||||
|
Flatpak package available [here](https://github.com/daa84/neovim-gtk-flatpak)
|
||||||
|
|
||||||
# Build
|
# Build
|
||||||
## Linux
|
## Linux
|
||||||
|
1
build.rs
1
build.rs
@ -37,6 +37,7 @@ fn main() {
|
|||||||
.entry("Return", "\"CR\"")
|
.entry("Return", "\"CR\"")
|
||||||
.entry("Escape", "\"Esc\"")
|
.entry("Escape", "\"Esc\"")
|
||||||
.entry("Delete", "\"Del\"")
|
.entry("Delete", "\"Del\"")
|
||||||
|
.entry("Insert", "\"Insert\"")
|
||||||
.entry("Page_Up", "\"PageUp\"")
|
.entry("Page_Up", "\"PageUp\"")
|
||||||
.entry("Page_Down", "\"PageDown\"")
|
.entry("Page_Down", "\"PageDown\"")
|
||||||
.entry("Enter", "\"CR\"")
|
.entry("Enter", "\"CR\"")
|
||||||
|
@ -89,15 +89,21 @@ impl Cursor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset_state(&mut self) {
|
pub fn reset_state(&mut self) {
|
||||||
self.start();
|
if self.state.borrow().anim_phase != AnimPhase::Busy {
|
||||||
|
self.start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn enter_focus(&mut self) {
|
pub fn enter_focus(&mut self) {
|
||||||
self.start();
|
if self.state.borrow().anim_phase != AnimPhase::Busy {
|
||||||
|
self.start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn leave_focus(&mut self) {
|
pub fn leave_focus(&mut self) {
|
||||||
self.state.borrow_mut().reset_to(AnimPhase::NoFocus);
|
if self.state.borrow().anim_phase != AnimPhase::Busy {
|
||||||
|
self.state.borrow_mut().reset_to(AnimPhase::NoFocus);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn busy_on(&mut self) {
|
pub fn busy_on(&mut self) {
|
||||||
|
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 env_logger;
|
||||||
extern crate htmlescape;
|
extern crate htmlescape;
|
||||||
|
|
||||||
|
extern crate serde;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
extern crate toml;
|
extern crate toml;
|
||||||
|
extern crate serde_json;
|
||||||
|
|
||||||
mod sys;
|
mod sys;
|
||||||
|
|
||||||
|
mod nvim_config;
|
||||||
|
mod dirs;
|
||||||
mod color;
|
mod color;
|
||||||
mod value;
|
mod value;
|
||||||
mod mode;
|
mod mode;
|
||||||
mod ui_model;
|
mod ui_model;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod ui;
|
mod ui;
|
||||||
|
mod plug_manager;
|
||||||
mod nvim;
|
mod nvim;
|
||||||
mod render;
|
mod render;
|
||||||
mod shell;
|
mod shell;
|
||||||
@ -71,6 +76,8 @@ fn main() {
|
|||||||
app.connect_open(open);
|
app.connect_open(open);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gtk::Window::set_default_icon_name("org.daa.NeovimGtk");
|
||||||
|
|
||||||
let args: Vec<String> = env::args().collect();
|
let args: Vec<String> = env::args().collect();
|
||||||
let argv: Vec<&str> = args.iter()
|
let argv: Vec<&str> = args.iter()
|
||||||
.filter(|a| !a.starts_with(BIN_PATH_ARG))
|
.filter(|a| !a.starts_with(BIN_PATH_ARG))
|
||||||
|
@ -21,6 +21,7 @@ use neovim_lib::{Neovim, NeovimApi, Session, UiAttachOptions, CallError};
|
|||||||
|
|
||||||
use ui::UiMutex;
|
use ui::UiMutex;
|
||||||
use shell;
|
use shell;
|
||||||
|
use nvim_config::NvimConfig;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct NvimInitError {
|
pub struct NvimInitError {
|
||||||
@ -107,6 +108,12 @@ pub fn start(
|
|||||||
cmd.arg("--cmd").arg("let &rtp.=',runtime'");
|
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 = Session::new_child_cmd(&mut cmd);
|
||||||
|
|
||||||
let session = match session {
|
let session = match session {
|
||||||
@ -149,16 +156,23 @@ pub fn post_start_init(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub trait ErrorReport {
|
pub trait ErrorReport<T> {
|
||||||
fn report_err(&self, nvim: &mut NeovimApi);
|
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) {
|
fn report_err(&self, _: &mut NeovimApi) {
|
||||||
if let Err(ref err) = *self {
|
if let Err(ref err) = *self {
|
||||||
println!("{}", err);
|
println!("{}", err);
|
||||||
//nvim.report_error(&err_msg).expect("Error report error :)");
|
//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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
// ----- Store / Load settings
|
||||||
//
|
//
|
||||||
use std::path::PathBuf;
|
use settings::SettingsLoader;
|
||||||
use std::fs::File;
|
|
||||||
use std::io::prelude::*;
|
|
||||||
use std;
|
|
||||||
use toml;
|
use toml;
|
||||||
|
|
||||||
const PROJECTS_SETTINGS_FILE: &str = "projects.toml";
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct ProjectSettings {
|
struct ProjectSettings {
|
||||||
projects: Vec<ProjectEntrySettings>,
|
projects: Vec<ProjectEntrySettings>,
|
||||||
@ -564,87 +559,21 @@ impl ProjectEntrySettings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProjectSettings {
|
impl SettingsLoader for ProjectSettings {
|
||||||
fn new(projects: Vec<ProjectEntrySettings>) -> ProjectSettings {
|
const SETTINGS_FILE: &'static str = "projects.toml";
|
||||||
ProjectSettings { projects }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn empty() -> ProjectSettings {
|
fn empty() -> ProjectSettings {
|
||||||
ProjectSettings { projects: vec![] }
|
ProjectSettings { projects: vec![] }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_from_file(path: &Path) -> Result<ProjectSettings, String> {
|
fn from_str(s: &str) -> Result<Self, String> {
|
||||||
if path.exists() {
|
toml::from_str(&s).map_err(|e| format!("{}", e))
|
||||||
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 get_app_config_dir_create() -> Result<PathBuf, String> {
|
impl ProjectSettings {
|
||||||
let config_dir = get_app_config_dir()?;
|
fn new(projects: Vec<ProjectEntrySettings>) -> ProjectSettings {
|
||||||
|
ProjectSettings { projects }
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
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(())
|
||||||
|
}
|
||||||
|
33
src/shell.rs
33
src/shell.rs
@ -79,6 +79,7 @@ pub struct State {
|
|||||||
options: ShellOptions,
|
options: ShellOptions,
|
||||||
|
|
||||||
detach_cb: Option<Box<RefCell<FnMut() + Send + 'static>>>,
|
detach_cb: Option<Box<RefCell<FnMut() + Send + 'static>>>,
|
||||||
|
nvim_started_cb: Option<Box<RefCell<FnMut() + Send + 'static>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
@ -112,6 +113,7 @@ impl State {
|
|||||||
options,
|
options,
|
||||||
|
|
||||||
detach_cb: None,
|
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 {
|
pub fn get_font_desc(&self) -> &FontDescription {
|
||||||
self.font_ctx.font_description()
|
self.font_ctx.font_description()
|
||||||
}
|
}
|
||||||
@ -581,6 +594,14 @@ impl Shell {
|
|||||||
let mut state = self.state.borrow_mut();
|
let mut state = self.state.borrow_mut();
|
||||||
state.set_detach_cb(cb);
|
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 {
|
impl Deref for Shell {
|
||||||
@ -643,10 +664,7 @@ fn gtk_button_press(shell: &mut State, ui_state: &mut UiState, ev: &EventButton)
|
|||||||
|
|
||||||
match ev.get_button() {
|
match ev.get_button() {
|
||||||
1 => mouse_input(shell, "LeftMouse", ev.get_state(), ev.get_position()),
|
1 => mouse_input(shell, "LeftMouse", ev.get_state(), ev.get_position()),
|
||||||
2 => {
|
2 => mouse_input(shell, "MiddleMouse", ev.get_state(), ev.get_position()),
|
||||||
mouse_input(shell, "LeftMouse", ev.get_state(), ev.get_position());
|
|
||||||
shell.edit_paste("*");
|
|
||||||
}
|
|
||||||
3 => mouse_input(shell, "RightMouse", ev.get_state(), ev.get_position()),
|
3 => mouse_input(shell, "RightMouse", ev.get_state(), ev.get_position()),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
@ -781,7 +799,7 @@ fn init_nvim_async(
|
|||||||
|
|
||||||
fn set_nvim_initialized(nvim: Neovim, state_arc: Arc<UiMutex<State>>) {
|
fn set_nvim_initialized(nvim: Neovim, state_arc: Arc<UiMutex<State>>) {
|
||||||
let mut nvim = Some(nvim);
|
let mut nvim = Some(nvim);
|
||||||
glib::idle_add(move || {
|
glib::idle_add(clone!(state_arc => move || {
|
||||||
let mut state = state_arc.borrow_mut();
|
let mut state = state_arc.borrow_mut();
|
||||||
state.nvim.borrow_mut().set_initialized(
|
state.nvim.borrow_mut().set_initialized(
|
||||||
nvim.take().unwrap(),
|
nvim.take().unwrap(),
|
||||||
@ -789,7 +807,10 @@ fn set_nvim_initialized(nvim: Neovim, state_arc: Arc<UiMutex<State>>) {
|
|||||||
state.cursor.as_mut().unwrap().start();
|
state.cursor.as_mut().unwrap().start();
|
||||||
|
|
||||||
Continue(false)
|
Continue(false)
|
||||||
});
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
idle_cb_call!(state_arc.nvim_started_cb());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_initializing(state: &State, ctx: &cairo::Context) {
|
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::{Shell, ShellOptions};
|
||||||
use shell_dlg;
|
use shell_dlg;
|
||||||
use project::Projects;
|
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 {
|
pub struct Ui {
|
||||||
initialized: bool,
|
initialized: bool,
|
||||||
@ -21,6 +39,7 @@ pub struct Ui {
|
|||||||
settings: Rc<RefCell<Settings>>,
|
settings: Rc<RefCell<Settings>>,
|
||||||
shell: Rc<RefCell<Shell>>,
|
shell: Rc<RefCell<Shell>>,
|
||||||
projects: Rc<RefCell<Projects>>,
|
projects: Rc<RefCell<Projects>>,
|
||||||
|
plug_manager: Arc<UiMutex<plug_manager::Manager>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Components {
|
pub struct Components {
|
||||||
@ -30,8 +49,8 @@ pub struct Components {
|
|||||||
|
|
||||||
impl Components {
|
impl Components {
|
||||||
fn new() -> Components {
|
fn new() -> Components {
|
||||||
let save_image = Image::new_from_icon_name("document-open",
|
let save_image =
|
||||||
gtk_sys::GTK_ICON_SIZE_SMALL_TOOLBAR as i32);
|
Image::new_from_icon_name("document-open", gtk_sys::GTK_ICON_SIZE_SMALL_TOOLBAR as i32);
|
||||||
|
|
||||||
Components {
|
Components {
|
||||||
open_btn: ToolButton::new(Some(&save_image), "Open"),
|
open_btn: ToolButton::new(Some(&save_image), "Open"),
|
||||||
@ -50,6 +69,9 @@ impl Components {
|
|||||||
|
|
||||||
impl Ui {
|
impl Ui {
|
||||||
pub fn new(options: ShellOptions) -> 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 comps = Arc::new(UiMutex::new(Components::new()));
|
||||||
let settings = Rc::new(RefCell::new(Settings::new()));
|
let settings = Rc::new(RefCell::new(Settings::new()));
|
||||||
let shell = Rc::new(RefCell::new(Shell::new(settings.clone(), options)));
|
let shell = Rc::new(RefCell::new(Shell::new(settings.clone(), options)));
|
||||||
@ -63,6 +85,7 @@ impl Ui {
|
|||||||
shell,
|
shell,
|
||||||
settings,
|
settings,
|
||||||
projects,
|
projects,
|
||||||
|
plug_manager,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,24 +119,29 @@ impl Ui {
|
|||||||
|
|
||||||
let projects = self.projects.clone();
|
let projects = self.projects.clone();
|
||||||
header_bar.pack_start(&comps.open_btn);
|
header_bar.pack_start(&comps.open_btn);
|
||||||
comps
|
comps.open_btn.connect_clicked(
|
||||||
.open_btn
|
move |_| projects.borrow_mut().show(),
|
||||||
.connect_clicked(move |_| projects.borrow_mut().show());
|
);
|
||||||
|
|
||||||
let save_image = Image::new_from_icon_name("document-save",
|
let save_image = Image::new_from_icon_name(
|
||||||
gtk_sys::GTK_ICON_SIZE_SMALL_TOOLBAR as i32);
|
"document-save",
|
||||||
|
gtk_sys::GTK_ICON_SIZE_SMALL_TOOLBAR as i32,
|
||||||
|
);
|
||||||
let save_btn = ToolButton::new(Some(&save_image), "Save");
|
let save_btn = ToolButton::new(Some(&save_image), "Save");
|
||||||
|
|
||||||
let shell = self.shell.clone();
|
let shell = self.shell.clone();
|
||||||
save_btn.connect_clicked(move |_| shell.borrow_mut().edit_save_all());
|
save_btn.connect_clicked(move |_| shell.borrow_mut().edit_save_all());
|
||||||
header_bar.pack_start(&save_btn);
|
header_bar.pack_start(&save_btn);
|
||||||
|
|
||||||
let paste_image = Image::new_from_icon_name("edit-paste",
|
let paste_image = Image::new_from_icon_name(
|
||||||
gtk_sys::GTK_ICON_SIZE_SMALL_TOOLBAR as i32);
|
"edit-paste",
|
||||||
|
gtk_sys::GTK_ICON_SIZE_SMALL_TOOLBAR as i32,
|
||||||
|
);
|
||||||
let paste_btn = ToolButton::new(Some(&paste_image), "Paste");
|
let paste_btn = ToolButton::new(Some(&paste_image), "Paste");
|
||||||
let shell = self.shell.clone();
|
let shell = self.shell.clone();
|
||||||
paste_btn.connect_clicked(move |_| shell.borrow_mut().edit_paste());
|
paste_btn.connect_clicked(move |_| shell.borrow_mut().edit_paste());
|
||||||
header_bar.pack_start(&paste_btn);
|
header_bar.pack_start(&paste_btn);
|
||||||
|
|
||||||
header_bar.set_show_close_button(true);
|
header_bar.set_show_close_button(true);
|
||||||
|
|
||||||
window.set_titlebar(Some(&header_bar));
|
window.set_titlebar(Some(&header_bar));
|
||||||
@ -134,29 +162,56 @@ impl Ui {
|
|||||||
shell.grab_focus();
|
shell.grab_focus();
|
||||||
|
|
||||||
let comps_ref = self.comps.clone();
|
let comps_ref = self.comps.clone();
|
||||||
shell.set_detach_cb(Some(move || {
|
shell.set_detach_cb(Some(move || {
|
||||||
let comps_ref = comps_ref.clone();
|
let comps_ref = comps_ref.clone();
|
||||||
gtk::idle_add(move || {
|
gtk::idle_add(move || {
|
||||||
comps_ref.borrow().close_window();
|
comps_ref.borrow().close_window();
|
||||||
Continue(false)
|
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) {
|
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 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);
|
let about = MenuItem::new("About", None);
|
||||||
about.set_detailed_action("app.HelpAbout");
|
about.set_detailed_action("app.HelpAbout");
|
||||||
menu.append_item(&about);
|
menu.append_item(&about);
|
||||||
|
|
||||||
app.set_app_menu(Some(&menu));
|
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 about_action = SimpleAction::new("HelpAbout", None);
|
||||||
let comps = self.comps.clone();
|
|
||||||
about_action.connect_activate(move |_, _| on_help_about(&*comps.borrow()));
|
about_action.connect_activate(move |_, _| on_help_about(&*comps.borrow()));
|
||||||
about_action.set_enabled(true);
|
about_action.set_enabled(true);
|
||||||
|
|
||||||
app.add_action(&about_action);
|
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) {
|
Inhibit(if shell_dlg::can_close_window(comps, shell) {
|
||||||
let comps = comps.borrow();
|
let comps = comps.borrow();
|
||||||
comps.close_window();
|
comps.close_window();
|
||||||
shell.borrow_mut().detach_ui();
|
shell.borrow_mut().detach_ui();
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct UiMutex<T: ?Sized> {
|
pub struct UiMutex<T: ?Sized> {
|
||||||
|
Loading…
Reference in New Issue
Block a user