use itertools::Itertools; use tree_sitter::{Point, Range}; pub fn select_ranges(buffer: &[String], ranges: &[Range]) -> String { if ranges.is_empty() { "fail no selections remaining".into() } else { format!("select {}", ranges_to_selections_desc(&buffer, &ranges)) } } pub fn highlight_ranges(buffer: &[String], ranges: &[Range]) -> String { format!("set-option buffer tree_highlight %val{{timestamp}} {}", ranges_to_range_desc(&buffer, &ranges, |_i| "%opt{tree_highlight_style}".to_string())) } pub fn ranges_to_range_desc String>(buffer: &[String], ranges: &[Range], decorate: F) -> String { ranges .iter() .enumerate() .map(|(i, range)| { let mut end_row = range.end_point.row; let mut end_column = range.end_point.column; if end_column > 0 { end_column -= 1; } else { end_row -= 1; end_column = 1_000_000; } format!( "\"{},{}|{}\"", point_to_kak_coords(buffer, range.start_point), point_to_kak_coords(buffer, Point::new(end_row, end_column)), decorate(i), ) }) .join(" ") } pub fn ranges_to_selections_desc(buffer: &[String], ranges: &[Range]) -> String { ranges .iter() .map(|range| { let mut end_row = range.end_point.row; let mut end_column = range.end_point.column; if end_column > 0 { end_column -= 1; } else { end_row -= 1; end_column = 1_000_000; } format!( "{},{}", point_to_kak_coords(buffer, Point::new(end_row, end_column)), point_to_kak_coords(buffer, range.start_point), ) }) .join(" ") } pub fn selections_desc_to_ranges(buffer: &[String], selections_desc: &str) -> Vec { selections_desc .split_whitespace() .map(|selection_desc| selection_desc_to_range(buffer, selection_desc)) .collect() } fn selection_desc_to_range(buffer: &[String], selection_desc: &str) -> Range { let mut range = selection_desc.split(','); let start = range.next().unwrap(); let end = range.next().unwrap(); let (start_byte, start_point) = kak_coords_to_byte_and_point(buffer, start); let (end_byte, end_point) = kak_coords_to_byte_and_point(buffer, end); let reverse = start_byte > end_byte; if reverse { Range { start_byte: end_byte, end_byte: start_byte, start_point: end_point, end_point: start_point, } } else { Range { start_byte, end_byte, start_point, end_point, } } } fn point_to_kak_coords(buffer: &[String], p: Point) -> String { let offset = buffer[p.row] .char_indices() .enumerate() .find_map(|(column, (offset, _))| { if column == p.column { Some(offset) } else { None } }) .unwrap_or_else(|| buffer[p.row].len()); format!("{}.{}", p.row + 1, offset + 1) } fn kak_coords_to_byte_and_point(buffer: &[String], coords: &str) -> (usize, Point) { let mut coords = coords.split('.'); let row = coords.next().unwrap().parse::().unwrap() - 1; let offset = coords.next().unwrap().parse::().unwrap() - 1; let byte = buffer[..row].iter().fold(0, |offset, c| offset + c.len()) + offset; let column = buffer[row] .char_indices() .position(|(i, _)| i == offset) .unwrap(); (byte, Point::new(row, column)) }