use crate::buffer::Buffer;
use crate::buffer_mode::BufferMode;
use crate::buffer_mode::TabOperation;
use crate::config::ConfigWithDefault;
use crate::core::CoreBuffer;
use crate::draw;
use crate::draw::CharStyle;
use crate::rmate::{start_server, RmateSave, RmateStorage};
use crate::storage::Storage;
use crate::syntax::SyntaxParent;
use std::cmp::min;
use std::sync::mpsc;
use std::thread;
use unicode_width::UnicodeWidthChar;
use termion::event::Event;
use termion::event::MouseButton;
use termion::event::MouseEvent;
struct TabLine {
buf: Vec<Option<(char, CharStyle)>>,
tab: Vec<Option<usize>>,
cursor: usize,
}
impl TabLine {
fn new(width: usize) -> Self {
Self {
buf: vec![None; width],
tab: vec![None; width],
cursor: 0,
}
}
fn put(&mut self, c: char, style: CharStyle, tab_num: Option<usize>) {
if let Some(w) = c.width() {
if self.cursor + w < self.buf.len() {
self.buf[self.cursor] = Some((c, style));
self.tab[self.cursor] = tab_num;
self.cursor += 1;
for _ in 1..w {
self.tab[self.cursor] = tab_num;
self.cursor += 1;
}
}
}
}
fn puts(&mut self, s: &str, style: CharStyle, tab_num: Option<usize>) {
for c in s.chars() {
self.put(c, style, tab_num);
}
}
}
pub struct BufferTab<'a, B: CoreBuffer> {
syntax_parent: &'a SyntaxParent,
config: &'a ConfigWithDefault,
buffers: Vec<BufferMode<'a, B>>,
index: usize,
rmate: Option<mpsc::Receiver<RmateSave>>,
}
impl<'a, B: CoreBuffer> BufferTab<'a, B> {
pub fn new(syntax_parent: &'a SyntaxParent, config: &'a ConfigWithDefault) -> Self {
Self {
syntax_parent,
config,
buffers: vec![BufferMode::new(Buffer::new(syntax_parent, config))],
index: 0,
rmate: None,
}
}
fn new_buffer_mode(&self) -> BufferMode<'a, B> {
BufferMode::new(Buffer::new(self.syntax_parent, self.config))
}
pub fn open<S: Storage<B> + 'static>(&mut self, s: S) {
if self.is_empty() {
self.buffers.clear();
}
let mut buffer_mode = self.new_buffer_mode();
buffer_mode.buf.open(s);
self.buffers.push(buffer_mode);
}
pub fn buffer_mode(&self) -> &BufferMode<'a, B> {
&self.buffers[self.index]
}
pub fn buffer_mode_mut(&mut self) -> &mut BufferMode<'a, B> {
&mut self.buffers[self.index]
}
pub fn is_empty(&self) -> bool {
self.buffers.len() == 1
&& self.buffer_mode().buf.storage().is_none()
&& self.buffer_mode().buf.core.get_string() == ""
}
pub async fn event(&mut self, event: Event) -> bool {
if let Event::Mouse(MouseEvent::Press(button, col, row)) = event {
let (width, height) = termion::terminal_size().unwrap();
if row == height {
let tab_line = self.draw_tab_line(width as usize);
let col = col as usize - 1;
if let Some(i) = tab_line.tab[col] {
match button {
MouseButton::Left => {
self.index = i;
}
MouseButton::Middle => {
self.buffers.remove(i);
if self.buffers.is_empty() {
return true;
} else {
self.index = min(self.buffers.len() - 1, self.index);
}
}
_ => {}
}
}
}
}
match self.buffer_mode_mut().event(event).await {
TabOperation::Close => {
if self.buffers.len() <= 1 {
return true;
} else {
self.buffers.remove(self.index);
self.index = min(self.buffers.len() - 1, self.index);
}
}
TabOperation::NewTab => {
self.buffers.push(BufferMode::new(Buffer::new(
self.syntax_parent,
self.config,
)));
self.index = self.buffers.len() - 1;
}
TabOperation::ChangeTab(i) => {
if i >= 1 && i <= self.buffers.len() {
self.index = i - 1;
}
}
TabOperation::StartRmate => {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let _ = start_server(tx);
});
self.rmate = Some(rx);
}
TabOperation::Nothing => {}
}
false
}
fn draw_tab_line(&self, width: usize) -> TabLine {
const TITLE_LEN: usize = 5;
let mut footer = TabLine::new(width);
if self.rmate.is_some() {
footer.puts("R", draw::styles::HIGHLIGHT, None);
footer.puts(" ", draw::styles::DEFAULT, None);
}
let get_title = |path: Option<&std::path::Path>| {
if let Some(path) = path {
path.file_name()
.map(|o| o.to_string_lossy().to_string())
.unwrap_or_default()
} else {
"*".to_string()
}
};
if self.buffers.len() == 1 {
footer.puts(
&format!(" {} {} ", 1, get_title(self.buffers[0].buf.path())),
draw::styles::TAB_BAR,
Some(0),
);
} else {
for i in 0..self.buffers.len() {
let title = get_title(self.buffers[i].buf.path());
let mut msg = String::new();
let mut w = 0;
let mut is_long = false;
for c in title.chars() {
let c_w = c.width().unwrap_or(0);
if w + c_w <= TITLE_LEN {
msg.push(c);
w += c_w;
} else {
is_long = true;
}
}
for _ in w..TITLE_LEN {
msg.push(' ');
}
if is_long {
msg.push('…');
}
if self.index == i {
footer.puts(
&format!(" {} {}", i + 1, msg),
draw::styles::TAB_BAR,
Some(i),
);
} else {
footer.puts(
&format!(" {} {}", i + 1, msg),
draw::styles::DEFAULT,
Some(i),
);
}
}
}
footer.put(' ', draw::styles::UI, None);
footer
}
pub fn draw(&mut self, mut view: draw::TermView) -> draw::CursorState {
{
if let Some(rmate) = self.rmate.as_ref() {
match rmate.try_recv() {
Ok(rmate) => {
let rmate: RmateStorage = rmate.into();
self.open(rmate);
self.index = self.buffers.len() - 1;
}
Err(mpsc::TryRecvError::Disconnected) => {
self.rmate = None;
}
_ => {}
}
}
}
let width = view.width();
let cursor =
self.buffer_mode_mut()
.draw(view.view((0, 0), view.height() - 1, view.width()));
let mut footer = view.view((view.height() - 1, 0), 1, view.width());
let tab_line = self.draw_tab_line(width);
for tile in tab_line.buf {
if let Some((c, style)) = tile {
footer.put(c, style, None);
}
}
cursor
}
}