diff options
author | Steve Blundy <sblundy@users.noreply.github.com> | 2018-12-15 13:33:33 -0800 |
---|---|---|
committer | Christian Duerr <chrisduerr@users.noreply.github.com> | 2018-12-15 21:33:33 +0000 |
commit | 0c3e28617a95b4ca30ad9bdbb9114f1e4d41dce5 (patch) | |
tree | 59d5afe96a755910aba3804a7a84e3b299c49222 /src | |
parent | 21f888ec4149e3078830dfcf9a17adda0ec7623a (diff) |
Fixing tabs in copy-paste
This resolves issues with copy-pasting tabs by including them in the
pasted string.
Selection of tabs is still inconsistent with what might be expected
based on other terminal emulators, however the behavior hasn't
regressed.
This fixes https://github.com/jwilm/alacritty/issues/219.
Diffstat (limited to 'src')
-rw-r--r-- | src/renderer/mod.rs | 7 | ||||
-rw-r--r-- | src/term/cell.rs | 2 | ||||
-rw-r--r-- | src/term/mod.rs | 120 |
3 files changed, 95 insertions, 34 deletions
diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 02d31524..5d7a661f 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -884,12 +884,17 @@ impl<'a> RenderApi<'a> { }; // Don't render text of HIDDEN cells - let chars = if cell.flags.contains(cell::Flags::HIDDEN) { + let mut chars = if cell.flags.contains(cell::Flags::HIDDEN) { [' '; cell::MAX_ZEROWIDTH_CHARS + 1] } else { cell.chars }; + // Render tabs as spaces in case the font doesn't support it + if chars[0] == '\t' { + chars[0] = ' '; + } + let mut glyph_key = GlyphKey { font_key, size: glyph_cache.font_size, diff --git a/src/term/cell.rs b/src/term/cell.rs index 259b6ac1..bd561482 100644 --- a/src/term/cell.rs +++ b/src/term/cell.rs @@ -114,7 +114,7 @@ impl Cell { #[inline] pub fn is_empty(&self) -> bool { - self.c == ' ' + (self.c == ' ' || self.c == '\t') && self.extra[0] == ' ' && self.bg == Color::Named(NamedColor::Background) && !self.flags.intersects(Flags::INVERSE | Flags::UNDERLINE) diff --git a/src/term/mod.rs b/src/term/mod.rs index fba509f5..fd2fcf88 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -763,7 +763,7 @@ pub struct Term { active_charset: CharsetIndex, /// Tabstops - tabs: Vec<bool>, + tabs: TabStops, /// Mode flags mode: TermMode, @@ -915,9 +915,7 @@ impl Term { let alt = Grid::new(num_lines, num_cols, 0 /* scroll history */, Cell::default()); let tabspaces = config.tabspaces(); - let tabs = IndexRange::from(Column(0)..grid.num_cols()) - .map(|i| (*i as usize) % tabspaces == 0) - .collect::<Vec<bool>>(); + let tabs = TabStops::new(grid.num_cols(), tabspaces); let scroll_region = Line(0)..grid.num_lines(); @@ -1014,11 +1012,23 @@ impl Term { use std::ops::Range; trait Append : PushChar { - fn append(&mut self, grid: &Grid<Cell>, line: usize, cols: Range<Column>); + fn append( + &mut self, + grid: &Grid<Cell>, + tabs: &TabStops, + line: usize, + cols: Range<Column>, + ); } impl Append for String { - fn append(&mut self, grid: &Grid<Cell>, mut line: usize, cols: Range<Column>) { + fn append( + &mut self, + grid: &Grid<Cell>, + tabs: &TabStops, + mut line: usize, + cols: Range<Column> + ) { // Select until last line still within the buffer line = min(line, grid.len() - 1); @@ -1029,13 +1039,30 @@ impl Term { if line_end.0 == 0 && cols.end >= grid.num_cols() - 1 { self.push('\n'); } else if cols.start < line_end { - for cell in &grid_line[cols.start..line_end] { + let mut tab_mode = false; + + for col in IndexRange::from(cols.start..line_end) { + let cell = grid_line[col]; + + if tab_mode { + // Skip over whitespace until next tab-stop once a tab was found + if tabs[col] { + tab_mode = false; + } else if cell.c == ' ' { + continue; + } + } + if !cell.flags.contains(cell::Flags::WIDE_CHAR_SPACER) { self.push(cell.c); for c in (&cell.chars()[1..]).iter().filter(|c| **c != ' ') { self.push(*c); } } + + if cell.c == '\t' { + tab_mode = true; + } } if cols.end >= grid.num_cols() - 1 { @@ -1063,32 +1090,31 @@ impl Term { match line_count { // Selection within single line 0 => { - res.append(&self.grid, start.line, start.col..end.col); + res.append(&self.grid, &self.tabs, start.line, start.col..end.col); }, // Selection ends on line following start 1 => { // Ending line - res.append(&self.grid, end.line, end.col..max_col); + res.append(&self.grid, &self.tabs, end.line, end.col..max_col); // Starting line - res.append(&self.grid, start.line, Column(0)..start.col); + res.append(&self.grid, &self.tabs, start.line, Column(0)..start.col); }, // Multi line selection _ => { // Ending line - res.append(&self.grid, end.line, end.col..max_col); + res.append(&self.grid, &self.tabs, end.line, end.col..max_col); let middle_range = (start.line + 1)..(end.line); for line in middle_range.rev() { - res.append(&self.grid, line, Column(0)..max_col); + res.append(&self.grid, &self.tabs, line, Column(0)..max_col); } // Starting line - res.append(&self.grid, start.line, Column(0)..start.col); - + res.append(&self.grid, &self.tabs, start.line, Column(0)..start.col); } } @@ -1232,9 +1258,7 @@ impl Term { self.cursor_save_alt.point.line = min(self.cursor_save_alt.point.line, num_lines - 1); // Recreate tabs list - self.tabs = IndexRange::from(Column(0)..self.grid.num_cols()) - .map(|i| (*i as usize) % self.tabspaces == 0) - .collect::<Vec<bool>>(); + self.tabs = TabStops::new(self.grid.num_cols(), self.tabspaces); } #[inline] @@ -1553,23 +1577,26 @@ impl ansi::Handler for Term { fn put_tab(&mut self, mut count: i64) { trace!("put_tab: {}", count); - let mut col = self.cursor.point.col; - while col < self.grid.num_cols() && count != 0 { + while self.cursor.point.col < self.grid.num_cols() && count != 0 { count -= 1; + + let cell = &mut self.grid[&self.cursor.point]; + *cell = self.cursor.template; + cell.c = self.cursor.charsets[self.active_charset].map('\t'); + loop { - if (col + 1) == self.grid.num_cols() { + if (self.cursor.point.col + 1) == self.grid.num_cols() { break; } - col += 1; + self.cursor.point.col += 1; - if self.tabs[*col as usize] { + if self.tabs[self.cursor.point.col] { break; } } } - self.cursor.point.col = col; self.input_needs_wrap = false; } @@ -1651,7 +1678,7 @@ impl ansi::Handler for Term { fn set_horizontal_tabstop(&mut self) { trace!("set_horizontal_tabstop"); let column = self.cursor.point.col; - self.tabs[column.0] = true; + self.tabs[column] = true; } #[inline] @@ -1731,7 +1758,7 @@ impl ansi::Handler for Term { for _ in 0..count { let mut col = self.cursor.point.col; for i in (0..(col.0)).rev() { - if self.tabs[i as usize] { + if self.tabs[index::Column(i)] { col = index::Column(i); break; } @@ -1874,15 +1901,10 @@ impl ansi::Handler for Term { match mode { ansi::TabulationClearMode::Current => { let column = self.cursor.point.col; - self.tabs[column.0] = false; + self.tabs[column] = false; }, ansi::TabulationClearMode::All => { - let len = self.tabs.len(); - // Safe since false boolean is null, each item occupies only 1 - // byte, and called on the length of the vec. - unsafe { - ::std::ptr::write_bytes(self.tabs.as_mut_ptr(), 0, len); - } + self.tabs.clear_all(); } } } @@ -2068,6 +2090,40 @@ impl ansi::Handler for Term { } } +struct TabStops { + tabs: Vec<bool> +} + +impl TabStops { + fn new(num_cols: Column, tabspaces: usize) -> TabStops { + TabStops { + tabs: IndexRange::from(Column(0)..num_cols) + .map(|i| (*i as usize) % tabspaces == 0) + .collect::<Vec<bool>>() + } + } + + fn clear_all(&mut self) { + unsafe { + ptr::write_bytes(self.tabs.as_mut_ptr(), 0, self.tabs.len()); + } + } +} + +impl Index<Column> for TabStops { + type Output = bool; + + fn index(&self, index: Column) -> &bool { + &self.tabs[index.0] + } +} + +impl IndexMut<Column> for TabStops { + fn index_mut(&mut self, index: Column) -> &mut bool { + self.tabs.index_mut(index.0) + } +} + #[cfg(test)] mod tests { use serde_json; |