#[derive(Debug, Clone, Default)]
pub struct MatcherInfo {
pub bufnr: i32,
pub buffer_name: Option<Vec<u8>>,
pub buffer_options: std::collections::HashMap<String, String>,
}
pub fn buffer_name(matcher_info: &MatcherInfo) -> Option<Vec<u8>> {
matcher_info.buffer_name.clone()
}
pub fn vim_getbufoption(matcher_info: &MatcherInfo, option: &str) -> String {
matcher_info
.buffer_options
.get(option)
.cloned()
.unwrap_or_default()
}
#[derive(Debug, Clone)]
pub struct VimTabpage {
pub number: i32,
pub window: VimWindow,
}
#[derive(Debug, Clone)]
pub struct VimWindow {
pub number: i32,
pub window_id: i32,
pub buffer: VimBuffer,
}
#[derive(Debug, Clone)]
pub struct VimBuffer {
pub number: i32,
pub name: Option<Vec<u8>>,
pub modified: bool,
pub listed: bool,
}
pub fn list_tabpages() -> Vec<VimTabpage> {
Vec::new()
}
pub fn current_tabpage() -> VimTabpage {
VimTabpage {
number: 1,
window: VimWindow {
number: 1,
window_id: -1,
buffer: VimBuffer {
number: 1,
name: None,
modified: false,
listed: true,
},
},
}
}
pub fn bufvar_exists(_matcher_info: Option<&MatcherInfo>, _var: &str) -> bool {
false
}
pub fn vim_func_exists(_name: &str) -> bool {
false
}
pub fn vim_global_exists(_name: &str) -> bool {
false
}
pub fn vim_command_exists(_name: &str) -> bool {
false
}
pub fn vim_get_autoload_func(_f: &str, _rettype: Option<&str>) -> Option<()> {
None
}
pub fn create_ruby_dpowerline() {}
pub fn get_vim_encoding() -> &'static str {
"utf-8"
}
pub fn python_to_vim(value: &serde_json::Value) -> Vec<u8> {
match value {
serde_json::Value::String(s) => {
let mut out = Vec::new();
out.push(b'\'');
for c in s.chars() {
if c == '\'' {
out.extend_from_slice(b"''");
} else {
let mut buf = [0u8; 4];
out.extend_from_slice(c.encode_utf8(&mut buf).as_bytes());
}
}
out.push(b'\'');
out
}
serde_json::Value::Array(arr) => {
let mut out = Vec::new();
out.push(b'[');
for (i, item) in arr.iter().enumerate() {
if i > 0 {
out.push(b',');
}
out.extend_from_slice(&python_to_vim(item));
}
out.push(b']');
out
}
serde_json::Value::Number(n) => n.to_string().into_bytes(),
serde_json::Value::Bool(b) => {
if *b {
b"1".to_vec()
} else {
b"0".to_vec()
}
}
serde_json::Value::Null => b"v:null".to_vec(),
serde_json::Value::Object(_) => {
b"''".to_vec()
}
}
}
pub fn str_to_bytes(s: &str) -> Vec<u8> {
s.as_bytes().to_vec()
}
pub fn vim_environ_value_escape(value: &str) -> String {
value
.replace('"', "\\\"")
.replace('\\', "\\\\")
.replace('\n', "\\n")
.replace('\0', "")
}
pub fn vim_environ_set_command(key: &str, value: &str) -> String {
format!("let ${}=\"{}\"", key, vim_environ_value_escape(value))
}
pub fn vim_environ_get_expr(key: &str) -> String {
format!("${}", key)
}
pub fn powerline_vim_strtrans_error(bytes: &[u8]) -> (String, usize) {
let mut out = String::with_capacity(bytes.len() * 5);
for b in bytes {
out.push_str(&format!("<<{:02X}>>", b));
}
(out, bytes.len())
}
pub fn unicode_eval<F>(eval: F) -> String
where
F: FnOnce() -> String,
{
eval()
}
pub fn safe_bytes_eval<F>(eval: F) -> Vec<u8>
where
F: FnOnce() -> Vec<u8>,
{
eval()
}
pub fn eval_bytes<U, S>(eval_unicode_fn: U, safe_bytes_fn: S) -> Vec<u8>
where
U: FnOnce() -> Result<String, ()>,
S: FnOnce() -> Vec<u8>,
{
match eval_unicode_fn() {
Ok(s) => str_to_bytes(&s),
Err(()) => safe_bytes_fn(),
}
}
pub fn eval_unicode<U, S>(eval_unicode_fn: U, safe_bytes_fn: S) -> String
where
U: FnOnce() -> Result<String, ()>,
S: FnOnce() -> Vec<u8>,
{
match eval_unicode_fn() {
Ok(s) => s,
Err(()) => String::from_utf8_lossy(&safe_bytes_fn()).into_owned(),
}
}
pub fn vim_get_func<D>(name: String, dispatcher: D) -> impl Fn(&[&str]) -> Option<String>
where
D: Fn(&str, &[&str]) -> Option<String>,
{
move |args: &[&str]| dispatcher(&name, args)
}
pub fn vim_getvar<F>(name: &str, eval: F) -> Result<String, String>
where
F: FnOnce(&str) -> Result<String, String>,
{
eval(&format!("g:{}", name))
}
pub fn vim_getwinvar<F>(winnr: i64, varname: &str, eval: F) -> Result<String, String>
where
F: FnOnce(&str) -> Result<String, String>,
{
eval(&format!("getwinvar({}, \"{}\")", winnr, varname))
}
pub fn getbufvar<F>(buffer_number: i64, varname: &str, eval: F) -> Result<String, String>
where
F: FnOnce(&str) -> Result<String, String>,
{
eval(&format!("getbufvar({}, \"{}\")", buffer_number, varname))
}
pub fn vim_getoption<F>(name: &str, eval: F) -> Result<String, String>
where
F: FnOnce(&str) -> Result<String, String>,
{
eval(&format!("&{}", name))
}
pub fn vim_setoption(name: &str, value: &str) -> String {
format!("let &{} = \"{}\"", name, value)
}
pub fn _vim_to_python(value: serde_json::Value) -> serde_json::Value {
value
}
pub fn get_buffer(number: i64, buffers: &[i64]) -> Option<i64> {
buffers.iter().copied().find(|b| *b == number)
}
pub fn get<F>(key: &str, default: Option<&str>, lookup: F) -> Option<String>
where
F: FnOnce(&str) -> Option<String>,
{
lookup(key).or_else(|| default.map(String::from))
}
pub fn buffer(buffer_number: i64, buffers: &[i64]) -> Option<i64> {
get_buffer(buffer_number, buffers)
}
pub fn window(tabnr: i64, winnr: i64) -> (i64, i64) {
(tabnr, winnr)
}
pub fn _last_tab_nr<F>(eval: F) -> Result<i64, String>
where
F: FnOnce(&str) -> Result<String, String>,
{
let s = eval("tabpagenr(\"$\")")?;
s.parse::<i64>().map_err(|e| e.to_string())
}
pub fn register_buffer_cache(
cache_id: u64,
buffer_caches: &mut Vec<u64>,
did_autocmd: &mut bool,
pycmd: &str,
) -> Vec<String> {
let mut commands: Vec<String> = Vec::new();
if !*did_autocmd {
commands.push("augroup Powerline".to_string());
commands.push(format!(
"\tautocmd! BufWipeout * :{} powerline_on_bwipe()",
pycmd
));
commands.push("augroup END".to_string());
*did_autocmd = true;
}
buffer_caches.push(cache_id);
commands
}
pub fn on_bwipe(
bufnr: i64,
buffer_caches: &mut [std::collections::HashMap<i64, serde_json::Value>],
) -> Vec<(usize, bool)> {
let mut results = Vec::with_capacity(buffer_caches.len());
for (idx, cache) in buffer_caches.iter_mut().enumerate() {
let was_present = cache.remove(&bufnr).is_some();
results.push((idx, was_present));
}
results
}
pub fn list_tabpage_buffers_segment_info(
windows: &[(i64, i64)], ) -> Vec<serde_json::Value> {
let mut seen: std::collections::HashSet<i64> = std::collections::HashSet::new();
let mut out: Vec<serde_json::Value> = Vec::new();
for (window_id, buf_number) in windows {
if seen.insert(*buf_number) {
out.push(serde_json::json!({
"window_id": window_id,
"buffer_number": buf_number,
}));
}
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn buffer_name_returns_set_value() {
let info = MatcherInfo {
bufnr: 1,
buffer_name: Some(b"/tmp/test.txt".to_vec()),
..Default::default()
};
assert_eq!(buffer_name(&info), Some(b"/tmp/test.txt".to_vec()));
}
#[test]
fn vim_getbufoption_returns_value_if_set() {
let mut opts = std::collections::HashMap::new();
opts.insert("filetype".into(), "rust".into());
let info = MatcherInfo {
buffer_options: opts,
..Default::default()
};
assert_eq!(vim_getbufoption(&info, "filetype"), "rust");
assert_eq!(vim_getbufoption(&info, "missing"), "");
}
#[test]
fn list_tabpages_empty_when_no_vim() {
assert!(list_tabpages().is_empty());
}
#[test]
fn get_vim_encoding_returns_utf8_fallback() {
assert_eq!(get_vim_encoding(), "utf-8");
}
#[test]
fn python_to_vim_unicode_quotes_and_escapes() {
let r = python_to_vim(&serde_json::json!("it's"));
assert_eq!(r, b"'it''s'".to_vec());
}
#[test]
fn python_to_vim_plain_string() {
let r = python_to_vim(&serde_json::json!("hello"));
assert_eq!(r, b"'hello'".to_vec());
}
#[test]
fn python_to_vim_int_emits_ascii_digits() {
let r = python_to_vim(&serde_json::json!(42));
assert_eq!(r, b"42".to_vec());
}
#[test]
fn python_to_vim_float_emits_ascii_digits() {
let r = python_to_vim(&serde_json::json!(3.5));
assert_eq!(r, b"3.5".to_vec());
}
#[test]
fn python_to_vim_list_emits_bracketed_csv() {
let r = python_to_vim(&serde_json::json!(["a", 1, "b"]));
assert_eq!(r, b"['a',1,'b']".to_vec());
}
#[test]
fn python_to_vim_empty_list() {
let r = python_to_vim(&serde_json::json!([]));
assert_eq!(r, b"[]".to_vec());
}
#[test]
fn python_to_vim_bool_emits_0_or_1() {
let r = python_to_vim(&serde_json::json!(true));
assert_eq!(r, b"1".to_vec());
let r = python_to_vim(&serde_json::json!(false));
assert_eq!(r, b"0".to_vec());
}
#[test]
fn str_to_bytes_returns_utf8_bytes() {
assert_eq!(str_to_bytes("hello"), b"hello".to_vec());
assert_eq!(str_to_bytes("héllo"), "héllo".as_bytes().to_vec());
}
#[test]
fn vim_environ_value_escape_quotes_double_quote() {
assert_eq!(vim_environ_value_escape("a\"b"), "a\\\\\"b");
}
#[test]
fn vim_environ_value_escape_escapes_backslash() {
assert_eq!(vim_environ_value_escape("a\\b"), "a\\\\b");
}
#[test]
fn vim_environ_value_escape_newline_becomes_backslash_n() {
assert_eq!(vim_environ_value_escape("a\nb"), "a\\nb");
}
#[test]
fn vim_environ_value_escape_strips_null_byte() {
assert_eq!(vim_environ_value_escape("a\0b"), "ab");
}
#[test]
fn vim_environ_set_command_builds_let_form() {
let cmd = vim_environ_set_command("FOO", "bar");
assert_eq!(cmd, "let $FOO=\"bar\"");
}
#[test]
fn vim_environ_set_command_escapes_value() {
let cmd = vim_environ_set_command("X", "a\"b");
assert_eq!(cmd, "let $X=\"a\\\\\"b\"");
}
#[test]
fn vim_environ_get_expr_builds_dollar_key() {
assert_eq!(vim_environ_get_expr("PATH"), "$PATH");
}
#[test]
fn powerline_vim_strtrans_error_emits_hex_per_byte() {
let (s, end) = powerline_vim_strtrans_error(&[0xff, 0xfe]);
assert_eq!(s, "<<FF>><<FE>>");
assert_eq!(end, 2);
}
#[test]
fn powerline_vim_strtrans_error_empty_input() {
let (s, end) = powerline_vim_strtrans_error(&[]);
assert_eq!(s, "");
assert_eq!(end, 0);
}
}