diff --git a/src/main.rs b/src/main.rs index 18f41d90..78569bed 100644 --- a/src/main.rs +++ b/src/main.rs @@ -162,7 +162,8 @@ pub fn start(mut os_input: Box) { } // cleanup(); let reset_style = "\u{1b}[m"; - let goodbye_message = format!("\n\r{}Bye from Mosaic!", reset_style); + let goto_start_of_last_line = format!("\u{1b}[{};{}H", full_screen_ws.ws_row, 1); + let goodbye_message = format!("{}\n{}Bye from Mosaic!", goto_start_of_last_line, reset_style); os_input.get_stdout_writer().write(goodbye_message.as_bytes()).unwrap(); os_input.get_stdout_writer().flush().unwrap(); diff --git a/src/screen.rs b/src/screen.rs index 9e0d573b..ec72bb0a 100644 --- a/src/screen.rs +++ b/src/screen.rs @@ -183,9 +183,11 @@ impl Screen { } fn get_active_terminal_cursor_position(&self) -> (usize, usize) { // (x, y) let active_terminal = &self.get_active_terminal().unwrap(); - let x = active_terminal.x_coords as usize + active_terminal.cursor_position_in_last_line(); - let y = active_terminal.y_coords + active_terminal.display_rows - 1; - (x, y as usize) + let (x_in_terminal, y_in_terminal) = active_terminal.cursor_coordinates(); + + let x = active_terminal.x_coords as usize + x_in_terminal; + let y = active_terminal.y_coords as usize + y_in_terminal; + (x, y) } pub fn render (&mut self) { let mut stdout = self.os_api.get_stdout_writer(); diff --git a/src/terminal_pane.rs b/src/terminal_pane.rs index f4fe35db..b555593c 100644 --- a/src/terminal_pane.rs +++ b/src/terminal_pane.rs @@ -242,6 +242,7 @@ impl TerminalOutput { x = 0; next_newline_index = newline_indices.next(); } else if x == self.display_cols as u64 && i <= self.cursor_position { + // TODO: maybe remove <= self.cursor_position? why not reflow lines after cursor? self.linebreak_indices.push(i); x = 0; } @@ -354,83 +355,65 @@ impl TerminalOutput { Vec::from(output) } - pub fn cursor_position_in_last_line (&self) -> usize { - if self.cursor_position <= self.characters.len() { - let start_of_last_line = self.index_of_beginning_of_last_line(); - if self.cursor_position < start_of_last_line { - // TODO: why does this happen? - return self.display_cols as usize - }; - let difference_from_last_newline = self.cursor_position - start_of_last_line; - difference_from_last_newline - } else { - self.display_cols as usize - } - } pub fn cursor_coordinates (&self) -> (usize, usize) { // (x, y) let mut lines_from_end = 0; let mut newline_indices = self.newline_indices.iter().rev(); let mut linebreak_indices = self.linebreak_indices.iter().rev(); - let mut next_newline_index = newline_indices.next().unwrap_or(&0); - let mut next_linebreak_index = linebreak_indices.next().unwrap_or(&0); + let mut next_newline_index = newline_indices.next().copied(); + let mut next_linebreak_index = linebreak_indices.next().copied(); let next_line_start = loop { - let next_line_start = ::std::cmp::max(*next_newline_index, *next_linebreak_index); - if self.cursor_position >= next_line_start { - break next_line_start; + let next_line_index = ::std::cmp::max(next_newline_index, next_linebreak_index); + if let Some(next_line_index) = next_line_index { + if self.cursor_position > next_line_index { + break next_line_index; + } + } else { + break next_line_index.unwrap_or(0); } - if next_line_start == *next_newline_index { - next_newline_index = newline_indices.next().unwrap_or(&0); + if next_line_index == next_newline_index { + next_newline_index = newline_indices.next().copied(); lines_from_end += 1; } - if next_line_start == *next_linebreak_index { - next_linebreak_index = linebreak_indices.next().unwrap_or(&0); + if next_line_index == next_linebreak_index { + next_linebreak_index = linebreak_indices.next().copied(); lines_from_end += 1; } }; - let y = self.display_rows - lines_from_end; // TODO: this might overflow, fix when introducing scrolling + let y = if self.display_rows < lines_from_end { 1 } else { self.display_rows - 1 - lines_from_end }; + // TODO: why 1? let x = self.cursor_position - next_line_start; (x, y as usize) } - fn index_of_beginning_of_last_line (&self) -> usize { - let last_newline_index = if self.newline_indices.is_empty() { - None - } else { - // return last - Some(*self.newline_indices.last().unwrap()) - }; - let last_linebreak_index = if self.linebreak_indices.is_empty() { - None - } else { - // return last - Some(*self.linebreak_indices.last().unwrap()) - }; - match (last_newline_index, last_linebreak_index) { - (Some(last_newline_index), Some(last_linebreak_index)) => { - max(last_newline_index, last_linebreak_index) - }, - (None, Some(last_linebreak_index)) => last_linebreak_index, - (Some(last_newline_index), None) => last_newline_index, - (None, None) => 0 - } - } fn index_of_beginning_of_last_canonical_line (&self) -> usize { if self.newline_indices.is_empty() { 0 } else { - // return last *self.newline_indices.last().unwrap() } } + fn index_of_end_of_canonical_line(&self, index_in_line: usize) -> usize { + let newlines = self.newline_indices.iter().rev(); + let mut index_of_end_of_canonical_line = self.characters.len(); + for line_index in newlines { + if *line_index <= index_in_line { + break + } + if index_of_end_of_canonical_line > *line_index { + index_of_end_of_canonical_line = *line_index; + } + } + index_of_end_of_canonical_line + } fn index_of_beginning_of_line (&self, index_in_line: usize) -> usize { let last_newline_index = if self.newline_indices.is_empty() { None } else { // return last less than index_in_line let last_newline_index = *self.newline_indices.last().unwrap(); - if last_newline_index <= index_in_line { + if last_newline_index < index_in_line { Some(last_newline_index) } else { let mut last_newline_index = 0; @@ -449,7 +432,7 @@ impl TerminalOutput { } else { // return last less than index_in_line let last_linebreak_index = *self.linebreak_indices.last().unwrap(); - if last_linebreak_index <= index_in_line { + if last_linebreak_index < index_in_line { Some(last_linebreak_index) } else { let mut last_linebreak_index = 0; @@ -473,8 +456,13 @@ impl TerminalOutput { } } fn add_newline (&mut self) { - self.newline_indices.push(self.characters.len()); - self.cursor_position = self.characters.len(); + let nearest_line_end = self.index_of_end_of_canonical_line(self.cursor_position); + if nearest_line_end == self.characters.len() { + self.newline_indices.push(nearest_line_end); + self.cursor_position = nearest_line_end; // + 1 ? + } else { + self.cursor_position = nearest_line_end; // + 1 ? + } self.should_render = true; self.pending_foreground_ansi_codes.clear(); self.pending_background_ansi_codes.clear(); @@ -489,7 +477,6 @@ impl TerminalOutput { impl vte::Perform for TerminalOutput { fn print(&mut self, c: char) { - // print!("\n\r{}", c); if DEBUGGING { println!("\r[print] {:?}", c); } else { @@ -534,9 +521,15 @@ impl vte::Perform for TerminalOutput { } else if self.characters.len() > self.cursor_position { self.characters.remove(self.cursor_position); self.characters.insert(self.cursor_position, terminal_character); + if self.newline_indices.contains(&(self.cursor_position + 1)) { + // TODO: ??? + return; + } } else { - for _ in self.characters.len()..self.cursor_position { - self.characters.push(EMPTY_TERMINAL_CHARACTER.clone()); + for i in self.characters.len()..self.cursor_position { + if !self.newline_indices.contains(&i) { + self.characters.push(EMPTY_TERMINAL_CHARACTER.clone()); + } }; self.characters.push(terminal_character); @@ -551,25 +544,12 @@ impl vte::Perform for TerminalOutput { } fn execute(&mut self, byte: u8) { - if DEBUGGING { - if byte == 13 { // 0d, carriage return - println!("\rEXECUTE CARRIAGE RETURN"); - } else if byte == 10 { // 0a, newline - println!("\rEXECUTE NEW LINE"); - } else if byte == 08 { // backspace - println!("\rEXECUTE BACKSPACE"); - } else { - println!("\r[execute] {:02x}", byte); - } - } else { - if byte == 13 { // 0d, carriage return - self.move_to_beginning_of_line(); - } else if byte == 08 { // backspace - self.cursor_position -= 1; - self.characters.truncate(self.cursor_position); - } else if byte == 10 { // 0a, newline - self.add_newline(); - } + if byte == 13 { // 0d, carriage return + self.move_to_beginning_of_line(); + } else if byte == 08 { // backspace + self.cursor_position -= 1; + } else if byte == 10 { // 0a, newline + self.add_newline(); } } @@ -602,10 +582,6 @@ impl vte::Perform for TerminalOutput { } fn csi_dispatch(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, c: char) { -// println!( -// "\n\r[csi_dispatch] params={:?}, intermediates={:?}, ignore={:?}, char={:?}", -// params, intermediates, ignore, c -// ); if DEBUGGING { println!( "\r[csi_dispatch] params={:?}, intermediates={:?}, ignore={:?}, char={:?}", @@ -642,16 +618,47 @@ impl vte::Perform for TerminalOutput { self.reset_misc_ansi_code = false; } } else if c == 'C' { // move cursor forward - self.cursor_position += params[0] as usize; // TODO: negative value? + let move_by = params[0] as usize; + let closest_newline = self.newline_indices.iter().find(|x| x > &&self.cursor_position).copied(); + let closest_linebreak = self.linebreak_indices.iter().find(|x| x > &&self.cursor_position).copied(); + let max_move_position = match (closest_newline, closest_linebreak) { + (Some(closest_newline), Some(closest_linebreak)) => { + ::std::cmp::min( + closest_newline, + closest_linebreak + ) + }, + (Some(closest_newline), None) => { + closest_newline + }, + (None, Some(closest_linebreak)) => { + closest_linebreak + }, + (None, None) => { + // TODO: NO!! + self.cursor_position + move_by + } + }; + if self.cursor_position + move_by < max_move_position { + self.cursor_position += move_by; + } else { + self.cursor_position = max_move_position; + } + } else if c == 'K' { // clear line (0 => right, 1 => left, 2 => all) if params[0] == 0 { - if let Some(position_of_first_newline_index_to_delete) = self.newline_indices.iter().position(|&ni| ni > self.cursor_position) { - self.newline_indices.truncate(position_of_first_newline_index_to_delete); + let newlines = self.newline_indices.iter().rev(); + let mut delete_until = self.characters.len(); + for newline_index in newlines { + if newline_index < &self.cursor_position { + break; + } + delete_until = *newline_index; } - if let Some(position_of_first_linebreak_index_to_delete) = self.linebreak_indices.iter().position(|&li| li > self.cursor_position) { - self.newline_indices.truncate(position_of_first_linebreak_index_to_delete); + // TODO: better + for i in self.cursor_position..delete_until { + self.characters[i] = EMPTY_TERMINAL_CHARACTER.clone(); } - self.characters.truncate(self.cursor_position + 1); } // TODO: implement 1 and 2 } else if c == 'J' { // clear all (0 => below, 1 => above, 2 => all, 3 => saved) @@ -689,9 +696,34 @@ impl vte::Perform for TerminalOutput { let index_of_start_of_row = self.newline_indices.get(row - 1).unwrap(); self.cursor_position = index_of_start_of_row + col; } + } else if c == 'A' { // move cursor up until edge of screen + let move_up_count = if params[0] == 0 { 1 } else { params[0] }; + let newlines = self.newline_indices.iter().rev(); + let mut position_in_line = None; + let mut lines_traversed = 0; + for newline_index in newlines { + if position_in_line.is_some() { + lines_traversed += 1; + } + if newline_index < &self.cursor_position && position_in_line.is_none() { + // this is the current cursor line + position_in_line = Some(self.cursor_position - newline_index); + } + if lines_traversed == move_up_count { + self.cursor_position = newline_index + position_in_line.unwrap(); + break; + } + } + // TODO: stop at upper edge of screen } else if c == 'D' { // move cursor backwards, stop at left edge of screen - self.cursor_position -= params[0] as usize; + let reduce_by = if params[0] == 0 { 1 } else { params[0] as usize }; + if reduce_by > self.cursor_position { + // TODO: no, this is incorrect + self.cursor_position = 0; + } else { + self.cursor_position -= reduce_by; + } // TODO: stop at left edge of screen } else if c == 'l' { // TBD