use crate::ported::lib::dict::mergedicts;
use serde_json::{Map, Value};
use std::sync::Mutex;
use std::sync::OnceLock;
pub fn pycmd() -> &'static Mutex<Option<String>> {
static M: OnceLock<Mutex<Option<String>>> = OnceLock::new();
M.get_or_init(|| Mutex::new(None))
}
pub fn set_pycmd(new_pycmd: impl Into<String>) {
let mut slot = pycmd().lock().unwrap_or_else(|e| e.into_inner());
*slot = Some(new_pycmd.into());
}
pub fn get_default_pycmd() -> &'static str {
"python3"
}
pub fn _override_from(
config: &mut Map<String, Value>,
key: Option<&str>,
vim_get_var: impl FnOnce() -> Option<Value>,
) {
let overrides = match vim_get_var() {
Some(v) => v,
None => return,
};
let overlay = if let Some(k) = key {
match overrides.get(k) {
Some(v) => v.clone(),
None => return,
}
} else {
overrides
};
if let Some(overlay_map) = overlay.as_object() {
mergedicts(config, overlay_map.clone(), false);
}
}
pub struct VimVarHandler {
pub vim_varname: String,
pub captured: Vec<String>,
}
impl VimVarHandler {
pub fn new(varname: impl Into<String>) -> Self {
Self {
vim_varname: varname.into(),
captured: Vec::new(),
}
}
pub fn emit(&mut self, message: &str, exc_text: Option<&str>) {
let mut combined = message.to_string();
if let Some(exc) = exc_text {
combined.push('\n');
combined.push_str(exc);
}
self.captured.push(combined);
}
}
pub fn create_window_statusline_format(pyeval: &str) -> impl Fn(u64) -> String + '_ {
move |idx: u64| -> String { format!("%!{}('powerline.statusline({})')", pyeval, idx) }
}
pub fn get_matcher_module(match_name: &str, ext: &str) -> (String, String) {
if let Some(dot_idx) = match_name.rfind('.') {
let (module, rest) = match_name.split_at(dot_idx);
let function = &rest[1..]; (module.to_string(), function.to_string())
} else {
(
format!("powerline.matchers.{}", ext),
match_name.to_string(),
)
}
}
pub struct VimPowerline {
pub last_window_id: u64,
pub pyeval: String,
pub local_themes: Vec<(String, Map<String, Value>)>,
}
impl Default for VimPowerline {
fn default() -> Self {
Self::new("PowerlinePyeval")
}
}
impl VimPowerline {
pub fn new(pyeval: impl Into<String>) -> Self {
Self {
last_window_id: 1,
pyeval: pyeval.into(),
local_themes: Vec::new(),
}
}
pub fn init(pyeval: impl Into<String>) -> Self {
Self::new(pyeval)
}
pub fn create_window_statusline_constructor(&self) -> impl Fn(u64) -> String + '_ {
create_window_statusline_format(&self.pyeval)
}
pub fn load_main_config(
base: Map<String, Value>,
config_overrides: Option<Map<String, Value>>,
use_var_handler: bool,
) -> Map<String, Value> {
let mut main_config = base;
if let Some(over) = config_overrides {
_override_from(&mut main_config, None, || Some(Value::Object(over)));
}
if use_var_handler {
let common_entry = main_config
.entry("common".to_string())
.or_insert_with(|| Value::Object(Map::new()));
if let Value::Object(common_obj) = common_entry {
let log_file = common_obj
.entry("log_file".to_string())
.or_insert_with(|| Value::Array(Vec::new()));
if let Value::Array(arr) = log_file {
arr.push(serde_json::json!([
"powerline.vim.VimVarHandler",
[["powerline_log_messages"]]
]));
}
}
}
main_config
}
pub fn load_theme_config(
base: Map<String, Value>,
theme_overrides: Option<Map<String, Value>>,
name: &str,
) -> Map<String, Value> {
let mut config = base;
if let Some(over) = theme_overrides {
_override_from(&mut config, Some(name), || Some(Value::Object(over)));
}
config
}
pub fn get_matcher<F>(
match_name: &str,
ext: &str,
get_module_attr: F,
) -> Option<(String, String)>
where
F: Fn(&str, &str) -> bool,
{
let (module, function) = if let Some(idx) = match_name.rfind('.') {
(
match_name[..idx].to_string(),
match_name[idx + 1..].to_string(),
)
} else {
(
format!("powerline.matchers.{}", ext),
match_name.to_string(),
)
};
if get_module_attr(&module, &function) {
Some((module, function))
} else {
None
}
}
pub fn do_setup<R>(
pyeval: Option<&str>,
pycmd: Option<&str>,
can_replace_pyeval: bool,
mut register_pycmd: R,
) -> (String, String)
where
R: FnMut(&str),
{
let resolved_pyeval = match pyeval {
Some(p) => p.to_string(),
None => {
let _ = can_replace_pyeval;
"py3eval".to_string()
}
};
let resolved_pycmd = match pycmd {
Some(p) => p.to_string(),
None => "py3".to_string(),
};
register_pycmd(&resolved_pycmd);
(resolved_pyeval, resolved_pycmd)
}
pub fn add_local_theme(&mut self, key: impl Into<String>, config: Map<String, Value>) -> bool {
self.local_themes.push((key.into(), config));
true
}
pub fn failed_unicode_message(window_id: u64) -> String {
format!("No window {}", window_id)
}
pub fn setup_components(&self, components: Option<&[&str]>) -> Vec<String> {
let defaults = ["statusline", "tabline"];
let comps = components.unwrap_or(&defaults);
let mut out: Vec<String> = Vec::new();
for c in comps {
if *c == "statusline" {
out.push(format!(
"set statusline=%!{}('powerline.new_window()')",
self.pyeval
));
}
if *c == "tabline" {
out.push(format!(
"set tabline=%!{}('powerline.tabline()')",
self.pyeval
));
}
}
out
}
pub fn reset_highlight<F>(reset_renderer: Option<F>)
where
F: FnOnce(),
{
if let Some(f) = reset_renderer {
f();
}
}
pub fn assign_window_id(
existing: Option<u64>,
last_window_id: u64,
conflict: bool,
) -> (u64, u64, bool) {
match existing {
Some(id) if !conflict => (id, last_window_id, false),
_ => (last_window_id, last_window_id + 1, true),
}
}
pub fn tabline_fallback_window(
current_window_existing_id: Option<u64>,
current_window_number: u64,
last_window_id: u64,
) -> (u64, u64) {
let window_id = current_window_existing_id.unwrap_or(last_window_id);
(window_id, current_window_number)
}
pub fn do_pyeval_command(json_encoded_result: &str) -> String {
format!("return {}", json_encoded_result)
}
pub fn statusline_no_window_message(window_id: Option<u64>) -> Option<String> {
Some(format!("No window {}", window_id.unwrap_or(0)))
}
pub fn statusline<W, R>(window_id: Option<u64>, win_idx: W, render: R) -> String
where
W: FnOnce(Option<u64>) -> Option<(u64, u64, i64)>,
R: FnOnce(u64, u64, i64) -> String,
{
match win_idx(window_id) {
Some((w, wid, wn)) => render(w, wid, wn),
None => Self::statusline_no_window_message(window_id).unwrap_or_default(),
}
}
pub fn tabline<W, F, R>(win_idx_none: W, current_window_fallback: F, render: R) -> String
where
W: FnOnce() -> Option<(u64, u64, i64)>,
F: FnOnce() -> (u64, u64, i64),
R: FnOnce(u64, u64, i64, bool) -> String,
{
match win_idx_none() {
Some((w, wid, wn)) => render(w, wid, wn, true),
None => {
let (w, wid, wn) = current_window_fallback();
render(w, wid, wn, true)
}
}
}
pub fn new_win_idx<F>(
windows: &[(u64, Option<u64>, i64, bool)],
window_id: Option<u64>,
mut last_window_id: u64,
mut set_window_var: F,
) -> Option<(u64, u64, i64)>
where
F: FnMut(u64, u64),
{
let mut r: Option<(u64, u64, i64)> = None;
for (window, curwindow_id, winnr, is_current) in windows {
let assigned_id = match curwindow_id {
Some(id) if !(r.is_some() && *id == window_id.unwrap_or(0)) => *id,
_ => {
let new_id = last_window_id;
last_window_id += 1;
set_window_var(*window, new_id);
new_id
}
};
let matches = if let Some(wid) = window_id {
assigned_id == wid
} else {
*is_current
};
if matches {
r = Some((*window, assigned_id, *winnr));
}
}
r
}
pub fn old_win_idx<F>(
windows: &[(u64, Option<u64>, i64, bool)],
window_id: Option<u64>,
last_window_id: u64,
set_window_var: F,
) -> Option<(u64, u64, i64)>
where
F: FnMut(u64, u64),
{
Self::new_win_idx(windows, window_id, last_window_id, set_window_var)
}
pub fn do_pyeval(evaluated: &serde_json::Value) -> String {
Self::do_pyeval_command(&serde_json::to_string(evaluated).unwrap_or_default())
}
pub fn get_local_themes<R, L>(
local_themes: &Map<String, Value>,
mut resolve_matcher: R,
mut load_theme: L,
) -> Map<String, Value>
where
R: FnMut(&str) -> Option<String>,
L: FnMut(&str) -> Value,
{
if local_themes.is_empty() {
return Map::new();
}
let mut out = Map::new();
for (k, v) in local_themes {
let matcher_key = if k == "__tabline__" {
Some("__tabline__".to_string())
} else {
resolve_matcher(k)
};
if let Some(matcher) = matcher_key {
let val_str = v.as_str().unwrap_or("");
let theme = load_theme(val_str);
let mut entry = Map::new();
entry.insert("config".to_string(), theme);
out.insert(matcher, Value::Object(entry));
}
}
out
}
pub fn get_config_paths(
powerline_config_paths: Option<Vec<String>>,
super_config_paths: impl FnOnce() -> Vec<String>,
) -> Vec<String> {
match powerline_config_paths {
Some(p) => p,
None => super_config_paths(),
}
}
pub fn new_window<W>(win_idx_none: W) -> Option<(u64, u64)>
where
W: FnOnce() -> Option<(u64, u64)>,
{
win_idx_none()
}
pub fn setup_entry(pyeval: impl Into<String>) -> Self {
Self::new(pyeval)
}
}
pub fn setup(pyeval: impl Into<String>) -> VimPowerline {
VimPowerline::setup_entry(pyeval)
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Mutex;
static TEST_LOCK: OnceLock<Mutex<()>> = OnceLock::new();
macro_rules! lock_globals {
() => {{
TEST_LOCK
.get_or_init(|| Mutex::new(()))
.lock()
.unwrap_or_else(|e| e.into_inner())
}};
}
fn reset_pycmd() {
let mut slot = pycmd().lock().unwrap_or_else(|e| e.into_inner());
*slot = None;
}
#[test]
fn get_default_pycmd_returns_python3() {
assert_eq!(get_default_pycmd(), "python3");
}
#[test]
fn set_pycmd_updates_global() {
let _g = lock_globals!();
reset_pycmd();
set_pycmd("py3eval");
let slot = pycmd().lock().unwrap_or_else(|e| e.into_inner());
assert_eq!(slot.as_deref(), Some("py3eval"));
}
#[test]
fn override_from_no_var_leaves_config_untouched() {
let mut cfg = Map::new();
cfg.insert("k".to_string(), Value::from(1));
_override_from(&mut cfg, None, || None);
assert_eq!(cfg.get("k"), Some(&Value::from(1)));
assert_eq!(cfg.len(), 1);
}
#[test]
fn override_from_no_key_merges_full_dict() {
let mut cfg = Map::new();
cfg.insert("k".to_string(), Value::from(1));
_override_from(&mut cfg, None, || {
let mut o = Map::new();
o.insert("k2".to_string(), Value::from(2));
Some(Value::Object(o))
});
assert_eq!(cfg.get("k"), Some(&Value::from(1)));
assert_eq!(cfg.get("k2"), Some(&Value::from(2)));
}
#[test]
fn override_from_with_matching_key_merges_subdict() {
let mut cfg = Map::new();
_override_from(&mut cfg, Some("theme1"), || {
let mut o = Map::new();
let mut inner = Map::new();
inner.insert("seg".to_string(), Value::String("custom".into()));
o.insert("theme1".to_string(), Value::Object(inner));
Some(Value::Object(o))
});
assert_eq!(cfg.get("seg"), Some(&Value::String("custom".into())));
}
#[test]
fn override_from_with_missing_key_no_op() {
let mut cfg = Map::new();
cfg.insert("k".to_string(), Value::from(1));
_override_from(&mut cfg, Some("not_present"), || {
let mut o = Map::new();
o.insert("other_theme".to_string(), Value::from(2));
Some(Value::Object(o))
});
assert_eq!(cfg.get("k"), Some(&Value::from(1)));
assert_eq!(cfg.len(), 1);
}
#[test]
fn create_window_statusline_format_substitutes_idx() {
let f = create_window_statusline_format("PowerlinePyeval");
let s = f(7);
assert_eq!(s, "%!PowerlinePyeval('powerline.statusline(7)')");
}
#[test]
fn create_window_statusline_format_works_for_zero_idx() {
let f = create_window_statusline_format("PowerlinePyeval");
assert_eq!(f(0), "%!PowerlinePyeval('powerline.statusline(0)')");
}
#[test]
fn create_window_statusline_format_with_custom_pyeval() {
let f = create_window_statusline_format("py3eval");
assert_eq!(f(42), "%!py3eval('powerline.statusline(42)')");
}
#[test]
fn get_matcher_module_splits_dotted_name() {
let (m, f) = get_matcher_module("mymodule.fn_name", "vim");
assert_eq!(m, "mymodule");
assert_eq!(f, "fn_name");
}
#[test]
fn get_matcher_module_unrpartitioned_defaults_to_powerline_matchers() {
let (m, f) = get_matcher_module("plain_name", "vim");
assert_eq!(m, "powerline.matchers.vim");
assert_eq!(f, "plain_name");
}
#[test]
fn get_matcher_module_takes_rightmost_dot() {
let (m, f) = get_matcher_module("a.b.c.fn", "vim");
assert_eq!(m, "a.b.c");
assert_eq!(f, "fn");
}
#[test]
fn vim_var_handler_captures_message() {
let mut h = VimVarHandler::new("powerline_log_messages");
h.emit("hello world", None);
assert_eq!(h.captured.len(), 1);
assert_eq!(h.captured[0], "hello world");
}
#[test]
fn vim_var_handler_appends_exc_text_with_newline() {
let mut h = VimVarHandler::new("v");
h.emit("error msg", Some("Traceback (most recent call last):..."));
assert_eq!(
h.captured[0],
"error msg\nTraceback (most recent call last):..."
);
}
#[test]
fn vim_var_handler_stores_varname_as_ascii() {
let h = VimVarHandler::new("powerline_log_messages");
assert_eq!(h.vim_varname, "powerline_log_messages");
}
#[test]
fn vim_powerline_init_defaults_to_powerlinepyeval() {
let p = VimPowerline::default();
assert_eq!(p.pyeval, "PowerlinePyeval");
assert_eq!(p.last_window_id, 1);
}
#[test]
fn vim_powerline_add_local_theme_records_kv_and_returns_true() {
let mut p = VimPowerline::default();
let mut cfg = Map::new();
cfg.insert("seg".to_string(), Value::String("custom".into()));
let r = p.add_local_theme("matcher_a", cfg.clone());
assert!(r);
assert_eq!(p.local_themes.len(), 1);
assert_eq!(p.local_themes[0].0, "matcher_a");
assert_eq!(p.local_themes[0].1.get("seg"), cfg.get("seg"));
}
#[test]
fn failed_unicode_message_includes_window_id() {
assert_eq!(VimPowerline::failed_unicode_message(7), "No window 7");
}
#[test]
fn setup_components_default_emits_statusline_and_tabline() {
let p = VimPowerline::default();
let r = p.setup_components(None);
assert_eq!(r.len(), 2);
assert!(r[0].contains("statusline=%!PowerlinePyeval"));
assert!(r[1].contains("tabline=%!PowerlinePyeval"));
}
#[test]
fn setup_components_only_statusline() {
let p = VimPowerline::default();
let r = p.setup_components(Some(&["statusline"]));
assert_eq!(r.len(), 1);
assert!(r[0].contains("statusline=%!"));
}
#[test]
fn setup_components_only_tabline() {
let p = VimPowerline::default();
let r = p.setup_components(Some(&["tabline"]));
assert_eq!(r.len(), 1);
assert!(r[0].contains("tabline=%!"));
}
#[test]
fn setup_components_unknown_ignored() {
let p = VimPowerline::default();
let r = p.setup_components(Some(&["unknown"]));
assert!(r.is_empty());
}
#[test]
fn setup_components_uses_custom_pyeval() {
let p = VimPowerline::new("py3eval");
let r = p.setup_components(Some(&["statusline"]));
assert!(r[0].contains("py3eval"));
}
#[test]
fn setup_entry_returns_powerline_with_given_pyeval() {
let p = VimPowerline::setup_entry("py3eval");
assert_eq!(p.pyeval, "py3eval");
assert_eq!(p.last_window_id, 1);
}
#[test]
fn reset_highlight_calls_renderer_when_present() {
use std::cell::Cell;
let called = Cell::new(false);
VimPowerline::reset_highlight(Some(|| called.set(true)));
assert!(called.get());
}
#[test]
fn reset_highlight_no_op_when_renderer_missing() {
let f: Option<fn()> = None;
VimPowerline::reset_highlight(f);
}
#[test]
fn assign_window_id_existing_no_conflict_reuses() {
let (id, last, assigned) = VimPowerline::assign_window_id(Some(7), 10, false);
assert_eq!(id, 7);
assert_eq!(last, 10);
assert!(!assigned);
}
#[test]
fn assign_window_id_no_existing_assigns_and_bumps() {
let (id, last, assigned) = VimPowerline::assign_window_id(None, 10, false);
assert_eq!(id, 10);
assert_eq!(last, 11);
assert!(assigned);
}
#[test]
fn assign_window_id_existing_with_conflict_reassigns() {
let (id, last, assigned) = VimPowerline::assign_window_id(Some(5), 10, true);
assert_eq!(id, 10);
assert_eq!(last, 11);
assert!(assigned);
}
#[test]
fn tabline_fallback_window_uses_existing_id_when_set() {
let (window_id, winnr) = VimPowerline::tabline_fallback_window(Some(3), 5, 100);
assert_eq!(window_id, 3);
assert_eq!(winnr, 5);
}
#[test]
fn tabline_fallback_window_defaults_to_last_window_id() {
let (window_id, winnr) = VimPowerline::tabline_fallback_window(None, 5, 100);
assert_eq!(window_id, 100);
assert_eq!(winnr, 5);
}
#[test]
fn do_pyeval_command_builds_return_statement() {
let cmd = VimPowerline::do_pyeval_command("[1, 2, 3]");
assert_eq!(cmd, "return [1, 2, 3]");
}
#[test]
fn do_pyeval_command_handles_dict_json() {
let cmd = VimPowerline::do_pyeval_command(r#"{"key": "value"}"#);
assert_eq!(cmd, r#"return {"key": "value"}"#);
}
#[test]
fn statusline_no_window_message_with_id() {
let msg = VimPowerline::statusline_no_window_message(Some(5)).unwrap();
assert_eq!(msg, "No window 5");
}
#[test]
fn statusline_no_window_message_without_id() {
let msg = VimPowerline::statusline_no_window_message(None).unwrap();
assert_eq!(msg, "No window 0");
}
#[test]
fn get_local_themes_empty_returns_empty() {
let r = VimPowerline::get_local_themes(
&Map::new(),
|_| Some("matcher".to_string()),
|_| Value::Object(Map::new()),
);
assert!(r.is_empty());
}
#[test]
fn get_local_themes_resolves_matchers() {
let mut input = Map::new();
input.insert("help".to_string(), Value::String("help_theme".into()));
input.insert(
"__tabline__".to_string(),
Value::String("tabline_theme".into()),
);
let r = VimPowerline::get_local_themes(
&input,
|k| Some(format!("matchers.vim.{}", k)),
|v| serde_json::json!({"name": v}),
);
assert!(r.contains_key("matchers.vim.help"));
assert_eq!(r["matchers.vim.help"]["config"]["name"], "help_theme");
assert!(r.contains_key("__tabline__"));
assert_eq!(r["__tabline__"]["config"]["name"], "tabline_theme");
}
#[test]
fn get_local_themes_drops_unresolved_matchers() {
let mut input = Map::new();
input.insert("bogus".to_string(), Value::String("v".into()));
let r = VimPowerline::get_local_themes(
&input,
|_| None, |v| serde_json::json!({"name": v}),
);
assert!(r.is_empty());
}
#[test]
fn get_config_paths_returns_vim_var_when_set() {
let paths =
VimPowerline::get_config_paths(Some(vec!["/etc/powerline".to_string()]), || {
vec!["/should/not/use".to_string()]
});
assert_eq!(paths, vec!["/etc/powerline".to_string()]);
}
#[test]
fn get_config_paths_falls_back_to_super_when_unset() {
let paths = VimPowerline::get_config_paths(None, || vec!["/super/path".to_string()]);
assert_eq!(paths, vec!["/super/path".to_string()]);
}
#[test]
fn new_window_returns_win_idx_result() {
let r = VimPowerline::new_window(|| Some((5, 1)));
assert_eq!(r, Some((5, 1)));
}
#[test]
fn new_window_returns_none_when_no_current_window() {
let r = VimPowerline::new_window(|| None);
assert_eq!(r, None);
}
#[test]
fn statusline_returns_no_window_message_when_win_idx_none() {
let r = VimPowerline::statusline(
Some(42),
|_| None,
|_, _, _| panic!("render should not be called"),
);
assert_eq!(r, "No window 42");
}
#[test]
fn statusline_dispatches_to_render_when_window_resolves() {
let r = VimPowerline::statusline(
Some(3),
|wid| Some((100, wid.unwrap_or(0), 7)),
|w, wid, wn| format!("w={w},wid={wid},wn={wn}"),
);
assert_eq!(r, "w=100,wid=3,wn=7");
}
#[test]
fn tabline_uses_win_idx_result_when_present() {
let r = VimPowerline::tabline(
|| Some((200, 5, 9)),
|| panic!("fallback should not be called"),
|w, wid, wn, tab| format!("w={w},wid={wid},wn={wn},tab={tab}"),
);
assert_eq!(r, "w=200,wid=5,wn=9,tab=true");
}
#[test]
fn tabline_falls_back_to_current_window_when_win_idx_none() {
let r = VimPowerline::tabline(
|| None,
|| (300, 6, 10),
|w, wid, wn, tab| format!("FB w={w},wid={wid},wn={wn},tab={tab}"),
);
assert_eq!(r, "FB w=300,wid=6,wn=10,tab=true");
}
#[test]
fn do_pyeval_returns_return_json_command() {
let r = VimPowerline::do_pyeval(&serde_json::json!(42));
assert_eq!(r, "return 42");
}
#[test]
fn new_win_idx_assigns_id_when_none_and_returns_current() {
let mut assignments: Vec<(u64, u64)> = Vec::new();
let windows = vec![(100_u64, None, 1_i64, true)];
let r = VimPowerline::new_win_idx(&windows, None, 10, |w, id| assignments.push((w, id)));
assert_eq!(r, Some((100, 10, 1)));
assert_eq!(assignments, vec![(100, 10)]);
}
#[test]
fn new_win_idx_reuses_existing_id() {
let windows = vec![(200_u64, Some(7), 2_i64, false)];
let r = VimPowerline::new_win_idx(&windows, Some(7), 99, |_, _| {});
assert_eq!(r, Some((200, 7, 2)));
}
#[test]
fn old_win_idx_delegates_to_new_win_idx() {
let windows = vec![(50_u64, Some(3), 1_i64, false)];
let r = VimPowerline::old_win_idx(&windows, Some(3), 0, |_, _| {});
assert_eq!(r, Some((50, 3, 1)));
}
#[test]
fn module_setup_returns_vim_powerline_with_pyeval() {
let p = setup("MyPyeval");
assert_eq!(p.pyeval, "MyPyeval");
}
#[test]
fn init_constructs_with_default_pyeval() {
let p = VimPowerline::init("MyEval");
assert_eq!(p.pyeval, "MyEval");
assert_eq!(p.last_window_id, 1);
}
#[test]
fn create_window_statusline_constructor_emits_pyeval_call() {
let p = VimPowerline::new("py3eval");
let fmt = p.create_window_statusline_constructor();
assert_eq!(fmt(42), "%!py3eval('powerline.statusline(42)')");
}
#[test]
fn load_main_config_no_overrides_passes_through() {
let mut base = Map::new();
base.insert("a".to_string(), Value::Number(1.into()));
let r = VimPowerline::load_main_config(base.clone(), None, false);
assert_eq!(r, base);
}
#[test]
fn load_main_config_use_var_handler_appends_to_log_file() {
let base = Map::new();
let r = VimPowerline::load_main_config(base, None, true);
let common = r.get("common").unwrap().as_object().unwrap();
let log_file = common.get("log_file").unwrap().as_array().unwrap();
assert_eq!(log_file.len(), 1);
let entry = log_file[0].as_array().unwrap();
assert_eq!(entry[0], "powerline.vim.VimVarHandler");
}
#[test]
fn load_theme_config_no_overrides_passes_through() {
let mut base = Map::new();
base.insert("name".to_string(), Value::String("test".to_string()));
let r = VimPowerline::load_theme_config(base.clone(), None, "test");
assert_eq!(r, base);
}
#[test]
fn get_matcher_dotted_name_splits_via_rpartition() {
let r =
VimPowerline::get_matcher("foo.bar.baz", "vim", |m, f| m == "foo.bar" && f == "baz");
assert_eq!(r, Some(("foo.bar".to_string(), "baz".to_string())));
}
#[test]
fn get_matcher_undotted_uses_powerline_matchers_ext() {
let r = VimPowerline::get_matcher("isfile", "vim", |m, f| {
m == "powerline.matchers.vim" && f == "isfile"
});
assert_eq!(
r,
Some(("powerline.matchers.vim".to_string(), "isfile".to_string()))
);
}
#[test]
fn get_matcher_returns_none_when_function_missing() {
let r = VimPowerline::get_matcher("missing", "vim", |_, _| false);
assert_eq!(r, None);
}
#[test]
fn do_setup_uses_supplied_pyeval_and_pycmd() {
let mut cmd_calls = Vec::<String>::new();
let (py, cm) = VimPowerline::do_setup(Some("CustomEval"), Some("custom_cmd"), true, |c| {
cmd_calls.push(c.to_string())
});
assert_eq!(py, "CustomEval");
assert_eq!(cm, "custom_cmd");
assert_eq!(cmd_calls, vec!["custom_cmd"]);
}
#[test]
fn do_setup_defaults_to_py3eval_when_unspecified() {
let (py, cm) = VimPowerline::do_setup(None, None, true, |_| {});
assert_eq!(py, "py3eval");
assert_eq!(cm, "py3");
}
}