1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
use std::cell::RefCell;

use gtk;
use gtk::prelude::*;
use gtk::{ButtonsType, MessageDialog, MessageType};

use neovim_lib::{CallError, NeovimApi, Value};
use crate::shell::Shell;
use crate::ui::{Components, UiMutex};

pub fn can_close_window(comps: &UiMutex<Components>, shell: &RefCell<Shell>) -> bool {
    let shell = shell.borrow();
    match get_changed_buffers(&*shell) {
        Ok(vec) => {
            if !vec.is_empty() {
                show_not_saved_dlg(comps, &*shell, &vec)
            } else {
                true
            }
        }
        Err(ref err) => {
            error!("Error getting info from nvim: {}", err);
            true
        }
    }
}

fn show_not_saved_dlg(comps: &UiMutex<Components>, shell: &Shell, changed_bufs: &[String]) -> bool {
    let mut changed_files = changed_bufs
        .iter()
        .map(|n| if n.is_empty() { "<No name>" } else { n })
        .fold(String::new(), |acc, v| acc + v + "\n");
    changed_files.pop();

    let flags = gtk::DialogFlags::MODAL | gtk::DialogFlags::DESTROY_WITH_PARENT;
    let dlg = MessageDialog::new(
        Some(comps.borrow().window()),
        flags,
        MessageType::Question,
        ButtonsType::None,
        &format!("Save changes to '{}'?", changed_files),
    );

    dlg.add_buttons(&[
        ("_Yes", gtk::ResponseType::Yes),
        ("_No", gtk::ResponseType::No),
        ("_Cancel", gtk::ResponseType::Cancel),
    ]);

    let res = match dlg.run() {
        gtk::ResponseType::Yes => {
            let state = shell.state.borrow();
            let mut nvim = state.nvim().unwrap();
            match nvim.command("wa") {
                Err(ref err) => {
                    error!("Error: {}", err);
                    false
                }
                _ => true,
            }
        }
        gtk::ResponseType::No => true,
        gtk::ResponseType::Cancel | _ => false,
    };

    dlg.destroy();

    res
}

fn get_changed_buffers(shell: &Shell) -> Result<Vec<String>, CallError> {
    let state = shell.state.borrow();
    let nvim = state.nvim();
    if let Some(mut nvim) = nvim {
        let buffers = nvim.list_bufs().unwrap();

        Ok(buffers
            .iter()
            .map(|buf| {
                (
                    match buf.get_option(&mut nvim, "modified") {
                        Ok(Value::Boolean(val)) => val,
                        Ok(_) => {
                            warn!("Value must be boolean");
                            false
                        }
                        Err(ref err) => {
                            error!("Something going wrong while getting buffer option: {}", err);
                            false
                        }
                    },
                    match buf.get_name(&mut nvim) {
                        Ok(name) => name,
                        Err(ref err) => {
                            error!("Something going wrong while getting buffer name: {}", err);
                            "<Error>".to_owned()
                        }
                    },
                )
            })
            .filter(|e| e.0)
            .map(|e| e.1)
            .collect())
    } else {
        Ok(vec![])
    }
}