use std::fmt::{self, Write as _};
use ropey::RopeSlice;
use serde::{Deserialize, Serialize};
use tree_house_bindings::Node;
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct Pos {
pub line: usize,
pub col: usize,
}
impl Pos {
pub fn parse_kak_str(s: &str) -> Option<Self> {
let mut parts = s.split('.').flat_map(|s| s.parse().ok());
let line = parts.next()?;
let col = parts.next()?;
Some(Self { line, col })
}
pub fn from_tree_sitter(buf: RopeSlice, byte: u32) -> Self {
let byte = byte as usize;
let line = buf.byte_to_line(byte);
let line_byte = buf.line_to_byte(line);
let col = byte - line_byte;
Self {
line: line + 1,
col: col + 1,
}
}
pub fn into_tree_sitter(self, buf: RopeSlice) -> usize {
buf.line_to_byte(self.line) + self.col
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Sel {
pub anchor: Pos,
pub cursor: Pos,
}
impl Sel {
pub fn parse_kak_str(s: &str) -> Option<Self> {
let mut parts = s.split(',').flat_map(Pos::parse_kak_str);
let anchor = parts.next()?;
let cursor = parts.next()?;
Some(Self { anchor, cursor })
}
pub fn parse_many(s: &str) -> Vec<Self> {
s.split_whitespace().flat_map(Self::parse_kak_str).collect()
}
pub fn serialize_into(&self, resp_str: &mut String) -> Result<(), fmt::Error> {
write!(
resp_str,
"{anchor_line}.{anchor_col},{cursor_line}.{cursor_col}",
anchor_line = self.anchor.line,
anchor_col = self.anchor.col,
cursor_line = self.cursor.line,
cursor_col = self.cursor.col
)
}
pub fn replace(&self, a: &Pos, b: &Pos) -> Self {
if self.anchor <= self.cursor {
Self {
anchor: *a,
cursor: *b,
}
} else {
Self {
anchor: *b,
cursor: *a,
}
}
}
pub fn replace_with_node(&self, buf: RopeSlice, node: &Node) -> Self {
let mut b = Pos::from_tree_sitter(buf, node.end_byte() as _);
b.col -= 1; self.replace(&Pos::from_tree_sitter(buf, node.start_byte() as _), &b)
}
pub fn selects(&self, buf: RopeSlice, node: &Node<'_>) -> bool {
let start = Pos::from_tree_sitter(buf, node.start_byte() as _);
let mut end = Pos::from_tree_sitter(buf, node.end_byte() as _);
end.col -= 1;
if self.anchor <= self.cursor {
self.anchor <= start && self.cursor >= end
} else {
self.cursor <= start && self.anchor >= end
}
}
pub fn fully_selects(&self, buf: RopeSlice, node: &Node) -> bool {
let start = Pos::from_tree_sitter(buf, node.start_byte() as _);
let mut end = Pos::from_tree_sitter(buf, node.end_byte() as _);
end.col -= 1;
if self.anchor <= self.cursor {
self.anchor == start && self.cursor == end
} else {
self.cursor == start && self.anchor == end
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct ObjectFlags {
pub to_begin: bool,
pub to_end: bool,
pub inner: bool,
}
impl ObjectFlags {
pub fn parse_kak_str(s: &str) -> Self {
s.split('|').fold(Self::default(), |flags, s| match s {
"to_begin" => flags.set_to_begin(),
"to_end" => flags.set_to_end(),
"inner" => flags.set_inner(),
_ => flags,
})
}
pub fn set_to_begin(mut self) -> Self {
self.to_begin = true;
self
}
pub fn set_to_end(mut self) -> Self {
self.to_end = true;
self
}
pub fn set_inner(mut self) -> Self {
self.inner = true;
self
}
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum SelectMode {
Replace,
Extend,
}
#[cfg(test)]
mod tests {
use super::{Pos, Sel};
#[test]
fn pos_parsing() {
let s = "123.456";
let parsed = Pos::parse_kak_str(s);
assert_eq!(
parsed,
Some(Pos {
line: 123,
col: 456
})
);
}
#[test]
fn sel_parsing() {
let s = "123.456,124.789";
let parsed = Sel::parse_kak_str(s);
assert_eq!(
parsed,
Some(Sel {
anchor: Pos {
line: 123,
col: 456,
},
cursor: Pos {
line: 124,
col: 789
}
})
);
}
#[test]
fn replace_sel() {
let anchor_cursor = Sel {
anchor: Pos {
line: 123,
col: 456,
},
cursor: Pos {
line: 124,
col: 789,
},
};
let cursor_anchor = Sel {
anchor: Pos {
line: 124,
col: 789,
},
cursor: Pos {
line: 123,
col: 456,
},
};
let a = Pos { line: 1, col: 1 };
let b = Pos {
line: 1000,
col: 1000,
};
assert_eq!(
anchor_cursor.replace(&a, &b),
Sel {
anchor: Pos {
line: a.line,
col: a.col,
},
cursor: Pos {
line: b.line,
col: b.col
}
}
);
assert_eq!(
cursor_anchor.replace(&a, &b),
Sel {
anchor: Pos {
line: b.line,
col: b.col,
},
cursor: Pos {
line: a.line,
col: a.col
}
}
);
}
}