mirror of
https://github.com/alacritty/alacritty.git
synced 2024-10-27 05:32:54 -04:00
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.
This commit is contained in:
parent
21f888ec41
commit
0c3e28617a
11 changed files with 111 additions and 50 deletions
|
@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
- Fix color issue in ncurses programs by updating terminfo pairs from 0x10000 to 0x7FFF
|
||||
- Fix panic after quitting Alacritty on macOS
|
||||
- Tabs are no longer replaced by spaces when copying them to the clipboard
|
||||
|
||||
## Version 0.2.4
|
||||
|
||||
|
|
12
Cargo.lock
generated
12
Cargo.lock
generated
|
@ -716,7 +716,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
dependencies = [
|
||||
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -1450,7 +1450,7 @@ dependencies = [
|
|||
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2057,7 +2057,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
dependencies = [
|
||||
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2203,7 +2203,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "0.15.22"
|
||||
version = "0.15.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -2218,7 +2218,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
dependencies = [
|
||||
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -3119,7 +3119,7 @@ dependencies = [
|
|||
"checksum static_assertions 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "389ce475f424f267dbed6479cbd8f126c5e1afb053b0acdaa019c74305fc65d1"
|
||||
"checksum stb_truetype 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "71a7d260b43b6129a22dc341be18a231044ca67a48b7e32625f380cc5ec9ad70"
|
||||
"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
|
||||
"checksum syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)" = "ae8b29eb5210bc5cf63ed6149cbf9adfc82ac0be023d8735c176ee74a2db4da7"
|
||||
"checksum syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9545a6a093a3f0bd59adb472700acc08cad3776f860f16a897dfce8c88721cbc"
|
||||
"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"
|
||||
"checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
|
||||
"checksum tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "7e91405c14320e5c79b3d148e1c86f40749a36e490642202a31689cb1a3452b2"
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
120
src/term/mod.rs
120
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;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
[1m[7m%[27m[1m[0m
]2;jwilm@jwilm-desk: ~/code/alacritty]1;..ode/alacritty
[0m[27m[24m[Jjwilm@jwilm-desk [01;32m➜ [01;32m [36m~/code/alacritty [00m [K[?1h=[?2004h
|
||||
bck-i-search: _[K[A[23Ctarget/debug/alacritty --ref-test --title alacritty-reft[4me[24mst
|
||||
bck-i-search: e_[K[A[A[24C[4me[4mc[24mho '\tb\na\tb\naa\tb\naaa\tb\naaaa\tb\naaaaa\tb\naaaaa[24ma\tb\naaaaaaa\tb\naaaaaaaa\tb\naaaaaaaaa\tb\naaaaaaaaaa\tb'[1B[58Dc_[A[A[21C[4me[4mc[4mh[24m[2B[25Dh_[A[A[20C[2C[4mh[4mo[24m[2B[25Do_[A[A[19C[24m[35me[24m[35mc[24m[35mh[24m[35mo[39m [33m'[33m\[33mt[33mb[33m\[33mn[33ma[33m\[33mt[33mb[33m\[33mn[33ma[33ma[33m\[33mt[33mb[33m\[33mn[33ma[33ma[33ma[33m\[33mt[33mb[33m\[33mn[33ma[33ma[33ma[33ma[33m\[33mt[33mb[33m\[33mn[33ma[33ma[33ma[33ma[33ma[33m\[33mt[33mb[33m\[33mn[33ma[33ma[33ma[33ma[33ma[33ma[33m\[33mt[33mb[33m\[33mn[33ma[33ma[33ma[33ma[33ma[33ma[33ma[33m\[33mt[33mb[33m\[33mn[33ma[33ma[33ma[33ma[33ma[33ma[33ma[33ma[33m\[33mt[33mb[33m\[33mn[33ma[33ma[33ma[33ma[33ma[33ma[33ma[33ma[33ma[33m\[33mt[33mb[33m\[33mn[33ma[33ma[33ma[33ma[33ma[33ma[33ma[33ma[33ma[33ma[33m\[33mt[33mb[33m'[39m[1B
[K[A[A[38C[?1l>[?2004l[2B
]2;echo ]1;echo b
|
||||
[1m[7m%[27m[1m[0m
[0m[27m[24m[J[0;30;101m UL [0m[0;37;100m ~/…/tests/ref/tab_rendering [0m[0;30;101m copy-tabs [0m [K[?2004hecho '\tb\na\tb\naa\tb\naaa\tb\naaaa\tb\naaaaa\tb\naaaaaa\tb\naaaaaaa\tb\naaaaaaaa\tb\naaaaaaaaa\tb\naaaaaaaaaa\tb'[K[?2004l
|
||||
b
|
||||
a b
|
||||
aa b
|
||||
aaa b
|
||||
|
@ -11,4 +10,4 @@ aaaaaaa b
|
|||
aaaaaaaa b
|
||||
aaaaaaaaa b
|
||||
aaaaaaaaaa b
|
||||
[1m[7m%[27m[1m[0m
]2;jwilm@jwilm-desk: ~/code/alacritty]1;..ode/alacritty
[0m[27m[24m[Jjwilm@jwilm-desk [01;32m➜ [01;32m [36m~/code/alacritty [00m [K[?1h=[?2004h
|
||||
[1m[7m%[27m[1m[0m
[0m[27m[24m[J[0;30;101m UL [0m[0;37;100m ~/…/tests/ref/tab_rendering [0m[0;30;101m copy-tabs [0m [K[?2004h
|
File diff suppressed because one or more lines are too long
|
@ -1 +1 @@
|
|||
{"width":644.0,"height":412.0,"cell_width":8.0,"cell_height":17.0,"padding_x":0.0,"padding_y":0.0}
|
||||
{"width":939.0,"height":1020.0,"cell_width":8.0,"cell_height":16.0,"padding_x":5.0,"padding_y":6.0,"dpr":1.0}
|
|
@ -1,5 +1,5 @@
|
|||
[1m[7m%[27m[1m[0m
[0m[27m[24m[Jjwilm@sanctuary.local [01;32m➜ [01;32m [36m~/code/alacritty [00m [K[?1h=[?2004h[1m[31mv[0m[39m[1m[31mv[1m[31mt[0m[39m[1m[31mv[1m[31mt[1m[31mt[0m[39m[1m[31mt[1m[31me[0m[39m[1m[31me[1m[31ms[0m[39m[0m[35mv[0m[35mt[0m[35mt[0m[35me[0m[35ms[35mt[39m[?1l>[?2004l
|
||||
[0c[?1l[?3l[?4l[?5l[?6l[?7h[?8l[?40h[?45l[r[0m[2J[3;10HVT100 test program, version 2.7 (20140305)[4;10H[5;10HChoose test type:
|
||||
[1m[7m%[27m[1m[0m
[0m[27m[24m[J[0;30;101m UL [0m[0;37;100m ~/…/tests/ref/vttest_tab_cle… [0m[0;30;101m copy-tabs [0m [K[?2004hv[90mttest[39mv[39mt[39mt[39me[39ms[39mt[?2004l
|
||||
[0c[?1l[?3l[?4l[?5l[?6l[?7h[?8l[?40h[?45l[r[0m[2J[3;10HVT100 test program, version 2.7 (20180911)[4;10HLine speed 38400bd [5;10HChoose test type:
|
||||
[6;1H[0J
|
||||
0. Exit
|
||||
1. Test of cursor movements
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1 +1 @@
|
|||
{"width":1124.0,"height":676.0,"cell_width":14.0,"cell_height":28.0,"padding_x":0.0,"padding_y":0.0}
|
||||
{"width":939.0,"height":503.0,"cell_width":8.0,"cell_height":16.0,"padding_x":5.0,"padding_y":3.0,"dpr":1.0}
|
Loading…
Reference in a new issue