2016-06-30 03:56:12 +00:00
|
|
|
// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
//
|
|
|
|
//! Exports the `Term` type which is a high-level API for the Grid
|
2017-02-11 20:49:40 +00:00
|
|
|
use std::ops::{Range, Index, IndexMut};
|
2018-09-19 19:18:51 +00:00
|
|
|
use std::{ptr, io, mem};
|
2017-04-20 03:08:44 +00:00
|
|
|
use std::cmp::{min, max};
|
2017-02-03 23:34:52 +00:00
|
|
|
use std::time::{Duration, Instant};
|
2016-05-31 03:44:37 +00:00
|
|
|
|
2017-05-28 16:38:10 +00:00
|
|
|
use arraydeque::ArrayDeque;
|
2017-03-02 06:24:37 +00:00
|
|
|
use unicode_width::UnicodeWidthChar;
|
2018-10-22 19:39:26 +00:00
|
|
|
use url::Url;
|
2017-03-02 06:24:37 +00:00
|
|
|
|
2018-01-05 03:22:58 +00:00
|
|
|
use font::{self, Size};
|
2017-05-28 16:38:10 +00:00
|
|
|
use ansi::{self, Color, NamedColor, Attr, Handler, CharsetIndex, StandardCharset, CursorStyle};
|
2018-09-28 22:07:24 +00:00
|
|
|
use grid::{BidirectionalIterator, Grid, Indexed, IndexRegion, DisplayIter, Scroll, ViewportPosition};
|
2018-03-07 04:57:40 +00:00
|
|
|
use index::{self, Point, Column, Line, IndexRange, Contains, RangeInclusive, Linear};
|
|
|
|
use selection::{self, Selection, Locations};
|
2017-02-03 23:34:52 +00:00
|
|
|
use config::{Config, VisualBellAnimation};
|
2017-12-24 20:15:42 +00:00
|
|
|
use {MouseCursor, Rgb};
|
2018-01-02 16:32:50 +00:00
|
|
|
use copypasta::{Clipboard, Load, Store};
|
2018-07-21 17:17:41 +00:00
|
|
|
use input::FONT_SIZE_STEP;
|
Display errors and warnings
To make sure that all error and information reporting to the user is
unified, all instances of `print!`, `eprint!`, `println!` and
`eprintln!` have been removed and replaced by logging.
When `RUST_LOG` is not specified, the default Alacritty logger now also
prints to both the stderr and a log file. The log file is only created
when a message is written to it and its name is printed to stdout the
first time it is used.
Whenever a warning or an error has been written to the log file/stderr,
a message is now displayed in Alacritty which points to the log file
where the full error is documented.
The message is cleared whenever the screen is cleared using either the
`clear` command or the `Ctrl+L` key binding.
To make sure that log files created by root don't prevent normal users
from interacting with them, the Alacritty log file is `/tmp/Alacritty-$PID.log`.
Since it's still possible that the log file can't be created, the UI
error/warning message now informs the user if the message was only
written to stderr. The reason why it couldn't be created is then printed
to stderr.
To make sure the deletion of the log file at runtime doesn't create any
issues, the file is re-created if a write is attempted without the file
being present.
To help with debugging Alacritty issues, a timestamp and the error
level are printed in all log messages.
All log messages now follow this format:
[YYYY-MM-DD HH:MM] [LEVEL] Message
Since it's not unusual to spawn a lot of different terminal emulators
without restarting, Alacritty can create a ton of different log files.
To combat this problem, logfiles are removed by default after
Alacritty has been closed. If the user wants to persist the log of a
single session, the `--persistent_logging` option can be used. For
persisting all log files, the `persistent_logging` option can be set in
the configuration file
2018-11-17 14:39:13 +00:00
|
|
|
use logging::LoggerProxy;
|
2016-05-31 03:44:37 +00:00
|
|
|
|
2016-11-28 22:30:08 +00:00
|
|
|
pub mod cell;
|
2017-02-11 20:49:40 +00:00
|
|
|
pub mod color;
|
2016-11-28 22:30:08 +00:00
|
|
|
pub use self::cell::Cell;
|
2016-12-27 03:52:37 +00:00
|
|
|
use self::cell::LineLength;
|
2016-11-28 22:30:08 +00:00
|
|
|
|
2018-10-22 19:39:26 +00:00
|
|
|
const URL_SEPARATOR_CHARS: [char; 3] = [' ', '"', '\''];
|
|
|
|
|
|
|
|
/// A type that can expand a given point to a region
|
|
|
|
///
|
|
|
|
/// Usually this is implemented for some 2-D array type since
|
|
|
|
/// points are two dimensional indices.
|
|
|
|
pub trait Search {
|
|
|
|
/// Find the nearest semantic boundary _to the left_ of provided point.
|
|
|
|
fn semantic_search_left(&self, _: Point<usize>) -> Point<usize>;
|
|
|
|
/// Find the nearest semantic boundary _to the point_ of provided point.
|
|
|
|
fn semantic_search_right(&self, _: Point<usize>) -> Point<usize>;
|
|
|
|
/// Find the nearest URL boundary in both directions.
|
|
|
|
fn url_search(&self, _: Point<usize>) -> Option<String>;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Search for Term {
|
2018-03-07 04:57:40 +00:00
|
|
|
fn semantic_search_left(&self, mut point: Point<usize>) -> Point<usize> {
|
2018-07-02 22:03:04 +00:00
|
|
|
// Limit the starting point to the last line in the history
|
|
|
|
point.line = min(point.line, self.grid.len() - 1);
|
|
|
|
|
2017-06-16 04:43:28 +00:00
|
|
|
let mut iter = self.grid.iter_from(point);
|
|
|
|
let last_col = self.grid.num_cols() - Column(1);
|
|
|
|
|
|
|
|
while let Some(cell) = iter.prev() {
|
|
|
|
if self.semantic_escape_chars.contains(cell.c) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-10-12 01:52:23 +00:00
|
|
|
if iter.cur.col == last_col && !cell.flags.contains(cell::Flags::WRAPLINE) {
|
2017-06-16 04:43:28 +00:00
|
|
|
break; // cut off if on new line or hit escape char
|
|
|
|
}
|
|
|
|
|
|
|
|
point = iter.cur;
|
|
|
|
}
|
|
|
|
|
|
|
|
point
|
|
|
|
}
|
|
|
|
|
2018-03-07 04:57:40 +00:00
|
|
|
fn semantic_search_right(&self, mut point: Point<usize>) -> Point<usize> {
|
2018-07-02 22:03:04 +00:00
|
|
|
// Limit the starting point to the last line in the history
|
|
|
|
point.line = min(point.line, self.grid.len() - 1);
|
|
|
|
|
2017-06-16 04:43:28 +00:00
|
|
|
let mut iter = self.grid.iter_from(point);
|
|
|
|
let last_col = self.grid.num_cols() - Column(1);
|
|
|
|
|
|
|
|
while let Some(cell) = iter.next() {
|
|
|
|
if self.semantic_escape_chars.contains(cell.c) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
point = iter.cur;
|
|
|
|
|
2017-10-12 01:52:23 +00:00
|
|
|
if iter.cur.col == last_col && !cell.flags.contains(cell::Flags::WRAPLINE) {
|
2017-06-16 04:43:28 +00:00
|
|
|
break; // cut off if on new line or hit escape char
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
point
|
|
|
|
}
|
2018-10-22 19:39:26 +00:00
|
|
|
|
|
|
|
fn url_search(&self, mut point: Point<usize>) -> Option<String> {
|
|
|
|
point.line = self.grid.num_lines().0 - point.line - 1;
|
|
|
|
|
|
|
|
// Limit the starting point to the last line in the history
|
|
|
|
point.line = min(point.line, self.grid.len() - 1);
|
|
|
|
|
|
|
|
// Create forwards and backwards iterators
|
|
|
|
let iterf = self.grid.iter_from(point);
|
|
|
|
point.col += 1;
|
|
|
|
let mut iterb = self.grid.iter_from(point);
|
|
|
|
|
|
|
|
// Put all characters until separators into a string
|
|
|
|
let mut buf = String::new();
|
|
|
|
while let Some(cell) = iterb.prev() {
|
|
|
|
if URL_SEPARATOR_CHARS.contains(&cell.c) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
buf.insert(0, cell.c);
|
|
|
|
}
|
|
|
|
for cell in iterf {
|
|
|
|
if URL_SEPARATOR_CHARS.contains(&cell.c) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
buf.push(cell.c);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Heuristic to remove all leading '('
|
|
|
|
while buf.starts_with('(') {
|
|
|
|
buf.remove(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Heuristic to remove all ')' from end of URLs without matching '('
|
|
|
|
let str_count = |text: &str, c: char| {
|
|
|
|
text.chars().filter(|tc| *tc == c).count()
|
|
|
|
};
|
|
|
|
while buf.ends_with(')') && str_count(&buf, '(') < str_count(&buf, ')') {
|
|
|
|
buf.pop();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if string is valid url
|
|
|
|
match Url::parse(&buf) {
|
|
|
|
Ok(_) => Some(buf),
|
|
|
|
Err(_) => None,
|
|
|
|
}
|
|
|
|
}
|
2017-06-16 04:43:28 +00:00
|
|
|
}
|
|
|
|
|
2018-01-06 01:42:55 +00:00
|
|
|
impl selection::Dimensions for Term {
|
2017-06-16 04:43:28 +00:00
|
|
|
fn dimensions(&self) -> Point {
|
|
|
|
Point {
|
|
|
|
col: self.grid.num_cols(),
|
|
|
|
line: self.grid.num_lines()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-28 22:13:11 +00:00
|
|
|
/// Iterator that yields cells needing render
|
|
|
|
///
|
|
|
|
/// Yields cells that require work to be displayed (that is, not a an empty
|
|
|
|
/// background cell). Additionally, this manages some state of the grid only
|
|
|
|
/// relevant for rendering like temporarily changing the cell with the cursor.
|
2016-07-04 21:46:25 +00:00
|
|
|
///
|
|
|
|
/// This manages the cursor during a render. The cursor location is inverted to
|
|
|
|
/// draw it, and reverted after drawing to maintain state.
|
2016-11-28 22:13:11 +00:00
|
|
|
pub struct RenderableCellsIter<'a> {
|
2018-02-16 02:35:49 +00:00
|
|
|
inner: DisplayIter<'a, Cell>,
|
2017-05-28 16:38:10 +00:00
|
|
|
grid: &'a Grid<Cell>,
|
2016-12-29 16:09:29 +00:00
|
|
|
cursor: &'a Point,
|
2018-02-16 02:35:49 +00:00
|
|
|
cursor_offset: usize,
|
2016-07-04 21:46:25 +00:00
|
|
|
mode: TermMode,
|
2017-02-11 20:49:40 +00:00
|
|
|
config: &'a Config,
|
|
|
|
colors: &'a color::List,
|
2016-12-22 18:43:06 +00:00
|
|
|
selection: Option<RangeInclusive<index::Linear>>,
|
2018-02-28 20:33:29 +00:00
|
|
|
cursor_cells: ArrayDeque<[Indexed<Cell>; 3]>,
|
2016-07-04 21:46:25 +00:00
|
|
|
}
|
|
|
|
|
2016-11-28 22:13:11 +00:00
|
|
|
impl<'a> RenderableCellsIter<'a> {
|
|
|
|
/// Create the renderable cells iterator
|
|
|
|
///
|
|
|
|
/// The cursor and terminal mode are required for properly displaying the
|
|
|
|
/// cursor.
|
|
|
|
fn new<'b>(
|
2017-05-28 16:38:10 +00:00
|
|
|
grid: &'b Grid<Cell>,
|
2016-12-29 16:09:29 +00:00
|
|
|
cursor: &'b Point,
|
2017-02-11 20:49:40 +00:00
|
|
|
colors: &'b color::List,
|
2016-12-22 18:43:06 +00:00
|
|
|
mode: TermMode,
|
2017-02-11 20:49:40 +00:00
|
|
|
config: &'b Config,
|
2018-03-07 04:57:40 +00:00
|
|
|
selection: Option<Locations>,
|
2017-05-28 16:38:10 +00:00
|
|
|
cursor_style: CursorStyle,
|
2016-11-28 22:13:11 +00:00
|
|
|
) -> RenderableCellsIter<'b> {
|
2018-02-16 02:35:49 +00:00
|
|
|
let cursor_offset = grid.line_to_offset(cursor.line);
|
|
|
|
let inner = grid.display_iter();
|
2017-05-28 16:38:10 +00:00
|
|
|
|
2018-03-10 13:53:54 +00:00
|
|
|
let mut selection_range = None;
|
2018-07-21 17:17:41 +00:00
|
|
|
if let Some(loc) = selection {
|
2018-03-10 13:53:54 +00:00
|
|
|
// Get on-screen lines of the selection's locations
|
|
|
|
let start_line = grid.buffer_line_to_visible(loc.start.line);
|
|
|
|
let end_line = grid.buffer_line_to_visible(loc.end.line);
|
|
|
|
|
|
|
|
// Get start/end locations based on what part of selection is on screen
|
|
|
|
let locations = match (start_line, end_line) {
|
2018-09-28 22:07:24 +00:00
|
|
|
(ViewportPosition::Visible(start_line), ViewportPosition::Visible(end_line)) => {
|
2018-03-10 13:53:54 +00:00
|
|
|
Some((start_line, loc.start.col, end_line, loc.end.col))
|
|
|
|
},
|
2018-09-28 22:07:24 +00:00
|
|
|
(ViewportPosition::Visible(start_line), ViewportPosition::Above) => {
|
2018-03-13 21:07:23 +00:00
|
|
|
Some((start_line, loc.start.col, Line(0), Column(0)))
|
2018-03-10 13:53:54 +00:00
|
|
|
},
|
2018-09-28 22:07:24 +00:00
|
|
|
(ViewportPosition::Below, ViewportPosition::Visible(end_line)) => {
|
2018-03-10 13:53:54 +00:00
|
|
|
Some((grid.num_lines(), Column(0), end_line, loc.end.col))
|
|
|
|
},
|
2018-09-28 22:07:24 +00:00
|
|
|
(ViewportPosition::Below, ViewportPosition::Above) => {
|
|
|
|
Some((grid.num_lines(), Column(0), Line(0), Column(0)))
|
|
|
|
},
|
|
|
|
_ => None,
|
2018-03-07 04:57:40 +00:00
|
|
|
};
|
|
|
|
|
2018-03-10 13:53:54 +00:00
|
|
|
if let Some((start_line, start_col, end_line, end_col)) = locations {
|
|
|
|
// start and end *lines* are swapped as we switch from buffer to
|
|
|
|
// Line coordinates.
|
|
|
|
let mut end = Point {
|
|
|
|
line: start_line,
|
|
|
|
col: start_col,
|
|
|
|
};
|
|
|
|
let mut start = Point {
|
|
|
|
line: end_line,
|
|
|
|
col: end_col,
|
|
|
|
};
|
|
|
|
|
|
|
|
if start > end {
|
|
|
|
::std::mem::swap(&mut start, &mut end);
|
|
|
|
}
|
|
|
|
|
|
|
|
let cols = grid.num_cols();
|
|
|
|
let start = Linear(start.line.0 * cols.0 + start.col.0);
|
|
|
|
let end = Linear(end.line.0 * cols.0 + end.col.0);
|
|
|
|
|
|
|
|
// Update the selection
|
|
|
|
selection_range = Some(RangeInclusive::new(start, end));
|
2018-03-07 04:57:40 +00:00
|
|
|
}
|
2018-07-21 17:17:41 +00:00
|
|
|
}
|
2018-03-07 04:57:40 +00:00
|
|
|
|
2016-11-28 22:13:11 +00:00
|
|
|
RenderableCellsIter {
|
2018-07-21 17:17:41 +00:00
|
|
|
cursor,
|
|
|
|
cursor_offset,
|
|
|
|
grid,
|
|
|
|
inner,
|
|
|
|
mode,
|
2018-03-10 13:53:54 +00:00
|
|
|
selection: selection_range,
|
2018-07-21 17:17:41 +00:00
|
|
|
config,
|
|
|
|
colors,
|
2017-05-28 16:38:10 +00:00
|
|
|
cursor_cells: ArrayDeque::new(),
|
2018-01-06 01:42:55 +00:00
|
|
|
}.initialize(cursor_style)
|
2016-11-28 22:13:11 +00:00
|
|
|
}
|
|
|
|
|
2018-03-05 17:26:36 +00:00
|
|
|
fn push_cursor_cells(&mut self, original: Cell, cursor: Cell, wide: Cell) {
|
2017-12-24 21:23:53 +00:00
|
|
|
// Prints the char under the cell if cursor is situated on a non-empty cell
|
|
|
|
self.cursor_cells.push_back(Indexed {
|
|
|
|
line: self.cursor.line,
|
|
|
|
column: self.cursor.col,
|
2018-03-05 17:26:36 +00:00
|
|
|
inner: original,
|
2018-02-28 20:33:29 +00:00
|
|
|
}).expect("won't exceed capacity");
|
2017-12-24 21:23:53 +00:00
|
|
|
|
|
|
|
// Prints the cursor
|
|
|
|
self.cursor_cells.push_back(Indexed {
|
|
|
|
line: self.cursor.line,
|
|
|
|
column: self.cursor.col,
|
2018-03-05 17:26:36 +00:00
|
|
|
inner: cursor,
|
2018-02-28 20:33:29 +00:00
|
|
|
}).expect("won't exceed capacity");
|
2017-12-24 21:23:53 +00:00
|
|
|
|
|
|
|
// If cursor is over a wide (2 cell size) character,
|
|
|
|
// print the second cursor cell
|
2018-03-05 17:26:36 +00:00
|
|
|
if self.is_wide_cursor(&cursor) {
|
2017-12-24 21:23:53 +00:00
|
|
|
self.cursor_cells.push_back(Indexed {
|
|
|
|
line: self.cursor.line,
|
|
|
|
column: self.cursor.col + 1,
|
2018-03-05 17:26:36 +00:00
|
|
|
inner: wide,
|
2018-02-28 20:33:29 +00:00
|
|
|
}).expect("won't exceed capacity");
|
2017-12-24 21:23:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-13 17:24:12 +00:00
|
|
|
fn populate_block_cursor(&mut self) {
|
2018-11-01 17:23:49 +00:00
|
|
|
let cell = &self.grid[self.cursor];
|
|
|
|
let text_color = self.config.cursor_text_color().unwrap_or(cell.bg);
|
|
|
|
let cursor_color = self.config.cursor_cursor_color().unwrap_or(cell.fg);
|
2017-05-28 16:38:10 +00:00
|
|
|
|
2017-12-24 21:23:53 +00:00
|
|
|
let original_cell = self.grid[self.cursor];
|
2017-05-28 16:38:10 +00:00
|
|
|
|
2017-12-24 21:23:53 +00:00
|
|
|
let mut cursor_cell = self.grid[self.cursor];
|
|
|
|
cursor_cell.fg = text_color;
|
|
|
|
cursor_cell.bg = cursor_color;
|
2017-05-28 16:38:10 +00:00
|
|
|
|
2017-12-24 21:23:53 +00:00
|
|
|
let mut wide_cell = cursor_cell;
|
|
|
|
wide_cell.c = ' ';
|
2017-03-02 06:24:37 +00:00
|
|
|
|
2017-12-24 21:23:53 +00:00
|
|
|
self.push_cursor_cells(original_cell, cursor_cell, wide_cell);
|
2017-05-28 16:38:10 +00:00
|
|
|
}
|
|
|
|
|
2017-12-24 21:23:53 +00:00
|
|
|
fn populate_char_cursor(&mut self, cursor_cell_char: char, wide_cell_char: char) {
|
|
|
|
let original_cell = self.grid[self.cursor];
|
|
|
|
|
|
|
|
let mut cursor_cell = self.grid[self.cursor];
|
2018-11-01 17:23:49 +00:00
|
|
|
let cursor_color = self.config.cursor_cursor_color().unwrap_or(cursor_cell.fg);
|
2017-12-24 21:23:53 +00:00
|
|
|
cursor_cell.c = cursor_cell_char;
|
|
|
|
cursor_cell.fg = cursor_color;
|
|
|
|
|
|
|
|
let mut wide_cell = cursor_cell;
|
|
|
|
wide_cell.c = wide_cell_char;
|
|
|
|
|
|
|
|
self.push_cursor_cells(original_cell, cursor_cell, wide_cell);
|
2017-05-28 16:38:10 +00:00
|
|
|
}
|
2017-02-11 20:49:40 +00:00
|
|
|
|
2017-05-28 16:38:10 +00:00
|
|
|
fn populate_underline_cursor(&mut self) {
|
2017-12-24 21:23:53 +00:00
|
|
|
self.populate_char_cursor(font::UNDERLINE_CURSOR_CHAR, font::UNDERLINE_CURSOR_CHAR);
|
2017-12-09 23:33:58 +00:00
|
|
|
}
|
|
|
|
|
2017-12-24 21:23:53 +00:00
|
|
|
fn populate_beam_cursor(&mut self) {
|
|
|
|
self.populate_char_cursor(font::BEAM_CURSOR_CHAR, ' ');
|
|
|
|
}
|
2017-05-28 16:38:10 +00:00
|
|
|
|
2018-01-06 01:42:55 +00:00
|
|
|
fn populate_box_cursor(&mut self) {
|
|
|
|
self.populate_char_cursor(font::BOX_CURSOR_CHAR, ' ');
|
|
|
|
}
|
|
|
|
|
2017-12-24 21:23:53 +00:00
|
|
|
#[inline]
|
|
|
|
fn is_wide_cursor(&self, cell: &Cell) -> bool {
|
|
|
|
cell.flags.contains(cell::Flags::WIDE_CHAR) && (self.cursor.col + 1) < self.grid.num_cols()
|
2017-05-28 16:38:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Populates list of cursor cells with the original cell
|
|
|
|
fn populate_no_cursor(&mut self) {
|
|
|
|
self.cursor_cells.push_back(Indexed {
|
|
|
|
line: self.cursor.line,
|
|
|
|
column: self.cursor.col,
|
|
|
|
inner: self.grid[self.cursor],
|
2018-02-28 20:33:29 +00:00
|
|
|
}).expect("won't exceed capacity");
|
2017-05-28 16:38:10 +00:00
|
|
|
}
|
|
|
|
|
2018-01-06 01:42:55 +00:00
|
|
|
fn initialize(mut self, cursor_style: CursorStyle) -> Self {
|
2017-05-28 16:38:10 +00:00
|
|
|
if self.cursor_is_visible() {
|
2018-01-06 01:42:55 +00:00
|
|
|
match cursor_style {
|
|
|
|
CursorStyle::HollowBlock => {
|
|
|
|
self.populate_box_cursor();
|
|
|
|
},
|
|
|
|
CursorStyle::Block => {
|
|
|
|
self.populate_block_cursor();
|
|
|
|
},
|
|
|
|
CursorStyle::Beam => {
|
|
|
|
self.populate_beam_cursor();
|
|
|
|
},
|
|
|
|
CursorStyle::Underline => {
|
|
|
|
self.populate_underline_cursor();
|
2017-03-02 06:24:37 +00:00
|
|
|
}
|
2017-01-09 03:18:39 +00:00
|
|
|
}
|
2017-05-28 16:38:10 +00:00
|
|
|
} else {
|
|
|
|
self.populate_no_cursor();
|
2017-01-09 03:18:39 +00:00
|
|
|
}
|
2016-11-28 22:13:11 +00:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Check if the cursor should be rendered.
|
|
|
|
#[inline]
|
|
|
|
fn cursor_is_visible(&self) -> bool {
|
2017-10-12 01:52:23 +00:00
|
|
|
self.mode.contains(mode::TermMode::SHOW_CURSOR) && self.grid.contains(self.cursor)
|
2016-07-04 21:46:25 +00:00
|
|
|
}
|
|
|
|
|
2018-07-21 17:17:41 +00:00
|
|
|
fn compute_fg_rgb(&self, fg: Color, cell: &Cell) -> Rgb {
|
2017-10-12 01:52:23 +00:00
|
|
|
use self::cell::Flags;
|
2018-07-21 17:17:41 +00:00
|
|
|
match fg {
|
2017-05-28 16:38:10 +00:00
|
|
|
Color::Spec(rgb) => rgb,
|
|
|
|
Color::Named(ansi) => {
|
2017-10-12 01:52:23 +00:00
|
|
|
match (self.config.draw_bold_text_with_bright_colors(), cell.flags & Flags::DIM_BOLD) {
|
Merge master into scrollback
* Allow disabling DPI scaling
This makes it possible to disable DPI scaling completely, instead the
the display pixel ration will always be fixed to 1.0.
By default nothing has changed and DPI is still enabled, this just seems
like a better way than running `WINIT_HIDPI_FACTOR=1.0 alacritty` every
time the user wants to start alacritty.
It would be possible to allow specifying any DPR, however I've decided
against this since I'd assume it's a very rare usecase. It's also still
possible to make use of `WINIT_HIDPI_FACTOR` to do this on X11.
Currently this is not updated at runtime using the live config update,
there is not really much of a technical limitation why this woudn't be
possible, however a solution for that issue should be first added in
jwilm/alacritty#1346, once a system is established for changing DPI at
runtime, porting that functionality to this PR should be simple.
* Add working --class and --title CLI parameters
* Reduce Increase-/DecreaseFontSize step to 0.5
Until now the Increase-/DecreaseFontSize keybinds hand a step size of 1.0. Since the font size however is multiplied by two to allow more granular font size control, this lead to the bindings skipping one font size (incrementing/decrementing by +-2).
To fix this the step size of the Increase-/DecreaseFontSize bindings has been reduced to the minimum step size that exists with the current font configuration (0.5). This should allow users to increment and decrement the font size by a single point instead of two.
This also adds a few tests to make sure the methods for increasing/decreasing/resetting font size work properly.
* Add Copy/Cut/Paste keys
This just adds support for the Copy/Cut/Paste keys and sets up
Copy/Paste as alternative defaults for Ctrl+Shift+C/V.
* Move to cargo clippy
Using clippy as a library has been deprecated, instead the `cargo
clippy` command should be used instead. To comply with this change
clippy has been removed from the `Cargo.toml` and is now installed with
cargo when building in CI.
This has also lead to a few new clippy issues to show up, this includes
everything in the `font` subdirectory. This has been fixed and `font`
should now be covered by clippy CI too.
This also upgrades all dependencies, as a result this fixes #1341 and
this fixes #1344.
* Override dynamic_title when --title is specified
* Change green implementation to use the macro
* Ignore mouse input if window is unfocused
* Make compilation of binary a phony target
* Add opensuse zypper install method to readme
* Fix clippy issues
* Update manpage to document all CLI options
The introduction of `--class` has added a flag to the CLI without adding
it to the manpage. This has been fixed by updating the manpage.
This also adds the default values of `--class` and `--title` to the CLI
options.
* Remove unnecessary clippy lint annotations
We moved to "cargo clippy" in 5ba34d4f9766a55a06ed5e3e44cc384af1b09f65 and
removing the clippy lint annotations in `src/lib.rs` does not cause any additional warnings.
This also changes `cargo clippy` to use the flags required for checking integration tests.
* Enable clippy in font/copypasta crates
Enabled clippy in the sub-crates font and copypasta. All issues
that were discovered by this change have also been fixed.
* Remove outdated comment about NixOS
* Replace debug asserts with static_assertions
To check that transmutes will work correctly without having to rely on
error-prone runtime checking, the `static_assertions` crate has been
introduced. This allows comparing the size of types at compile time,
preventing potentially silent breakage.
This fixes #1417.
* Add `cargo deb` build instructions
Updated the `Cargo.toml` file and added a `package.metadata.deb`
subsection to define how to build a debian "deb" install file using
`cargo deb`. This will allow debian/ubuntu users to install `alacritty`
using their system's package manager. It also will make it easier to
provide pre-built binaries for those systems.
Also fixed a stray debug line in the bash autocomplete script that was
writting to a tempfile.
* Add config for unfocused window cursor change
* Add support for cursor shape escape sequence
* Add bright foreground color option
It was requested in jwilm/alacritty#825 that it should be possible to
add an optional bright foreground color.
This is now added to the primary colors structure and allows the user to
set a foreground color for bold normal text. This has no effect unless
the draw_bold_text_with_bright_colors option is also enabled.
If the color is not specified, the bright foreground color will fall
back to the normal foreground color.
This fixes #825.
* Fix clone URL in deb install instructions
* Fix 'cargo-deb' desktop file name
* Remove redundant dependency from deb build
* Switch from deprecated `std::env::home_dir` to `dirs::home_dir`
* Allow specifying modifiers for mouse bindings
* Send newline with NumpadEnter
* Add support for LCD-V pixel mode
* Add binding action for hiding the window
* Switch to rustup clippy component
* Add optional dim foreground color
Add optional color for the dim foreground (`\e[2m;`)
Defaults to 2/3 of the foreground color. (same as other colors).
If a bright color is dimmed, it's displayed as the normal color. The
exception for this is when the bright foreground is dimmed when no
bright foreground color is set. In that case it's treated as a normal
foreground color and dimmed to DimForeground.
To minimize the surprise for the user, the bright and dim colors have
been completely removed from the default configuration file.
Some documentation has also been added to make it clear to users what
these options can be used for.
This fixes #1448.
* Fix clippy lints and run font tests on travis
This fixes some existing clippy issues and runs the `font` tests through travis.
Testing of copypasta crate was omitted due to problens when running on headless travis-ci environment (x11 clipboard would fail).
* Ignore errors when logger can't write to output
The (e)print macro will panic when there is no output available to
write to, however in our scenario where we only log user errors to
stderr, the better choice would be to ignore when writing to stdout or
stderr is not possible.
This changes the (e)print macro to make use of `write` and ignore
any potential errors.
Since (e)println rely on (e)print, this also solves potential failuers
when calling (e)println.
With this change implemented, all of logging, (e)println and (e)print
should never fail even if the stdout/stderr is not available.
2018-07-28 23:10:13 +00:00
|
|
|
// If no bright foreground is set, treat it like the BOLD flag doesn't exist
|
|
|
|
(_, self::cell::Flags::DIM_BOLD)
|
|
|
|
if ansi == NamedColor::Foreground
|
|
|
|
&& self.config.colors().primary.bright_foreground.is_none() =>
|
|
|
|
{
|
|
|
|
self.colors[NamedColor::DimForeground]
|
|
|
|
}
|
2017-06-23 17:01:53 +00:00
|
|
|
// Draw bold text in bright colors *and* contains bold flag.
|
Merge master into scrollback
* Allow disabling DPI scaling
This makes it possible to disable DPI scaling completely, instead the
the display pixel ration will always be fixed to 1.0.
By default nothing has changed and DPI is still enabled, this just seems
like a better way than running `WINIT_HIDPI_FACTOR=1.0 alacritty` every
time the user wants to start alacritty.
It would be possible to allow specifying any DPR, however I've decided
against this since I'd assume it's a very rare usecase. It's also still
possible to make use of `WINIT_HIDPI_FACTOR` to do this on X11.
Currently this is not updated at runtime using the live config update,
there is not really much of a technical limitation why this woudn't be
possible, however a solution for that issue should be first added in
jwilm/alacritty#1346, once a system is established for changing DPI at
runtime, porting that functionality to this PR should be simple.
* Add working --class and --title CLI parameters
* Reduce Increase-/DecreaseFontSize step to 0.5
Until now the Increase-/DecreaseFontSize keybinds hand a step size of 1.0. Since the font size however is multiplied by two to allow more granular font size control, this lead to the bindings skipping one font size (incrementing/decrementing by +-2).
To fix this the step size of the Increase-/DecreaseFontSize bindings has been reduced to the minimum step size that exists with the current font configuration (0.5). This should allow users to increment and decrement the font size by a single point instead of two.
This also adds a few tests to make sure the methods for increasing/decreasing/resetting font size work properly.
* Add Copy/Cut/Paste keys
This just adds support for the Copy/Cut/Paste keys and sets up
Copy/Paste as alternative defaults for Ctrl+Shift+C/V.
* Move to cargo clippy
Using clippy as a library has been deprecated, instead the `cargo
clippy` command should be used instead. To comply with this change
clippy has been removed from the `Cargo.toml` and is now installed with
cargo when building in CI.
This has also lead to a few new clippy issues to show up, this includes
everything in the `font` subdirectory. This has been fixed and `font`
should now be covered by clippy CI too.
This also upgrades all dependencies, as a result this fixes #1341 and
this fixes #1344.
* Override dynamic_title when --title is specified
* Change green implementation to use the macro
* Ignore mouse input if window is unfocused
* Make compilation of binary a phony target
* Add opensuse zypper install method to readme
* Fix clippy issues
* Update manpage to document all CLI options
The introduction of `--class` has added a flag to the CLI without adding
it to the manpage. This has been fixed by updating the manpage.
This also adds the default values of `--class` and `--title` to the CLI
options.
* Remove unnecessary clippy lint annotations
We moved to "cargo clippy" in 5ba34d4f9766a55a06ed5e3e44cc384af1b09f65 and
removing the clippy lint annotations in `src/lib.rs` does not cause any additional warnings.
This also changes `cargo clippy` to use the flags required for checking integration tests.
* Enable clippy in font/copypasta crates
Enabled clippy in the sub-crates font and copypasta. All issues
that were discovered by this change have also been fixed.
* Remove outdated comment about NixOS
* Replace debug asserts with static_assertions
To check that transmutes will work correctly without having to rely on
error-prone runtime checking, the `static_assertions` crate has been
introduced. This allows comparing the size of types at compile time,
preventing potentially silent breakage.
This fixes #1417.
* Add `cargo deb` build instructions
Updated the `Cargo.toml` file and added a `package.metadata.deb`
subsection to define how to build a debian "deb" install file using
`cargo deb`. This will allow debian/ubuntu users to install `alacritty`
using their system's package manager. It also will make it easier to
provide pre-built binaries for those systems.
Also fixed a stray debug line in the bash autocomplete script that was
writting to a tempfile.
* Add config for unfocused window cursor change
* Add support for cursor shape escape sequence
* Add bright foreground color option
It was requested in jwilm/alacritty#825 that it should be possible to
add an optional bright foreground color.
This is now added to the primary colors structure and allows the user to
set a foreground color for bold normal text. This has no effect unless
the draw_bold_text_with_bright_colors option is also enabled.
If the color is not specified, the bright foreground color will fall
back to the normal foreground color.
This fixes #825.
* Fix clone URL in deb install instructions
* Fix 'cargo-deb' desktop file name
* Remove redundant dependency from deb build
* Switch from deprecated `std::env::home_dir` to `dirs::home_dir`
* Allow specifying modifiers for mouse bindings
* Send newline with NumpadEnter
* Add support for LCD-V pixel mode
* Add binding action for hiding the window
* Switch to rustup clippy component
* Add optional dim foreground color
Add optional color for the dim foreground (`\e[2m;`)
Defaults to 2/3 of the foreground color. (same as other colors).
If a bright color is dimmed, it's displayed as the normal color. The
exception for this is when the bright foreground is dimmed when no
bright foreground color is set. In that case it's treated as a normal
foreground color and dimmed to DimForeground.
To minimize the surprise for the user, the bright and dim colors have
been completely removed from the default configuration file.
Some documentation has also been added to make it clear to users what
these options can be used for.
This fixes #1448.
* Fix clippy lints and run font tests on travis
This fixes some existing clippy issues and runs the `font` tests through travis.
Testing of copypasta crate was omitted due to problens when running on headless travis-ci environment (x11 clipboard would fail).
* Ignore errors when logger can't write to output
The (e)print macro will panic when there is no output available to
write to, however in our scenario where we only log user errors to
stderr, the better choice would be to ignore when writing to stdout or
stderr is not possible.
This changes the (e)print macro to make use of `write` and ignore
any potential errors.
Since (e)println rely on (e)print, this also solves potential failuers
when calling (e)println.
With this change implemented, all of logging, (e)println and (e)print
should never fail even if the stdout/stderr is not available.
2018-07-28 23:10:13 +00:00
|
|
|
(true, self::cell::Flags::BOLD) => self.colors[ansi.to_bright()],
|
2017-06-23 17:01:53 +00:00
|
|
|
// Cell is marked as dim and not bold
|
Merge master into scrollback
* Allow disabling DPI scaling
This makes it possible to disable DPI scaling completely, instead the
the display pixel ration will always be fixed to 1.0.
By default nothing has changed and DPI is still enabled, this just seems
like a better way than running `WINIT_HIDPI_FACTOR=1.0 alacritty` every
time the user wants to start alacritty.
It would be possible to allow specifying any DPR, however I've decided
against this since I'd assume it's a very rare usecase. It's also still
possible to make use of `WINIT_HIDPI_FACTOR` to do this on X11.
Currently this is not updated at runtime using the live config update,
there is not really much of a technical limitation why this woudn't be
possible, however a solution for that issue should be first added in
jwilm/alacritty#1346, once a system is established for changing DPI at
runtime, porting that functionality to this PR should be simple.
* Add working --class and --title CLI parameters
* Reduce Increase-/DecreaseFontSize step to 0.5
Until now the Increase-/DecreaseFontSize keybinds hand a step size of 1.0. Since the font size however is multiplied by two to allow more granular font size control, this lead to the bindings skipping one font size (incrementing/decrementing by +-2).
To fix this the step size of the Increase-/DecreaseFontSize bindings has been reduced to the minimum step size that exists with the current font configuration (0.5). This should allow users to increment and decrement the font size by a single point instead of two.
This also adds a few tests to make sure the methods for increasing/decreasing/resetting font size work properly.
* Add Copy/Cut/Paste keys
This just adds support for the Copy/Cut/Paste keys and sets up
Copy/Paste as alternative defaults for Ctrl+Shift+C/V.
* Move to cargo clippy
Using clippy as a library has been deprecated, instead the `cargo
clippy` command should be used instead. To comply with this change
clippy has been removed from the `Cargo.toml` and is now installed with
cargo when building in CI.
This has also lead to a few new clippy issues to show up, this includes
everything in the `font` subdirectory. This has been fixed and `font`
should now be covered by clippy CI too.
This also upgrades all dependencies, as a result this fixes #1341 and
this fixes #1344.
* Override dynamic_title when --title is specified
* Change green implementation to use the macro
* Ignore mouse input if window is unfocused
* Make compilation of binary a phony target
* Add opensuse zypper install method to readme
* Fix clippy issues
* Update manpage to document all CLI options
The introduction of `--class` has added a flag to the CLI without adding
it to the manpage. This has been fixed by updating the manpage.
This also adds the default values of `--class` and `--title` to the CLI
options.
* Remove unnecessary clippy lint annotations
We moved to "cargo clippy" in 5ba34d4f9766a55a06ed5e3e44cc384af1b09f65 and
removing the clippy lint annotations in `src/lib.rs` does not cause any additional warnings.
This also changes `cargo clippy` to use the flags required for checking integration tests.
* Enable clippy in font/copypasta crates
Enabled clippy in the sub-crates font and copypasta. All issues
that were discovered by this change have also been fixed.
* Remove outdated comment about NixOS
* Replace debug asserts with static_assertions
To check that transmutes will work correctly without having to rely on
error-prone runtime checking, the `static_assertions` crate has been
introduced. This allows comparing the size of types at compile time,
preventing potentially silent breakage.
This fixes #1417.
* Add `cargo deb` build instructions
Updated the `Cargo.toml` file and added a `package.metadata.deb`
subsection to define how to build a debian "deb" install file using
`cargo deb`. This will allow debian/ubuntu users to install `alacritty`
using their system's package manager. It also will make it easier to
provide pre-built binaries for those systems.
Also fixed a stray debug line in the bash autocomplete script that was
writting to a tempfile.
* Add config for unfocused window cursor change
* Add support for cursor shape escape sequence
* Add bright foreground color option
It was requested in jwilm/alacritty#825 that it should be possible to
add an optional bright foreground color.
This is now added to the primary colors structure and allows the user to
set a foreground color for bold normal text. This has no effect unless
the draw_bold_text_with_bright_colors option is also enabled.
If the color is not specified, the bright foreground color will fall
back to the normal foreground color.
This fixes #825.
* Fix clone URL in deb install instructions
* Fix 'cargo-deb' desktop file name
* Remove redundant dependency from deb build
* Switch from deprecated `std::env::home_dir` to `dirs::home_dir`
* Allow specifying modifiers for mouse bindings
* Send newline with NumpadEnter
* Add support for LCD-V pixel mode
* Add binding action for hiding the window
* Switch to rustup clippy component
* Add optional dim foreground color
Add optional color for the dim foreground (`\e[2m;`)
Defaults to 2/3 of the foreground color. (same as other colors).
If a bright color is dimmed, it's displayed as the normal color. The
exception for this is when the bright foreground is dimmed when no
bright foreground color is set. In that case it's treated as a normal
foreground color and dimmed to DimForeground.
To minimize the surprise for the user, the bright and dim colors have
been completely removed from the default configuration file.
Some documentation has also been added to make it clear to users what
these options can be used for.
This fixes #1448.
* Fix clippy lints and run font tests on travis
This fixes some existing clippy issues and runs the `font` tests through travis.
Testing of copypasta crate was omitted due to problens when running on headless travis-ci environment (x11 clipboard would fail).
* Ignore errors when logger can't write to output
The (e)print macro will panic when there is no output available to
write to, however in our scenario where we only log user errors to
stderr, the better choice would be to ignore when writing to stdout or
stderr is not possible.
This changes the (e)print macro to make use of `write` and ignore
any potential errors.
Since (e)println rely on (e)print, this also solves potential failuers
when calling (e)println.
With this change implemented, all of logging, (e)println and (e)print
should never fail even if the stdout/stderr is not available.
2018-07-28 23:10:13 +00:00
|
|
|
(_, self::cell::Flags::DIM) |
|
|
|
|
(false, self::cell::Flags::DIM_BOLD) => self.colors[ansi.to_dim()],
|
2017-06-23 17:01:53 +00:00
|
|
|
// None of the above, keep original color.
|
|
|
|
_ => self.colors[ansi]
|
2017-05-28 16:38:10 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
Color::Indexed(idx) => {
|
2017-06-23 17:01:53 +00:00
|
|
|
let idx = match (
|
|
|
|
self.config.draw_bold_text_with_bright_colors(),
|
2017-10-12 01:52:23 +00:00
|
|
|
cell.flags & Flags::DIM_BOLD,
|
2017-05-28 16:38:10 +00:00
|
|
|
idx
|
2017-06-23 17:01:53 +00:00
|
|
|
) {
|
2017-10-12 01:52:23 +00:00
|
|
|
(true, self::cell::Flags::BOLD, 0...7) => idx as usize + 8,
|
|
|
|
(false, self::cell::Flags::DIM, 8...15) => idx as usize - 8,
|
|
|
|
(false, self::cell::Flags::DIM, 0...7) => idx as usize + 260,
|
2017-06-23 17:01:53 +00:00
|
|
|
_ => idx as usize,
|
2017-05-28 16:38:10 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
self.colors[idx]
|
2017-03-02 06:24:37 +00:00
|
|
|
}
|
2016-07-04 21:46:25 +00:00
|
|
|
}
|
|
|
|
}
|
2017-05-28 16:38:10 +00:00
|
|
|
|
2017-10-21 22:26:42 +00:00
|
|
|
#[inline]
|
2018-07-21 17:17:41 +00:00
|
|
|
fn compute_bg_alpha(&self, bg: Color) -> f32 {
|
|
|
|
match bg {
|
2017-10-21 22:26:42 +00:00
|
|
|
Color::Named(NamedColor::Background) => 0.0,
|
|
|
|
_ => 1.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-21 17:17:41 +00:00
|
|
|
fn compute_bg_rgb(&self, bg: Color) -> Rgb {
|
|
|
|
match bg {
|
2017-05-28 16:38:10 +00:00
|
|
|
Color::Spec(rgb) => rgb,
|
|
|
|
Color::Named(ansi) => self.colors[ansi],
|
|
|
|
Color::Indexed(idx) => self.colors[idx],
|
|
|
|
}
|
|
|
|
}
|
2016-07-04 21:46:25 +00:00
|
|
|
}
|
|
|
|
|
2018-09-30 21:54:08 +00:00
|
|
|
#[derive(Debug)]
|
2017-02-11 20:49:40 +00:00
|
|
|
pub struct RenderableCell {
|
2018-02-16 02:35:49 +00:00
|
|
|
/// A _Display_ line (not necessarily an _Active_ line)
|
2016-11-28 22:13:11 +00:00
|
|
|
pub line: Line,
|
|
|
|
pub column: Column,
|
2017-02-11 20:49:40 +00:00
|
|
|
pub c: char,
|
|
|
|
pub fg: Rgb,
|
|
|
|
pub bg: Rgb,
|
2017-10-21 22:26:42 +00:00
|
|
|
pub bg_alpha: f32,
|
2017-02-11 20:49:40 +00:00
|
|
|
pub flags: cell::Flags,
|
2016-11-28 22:13:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Iterator for RenderableCellsIter<'a> {
|
2017-02-11 20:49:40 +00:00
|
|
|
type Item = RenderableCell;
|
2016-11-28 22:13:11 +00:00
|
|
|
|
|
|
|
/// Gets the next renderable cell
|
|
|
|
///
|
|
|
|
/// Skips empty (background) cells and applies any flags to the cell state
|
|
|
|
/// (eg. invert fg and bg colors).
|
2016-12-17 06:13:51 +00:00
|
|
|
#[inline]
|
2016-11-28 22:13:11 +00:00
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
2018-02-16 02:35:49 +00:00
|
|
|
loop {
|
|
|
|
// Handle cursor
|
|
|
|
let (cell, selected) = if self.cursor_offset == self.inner.offset() &&
|
|
|
|
self.inner.column() == self.cursor.col
|
|
|
|
{
|
|
|
|
// Cursor cell
|
2018-02-16 03:34:09 +00:00
|
|
|
let mut cell = self.cursor_cells.pop_front().unwrap();
|
|
|
|
cell.line = self.inner.line();
|
2018-02-16 02:35:49 +00:00
|
|
|
|
|
|
|
// Since there may be multiple cursor cells (for a wide
|
|
|
|
// char), only update iteration position after all cursor
|
|
|
|
// cells have been drawn.
|
|
|
|
if self.cursor_cells.is_empty() {
|
|
|
|
self.inner.next();
|
|
|
|
}
|
|
|
|
(cell, false)
|
|
|
|
} else {
|
|
|
|
let cell = self.inner.next()?;
|
|
|
|
|
2018-03-07 04:57:40 +00:00
|
|
|
let index = Linear(cell.line.0 * self.grid.num_cols().0 + cell.column.0);
|
|
|
|
|
|
|
|
let selected = self.selection.as_ref()
|
|
|
|
.map(|range| range.contains_(index))
|
|
|
|
.unwrap_or(false);
|
2018-02-16 02:35:49 +00:00
|
|
|
|
|
|
|
// Skip empty cells
|
|
|
|
if cell.is_empty() && !selected {
|
|
|
|
continue;
|
|
|
|
}
|
Merge master into scrollback
* Allow disabling DPI scaling
This makes it possible to disable DPI scaling completely, instead the
the display pixel ration will always be fixed to 1.0.
By default nothing has changed and DPI is still enabled, this just seems
like a better way than running `WINIT_HIDPI_FACTOR=1.0 alacritty` every
time the user wants to start alacritty.
It would be possible to allow specifying any DPR, however I've decided
against this since I'd assume it's a very rare usecase. It's also still
possible to make use of `WINIT_HIDPI_FACTOR` to do this on X11.
Currently this is not updated at runtime using the live config update,
there is not really much of a technical limitation why this woudn't be
possible, however a solution for that issue should be first added in
jwilm/alacritty#1346, once a system is established for changing DPI at
runtime, porting that functionality to this PR should be simple.
* Add working --class and --title CLI parameters
* Reduce Increase-/DecreaseFontSize step to 0.5
Until now the Increase-/DecreaseFontSize keybinds hand a step size of 1.0. Since the font size however is multiplied by two to allow more granular font size control, this lead to the bindings skipping one font size (incrementing/decrementing by +-2).
To fix this the step size of the Increase-/DecreaseFontSize bindings has been reduced to the minimum step size that exists with the current font configuration (0.5). This should allow users to increment and decrement the font size by a single point instead of two.
This also adds a few tests to make sure the methods for increasing/decreasing/resetting font size work properly.
* Add Copy/Cut/Paste keys
This just adds support for the Copy/Cut/Paste keys and sets up
Copy/Paste as alternative defaults for Ctrl+Shift+C/V.
* Move to cargo clippy
Using clippy as a library has been deprecated, instead the `cargo
clippy` command should be used instead. To comply with this change
clippy has been removed from the `Cargo.toml` and is now installed with
cargo when building in CI.
This has also lead to a few new clippy issues to show up, this includes
everything in the `font` subdirectory. This has been fixed and `font`
should now be covered by clippy CI too.
This also upgrades all dependencies, as a result this fixes #1341 and
this fixes #1344.
* Override dynamic_title when --title is specified
* Change green implementation to use the macro
* Ignore mouse input if window is unfocused
* Make compilation of binary a phony target
* Add opensuse zypper install method to readme
* Fix clippy issues
* Update manpage to document all CLI options
The introduction of `--class` has added a flag to the CLI without adding
it to the manpage. This has been fixed by updating the manpage.
This also adds the default values of `--class` and `--title` to the CLI
options.
* Remove unnecessary clippy lint annotations
We moved to "cargo clippy" in 5ba34d4f9766a55a06ed5e3e44cc384af1b09f65 and
removing the clippy lint annotations in `src/lib.rs` does not cause any additional warnings.
This also changes `cargo clippy` to use the flags required for checking integration tests.
* Enable clippy in font/copypasta crates
Enabled clippy in the sub-crates font and copypasta. All issues
that were discovered by this change have also been fixed.
* Remove outdated comment about NixOS
* Replace debug asserts with static_assertions
To check that transmutes will work correctly without having to rely on
error-prone runtime checking, the `static_assertions` crate has been
introduced. This allows comparing the size of types at compile time,
preventing potentially silent breakage.
This fixes #1417.
* Add `cargo deb` build instructions
Updated the `Cargo.toml` file and added a `package.metadata.deb`
subsection to define how to build a debian "deb" install file using
`cargo deb`. This will allow debian/ubuntu users to install `alacritty`
using their system's package manager. It also will make it easier to
provide pre-built binaries for those systems.
Also fixed a stray debug line in the bash autocomplete script that was
writting to a tempfile.
* Add config for unfocused window cursor change
* Add support for cursor shape escape sequence
* Add bright foreground color option
It was requested in jwilm/alacritty#825 that it should be possible to
add an optional bright foreground color.
This is now added to the primary colors structure and allows the user to
set a foreground color for bold normal text. This has no effect unless
the draw_bold_text_with_bright_colors option is also enabled.
If the color is not specified, the bright foreground color will fall
back to the normal foreground color.
This fixes #825.
* Fix clone URL in deb install instructions
* Fix 'cargo-deb' desktop file name
* Remove redundant dependency from deb build
* Switch from deprecated `std::env::home_dir` to `dirs::home_dir`
* Allow specifying modifiers for mouse bindings
* Send newline with NumpadEnter
* Add support for LCD-V pixel mode
* Add binding action for hiding the window
* Switch to rustup clippy component
* Add optional dim foreground color
Add optional color for the dim foreground (`\e[2m;`)
Defaults to 2/3 of the foreground color. (same as other colors).
If a bright color is dimmed, it's displayed as the normal color. The
exception for this is when the bright foreground is dimmed when no
bright foreground color is set. In that case it's treated as a normal
foreground color and dimmed to DimForeground.
To minimize the surprise for the user, the bright and dim colors have
been completely removed from the default configuration file.
Some documentation has also been added to make it clear to users what
these options can be used for.
This fixes #1448.
* Fix clippy lints and run font tests on travis
This fixes some existing clippy issues and runs the `font` tests through travis.
Testing of copypasta crate was omitted due to problens when running on headless travis-ci environment (x11 clipboard would fail).
* Ignore errors when logger can't write to output
The (e)print macro will panic when there is no output available to
write to, however in our scenario where we only log user errors to
stderr, the better choice would be to ignore when writing to stdout or
stderr is not possible.
This changes the (e)print macro to make use of `write` and ignore
any potential errors.
Since (e)println rely on (e)print, this also solves potential failuers
when calling (e)println.
With this change implemented, all of logging, (e)println and (e)print
should never fail even if the stdout/stderr is not available.
2018-07-28 23:10:13 +00:00
|
|
|
|
2018-02-16 02:35:49 +00:00
|
|
|
(cell, selected)
|
|
|
|
};
|
2016-11-28 22:13:11 +00:00
|
|
|
|
2018-02-16 02:35:49 +00:00
|
|
|
// Apply inversion and lookup RGB values
|
2018-09-19 19:18:51 +00:00
|
|
|
let mut fg_rgb = self.compute_fg_rgb(cell.fg, &cell);
|
|
|
|
let mut bg_rgb = self.compute_bg_rgb(cell.bg);
|
2017-05-28 16:38:10 +00:00
|
|
|
|
2018-09-19 19:18:51 +00:00
|
|
|
let bg_alpha = if selected ^ cell.inverse() {
|
|
|
|
mem::swap(&mut fg_rgb, &mut bg_rgb);
|
|
|
|
self.compute_bg_alpha(cell.fg)
|
2018-02-16 02:35:49 +00:00
|
|
|
} else {
|
2018-09-19 19:18:51 +00:00
|
|
|
self.compute_bg_alpha(cell.bg)
|
|
|
|
};
|
2016-11-28 22:13:11 +00:00
|
|
|
|
2018-02-16 02:35:49 +00:00
|
|
|
return Some(RenderableCell {
|
|
|
|
line: cell.line,
|
|
|
|
column: cell.column,
|
|
|
|
flags: cell.flags,
|
|
|
|
c: cell.c,
|
|
|
|
fg: fg_rgb,
|
|
|
|
bg: bg_rgb,
|
2018-07-21 17:17:41 +00:00
|
|
|
bg_alpha,
|
2018-02-16 02:35:49 +00:00
|
|
|
})
|
2016-11-28 22:13:11 +00:00
|
|
|
}
|
2016-07-04 21:46:25 +00:00
|
|
|
}
|
2017-05-28 16:38:10 +00:00
|
|
|
|
2016-07-04 21:46:25 +00:00
|
|
|
}
|
|
|
|
|
2016-06-08 04:17:48 +00:00
|
|
|
pub mod mode {
|
|
|
|
bitflags! {
|
2017-08-30 18:43:37 +00:00
|
|
|
pub struct TermMode: u16 {
|
2018-01-26 21:28:43 +00:00
|
|
|
const SHOW_CURSOR = 0b00_0000_0000_0001;
|
|
|
|
const APP_CURSOR = 0b00_0000_0000_0010;
|
|
|
|
const APP_KEYPAD = 0b00_0000_0000_0100;
|
|
|
|
const MOUSE_REPORT_CLICK = 0b00_0000_0000_1000;
|
|
|
|
const BRACKETED_PASTE = 0b00_0000_0001_0000;
|
|
|
|
const SGR_MOUSE = 0b00_0000_0010_0000;
|
|
|
|
const MOUSE_MOTION = 0b00_0000_0100_0000;
|
|
|
|
const LINE_WRAP = 0b00_0000_1000_0000;
|
|
|
|
const LINE_FEED_NEW_LINE = 0b00_0001_0000_0000;
|
|
|
|
const ORIGIN = 0b00_0010_0000_0000;
|
|
|
|
const INSERT = 0b00_0100_0000_0000;
|
|
|
|
const FOCUS_IN_OUT = 0b00_1000_0000_0000;
|
|
|
|
const ALT_SCREEN = 0b01_0000_0000_0000;
|
|
|
|
const MOUSE_DRAG = 0b10_0000_0000_0000;
|
|
|
|
const ANY = 0b11_1111_1111_1111;
|
2017-08-30 18:43:37 +00:00
|
|
|
const NONE = 0;
|
2016-06-23 16:42:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for TermMode {
|
|
|
|
fn default() -> TermMode {
|
2017-10-12 01:52:23 +00:00
|
|
|
TermMode::SHOW_CURSOR | TermMode::LINE_WRAP
|
2016-06-08 04:17:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub use self::mode::TermMode;
|
|
|
|
|
2017-01-09 20:07:23 +00:00
|
|
|
trait CharsetMapping {
|
|
|
|
fn map(&self, c: char) -> char {
|
|
|
|
c
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl CharsetMapping for StandardCharset {
|
|
|
|
/// Switch/Map character to the active charset. Ascii is the common case and
|
|
|
|
/// for that we want to do as little as possible.
|
|
|
|
#[inline]
|
|
|
|
fn map(&self, c: char) -> char {
|
|
|
|
match *self {
|
|
|
|
StandardCharset::Ascii => c,
|
|
|
|
StandardCharset::SpecialCharacterAndLineDrawing =>
|
|
|
|
match c {
|
|
|
|
'`' => '◆',
|
|
|
|
'a' => '▒',
|
|
|
|
'b' => '\t',
|
|
|
|
'c' => '\u{000c}',
|
|
|
|
'd' => '\r',
|
|
|
|
'e' => '\n',
|
|
|
|
'f' => '°',
|
|
|
|
'g' => '±',
|
|
|
|
'h' => '\u{2424}',
|
|
|
|
'i' => '\u{000b}',
|
|
|
|
'j' => '┘',
|
|
|
|
'k' => '┐',
|
|
|
|
'l' => '┌',
|
|
|
|
'm' => '└',
|
|
|
|
'n' => '┼',
|
|
|
|
'o' => '⎺',
|
|
|
|
'p' => '⎻',
|
|
|
|
'q' => '─',
|
|
|
|
'r' => '⎼',
|
|
|
|
's' => '⎽',
|
|
|
|
't' => '├',
|
|
|
|
'u' => '┤',
|
|
|
|
'v' => '┴',
|
|
|
|
'w' => '┬',
|
|
|
|
'x' => '│',
|
|
|
|
'y' => '≤',
|
|
|
|
'z' => '≥',
|
|
|
|
'{' => 'π',
|
|
|
|
'|' => '≠',
|
|
|
|
'}' => '£',
|
|
|
|
'~' => '·',
|
|
|
|
_ => c
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-01 17:55:23 +00:00
|
|
|
#[derive(Default, Copy, Clone)]
|
2017-01-09 20:07:23 +00:00
|
|
|
struct Charsets([StandardCharset; 4]);
|
|
|
|
|
|
|
|
impl Index<CharsetIndex> for Charsets {
|
|
|
|
type Output = StandardCharset;
|
|
|
|
fn index(&self, index: CharsetIndex) -> &StandardCharset {
|
|
|
|
&self.0[index as usize]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl IndexMut<CharsetIndex> for Charsets {
|
|
|
|
fn index_mut(&mut self, index: CharsetIndex) -> &mut StandardCharset {
|
|
|
|
&mut self.0[index as usize]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-01 17:55:23 +00:00
|
|
|
#[derive(Default, Copy, Clone)]
|
|
|
|
pub struct Cursor {
|
|
|
|
/// The location of this cursor
|
2017-07-25 00:54:06 +00:00
|
|
|
pub point: Point,
|
2017-02-01 17:55:23 +00:00
|
|
|
|
|
|
|
/// Template cell when using this cursor
|
|
|
|
template: Cell,
|
|
|
|
|
|
|
|
/// Currently configured graphic character sets
|
|
|
|
charsets: Charsets,
|
|
|
|
}
|
|
|
|
|
2017-02-03 23:34:52 +00:00
|
|
|
pub struct VisualBell {
|
|
|
|
/// Visual bell animation
|
|
|
|
animation: VisualBellAnimation,
|
|
|
|
|
|
|
|
/// Visual bell duration
|
|
|
|
duration: Duration,
|
|
|
|
|
|
|
|
/// The last time the visual bell rang, if at all
|
|
|
|
start_time: Option<Instant>,
|
|
|
|
}
|
|
|
|
|
|
|
|
fn cubic_bezier(p0: f64, p1: f64, p2: f64, p3: f64, x: f64) -> f64 {
|
|
|
|
(1.0 - x).powi(3) * p0 +
|
|
|
|
3.0 * (1.0 - x).powi(2) * x * p1 +
|
|
|
|
3.0 * (1.0 - x) * x.powi(2) * p2 +
|
|
|
|
x.powi(3) * p3
|
|
|
|
}
|
|
|
|
|
|
|
|
impl VisualBell {
|
|
|
|
pub fn new(config: &Config) -> VisualBell {
|
|
|
|
let visual_bell_config = config.visual_bell();
|
|
|
|
VisualBell {
|
|
|
|
animation: visual_bell_config.animation(),
|
|
|
|
duration: visual_bell_config.duration(),
|
|
|
|
start_time: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Ring the visual bell, and return its intensity.
|
|
|
|
pub fn ring(&mut self) -> f64 {
|
|
|
|
let now = Instant::now();
|
|
|
|
self.start_time = Some(now);
|
|
|
|
self.intensity_at_instant(now)
|
|
|
|
}
|
|
|
|
|
2017-10-30 15:03:58 +00:00
|
|
|
/// Get the currently intensity of the visual bell. The bell's intensity
|
2017-02-03 23:34:52 +00:00
|
|
|
/// ramps down from 1.0 to 0.0 at a rate determined by the bell's duration.
|
|
|
|
pub fn intensity(&self) -> f64 {
|
|
|
|
self.intensity_at_instant(Instant::now())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Check whether or not the visual bell has completed "ringing".
|
2017-02-20 20:22:26 +00:00
|
|
|
pub fn completed(&mut self) -> bool {
|
2017-02-03 23:34:52 +00:00
|
|
|
match self.start_time {
|
2017-02-20 20:22:26 +00:00
|
|
|
Some(earlier) => {
|
|
|
|
if Instant::now().duration_since(earlier) >= self.duration {
|
|
|
|
self.start_time = None;
|
|
|
|
}
|
|
|
|
false
|
|
|
|
},
|
2017-02-03 23:34:52 +00:00
|
|
|
None => true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the intensity of the visual bell at a particular instant. The bell's
|
|
|
|
/// intensity ramps down from 1.0 to 0.0 at a rate determined by the bell's
|
|
|
|
/// duration.
|
|
|
|
pub fn intensity_at_instant(&self, instant: Instant) -> f64 {
|
|
|
|
// If `duration` is zero, then the VisualBell is disabled; therefore,
|
|
|
|
// its `intensity` is zero.
|
|
|
|
if self.duration == Duration::from_secs(0) {
|
|
|
|
return 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
match self.start_time {
|
|
|
|
// Similarly, if `start_time` is `None`, then the VisualBell has not
|
|
|
|
// been "rung"; therefore, its `intensity` is zero.
|
|
|
|
None => 0.0,
|
|
|
|
|
|
|
|
Some(earlier) => {
|
|
|
|
// Finally, if the `instant` at which we wish to compute the
|
|
|
|
// VisualBell's `intensity` occurred before the VisualBell was
|
|
|
|
// "rung", then its `intensity` is also zero.
|
|
|
|
if instant < earlier {
|
|
|
|
return 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
let elapsed = instant.duration_since(earlier);
|
|
|
|
let elapsed_f = elapsed.as_secs() as f64 +
|
2018-01-06 01:42:55 +00:00
|
|
|
f64::from(elapsed.subsec_nanos()) / 1e9f64;
|
2017-02-03 23:34:52 +00:00
|
|
|
let duration_f = self.duration.as_secs() as f64 +
|
2018-01-06 01:42:55 +00:00
|
|
|
f64::from(self.duration.subsec_nanos()) / 1e9f64;
|
2017-02-03 23:34:52 +00:00
|
|
|
|
|
|
|
// Otherwise, we compute a value `time` from 0.0 to 1.0
|
|
|
|
// inclusive that represents the ratio of `elapsed` time to the
|
|
|
|
// `duration` of the VisualBell.
|
|
|
|
let time = (elapsed_f / duration_f).min(1.0);
|
|
|
|
|
|
|
|
// We use this to compute the inverse `intensity` of the
|
|
|
|
// VisualBell. When `time` is 0.0, `inverse_intensity` is 0.0,
|
|
|
|
// and when `time` is 1.0, `inverse_intensity` is 1.0.
|
|
|
|
let inverse_intensity = match self.animation {
|
2018-01-06 01:42:55 +00:00
|
|
|
VisualBellAnimation::Ease | VisualBellAnimation::EaseOut => {
|
|
|
|
cubic_bezier(0.25, 0.1, 0.25, 1.0, time)
|
|
|
|
},
|
2017-02-03 23:34:52 +00:00
|
|
|
VisualBellAnimation::EaseOutSine => cubic_bezier(0.39, 0.575, 0.565, 1.0, time),
|
|
|
|
VisualBellAnimation::EaseOutQuad => cubic_bezier(0.25, 0.46, 0.45, 0.94, time),
|
|
|
|
VisualBellAnimation::EaseOutCubic => cubic_bezier(0.215, 0.61, 0.355, 1.0, time),
|
|
|
|
VisualBellAnimation::EaseOutQuart => cubic_bezier(0.165, 0.84, 0.44, 1.0, time),
|
|
|
|
VisualBellAnimation::EaseOutQuint => cubic_bezier(0.23, 1.0, 0.32, 1.0, time),
|
|
|
|
VisualBellAnimation::EaseOutExpo => cubic_bezier(0.19, 1.0, 0.22, 1.0, time),
|
|
|
|
VisualBellAnimation::EaseOutCirc => cubic_bezier(0.075, 0.82, 0.165, 1.0, time),
|
|
|
|
VisualBellAnimation::Linear => time,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Since we want the `intensity` of the VisualBell to decay over
|
|
|
|
// `time`, we subtract the `inverse_intensity` from 1.0.
|
|
|
|
1.0 - inverse_intensity
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn update_config(&mut self, config: &Config) {
|
|
|
|
let visual_bell_config = config.visual_bell();
|
|
|
|
self.animation = visual_bell_config.animation();
|
|
|
|
self.duration = visual_bell_config.duration();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-31 03:44:37 +00:00
|
|
|
pub struct Term {
|
|
|
|
/// The grid
|
2016-07-04 00:00:00 +00:00
|
|
|
grid: Grid<Cell>,
|
2016-05-31 03:44:37 +00:00
|
|
|
|
2017-01-12 03:00:29 +00:00
|
|
|
/// Tracks if the next call to input will need to first handle wrapping.
|
2017-01-08 03:46:02 +00:00
|
|
|
/// This is true after the last column is set with the input function. Any function that
|
|
|
|
/// implicitly sets the line or column needs to set this to false to avoid wrapping twice.
|
|
|
|
/// input_needs_wrap ensures that cursor.col is always valid for use into indexing into
|
2017-10-30 15:03:58 +00:00
|
|
|
/// arrays. Without it we would have to sanitize cursor.col every time we used it.
|
2017-01-12 03:00:29 +00:00
|
|
|
input_needs_wrap: bool,
|
2017-01-07 23:13:42 +00:00
|
|
|
|
2017-01-11 05:21:19 +00:00
|
|
|
/// Got a request to set title; it's buffered here until next draw.
|
|
|
|
///
|
|
|
|
/// Would be nice to avoid the allocation...
|
|
|
|
next_title: Option<String>,
|
|
|
|
|
2017-12-24 20:15:42 +00:00
|
|
|
/// Got a request to set the mouse cursor; it's buffered here until the next draw
|
|
|
|
next_mouse_cursor: Option<MouseCursor>,
|
|
|
|
|
2016-05-31 03:44:37 +00:00
|
|
|
/// Alternate grid
|
2016-07-04 00:00:00 +00:00
|
|
|
alt_grid: Grid<Cell>,
|
2016-05-31 03:44:37 +00:00
|
|
|
|
|
|
|
/// Alt is active
|
|
|
|
alt: bool,
|
|
|
|
|
|
|
|
/// The cursor
|
2017-02-01 17:55:23 +00:00
|
|
|
cursor: Cursor,
|
2017-01-09 20:07:23 +00:00
|
|
|
|
|
|
|
/// The graphic character set, out of `charsets`, which ASCII is currently
|
|
|
|
/// being mapped to
|
|
|
|
active_charset: CharsetIndex,
|
|
|
|
|
2016-05-31 03:44:37 +00:00
|
|
|
/// Tabstops
|
2016-06-06 23:54:15 +00:00
|
|
|
tabs: Vec<bool>,
|
|
|
|
|
2016-06-08 04:17:48 +00:00
|
|
|
/// Mode flags
|
|
|
|
mode: TermMode,
|
2016-06-08 17:39:49 +00:00
|
|
|
|
|
|
|
/// Scroll region
|
2016-07-04 00:00:00 +00:00
|
|
|
scroll_region: Range<Line>,
|
2016-06-28 16:18:54 +00:00
|
|
|
|
2018-01-05 03:22:58 +00:00
|
|
|
/// Font size
|
|
|
|
pub font_size: Size,
|
|
|
|
original_font_size: Size,
|
2017-10-14 17:35:56 +00:00
|
|
|
|
2016-06-28 16:18:54 +00:00
|
|
|
/// Size
|
|
|
|
size_info: SizeInfo,
|
2016-07-30 03:16:13 +00:00
|
|
|
|
2016-09-01 17:24:20 +00:00
|
|
|
pub dirty: bool,
|
2017-01-09 03:18:39 +00:00
|
|
|
|
2017-02-03 23:34:52 +00:00
|
|
|
pub visual_bell: VisualBell,
|
2017-10-21 23:03:58 +00:00
|
|
|
pub next_is_urgent: Option<bool>,
|
2017-02-03 23:34:52 +00:00
|
|
|
|
2017-02-01 17:55:23 +00:00
|
|
|
/// Saved cursor from main grid
|
|
|
|
cursor_save: Cursor,
|
|
|
|
|
|
|
|
/// Saved cursor from alt grid
|
|
|
|
cursor_save_alt: Cursor,
|
2017-01-15 01:53:48 +00:00
|
|
|
|
|
|
|
semantic_escape_chars: String,
|
2017-02-11 20:49:40 +00:00
|
|
|
|
|
|
|
/// Colors used for rendering
|
2017-08-29 15:54:12 +00:00
|
|
|
colors: color::List,
|
|
|
|
|
|
|
|
/// Is color in `colors` modified or not
|
|
|
|
color_modified: [bool; color::COUNT],
|
2017-07-28 22:14:18 +00:00
|
|
|
|
|
|
|
/// Original colors from config
|
|
|
|
original_colors: color::List,
|
2017-05-28 16:38:10 +00:00
|
|
|
|
2017-12-03 21:38:42 +00:00
|
|
|
/// Current style of the cursor
|
|
|
|
cursor_style: Option<CursorStyle>,
|
|
|
|
|
|
|
|
/// Default style for resetting the cursor
|
|
|
|
default_cursor_style: CursorStyle,
|
2017-12-24 18:24:28 +00:00
|
|
|
|
|
|
|
dynamic_title: bool,
|
2018-01-06 00:50:12 +00:00
|
|
|
|
|
|
|
/// Number of spaces in one tab
|
|
|
|
tabspaces: usize,
|
2018-03-23 00:01:55 +00:00
|
|
|
|
|
|
|
/// Automatically scroll to bottom when new lines are added
|
|
|
|
auto_scroll: bool,
|
Display errors and warnings
To make sure that all error and information reporting to the user is
unified, all instances of `print!`, `eprint!`, `println!` and
`eprintln!` have been removed and replaced by logging.
When `RUST_LOG` is not specified, the default Alacritty logger now also
prints to both the stderr and a log file. The log file is only created
when a message is written to it and its name is printed to stdout the
first time it is used.
Whenever a warning or an error has been written to the log file/stderr,
a message is now displayed in Alacritty which points to the log file
where the full error is documented.
The message is cleared whenever the screen is cleared using either the
`clear` command or the `Ctrl+L` key binding.
To make sure that log files created by root don't prevent normal users
from interacting with them, the Alacritty log file is `/tmp/Alacritty-$PID.log`.
Since it's still possible that the log file can't be created, the UI
error/warning message now informs the user if the message was only
written to stderr. The reason why it couldn't be created is then printed
to stderr.
To make sure the deletion of the log file at runtime doesn't create any
issues, the file is re-created if a write is attempted without the file
being present.
To help with debugging Alacritty issues, a timestamp and the error
level are printed in all log messages.
All log messages now follow this format:
[YYYY-MM-DD HH:MM] [LEVEL] Message
Since it's not unusual to spawn a lot of different terminal emulators
without restarting, Alacritty can create a ton of different log files.
To combat this problem, logfiles are removed by default after
Alacritty has been closed. If the user wants to persist the log of a
single session, the `--persistent_logging` option can be used. For
persisting all log files, the `persistent_logging` option can be set in
the configuration file
2018-11-17 14:39:13 +00:00
|
|
|
|
|
|
|
/// Proxy object for clearing displayed errors and warnings
|
|
|
|
logger_proxy: Option<LoggerProxy>,
|
2016-06-28 16:18:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Terminal size info
|
2016-11-20 00:16:20 +00:00
|
|
|
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
|
2016-06-28 16:18:54 +00:00
|
|
|
pub struct SizeInfo {
|
|
|
|
/// Terminal window width
|
|
|
|
pub width: f32,
|
|
|
|
|
|
|
|
/// Terminal window height
|
|
|
|
pub height: f32,
|
|
|
|
|
|
|
|
/// Width of individual cell
|
|
|
|
pub cell_width: f32,
|
|
|
|
|
|
|
|
/// Height of individual cell
|
|
|
|
pub cell_height: f32,
|
2017-05-06 15:45:23 +00:00
|
|
|
|
|
|
|
/// Horizontal window padding
|
|
|
|
pub padding_x: f32,
|
|
|
|
|
|
|
|
/// Horizontal window padding
|
|
|
|
pub padding_y: f32,
|
2018-11-10 16:08:48 +00:00
|
|
|
|
|
|
|
/// DPI factor of the current window
|
|
|
|
#[serde(default)]
|
|
|
|
pub dpr: f64,
|
2016-06-28 16:18:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl SizeInfo {
|
|
|
|
#[inline]
|
2016-07-04 00:00:00 +00:00
|
|
|
pub fn lines(&self) -> Line {
|
2017-05-06 15:45:23 +00:00
|
|
|
Line(((self.height - 2. * self.padding_y) / self.cell_height) as usize)
|
2016-06-28 16:18:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 00:00:00 +00:00
|
|
|
pub fn cols(&self) -> Column {
|
2017-05-06 15:45:23 +00:00
|
|
|
Column(((self.width - 2. * self.padding_x) / self.cell_width) as usize)
|
|
|
|
}
|
|
|
|
|
2018-03-13 18:00:14 +00:00
|
|
|
pub fn contains_point(&self, x: usize, y:usize) -> bool {
|
2017-05-07 00:37:01 +00:00
|
|
|
x <= (self.width - self.padding_x) as usize &&
|
|
|
|
x >= self.padding_x as usize &&
|
|
|
|
y <= (self.height - self.padding_y) as usize &&
|
2017-05-06 15:45:23 +00:00
|
|
|
y >= self.padding_y as usize
|
2016-06-28 16:18:54 +00:00
|
|
|
}
|
2016-12-12 06:02:03 +00:00
|
|
|
|
2018-03-13 18:00:14 +00:00
|
|
|
pub fn pixels_to_coords(&self, x: usize, y: usize) -> Point {
|
|
|
|
let col = Column(x.saturating_sub(self.padding_x as usize) / (self.cell_width as usize));
|
|
|
|
let line = Line(y.saturating_sub(self.padding_y as usize) / (self.cell_height as usize));
|
2016-12-12 06:02:03 +00:00
|
|
|
|
2018-03-13 18:00:14 +00:00
|
|
|
Point {
|
2018-11-24 21:08:02 +00:00
|
|
|
line: min(line, Line(self.lines().saturating_sub(1))),
|
|
|
|
col: min(col, Column(self.cols().saturating_sub(1)))
|
2018-03-13 18:00:14 +00:00
|
|
|
}
|
2016-12-12 06:02:03 +00:00
|
|
|
}
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
2017-02-11 20:49:40 +00:00
|
|
|
|
2016-05-31 03:44:37 +00:00
|
|
|
impl Term {
|
2018-03-05 17:57:34 +00:00
|
|
|
pub fn selection(&self) -> &Option<Selection> {
|
|
|
|
&self.grid.selection
|
|
|
|
}
|
|
|
|
|
2018-11-19 08:33:48 +00:00
|
|
|
/// Clear displayed errors and warnings.
|
|
|
|
pub fn clear_log(&mut self) {
|
|
|
|
if let Some(ref mut logger_proxy) = self.logger_proxy {
|
|
|
|
logger_proxy.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-03-05 17:57:34 +00:00
|
|
|
pub fn selection_mut(&mut self) -> &mut Option<Selection> {
|
|
|
|
&mut self.grid.selection
|
|
|
|
}
|
|
|
|
|
2017-01-11 05:21:19 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn get_next_title(&mut self) -> Option<String> {
|
|
|
|
self.next_title.take()
|
|
|
|
}
|
|
|
|
|
2018-03-11 12:01:06 +00:00
|
|
|
pub fn scroll_display(&mut self, scroll: Scroll) {
|
|
|
|
self.grid.scroll_display(scroll);
|
2018-03-10 19:24:10 +00:00
|
|
|
self.dirty = true;
|
2018-02-17 01:33:32 +00:00
|
|
|
}
|
|
|
|
|
2017-12-24 20:15:42 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn get_next_mouse_cursor(&mut self) -> Option<MouseCursor> {
|
|
|
|
self.next_mouse_cursor.take()
|
|
|
|
}
|
|
|
|
|
2017-12-03 21:38:42 +00:00
|
|
|
pub fn new(config: &Config, size: SizeInfo) -> Term {
|
2016-06-28 16:18:54 +00:00
|
|
|
let num_cols = size.cols();
|
2016-07-04 00:00:00 +00:00
|
|
|
let num_lines = size.lines();
|
2016-06-28 16:18:54 +00:00
|
|
|
|
2018-03-09 18:45:40 +00:00
|
|
|
let history_size = config.scrolling().history as usize;
|
2018-04-28 14:14:45 +00:00
|
|
|
let grid = Grid::new(num_lines, num_cols, history_size, Cell::default());
|
2018-07-06 14:45:10 +00:00
|
|
|
let alt = Grid::new(num_lines, num_cols, 0 /* scroll history */, Cell::default());
|
2016-06-28 16:18:54 +00:00
|
|
|
|
2018-01-06 00:50:12 +00:00
|
|
|
let tabspaces = config.tabspaces();
|
2017-06-15 19:46:53 +00:00
|
|
|
let tabs = IndexRange::from(Column(0)..grid.num_cols())
|
2018-01-06 00:50:12 +00:00
|
|
|
.map(|i| (*i as usize) % tabspaces == 0)
|
2016-07-30 03:16:13 +00:00
|
|
|
.collect::<Vec<bool>>();
|
|
|
|
|
2016-07-04 00:00:00 +00:00
|
|
|
let scroll_region = Line(0)..grid.num_lines();
|
2016-05-31 03:44:37 +00:00
|
|
|
|
|
|
|
Term {
|
2017-01-11 05:21:19 +00:00
|
|
|
next_title: None,
|
2017-12-24 20:15:42 +00:00
|
|
|
next_mouse_cursor: None,
|
2016-12-30 02:38:22 +00:00
|
|
|
dirty: false,
|
2017-02-03 23:34:52 +00:00
|
|
|
visual_bell: VisualBell::new(config),
|
2017-10-21 23:03:58 +00:00
|
|
|
next_is_urgent: None,
|
2017-01-12 03:00:29 +00:00
|
|
|
input_needs_wrap: false,
|
2018-03-04 22:40:15 +00:00
|
|
|
grid,
|
2016-05-31 03:44:37 +00:00
|
|
|
alt_grid: alt,
|
|
|
|
alt: false,
|
2018-01-05 03:22:58 +00:00
|
|
|
font_size: config.font().size(),
|
|
|
|
original_font_size: config.font().size(),
|
2017-02-01 17:55:23 +00:00
|
|
|
active_charset: Default::default(),
|
|
|
|
cursor: Default::default(),
|
|
|
|
cursor_save: Default::default(),
|
|
|
|
cursor_save_alt: Default::default(),
|
2018-03-04 22:40:15 +00:00
|
|
|
tabs,
|
2016-06-23 16:42:00 +00:00
|
|
|
mode: Default::default(),
|
2018-03-04 22:40:15 +00:00
|
|
|
scroll_region,
|
2016-07-30 03:16:13 +00:00
|
|
|
size_info: size,
|
2017-02-11 20:49:40 +00:00
|
|
|
colors: color::List::from(config.colors()),
|
2017-08-29 15:54:12 +00:00
|
|
|
color_modified: [false; color::COUNT],
|
2017-07-28 22:14:18 +00:00
|
|
|
original_colors: color::List::from(config.colors()),
|
2017-01-15 01:53:48 +00:00
|
|
|
semantic_escape_chars: config.selection().semantic_escape_chars.clone(),
|
2017-12-03 21:38:42 +00:00
|
|
|
cursor_style: None,
|
|
|
|
default_cursor_style: config.cursor_style(),
|
2017-12-24 18:24:28 +00:00
|
|
|
dynamic_title: config.dynamic_title(),
|
2018-01-06 00:50:12 +00:00
|
|
|
tabspaces,
|
2018-03-23 00:01:55 +00:00
|
|
|
auto_scroll: config.scrolling().auto_scroll,
|
Display errors and warnings
To make sure that all error and information reporting to the user is
unified, all instances of `print!`, `eprint!`, `println!` and
`eprintln!` have been removed and replaced by logging.
When `RUST_LOG` is not specified, the default Alacritty logger now also
prints to both the stderr and a log file. The log file is only created
when a message is written to it and its name is printed to stdout the
first time it is used.
Whenever a warning or an error has been written to the log file/stderr,
a message is now displayed in Alacritty which points to the log file
where the full error is documented.
The message is cleared whenever the screen is cleared using either the
`clear` command or the `Ctrl+L` key binding.
To make sure that log files created by root don't prevent normal users
from interacting with them, the Alacritty log file is `/tmp/Alacritty-$PID.log`.
Since it's still possible that the log file can't be created, the UI
error/warning message now informs the user if the message was only
written to stderr. The reason why it couldn't be created is then printed
to stderr.
To make sure the deletion of the log file at runtime doesn't create any
issues, the file is re-created if a write is attempted without the file
being present.
To help with debugging Alacritty issues, a timestamp and the error
level are printed in all log messages.
All log messages now follow this format:
[YYYY-MM-DD HH:MM] [LEVEL] Message
Since it's not unusual to spawn a lot of different terminal emulators
without restarting, Alacritty can create a ton of different log files.
To combat this problem, logfiles are removed by default after
Alacritty has been closed. If the user wants to persist the log of a
single session, the `--persistent_logging` option can be used. For
persisting all log files, the `persistent_logging` option can be set in
the configuration file
2018-11-17 14:39:13 +00:00
|
|
|
logger_proxy: None,
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Display errors and warnings
To make sure that all error and information reporting to the user is
unified, all instances of `print!`, `eprint!`, `println!` and
`eprintln!` have been removed and replaced by logging.
When `RUST_LOG` is not specified, the default Alacritty logger now also
prints to both the stderr and a log file. The log file is only created
when a message is written to it and its name is printed to stdout the
first time it is used.
Whenever a warning or an error has been written to the log file/stderr,
a message is now displayed in Alacritty which points to the log file
where the full error is documented.
The message is cleared whenever the screen is cleared using either the
`clear` command or the `Ctrl+L` key binding.
To make sure that log files created by root don't prevent normal users
from interacting with them, the Alacritty log file is `/tmp/Alacritty-$PID.log`.
Since it's still possible that the log file can't be created, the UI
error/warning message now informs the user if the message was only
written to stderr. The reason why it couldn't be created is then printed
to stderr.
To make sure the deletion of the log file at runtime doesn't create any
issues, the file is re-created if a write is attempted without the file
being present.
To help with debugging Alacritty issues, a timestamp and the error
level are printed in all log messages.
All log messages now follow this format:
[YYYY-MM-DD HH:MM] [LEVEL] Message
Since it's not unusual to spawn a lot of different terminal emulators
without restarting, Alacritty can create a ton of different log files.
To combat this problem, logfiles are removed by default after
Alacritty has been closed. If the user wants to persist the log of a
single session, the `--persistent_logging` option can be used. For
persisting all log files, the `persistent_logging` option can be set in
the configuration file
2018-11-17 14:39:13 +00:00
|
|
|
pub fn set_logger_proxy(&mut self, logger_proxy: LoggerProxy) {
|
|
|
|
self.logger_proxy = Some(logger_proxy);
|
|
|
|
}
|
|
|
|
|
2018-07-21 17:17:41 +00:00
|
|
|
pub fn change_font_size(&mut self, delta: f32) {
|
|
|
|
// Saturating addition with minimum font size FONT_SIZE_STEP
|
|
|
|
let new_size = self.font_size + Size::new(delta);
|
|
|
|
self.font_size = max(new_size, Size::new(FONT_SIZE_STEP));
|
2018-01-05 03:22:58 +00:00
|
|
|
self.dirty = true;
|
2017-10-14 17:35:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn reset_font_size(&mut self) {
|
2018-01-05 03:22:58 +00:00
|
|
|
self.font_size = self.original_font_size;
|
2017-10-14 17:35:56 +00:00
|
|
|
self.dirty = true;
|
|
|
|
}
|
|
|
|
|
2017-01-09 03:18:39 +00:00
|
|
|
pub fn update_config(&mut self, config: &Config) {
|
2017-01-15 01:53:48 +00:00
|
|
|
self.semantic_escape_chars = config.selection().semantic_escape_chars.clone();
|
2017-07-28 22:14:18 +00:00
|
|
|
self.original_colors.fill_named(config.colors());
|
2018-09-23 23:05:15 +00:00
|
|
|
self.original_colors.fill_cube(config.colors());
|
|
|
|
self.original_colors.fill_gray_ramp(config.colors());
|
2017-08-29 15:54:12 +00:00
|
|
|
for i in 0..color::COUNT {
|
|
|
|
if !self.color_modified[i] {
|
|
|
|
self.colors[i] = self.original_colors[i];
|
|
|
|
}
|
|
|
|
}
|
2017-02-03 23:34:52 +00:00
|
|
|
self.visual_bell.update_config(config);
|
2017-12-03 21:38:42 +00:00
|
|
|
self.default_cursor_style = config.cursor_style();
|
2017-12-24 18:24:28 +00:00
|
|
|
self.dynamic_title = config.dynamic_title();
|
2018-03-23 00:01:55 +00:00
|
|
|
self.auto_scroll = config.scrolling().auto_scroll;
|
2018-05-30 08:20:47 +00:00
|
|
|
self.grid
|
|
|
|
.update_history(config.scrolling().history as usize, &self.cursor.template);
|
2017-01-09 03:18:39 +00:00
|
|
|
}
|
|
|
|
|
2016-12-30 02:38:22 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn needs_draw(&self) -> bool {
|
|
|
|
self.dirty
|
|
|
|
}
|
|
|
|
|
2018-03-05 17:57:34 +00:00
|
|
|
pub fn selection_to_string(&self) -> Option<String> {
|
2016-12-29 15:43:58 +00:00
|
|
|
/// Need a generic push() for the Append trait
|
|
|
|
trait PushChar {
|
|
|
|
fn push_char(&mut self, c: char);
|
2018-03-07 04:57:40 +00:00
|
|
|
fn maybe_newline(&mut self, grid: &Grid<Cell>, line: usize, ending: Column) {
|
2017-10-12 01:52:23 +00:00
|
|
|
if ending != Column(0) && !grid[line][ending - 1].flags.contains(cell::Flags::WRAPLINE) {
|
2016-12-29 15:43:58 +00:00
|
|
|
self.push_char('\n');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PushChar for String {
|
|
|
|
#[inline]
|
|
|
|
fn push_char(&mut self, c: char) {
|
|
|
|
self.push(c);
|
|
|
|
}
|
|
|
|
}
|
2016-12-27 03:52:37 +00:00
|
|
|
|
2017-12-23 05:17:04 +00:00
|
|
|
use std::ops::Range;
|
|
|
|
|
|
|
|
trait Append : PushChar {
|
2018-09-26 18:42:41 +00:00
|
|
|
fn append(&mut self, grid: &Grid<Cell>, line: usize, cols: Range<Column>);
|
2017-12-23 05:17:04 +00:00
|
|
|
}
|
2016-12-27 03:52:37 +00:00
|
|
|
|
2017-12-23 05:17:04 +00:00
|
|
|
impl Append for String {
|
2018-09-26 18:42:41 +00:00
|
|
|
fn append(&mut self, grid: &Grid<Cell>, mut line: usize, cols: Range<Column>) {
|
2018-06-16 17:50:44 +00:00
|
|
|
// Select until last line still within the buffer
|
|
|
|
line = min(line, grid.len() - 1);
|
|
|
|
|
2017-12-23 05:17:04 +00:00
|
|
|
let grid_line = &grid[line];
|
|
|
|
let line_length = grid_line.line_length();
|
2017-01-08 03:46:02 +00:00
|
|
|
let line_end = min(line_length, cols.end + 1);
|
2016-12-29 15:43:58 +00:00
|
|
|
|
2018-09-26 18:42:41 +00:00
|
|
|
if line_end.0 == 0 && cols.end >= grid.num_cols() - 1 {
|
|
|
|
self.push('\n');
|
|
|
|
} else if cols.start < line_end {
|
2017-12-23 05:17:04 +00:00
|
|
|
for cell in &grid_line[cols.start..line_end] {
|
2017-10-12 01:52:23 +00:00
|
|
|
if !cell.flags.contains(cell::Flags::WIDE_CHAR_SPACER) {
|
2017-03-02 06:24:37 +00:00
|
|
|
self.push(cell.c);
|
|
|
|
}
|
2016-12-30 01:39:30 +00:00
|
|
|
}
|
|
|
|
|
2017-12-23 05:17:04 +00:00
|
|
|
if cols.end >= grid.num_cols() - 1 {
|
2018-09-26 18:42:41 +00:00
|
|
|
self.maybe_newline(grid, line, line_end);
|
2017-12-23 05:17:04 +00:00
|
|
|
}
|
|
|
|
}
|
2016-12-27 03:52:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-20 22:30:59 +00:00
|
|
|
let alt_screen = self.mode.contains(TermMode::ALT_SCREEN);
|
2018-03-05 17:57:34 +00:00
|
|
|
let selection = self.grid.selection.clone()?;
|
2018-10-20 22:30:59 +00:00
|
|
|
let span = selection.to_span(self, alt_screen)?;
|
2018-03-05 17:57:34 +00:00
|
|
|
|
2016-12-27 03:52:37 +00:00
|
|
|
let mut res = String::new();
|
|
|
|
|
2018-03-07 04:57:40 +00:00
|
|
|
let Locations { mut start, mut end } = span.to_locations();
|
|
|
|
|
|
|
|
if start > end {
|
|
|
|
::std::mem::swap(&mut start, &mut end);
|
|
|
|
}
|
|
|
|
|
2016-12-27 03:52:37 +00:00
|
|
|
let line_count = end.line - start.line;
|
2017-12-23 05:17:04 +00:00
|
|
|
let max_col = Column(usize::max_value() - 1);
|
2016-12-27 03:52:37 +00:00
|
|
|
|
|
|
|
match line_count {
|
|
|
|
// Selection within single line
|
2018-03-07 04:57:40 +00:00
|
|
|
0 => {
|
2016-12-27 03:52:37 +00:00
|
|
|
res.append(&self.grid, start.line, start.col..end.col);
|
|
|
|
},
|
|
|
|
|
|
|
|
// Selection ends on line following start
|
2018-03-07 04:57:40 +00:00
|
|
|
1 => {
|
|
|
|
// Ending line
|
|
|
|
res.append(&self.grid, end.line, end.col..max_col);
|
|
|
|
|
2016-12-27 03:52:37 +00:00
|
|
|
// Starting line
|
2018-03-07 04:57:40 +00:00
|
|
|
res.append(&self.grid, start.line, Column(0)..start.col);
|
2016-12-27 03:52:37 +00:00
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
// Multi line selection
|
|
|
|
_ => {
|
2018-03-07 04:57:40 +00:00
|
|
|
// Ending line
|
|
|
|
res.append(&self.grid, end.line, end.col..max_col);
|
2016-12-27 03:52:37 +00:00
|
|
|
|
2018-03-07 04:57:40 +00:00
|
|
|
let middle_range = (start.line + 1)..(end.line);
|
2018-03-09 04:12:48 +00:00
|
|
|
for line in middle_range.rev() {
|
2017-12-23 05:17:04 +00:00
|
|
|
res.append(&self.grid, line, Column(0)..max_col);
|
2016-12-27 03:52:37 +00:00
|
|
|
}
|
|
|
|
|
2018-03-07 04:57:40 +00:00
|
|
|
// Starting line
|
2018-07-02 22:03:04 +00:00
|
|
|
res.append(&self.grid, start.line, Column(0)..start.col);
|
2018-03-07 04:57:40 +00:00
|
|
|
|
2016-12-27 03:52:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-05 17:57:34 +00:00
|
|
|
Some(res)
|
2016-12-27 03:52:37 +00:00
|
|
|
}
|
|
|
|
|
2018-03-07 04:57:40 +00:00
|
|
|
pub(crate) fn visible_to_buffer(&self, point: Point) -> Point<usize> {
|
|
|
|
self.grid.visible_to_buffer(point)
|
|
|
|
}
|
|
|
|
|
2016-11-24 04:25:37 +00:00
|
|
|
/// Convert the given pixel values to a grid coordinate
|
|
|
|
///
|
|
|
|
/// The mouse coordinates are expected to be relative to the top left. The
|
|
|
|
/// line and column returned are also relative to the top left.
|
|
|
|
///
|
|
|
|
/// Returns None if the coordinates are outside the screen
|
2016-12-30 01:39:30 +00:00
|
|
|
pub fn pixels_to_coords(&self, x: usize, y: usize) -> Option<Point> {
|
2018-03-13 18:00:14 +00:00
|
|
|
if self.size_info.contains_point(x, y) {
|
|
|
|
Some(self.size_info.pixels_to_coords(x, y))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
2016-11-24 04:25:37 +00:00
|
|
|
}
|
|
|
|
|
2016-11-28 22:27:52 +00:00
|
|
|
/// Access to the raw grid data structure
|
|
|
|
///
|
|
|
|
/// This is a bit of a hack; when the window is closed, the event processor
|
|
|
|
/// serializes the grid state to a file.
|
2016-11-20 00:16:20 +00:00
|
|
|
pub fn grid(&self) -> &Grid<Cell> {
|
|
|
|
&self.grid
|
|
|
|
}
|
|
|
|
|
2016-11-28 22:27:52 +00:00
|
|
|
/// Iterate over the *renderable* cells in the terminal
|
|
|
|
///
|
|
|
|
/// A renderable cell is any cell which has content other than the default
|
|
|
|
/// background color. Cells with an alternate background color are
|
|
|
|
/// considered renderable as are cells with any text content.
|
2017-02-11 20:49:40 +00:00
|
|
|
pub fn renderable_cells<'b>(
|
2017-05-28 16:38:10 +00:00
|
|
|
&'b self,
|
2017-02-11 20:49:40 +00:00
|
|
|
config: &'b Config,
|
2017-12-09 23:33:58 +00:00
|
|
|
window_focused: bool,
|
2017-02-11 20:49:40 +00:00
|
|
|
) -> RenderableCellsIter {
|
2018-10-20 22:30:59 +00:00
|
|
|
let alt_screen = self.mode.contains(TermMode::ALT_SCREEN);
|
2018-03-07 04:57:40 +00:00
|
|
|
let selection = self.grid.selection.as_ref()
|
2018-10-20 22:30:59 +00:00
|
|
|
.and_then(|s| s.to_span(self, alt_screen))
|
2018-03-07 04:57:40 +00:00
|
|
|
.map(|span| {
|
|
|
|
span.to_locations()
|
|
|
|
});
|
2018-07-28 23:16:28 +00:00
|
|
|
|
Merge master into scrollback
* Allow disabling DPI scaling
This makes it possible to disable DPI scaling completely, instead the
the display pixel ration will always be fixed to 1.0.
By default nothing has changed and DPI is still enabled, this just seems
like a better way than running `WINIT_HIDPI_FACTOR=1.0 alacritty` every
time the user wants to start alacritty.
It would be possible to allow specifying any DPR, however I've decided
against this since I'd assume it's a very rare usecase. It's also still
possible to make use of `WINIT_HIDPI_FACTOR` to do this on X11.
Currently this is not updated at runtime using the live config update,
there is not really much of a technical limitation why this woudn't be
possible, however a solution for that issue should be first added in
jwilm/alacritty#1346, once a system is established for changing DPI at
runtime, porting that functionality to this PR should be simple.
* Add working --class and --title CLI parameters
* Reduce Increase-/DecreaseFontSize step to 0.5
Until now the Increase-/DecreaseFontSize keybinds hand a step size of 1.0. Since the font size however is multiplied by two to allow more granular font size control, this lead to the bindings skipping one font size (incrementing/decrementing by +-2).
To fix this the step size of the Increase-/DecreaseFontSize bindings has been reduced to the minimum step size that exists with the current font configuration (0.5). This should allow users to increment and decrement the font size by a single point instead of two.
This also adds a few tests to make sure the methods for increasing/decreasing/resetting font size work properly.
* Add Copy/Cut/Paste keys
This just adds support for the Copy/Cut/Paste keys and sets up
Copy/Paste as alternative defaults for Ctrl+Shift+C/V.
* Move to cargo clippy
Using clippy as a library has been deprecated, instead the `cargo
clippy` command should be used instead. To comply with this change
clippy has been removed from the `Cargo.toml` and is now installed with
cargo when building in CI.
This has also lead to a few new clippy issues to show up, this includes
everything in the `font` subdirectory. This has been fixed and `font`
should now be covered by clippy CI too.
This also upgrades all dependencies, as a result this fixes #1341 and
this fixes #1344.
* Override dynamic_title when --title is specified
* Change green implementation to use the macro
* Ignore mouse input if window is unfocused
* Make compilation of binary a phony target
* Add opensuse zypper install method to readme
* Fix clippy issues
* Update manpage to document all CLI options
The introduction of `--class` has added a flag to the CLI without adding
it to the manpage. This has been fixed by updating the manpage.
This also adds the default values of `--class` and `--title` to the CLI
options.
* Remove unnecessary clippy lint annotations
We moved to "cargo clippy" in 5ba34d4f9766a55a06ed5e3e44cc384af1b09f65 and
removing the clippy lint annotations in `src/lib.rs` does not cause any additional warnings.
This also changes `cargo clippy` to use the flags required for checking integration tests.
* Enable clippy in font/copypasta crates
Enabled clippy in the sub-crates font and copypasta. All issues
that were discovered by this change have also been fixed.
* Remove outdated comment about NixOS
* Replace debug asserts with static_assertions
To check that transmutes will work correctly without having to rely on
error-prone runtime checking, the `static_assertions` crate has been
introduced. This allows comparing the size of types at compile time,
preventing potentially silent breakage.
This fixes #1417.
* Add `cargo deb` build instructions
Updated the `Cargo.toml` file and added a `package.metadata.deb`
subsection to define how to build a debian "deb" install file using
`cargo deb`. This will allow debian/ubuntu users to install `alacritty`
using their system's package manager. It also will make it easier to
provide pre-built binaries for those systems.
Also fixed a stray debug line in the bash autocomplete script that was
writting to a tempfile.
* Add config for unfocused window cursor change
* Add support for cursor shape escape sequence
* Add bright foreground color option
It was requested in jwilm/alacritty#825 that it should be possible to
add an optional bright foreground color.
This is now added to the primary colors structure and allows the user to
set a foreground color for bold normal text. This has no effect unless
the draw_bold_text_with_bright_colors option is also enabled.
If the color is not specified, the bright foreground color will fall
back to the normal foreground color.
This fixes #825.
* Fix clone URL in deb install instructions
* Fix 'cargo-deb' desktop file name
* Remove redundant dependency from deb build
* Switch from deprecated `std::env::home_dir` to `dirs::home_dir`
* Allow specifying modifiers for mouse bindings
* Send newline with NumpadEnter
* Add support for LCD-V pixel mode
* Add binding action for hiding the window
* Switch to rustup clippy component
* Add optional dim foreground color
Add optional color for the dim foreground (`\e[2m;`)
Defaults to 2/3 of the foreground color. (same as other colors).
If a bright color is dimmed, it's displayed as the normal color. The
exception for this is when the bright foreground is dimmed when no
bright foreground color is set. In that case it's treated as a normal
foreground color and dimmed to DimForeground.
To minimize the surprise for the user, the bright and dim colors have
been completely removed from the default configuration file.
Some documentation has also been added to make it clear to users what
these options can be used for.
This fixes #1448.
* Fix clippy lints and run font tests on travis
This fixes some existing clippy issues and runs the `font` tests through travis.
Testing of copypasta crate was omitted due to problens when running on headless travis-ci environment (x11 clipboard would fail).
* Ignore errors when logger can't write to output
The (e)print macro will panic when there is no output available to
write to, however in our scenario where we only log user errors to
stderr, the better choice would be to ignore when writing to stdout or
stderr is not possible.
This changes the (e)print macro to make use of `write` and ignore
any potential errors.
Since (e)println rely on (e)print, this also solves potential failuers
when calling (e)println.
With this change implemented, all of logging, (e)println and (e)print
should never fail even if the stdout/stderr is not available.
2018-07-28 23:10:13 +00:00
|
|
|
let cursor = if window_focused || !config.unfocused_hollow_cursor() {
|
2018-01-06 01:42:55 +00:00
|
|
|
self.cursor_style.unwrap_or(self.default_cursor_style)
|
|
|
|
} else {
|
|
|
|
CursorStyle::HollowBlock
|
|
|
|
};
|
2017-06-16 04:43:28 +00:00
|
|
|
|
2017-02-01 17:55:23 +00:00
|
|
|
RenderableCellsIter::new(
|
2017-05-28 16:38:10 +00:00
|
|
|
&self.grid,
|
2017-02-01 17:55:23 +00:00
|
|
|
&self.cursor.point,
|
2017-02-11 20:49:40 +00:00
|
|
|
&self.colors,
|
2017-02-01 17:55:23 +00:00
|
|
|
self.mode,
|
2017-02-11 20:49:40 +00:00
|
|
|
config,
|
2017-02-01 17:55:23 +00:00
|
|
|
selection,
|
2018-01-06 01:42:55 +00:00
|
|
|
cursor,
|
2017-02-01 17:55:23 +00:00
|
|
|
)
|
2016-07-04 21:46:25 +00:00
|
|
|
}
|
|
|
|
|
2016-06-29 17:21:02 +00:00
|
|
|
/// Resize terminal to new dimensions
|
2017-10-14 17:35:56 +00:00
|
|
|
pub fn resize(&mut self, size : &SizeInfo) {
|
2017-05-06 15:45:23 +00:00
|
|
|
debug!("Term::resize");
|
2017-10-14 17:35:56 +00:00
|
|
|
|
2017-05-06 15:45:23 +00:00
|
|
|
// Bounds check; lots of math assumes width and height are > 0
|
2017-10-14 17:35:56 +00:00
|
|
|
if size.width as usize <= 2 * self.size_info.padding_x as usize ||
|
|
|
|
size.height as usize <= 2 * self.size_info.padding_y as usize
|
2017-05-06 15:45:23 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let old_cols = self.grid.num_cols();
|
|
|
|
let old_lines = self.grid.num_lines();
|
2017-01-08 00:07:03 +00:00
|
|
|
let mut num_cols = size.cols();
|
|
|
|
let mut num_lines = size.lines();
|
2016-06-29 17:21:02 +00:00
|
|
|
|
2017-10-14 17:35:56 +00:00
|
|
|
self.size_info = *size;
|
2016-06-29 17:21:02 +00:00
|
|
|
|
2016-07-04 00:00:00 +00:00
|
|
|
if old_cols == num_cols && old_lines == num_lines {
|
2017-05-06 15:45:23 +00:00
|
|
|
debug!("Term::resize dimensions unchanged");
|
2016-06-29 17:21:02 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-03-05 17:57:34 +00:00
|
|
|
self.grid.selection = None;
|
|
|
|
self.alt_grid.selection = None;
|
|
|
|
|
2017-01-12 03:00:29 +00:00
|
|
|
// Should not allow less than 1 col, causes all sorts of checks to be required.
|
2017-01-08 00:07:03 +00:00
|
|
|
if num_cols <= Column(1) {
|
2017-01-12 03:00:29 +00:00
|
|
|
num_cols = Column(2);
|
2017-01-08 00:07:03 +00:00
|
|
|
}
|
|
|
|
|
2017-01-12 03:00:29 +00:00
|
|
|
// Should not allow less than 1 line, causes all sorts of checks to be required.
|
2017-01-08 00:07:03 +00:00
|
|
|
if num_lines <= Line(1) {
|
2017-01-12 03:00:29 +00:00
|
|
|
num_lines = Line(2);
|
2017-01-08 00:07:03 +00:00
|
|
|
}
|
|
|
|
|
2016-07-04 00:00:00 +00:00
|
|
|
// Scroll up to keep cursor in terminal
|
2017-02-01 17:55:23 +00:00
|
|
|
if self.cursor.point.line >= num_lines {
|
|
|
|
let lines = self.cursor.point.line - num_lines + 1;
|
2018-04-02 15:44:54 +00:00
|
|
|
self.grid.scroll_up(&(Line(0)..old_lines), lines, &self.cursor.template);
|
2017-04-20 17:13:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Scroll up alt grid as well
|
|
|
|
if self.cursor_save_alt.point.line >= num_lines {
|
|
|
|
let lines = self.cursor_save_alt.point.line - num_lines + 1;
|
2018-04-02 15:44:54 +00:00
|
|
|
self.alt_grid.scroll_up(&(Line(0)..old_lines), lines, &self.cursor_save_alt.template);
|
2016-06-29 17:21:02 +00:00
|
|
|
}
|
2018-08-15 20:09:59 +00:00
|
|
|
|
|
|
|
// Move prompt down when growing if scrollback lines are available
|
|
|
|
if num_lines > old_lines {
|
|
|
|
if self.mode.contains(TermMode::ALT_SCREEN) {
|
|
|
|
let growage = min(num_lines - old_lines, Line(self.alt_grid.scroll_limit()));
|
|
|
|
self.cursor_save.point.line += growage;
|
|
|
|
} else {
|
|
|
|
let growage = min(num_lines - old_lines, Line(self.grid.scroll_limit()));
|
|
|
|
self.cursor.point.line += growage;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-12 10:32:15 +00:00
|
|
|
debug!("num_cols, num_lines = {}, {}", num_cols, num_lines);
|
2016-06-29 17:21:02 +00:00
|
|
|
|
|
|
|
// Resize grids to new size
|
2018-07-26 20:47:33 +00:00
|
|
|
self.grid.resize(num_lines, num_cols, &Cell::default());
|
|
|
|
self.alt_grid.resize(num_lines, num_cols, &Cell::default());
|
2016-06-29 17:21:02 +00:00
|
|
|
|
2017-04-20 17:13:28 +00:00
|
|
|
// Reset scrolling region to new size
|
|
|
|
self.scroll_region = Line(0)..self.grid.num_lines();
|
|
|
|
|
|
|
|
// Ensure cursors are in-bounds.
|
|
|
|
self.cursor.point.col = min(self.cursor.point.col, num_cols - 1);
|
|
|
|
self.cursor.point.line = min(self.cursor.point.line, num_lines - 1);
|
|
|
|
self.cursor_save.point.col = min(self.cursor_save.point.col, num_cols - 1);
|
|
|
|
self.cursor_save.point.line = min(self.cursor_save.point.line, num_lines - 1);
|
|
|
|
self.cursor_save_alt.point.col = min(self.cursor_save_alt.point.col, num_cols - 1);
|
|
|
|
self.cursor_save_alt.point.line = min(self.cursor_save_alt.point.line, num_lines - 1);
|
2016-06-29 17:21:02 +00:00
|
|
|
|
|
|
|
// Recreate tabs list
|
2017-01-06 23:25:04 +00:00
|
|
|
self.tabs = IndexRange::from(Column(0)..self.grid.num_cols())
|
2018-01-06 00:50:12 +00:00
|
|
|
.map(|i| (*i as usize) % self.tabspaces == 0)
|
2016-12-17 06:48:04 +00:00
|
|
|
.collect::<Vec<bool>>();
|
2016-06-28 16:18:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn size_info(&self) -> &SizeInfo {
|
|
|
|
&self.size_info
|
|
|
|
}
|
|
|
|
|
2016-06-08 04:17:48 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn mode(&self) -> &TermMode {
|
|
|
|
&self.mode
|
|
|
|
}
|
|
|
|
|
2017-07-25 00:54:06 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn cursor(&self) -> &Cursor {
|
|
|
|
&self.cursor
|
|
|
|
}
|
|
|
|
|
2016-05-31 03:44:37 +00:00
|
|
|
pub fn swap_alt(&mut self) {
|
|
|
|
if self.alt {
|
2017-08-09 18:22:07 +00:00
|
|
|
let template = &self.cursor.template;
|
2017-10-13 03:12:29 +00:00
|
|
|
self.grid.region_mut(..).each(|c| c.reset(template));
|
2016-06-08 17:39:49 +00:00
|
|
|
}
|
2017-02-03 04:51:14 +00:00
|
|
|
|
|
|
|
self.alt = !self.alt;
|
|
|
|
::std::mem::swap(&mut self.grid, &mut self.alt_grid);
|
2016-06-08 17:39:49 +00:00
|
|
|
}
|
2016-08-22 15:37:50 +00:00
|
|
|
|
|
|
|
/// Scroll screen down
|
|
|
|
///
|
|
|
|
/// Text moves down; clear at bottom
|
2017-01-10 06:05:26 +00:00
|
|
|
/// Expects origin to be in scroll range.
|
2016-08-22 15:37:50 +00:00
|
|
|
#[inline]
|
2018-09-05 21:15:16 +00:00
|
|
|
fn scroll_down_relative(&mut self, origin: Line, mut lines: Line) {
|
2017-05-01 04:32:11 +00:00
|
|
|
trace!("scroll_down_relative: origin={}, lines={}", origin, lines);
|
2018-09-05 21:15:16 +00:00
|
|
|
lines = min(lines, self.scroll_region.end - self.scroll_region.start);
|
|
|
|
lines = min(lines, self.scroll_region.end - origin);
|
2016-08-22 15:37:50 +00:00
|
|
|
|
|
|
|
// Scroll between origin and bottom
|
2018-04-02 15:44:54 +00:00
|
|
|
self.grid.scroll_down(&(origin..self.scroll_region.end), lines, &self.cursor.template);
|
2016-08-22 15:37:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Scroll screen up
|
|
|
|
///
|
|
|
|
/// Text moves up; clear at top
|
2017-01-10 06:05:26 +00:00
|
|
|
/// Expects origin to be in scroll range.
|
2016-08-22 15:37:50 +00:00
|
|
|
#[inline]
|
|
|
|
fn scroll_up_relative(&mut self, origin: Line, lines: Line) {
|
2017-05-01 04:32:11 +00:00
|
|
|
trace!("scroll_up_relative: origin={}, lines={}", origin, lines);
|
|
|
|
let lines = min(lines, self.scroll_region.end - self.scroll_region.start);
|
2016-08-22 15:37:50 +00:00
|
|
|
|
|
|
|
// Scroll from origin to bottom less number of lines
|
2018-04-02 15:44:54 +00:00
|
|
|
self.grid.scroll_up(&(origin..self.scroll_region.end), lines, &self.cursor.template);
|
2016-08-22 15:37:50 +00:00
|
|
|
}
|
2017-04-19 16:03:33 +00:00
|
|
|
|
|
|
|
fn deccolm(&mut self) {
|
|
|
|
// Setting 132 column font makes no sense, but run the other side effects
|
|
|
|
// Clear scrolling region
|
|
|
|
let scroll_region = Line(0)..self.grid.num_lines();
|
|
|
|
self.set_scrolling_region(scroll_region);
|
|
|
|
|
|
|
|
// Clear grid
|
2017-08-09 18:22:07 +00:00
|
|
|
let template = self.cursor.template;
|
2017-10-13 03:12:29 +00:00
|
|
|
self.grid.region_mut(..).each(|c| c.reset(&template));
|
2017-04-19 16:03:33 +00:00
|
|
|
}
|
2017-07-28 22:14:18 +00:00
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn background_color(&self) -> Rgb {
|
|
|
|
self.colors[NamedColor::Background]
|
|
|
|
}
|
2016-06-08 17:39:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ansi::TermInfo for Term {
|
|
|
|
#[inline]
|
2016-07-04 04:12:43 +00:00
|
|
|
fn lines(&self) -> Line {
|
|
|
|
self.grid.num_lines()
|
2016-06-08 17:39:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 04:12:43 +00:00
|
|
|
fn cols(&self) -> Column {
|
|
|
|
self.grid.num_cols()
|
2016-06-08 17:39:49 +00:00
|
|
|
}
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ansi::Handler for Term {
|
2017-01-11 05:21:19 +00:00
|
|
|
/// Set the window title
|
|
|
|
#[inline]
|
|
|
|
fn set_title(&mut self, title: &str) {
|
2017-12-24 18:24:28 +00:00
|
|
|
if self.dynamic_title {
|
|
|
|
self.next_title = Some(title.to_owned());
|
|
|
|
}
|
2017-01-11 05:21:19 +00:00
|
|
|
}
|
|
|
|
|
2017-12-24 20:15:42 +00:00
|
|
|
/// Set the mouse cursor
|
|
|
|
#[inline]
|
|
|
|
fn set_mouse_cursor(&mut self, cursor: MouseCursor) {
|
|
|
|
self.next_mouse_cursor = Some(cursor);
|
|
|
|
}
|
|
|
|
|
2016-05-31 03:44:37 +00:00
|
|
|
/// A character to be displayed
|
|
|
|
#[inline]
|
|
|
|
fn input(&mut self, c: char) {
|
2018-03-23 00:01:55 +00:00
|
|
|
// If enabled, scroll to bottom when character is received
|
|
|
|
if self.auto_scroll {
|
|
|
|
self.scroll_display(Scroll::Bottom);
|
|
|
|
}
|
|
|
|
|
2017-01-08 03:46:02 +00:00
|
|
|
if self.input_needs_wrap {
|
2017-10-12 01:52:23 +00:00
|
|
|
if !self.mode.contains(mode::TermMode::LINE_WRAP) {
|
2017-01-12 03:00:29 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("wrapping");
|
2017-01-12 03:00:29 +00:00
|
|
|
|
2016-12-29 15:43:58 +00:00
|
|
|
{
|
2016-12-29 16:09:29 +00:00
|
|
|
let location = Point {
|
2017-02-01 17:55:23 +00:00
|
|
|
line: self.cursor.point.line,
|
|
|
|
col: self.cursor.point.col
|
2016-12-29 15:43:58 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let cell = &mut self.grid[&location];
|
2017-10-12 01:52:23 +00:00
|
|
|
cell.flags.insert(cell::Flags::WRAPLINE);
|
2016-12-29 15:43:58 +00:00
|
|
|
}
|
2017-01-12 03:00:29 +00:00
|
|
|
|
2017-02-01 17:55:23 +00:00
|
|
|
if (self.cursor.point.line + 1) >= self.scroll_region.end {
|
2016-08-20 00:55:44 +00:00
|
|
|
self.linefeed();
|
|
|
|
} else {
|
2017-02-01 17:55:23 +00:00
|
|
|
self.cursor.point.line += 1;
|
2016-08-20 00:55:44 +00:00
|
|
|
}
|
2017-01-12 03:00:29 +00:00
|
|
|
|
2017-02-01 17:55:23 +00:00
|
|
|
self.cursor.point.col = Column(0);
|
2017-01-08 03:46:02 +00:00
|
|
|
self.input_needs_wrap = false;
|
2016-07-30 03:16:13 +00:00
|
|
|
}
|
|
|
|
|
2017-01-07 21:47:05 +00:00
|
|
|
{
|
2017-03-02 06:24:37 +00:00
|
|
|
// Number of cells the char will occupy
|
2017-05-01 05:10:38 +00:00
|
|
|
if let Some(width) = c.width() {
|
|
|
|
// Sigh, borrowck making us check the width twice. Hopefully the
|
|
|
|
// optimizer can fix it.
|
|
|
|
let num_cols = self.grid.num_cols();
|
|
|
|
{
|
|
|
|
// If in insert mode, first shift cells to the right.
|
2017-10-12 01:52:23 +00:00
|
|
|
if self.mode.contains(mode::TermMode::INSERT) && self.cursor.point.col + width < num_cols {
|
2017-05-01 05:10:38 +00:00
|
|
|
let line = self.cursor.point.line; // borrowck
|
|
|
|
let col = self.cursor.point.col;
|
|
|
|
let line = &mut self.grid[line];
|
|
|
|
|
|
|
|
let src = line[col..].as_ptr();
|
|
|
|
let dst = line[(col + width)..].as_mut_ptr();
|
|
|
|
unsafe {
|
|
|
|
// memmove
|
|
|
|
ptr::copy(src, dst, (num_cols - col - width).0);
|
|
|
|
}
|
|
|
|
}
|
2017-03-02 06:24:37 +00:00
|
|
|
|
2017-05-01 05:10:38 +00:00
|
|
|
let cell = &mut self.grid[&self.cursor.point];
|
|
|
|
*cell = self.cursor.template;
|
|
|
|
cell.c = self.cursor.charsets[self.active_charset].map(c);
|
2017-03-02 06:24:37 +00:00
|
|
|
|
2017-05-01 05:10:38 +00:00
|
|
|
// Handle wide chars
|
|
|
|
if width == 2 {
|
2017-10-12 01:52:23 +00:00
|
|
|
cell.flags.insert(cell::Flags::WIDE_CHAR);
|
2017-05-01 05:10:38 +00:00
|
|
|
}
|
2017-03-02 06:24:37 +00:00
|
|
|
}
|
|
|
|
|
2017-05-01 05:10:38 +00:00
|
|
|
// Set spacer cell for wide chars.
|
2018-01-06 01:42:55 +00:00
|
|
|
if width == 2 && self.cursor.point.col + 1 < num_cols {
|
|
|
|
self.cursor.point.col += 1;
|
|
|
|
let spacer = &mut self.grid[&self.cursor.point];
|
|
|
|
*spacer = self.cursor.template;
|
|
|
|
spacer.flags.insert(cell::Flags::WIDE_CHAR_SPACER);
|
2017-03-02 06:24:37 +00:00
|
|
|
}
|
|
|
|
}
|
2017-01-12 03:00:29 +00:00
|
|
|
}
|
|
|
|
|
2017-02-01 17:55:23 +00:00
|
|
|
if (self.cursor.point.col + 1) < self.grid.num_cols() {
|
|
|
|
self.cursor.point.col += 1;
|
2017-01-07 23:13:42 +00:00
|
|
|
} else {
|
2017-01-08 03:46:02 +00:00
|
|
|
self.input_needs_wrap = true;
|
2016-07-30 03:16:13 +00:00
|
|
|
}
|
|
|
|
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
2017-04-18 17:42:29 +00:00
|
|
|
#[inline]
|
|
|
|
fn dectest(&mut self) {
|
|
|
|
trace!("dectest");
|
|
|
|
let mut template = self.cursor.template;
|
|
|
|
template.c = 'E';
|
|
|
|
|
2017-10-13 03:29:35 +00:00
|
|
|
self.grid.region_mut(..)
|
|
|
|
.each(|c| c.reset(&template));
|
2017-04-18 17:42:29 +00:00
|
|
|
}
|
|
|
|
|
2016-07-04 00:00:00 +00:00
|
|
|
#[inline]
|
2016-07-04 04:58:35 +00:00
|
|
|
fn goto(&mut self, line: Line, col: Column) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("goto: line={}, col={}", line, col);
|
2017-10-12 01:52:23 +00:00
|
|
|
let (y_offset, max_y) = if self.mode.contains(mode::TermMode::ORIGIN) {
|
2017-04-20 03:08:44 +00:00
|
|
|
(self.scroll_region.start, self.scroll_region.end - 1)
|
2017-04-19 05:59:24 +00:00
|
|
|
} else {
|
|
|
|
(Line(0), self.grid.num_lines() - 1)
|
|
|
|
};
|
|
|
|
|
|
|
|
self.cursor.point.line = min(line + y_offset, max_y);
|
2017-02-01 17:55:23 +00:00
|
|
|
self.cursor.point.col = min(col, self.grid.num_cols() - 1);
|
2017-01-08 03:46:02 +00:00
|
|
|
self.input_needs_wrap = false;
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
2016-07-04 00:00:00 +00:00
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-07-04 04:58:35 +00:00
|
|
|
fn goto_line(&mut self, line: Line) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("goto_line: {}", line);
|
2017-04-19 05:59:24 +00:00
|
|
|
let col = self.cursor.point.col; // borrowck
|
|
|
|
self.goto(line, col)
|
2016-06-06 22:13:45 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 04:58:35 +00:00
|
|
|
fn goto_col(&mut self, col: Column) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("goto_col: {}", col);
|
2017-04-19 05:59:24 +00:00
|
|
|
let line = self.cursor.point.line; // borrowck
|
|
|
|
self.goto(line, col)
|
2016-06-06 22:13:45 +00:00
|
|
|
}
|
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-10-10 15:29:51 +00:00
|
|
|
fn insert_blank(&mut self, count: Column) {
|
|
|
|
// Ensure inserting within terminal bounds
|
2017-01-12 03:00:29 +00:00
|
|
|
|
2017-02-01 17:55:23 +00:00
|
|
|
let count = min(count, self.size_info.cols() - self.cursor.point.col);
|
2016-10-10 15:29:51 +00:00
|
|
|
|
2017-02-01 17:55:23 +00:00
|
|
|
let source = self.cursor.point.col;
|
|
|
|
let destination = self.cursor.point.col + count;
|
2016-10-10 15:29:51 +00:00
|
|
|
let num_cells = (self.size_info.cols() - destination).0;
|
|
|
|
|
2017-02-01 17:55:23 +00:00
|
|
|
let line = self.cursor.point.line; // borrowck
|
2016-10-10 15:29:51 +00:00
|
|
|
let line = &mut self.grid[line];
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
let src = line[source..].as_ptr();
|
|
|
|
let dst = line[destination..].as_mut_ptr();
|
|
|
|
|
|
|
|
ptr::copy(src, dst, num_cells);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cells were just moved out towards the end of the line; fill in
|
|
|
|
// between source and dest with blanks.
|
2017-02-27 08:12:04 +00:00
|
|
|
let template = self.cursor.template;
|
2016-10-10 15:29:51 +00:00
|
|
|
for c in &mut line[source..destination] {
|
|
|
|
c.reset(&template);
|
|
|
|
}
|
2016-07-04 20:37:08 +00:00
|
|
|
}
|
2016-05-31 03:44:37 +00:00
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-07-04 04:58:35 +00:00
|
|
|
fn move_up(&mut self, lines: Line) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("move_up: {}", lines);
|
2017-04-19 05:59:24 +00:00
|
|
|
let move_to = Line(self.cursor.point.line.0.saturating_sub(lines.0));
|
|
|
|
let col = self.cursor.point.col; // borrowck
|
|
|
|
self.goto(move_to, col)
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-07-04 04:58:35 +00:00
|
|
|
fn move_down(&mut self, lines: Line) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("move_down: {}", lines);
|
2017-04-19 05:59:24 +00:00
|
|
|
let move_to = self.cursor.point.line + lines;
|
|
|
|
let col = self.cursor.point.col; // borrowck
|
|
|
|
self.goto(move_to, col)
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-07-04 04:58:35 +00:00
|
|
|
fn move_forward(&mut self, cols: Column) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("move_forward: {}", cols);
|
2017-02-01 17:55:23 +00:00
|
|
|
self.cursor.point.col = min(self.cursor.point.col + cols, self.grid.num_cols() - 1);
|
2017-01-08 03:46:02 +00:00
|
|
|
self.input_needs_wrap = false;
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-07-04 04:58:35 +00:00
|
|
|
fn move_backward(&mut self, cols: Column) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("move_backward: {}", cols);
|
2017-02-01 17:55:23 +00:00
|
|
|
self.cursor.point.col -= min(self.cursor.point.col, cols);
|
2017-01-08 03:46:02 +00:00
|
|
|
self.input_needs_wrap = false;
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-12-04 23:48:30 +00:00
|
|
|
fn identify_terminal<W: io::Write>(&mut self, writer: &mut W) {
|
2016-12-17 06:13:51 +00:00
|
|
|
let _ = writer.write_all(b"\x1b[?6c");
|
2016-07-04 20:37:08 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
2017-05-07 20:08:23 +00:00
|
|
|
#[inline]
|
2017-05-08 14:50:34 +00:00
|
|
|
fn device_status<W: io::Write>(&mut self, writer: &mut W, arg: usize) {
|
|
|
|
trace!("device status: {}", arg);
|
|
|
|
match arg {
|
|
|
|
5 => {
|
|
|
|
let _ = writer.write_all(b"\x1b[0n");
|
|
|
|
},
|
|
|
|
6 => {
|
|
|
|
let pos = self.cursor.point;
|
|
|
|
let _ = write!(writer, "\x1b[{};{}R", pos.line + 1, pos.col + 1);
|
|
|
|
},
|
|
|
|
_ => debug!("unknown device status query: {}", arg),
|
|
|
|
};
|
2017-05-07 20:08:23 +00:00
|
|
|
}
|
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-07-04 20:37:08 +00:00
|
|
|
fn move_down_and_cr(&mut self, lines: Line) {
|
2018-09-26 17:38:41 +00:00
|
|
|
trace!("move_down_and_cr: {}", lines);
|
|
|
|
let move_to = self.cursor.point.line + lines;
|
|
|
|
self.goto(move_to, Column(0))
|
2016-07-04 20:37:08 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 20:37:08 +00:00
|
|
|
fn move_up_and_cr(&mut self, lines: Line) {
|
2018-09-26 17:38:41 +00:00
|
|
|
trace!("move_up_and_cr: {}", lines);
|
|
|
|
let move_to = Line(self.cursor.point.line.0.saturating_sub(lines.0));
|
|
|
|
self.goto(move_to, Column(0))
|
2016-07-04 20:37:08 +00:00
|
|
|
}
|
2016-07-04 00:00:00 +00:00
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-05-31 03:44:37 +00:00
|
|
|
fn put_tab(&mut self, mut count: i64) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("put_tab: {}", count);
|
2016-05-31 03:44:37 +00:00
|
|
|
|
2017-02-01 17:55:23 +00:00
|
|
|
let mut col = self.cursor.point.col;
|
2016-07-04 00:00:00 +00:00
|
|
|
while col < self.grid.num_cols() && count != 0 {
|
2016-05-31 03:44:37 +00:00
|
|
|
count -= 1;
|
|
|
|
loop {
|
2017-04-04 12:08:10 +00:00
|
|
|
if (col + 1) == self.grid.num_cols() {
|
2016-05-31 03:44:37 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-02-27 08:12:04 +00:00
|
|
|
|
2016-07-04 00:00:00 +00:00
|
|
|
col += 1;
|
2017-04-04 12:08:10 +00:00
|
|
|
|
|
|
|
if self.tabs[*col as usize] {
|
|
|
|
break;
|
|
|
|
}
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-01 17:55:23 +00:00
|
|
|
self.cursor.point.col = col;
|
2017-01-08 03:46:02 +00:00
|
|
|
self.input_needs_wrap = false;
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Backspace `count` characters
|
|
|
|
#[inline]
|
2016-06-09 15:37:59 +00:00
|
|
|
fn backspace(&mut self) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("backspace");
|
2017-02-01 17:55:23 +00:00
|
|
|
if self.cursor.point.col > Column(0) {
|
|
|
|
self.cursor.point.col -= 1;
|
2017-01-08 03:46:02 +00:00
|
|
|
self.input_needs_wrap = false;
|
2016-12-10 18:14:45 +00:00
|
|
|
}
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Carriage return
|
|
|
|
#[inline]
|
|
|
|
fn carriage_return(&mut self) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("carriage_return");
|
2017-02-01 17:55:23 +00:00
|
|
|
self.cursor.point.col = Column(0);
|
2017-01-08 03:46:02 +00:00
|
|
|
self.input_needs_wrap = false;
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Linefeed
|
|
|
|
#[inline]
|
|
|
|
fn linefeed(&mut self) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("linefeed");
|
2017-11-11 16:45:17 +00:00
|
|
|
let next = self.cursor.point.line + 1;
|
|
|
|
if next == self.scroll_region.end {
|
2016-08-20 03:35:13 +00:00
|
|
|
self.scroll_up(Line(1));
|
2017-11-11 16:45:17 +00:00
|
|
|
} else if next < self.grid.num_lines() {
|
|
|
|
self.cursor.point.line += 1;
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Set current position as a tabstop
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-07-04 15:42:18 +00:00
|
|
|
fn bell(&mut self) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("bell");
|
2017-02-03 23:34:52 +00:00
|
|
|
self.visual_bell.ring();
|
2017-10-21 23:03:58 +00:00
|
|
|
self.next_is_urgent = Some(true);
|
2016-07-04 15:42:18 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 15:42:18 +00:00
|
|
|
fn substitute(&mut self) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("[unimplemented] substitute");
|
2016-07-04 15:42:18 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
2017-04-18 17:41:11 +00:00
|
|
|
/// Run LF/NL
|
|
|
|
///
|
|
|
|
/// LF/NL mode has some interesting history. According to ECMA-48 4th
|
|
|
|
/// edition, in LINE FEED mode,
|
|
|
|
///
|
2017-10-30 15:03:58 +00:00
|
|
|
/// > The execution of the formatter functions LINE FEED (LF), FORM FEED
|
2017-04-18 17:41:11 +00:00
|
|
|
/// (FF), LINE TABULATION (VT) cause only movement of the active position in
|
|
|
|
/// the direction of the line progression.
|
|
|
|
///
|
|
|
|
/// In NEW LINE mode,
|
|
|
|
///
|
2017-10-30 15:03:58 +00:00
|
|
|
/// > The execution of the formatter functions LINE FEED (LF), FORM FEED
|
2017-04-18 17:41:11 +00:00
|
|
|
/// (FF), LINE TABULATION (VT) cause movement to the line home position on
|
|
|
|
/// the following line, the following form, etc. In the case of LF this is
|
|
|
|
/// referred to as the New Line (NL) option.
|
|
|
|
///
|
|
|
|
/// Additionally, ECMA-48 4th edition says that this option is deprecated.
|
|
|
|
/// ECMA-48 5th edition only mentions this option (without explanation)
|
|
|
|
/// saying that it's been removed.
|
|
|
|
///
|
|
|
|
/// As an emulator, we need to support it since applications may still rely
|
|
|
|
/// on it.
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-07-04 15:42:18 +00:00
|
|
|
fn newline(&mut self) {
|
2017-04-18 17:41:11 +00:00
|
|
|
self.linefeed();
|
|
|
|
|
2017-10-12 01:52:23 +00:00
|
|
|
if self.mode.contains(mode::TermMode::LINE_FEED_NEW_LINE) {
|
2017-04-18 17:41:11 +00:00
|
|
|
self.carriage_return();
|
|
|
|
}
|
2016-07-04 15:42:18 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 15:42:18 +00:00
|
|
|
fn set_horizontal_tabstop(&mut self) {
|
2017-05-01 04:11:47 +00:00
|
|
|
trace!("set_horizontal_tabstop");
|
|
|
|
let column = self.cursor.point.col;
|
|
|
|
self.tabs[column.0] = true;
|
2016-07-04 15:42:18 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-08-22 15:37:50 +00:00
|
|
|
fn scroll_up(&mut self, lines: Line) {
|
|
|
|
let origin = self.scroll_region.start;
|
|
|
|
self.scroll_up_relative(origin, lines);
|
2016-06-08 17:39:49 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-08-22 15:37:50 +00:00
|
|
|
fn scroll_down(&mut self, lines: Line) {
|
|
|
|
let origin = self.scroll_region.start;
|
|
|
|
self.scroll_down_relative(origin, lines);
|
2016-06-08 17:39:49 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 04:58:35 +00:00
|
|
|
fn insert_blank_lines(&mut self, lines: Line) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("insert_blank_lines: {}", lines);
|
2017-02-01 17:55:23 +00:00
|
|
|
if self.scroll_region.contains_(self.cursor.point.line) {
|
|
|
|
let origin = self.cursor.point.line;
|
2016-08-22 15:37:50 +00:00
|
|
|
self.scroll_down_relative(origin, lines);
|
2016-06-08 17:39:49 +00:00
|
|
|
}
|
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 04:58:35 +00:00
|
|
|
fn delete_lines(&mut self, lines: Line) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("delete_lines: {}", lines);
|
2017-02-01 17:55:23 +00:00
|
|
|
if self.scroll_region.contains_(self.cursor.point.line) {
|
|
|
|
let origin = self.cursor.point.line;
|
2016-08-22 15:37:50 +00:00
|
|
|
self.scroll_up_relative(origin, lines);
|
2016-06-08 17:39:49 +00:00
|
|
|
}
|
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 04:58:35 +00:00
|
|
|
fn erase_chars(&mut self, count: Column) {
|
2017-02-01 17:55:23 +00:00
|
|
|
trace!("erase_chars: {}, {}", count, self.cursor.point.col);
|
|
|
|
let start = self.cursor.point.col;
|
2017-01-08 03:46:02 +00:00
|
|
|
let end = min(start + count, self.grid.num_cols() - 1);
|
2016-07-02 04:13:09 +00:00
|
|
|
|
2017-02-01 17:55:23 +00:00
|
|
|
let row = &mut self.grid[self.cursor.point.line];
|
2017-06-14 16:38:44 +00:00
|
|
|
let template = self.cursor.template; // Cleared cells have current background color set
|
2016-07-04 04:58:35 +00:00
|
|
|
for c in &mut row[start..end] {
|
2016-07-30 03:16:13 +00:00
|
|
|
c.reset(&template);
|
2016-07-02 04:13:09 +00:00
|
|
|
}
|
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 15:42:18 +00:00
|
|
|
fn delete_chars(&mut self, count: Column) {
|
2016-09-15 15:49:55 +00:00
|
|
|
// Ensure deleting within terminal bounds
|
2017-01-08 03:46:02 +00:00
|
|
|
let count = min(count, self.size_info.cols());
|
2017-01-12 03:00:29 +00:00
|
|
|
|
2017-02-01 17:55:23 +00:00
|
|
|
let start = self.cursor.point.col;
|
2017-01-08 03:46:02 +00:00
|
|
|
let end = min(start + count, self.grid.num_cols() - 1);
|
2016-09-15 15:49:55 +00:00
|
|
|
let n = (self.size_info.cols() - end).0;
|
|
|
|
|
2017-02-01 17:55:23 +00:00
|
|
|
let line = self.cursor.point.line; // borrowck
|
2016-09-15 15:49:55 +00:00
|
|
|
let line = &mut self.grid[line];
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
let src = line[end..].as_ptr();
|
|
|
|
let dst = line[start..].as_mut_ptr();
|
|
|
|
|
|
|
|
ptr::copy(src, dst, n);
|
|
|
|
}
|
|
|
|
|
2016-12-17 06:48:04 +00:00
|
|
|
// Clear last `count` cells in line. If deleting 1 char, need to delete
|
|
|
|
// 1 cell.
|
2017-08-09 18:22:07 +00:00
|
|
|
let template = self.cursor.template;
|
2016-09-15 15:49:55 +00:00
|
|
|
let end = self.size_info.cols() - count;
|
|
|
|
for c in &mut line[end..] {
|
|
|
|
c.reset(&template);
|
|
|
|
}
|
2016-07-04 15:42:18 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 15:42:18 +00:00
|
|
|
fn move_backward_tabs(&mut self, count: i64) {
|
2017-06-15 19:46:53 +00:00
|
|
|
trace!("move_backward_tabs: {}", count);
|
|
|
|
|
|
|
|
for _ in 0..count {
|
|
|
|
let mut col = self.cursor.point.col;
|
|
|
|
for i in (0..(col.0)).rev() {
|
|
|
|
if self.tabs[i as usize] {
|
|
|
|
col = index::Column(i);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self.cursor.point.col = col;
|
|
|
|
}
|
2016-07-04 15:42:18 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 15:42:18 +00:00
|
|
|
fn move_forward_tabs(&mut self, count: i64) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("[unimplemented] move_forward_tabs: {}", count);
|
2016-07-04 15:42:18 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 15:42:18 +00:00
|
|
|
fn save_cursor_position(&mut self) {
|
2017-02-01 17:55:23 +00:00
|
|
|
trace!("CursorSave");
|
2017-09-28 00:29:44 +00:00
|
|
|
let cursor = if self.alt {
|
2017-02-01 17:55:23 +00:00
|
|
|
&mut self.cursor_save_alt
|
|
|
|
} else {
|
|
|
|
&mut self.cursor_save
|
|
|
|
};
|
|
|
|
|
2017-08-09 18:24:42 +00:00
|
|
|
*cursor = self.cursor;
|
2016-07-04 15:42:18 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 15:42:18 +00:00
|
|
|
fn restore_cursor_position(&mut self) {
|
2017-02-01 17:55:23 +00:00
|
|
|
trace!("CursorRestore");
|
2017-08-09 18:24:42 +00:00
|
|
|
let source = if self.alt {
|
2017-02-01 17:55:23 +00:00
|
|
|
&self.cursor_save_alt
|
|
|
|
} else {
|
|
|
|
&self.cursor_save
|
|
|
|
};
|
|
|
|
|
2017-08-09 18:24:42 +00:00
|
|
|
self.cursor = *source;
|
2017-02-04 03:42:25 +00:00
|
|
|
self.cursor.point.line = min(self.cursor.point.line, self.grid.num_lines() - 1);
|
|
|
|
self.cursor.point.col = min(self.cursor.point.col, self.grid.num_cols() - 1);
|
2016-07-04 15:42:18 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-05-31 03:44:37 +00:00
|
|
|
fn clear_line(&mut self, mode: ansi::LineClearMode) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("clear_line: {:?}", mode);
|
2017-04-20 03:43:02 +00:00
|
|
|
let mut template = self.cursor.template;
|
|
|
|
template.flags ^= template.flags;
|
|
|
|
|
2017-02-01 17:55:23 +00:00
|
|
|
let col = self.cursor.point.col;
|
2017-01-12 03:00:29 +00:00
|
|
|
|
2016-05-31 03:44:37 +00:00
|
|
|
match mode {
|
|
|
|
ansi::LineClearMode::Right => {
|
2017-02-01 17:55:23 +00:00
|
|
|
let row = &mut self.grid[self.cursor.point.line];
|
2017-01-07 05:08:52 +00:00
|
|
|
for cell in &mut row[col..] {
|
2016-07-30 03:16:13 +00:00
|
|
|
cell.reset(&template);
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
},
|
2016-08-22 15:11:34 +00:00
|
|
|
ansi::LineClearMode::Left => {
|
2017-02-01 17:55:23 +00:00
|
|
|
let row = &mut self.grid[self.cursor.point.line];
|
2018-09-17 23:34:56 +00:00
|
|
|
for cell in &mut row[..=col] {
|
2016-08-22 15:11:34 +00:00
|
|
|
cell.reset(&template);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
ansi::LineClearMode::All => {
|
2017-02-01 17:55:23 +00:00
|
|
|
let row = &mut self.grid[self.cursor.point.line];
|
2016-08-22 15:11:34 +00:00
|
|
|
for cell in &mut row[..] {
|
|
|
|
cell.reset(&template);
|
|
|
|
}
|
|
|
|
},
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
2017-02-06 21:28:33 +00:00
|
|
|
/// Set the indexed color value
|
|
|
|
#[inline]
|
|
|
|
fn set_color(&mut self, index: usize, color: Rgb) {
|
|
|
|
trace!("set_color[{}] = {:?}", index, color);
|
|
|
|
self.colors[index] = color;
|
2017-08-29 15:54:12 +00:00
|
|
|
self.color_modified[index] = true;
|
2017-02-06 21:28:33 +00:00
|
|
|
}
|
|
|
|
|
2017-07-28 22:14:18 +00:00
|
|
|
/// Reset the indexed color to original value
|
|
|
|
#[inline]
|
|
|
|
fn reset_color(&mut self, index: usize) {
|
|
|
|
trace!("reset_color[{}]", index);
|
|
|
|
self.colors[index] = self.original_colors[index];
|
2017-08-29 15:54:12 +00:00
|
|
|
self.color_modified[index] = false;
|
2017-07-28 22:14:18 +00:00
|
|
|
}
|
|
|
|
|
2018-01-02 16:32:50 +00:00
|
|
|
/// Set the clipboard
|
|
|
|
#[inline]
|
|
|
|
fn set_clipboard(&mut self, string: &str)
|
|
|
|
{
|
|
|
|
Clipboard::new()
|
|
|
|
.and_then(|mut clipboard| clipboard.store_primary(string))
|
|
|
|
.unwrap_or_else(|err| {
|
|
|
|
warn!("Error storing selection to clipboard. {}", err);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-05-31 03:44:37 +00:00
|
|
|
fn clear_screen(&mut self, mode: ansi::ClearMode) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("clear_screen: {:?}", mode);
|
2017-04-20 03:43:02 +00:00
|
|
|
let mut template = self.cursor.template;
|
|
|
|
template.flags ^= template.flags;
|
|
|
|
|
2016-05-31 03:44:37 +00:00
|
|
|
match mode {
|
|
|
|
ansi::ClearMode::Below => {
|
2017-02-06 20:11:35 +00:00
|
|
|
for cell in &mut self.grid[self.cursor.point.line][self.cursor.point.col..] {
|
|
|
|
cell.reset(&template);
|
|
|
|
}
|
|
|
|
if self.cursor.point.line < self.grid.num_lines() - 1 {
|
2017-10-13 03:12:29 +00:00
|
|
|
self.grid.region_mut((self.cursor.point.line + 1)..)
|
|
|
|
.each(|cell| cell.reset(&template));
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
ansi::ClearMode::All => {
|
2018-11-19 08:33:48 +00:00
|
|
|
self.clear_log();
|
2017-10-13 03:12:29 +00:00
|
|
|
self.grid.region_mut(..).each(|c| c.reset(&template));
|
2016-05-31 03:44:37 +00:00
|
|
|
},
|
2017-02-06 21:13:25 +00:00
|
|
|
ansi::ClearMode::Above => {
|
|
|
|
// If clearing more than one line
|
|
|
|
if self.cursor.point.line > Line(1) {
|
|
|
|
// Fully clear all lines before the current line
|
2017-10-13 03:12:29 +00:00
|
|
|
self.grid.region_mut(..self.cursor.point.line)
|
|
|
|
.each(|cell| cell.reset(&template));
|
2017-02-06 21:13:25 +00:00
|
|
|
}
|
|
|
|
// Clear up to the current column in the current line
|
2017-04-18 17:42:29 +00:00
|
|
|
let end = min(self.cursor.point.col + 1, self.grid.num_cols());
|
|
|
|
for cell in &mut self.grid[self.cursor.point.line][..end] {
|
2017-02-06 21:13:25 +00:00
|
|
|
cell.reset(&template);
|
|
|
|
}
|
2017-03-04 04:22:54 +00:00
|
|
|
},
|
|
|
|
// If scrollback is implemented, this should clear it
|
2018-09-02 00:30:03 +00:00
|
|
|
ansi::ClearMode::Saved => {
|
|
|
|
self.grid.clear_history();
|
|
|
|
}
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 15:42:18 +00:00
|
|
|
fn clear_tabs(&mut self, mode: ansi::TabulationClearMode) {
|
2017-05-01 04:11:47 +00:00
|
|
|
trace!("clear_tabs: {:?}", mode);
|
|
|
|
match mode {
|
|
|
|
ansi::TabulationClearMode::Current => {
|
|
|
|
let column = self.cursor.point.col;
|
|
|
|
self.tabs[column.0] = 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-07-04 15:42:18 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
2018-01-18 17:27:07 +00:00
|
|
|
// Reset all important fields in the term struct
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-07-04 15:42:18 +00:00
|
|
|
fn reset_state(&mut self) {
|
2018-01-18 17:27:07 +00:00
|
|
|
self.input_needs_wrap = false;
|
|
|
|
self.next_title = None;
|
|
|
|
self.next_mouse_cursor = None;
|
|
|
|
self.alt = false;
|
|
|
|
self.cursor = Default::default();
|
|
|
|
self.active_charset = Default::default();
|
|
|
|
self.mode = Default::default();
|
|
|
|
self.font_size = self.original_font_size;
|
|
|
|
self.next_is_urgent = None;
|
|
|
|
self.cursor_save = Default::default();
|
|
|
|
self.cursor_save_alt = Default::default();
|
|
|
|
self.colors = self.original_colors;
|
|
|
|
self.color_modified = [false; color::COUNT];
|
|
|
|
self.cursor_style = None;
|
2018-08-03 22:12:23 +00:00
|
|
|
self.grid.clear_history();
|
|
|
|
self.grid.region_mut(..).each(|c| c.reset(&Cell::default()));
|
2016-07-04 15:42:18 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-05-31 03:44:37 +00:00
|
|
|
fn reverse_index(&mut self) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("reverse_index");
|
2016-05-31 03:44:37 +00:00
|
|
|
// if cursor is at the top
|
2017-02-01 17:55:23 +00:00
|
|
|
if self.cursor.point.line == self.scroll_region.start {
|
2016-08-20 03:35:13 +00:00
|
|
|
self.scroll_down(Line(1));
|
2016-05-31 03:44:37 +00:00
|
|
|
} else {
|
2017-02-01 17:55:23 +00:00
|
|
|
self.cursor.point.line -= min(self.cursor.point.line, Line(1));
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// set a terminal attribute
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-05-31 03:44:37 +00:00
|
|
|
fn terminal_attribute(&mut self, attr: Attr) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("Set Attribute: {:?}", attr);
|
2016-05-31 03:44:37 +00:00
|
|
|
match attr {
|
2017-02-01 17:55:23 +00:00
|
|
|
Attr::Foreground(color) => self.cursor.template.fg = color,
|
|
|
|
Attr::Background(color) => self.cursor.template.bg = color,
|
2016-05-31 03:44:37 +00:00
|
|
|
Attr::Reset => {
|
2017-02-01 17:55:23 +00:00
|
|
|
self.cursor.template.fg = Color::Named(NamedColor::Foreground);
|
|
|
|
self.cursor.template.bg = Color::Named(NamedColor::Background);
|
|
|
|
self.cursor.template.flags = cell::Flags::empty();
|
2016-06-06 23:54:15 +00:00
|
|
|
},
|
2017-10-12 01:52:23 +00:00
|
|
|
Attr::Reverse => self.cursor.template.flags.insert(cell::Flags::INVERSE),
|
|
|
|
Attr::CancelReverse => self.cursor.template.flags.remove(cell::Flags::INVERSE),
|
|
|
|
Attr::Bold => self.cursor.template.flags.insert(cell::Flags::BOLD),
|
|
|
|
Attr::CancelBold => self.cursor.template.flags.remove(cell::Flags::BOLD),
|
|
|
|
Attr::Dim => self.cursor.template.flags.insert(cell::Flags::DIM),
|
|
|
|
Attr::CancelBoldDim => self.cursor.template.flags.remove(cell::Flags::BOLD | cell::Flags::DIM),
|
|
|
|
Attr::Italic => self.cursor.template.flags.insert(cell::Flags::ITALIC),
|
|
|
|
Attr::CancelItalic => self.cursor.template.flags.remove(cell::Flags::ITALIC),
|
|
|
|
Attr::Underscore => self.cursor.template.flags.insert(cell::Flags::UNDERLINE),
|
|
|
|
Attr::CancelUnderline => self.cursor.template.flags.remove(cell::Flags::UNDERLINE),
|
2018-09-19 19:18:51 +00:00
|
|
|
Attr::Hidden => self.cursor.template.flags.insert(cell::Flags::HIDDEN),
|
|
|
|
Attr::CancelHidden => self.cursor.template.flags.remove(cell::Flags::HIDDEN),
|
2016-05-31 03:44:37 +00:00
|
|
|
_ => {
|
2017-01-13 07:15:06 +00:00
|
|
|
debug!("Term got unhandled attr: {:?}", attr);
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-05-31 03:44:37 +00:00
|
|
|
fn set_mode(&mut self, mode: ansi::Mode) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("set_mode: {:?}", mode);
|
2016-05-31 03:44:37 +00:00
|
|
|
match mode {
|
2017-02-01 17:55:23 +00:00
|
|
|
ansi::Mode::SwapScreenAndSetRestoreCursor => {
|
2017-10-12 01:52:23 +00:00
|
|
|
self.mode.insert(mode::TermMode::ALT_SCREEN);
|
2017-02-01 17:55:23 +00:00
|
|
|
self.save_cursor_position();
|
2017-08-09 18:24:51 +00:00
|
|
|
if !self.alt {
|
|
|
|
self.swap_alt();
|
|
|
|
}
|
2017-02-03 04:51:14 +00:00
|
|
|
self.save_cursor_position();
|
2017-02-01 17:55:23 +00:00
|
|
|
},
|
2017-10-12 01:52:23 +00:00
|
|
|
ansi::Mode::ShowCursor => self.mode.insert(mode::TermMode::SHOW_CURSOR),
|
|
|
|
ansi::Mode::CursorKeys => self.mode.insert(mode::TermMode::APP_CURSOR),
|
2017-12-24 20:15:42 +00:00
|
|
|
ansi::Mode::ReportMouseClicks => {
|
|
|
|
self.mode.insert(mode::TermMode::MOUSE_REPORT_CLICK);
|
|
|
|
self.set_mouse_cursor(MouseCursor::Arrow);
|
|
|
|
},
|
2018-01-26 21:28:43 +00:00
|
|
|
ansi::Mode::ReportCellMouseMotion => {
|
|
|
|
self.mode.insert(mode::TermMode::MOUSE_DRAG);
|
|
|
|
self.set_mouse_cursor(MouseCursor::Arrow);
|
|
|
|
},
|
2017-12-24 20:15:42 +00:00
|
|
|
ansi::Mode::ReportAllMouseMotion => {
|
|
|
|
self.mode.insert(mode::TermMode::MOUSE_MOTION);
|
|
|
|
self.set_mouse_cursor(MouseCursor::Arrow);
|
|
|
|
},
|
2017-10-12 01:52:23 +00:00
|
|
|
ansi::Mode::ReportFocusInOut => self.mode.insert(mode::TermMode::FOCUS_IN_OUT),
|
|
|
|
ansi::Mode::BracketedPaste => self.mode.insert(mode::TermMode::BRACKETED_PASTE),
|
|
|
|
ansi::Mode::SgrMouse => self.mode.insert(mode::TermMode::SGR_MOUSE),
|
|
|
|
ansi::Mode::LineWrap => self.mode.insert(mode::TermMode::LINE_WRAP),
|
|
|
|
ansi::Mode::LineFeedNewLine => self.mode.insert(mode::TermMode::LINE_FEED_NEW_LINE),
|
|
|
|
ansi::Mode::Origin => self.mode.insert(mode::TermMode::ORIGIN),
|
2017-04-19 16:03:33 +00:00
|
|
|
ansi::Mode::DECCOLM => self.deccolm(),
|
2017-10-12 01:52:23 +00:00
|
|
|
ansi::Mode::Insert => self.mode.insert(mode::TermMode::INSERT), // heh
|
2016-06-08 04:17:48 +00:00
|
|
|
_ => {
|
2017-05-06 15:45:23 +00:00
|
|
|
trace!(".. ignoring set_mode");
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-06-08 04:17:48 +00:00
|
|
|
fn unset_mode(&mut self,mode: ansi::Mode) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("unset_mode: {:?}", mode);
|
2016-05-31 03:44:37 +00:00
|
|
|
match mode {
|
2017-02-01 17:55:23 +00:00
|
|
|
ansi::Mode::SwapScreenAndSetRestoreCursor => {
|
2017-10-12 01:52:23 +00:00
|
|
|
self.mode.remove(mode::TermMode::ALT_SCREEN);
|
2017-02-01 17:55:23 +00:00
|
|
|
self.restore_cursor_position();
|
2017-08-09 18:24:51 +00:00
|
|
|
if self.alt {
|
|
|
|
self.swap_alt();
|
|
|
|
}
|
2017-02-03 04:51:14 +00:00
|
|
|
self.restore_cursor_position();
|
2017-02-01 17:55:23 +00:00
|
|
|
},
|
2017-10-12 01:52:23 +00:00
|
|
|
ansi::Mode::ShowCursor => self.mode.remove(mode::TermMode::SHOW_CURSOR),
|
|
|
|
ansi::Mode::CursorKeys => self.mode.remove(mode::TermMode::APP_CURSOR),
|
2017-12-24 20:15:42 +00:00
|
|
|
ansi::Mode::ReportMouseClicks => {
|
|
|
|
self.mode.remove(mode::TermMode::MOUSE_REPORT_CLICK);
|
|
|
|
self.set_mouse_cursor(MouseCursor::Text);
|
|
|
|
},
|
2018-01-26 21:28:43 +00:00
|
|
|
ansi::Mode::ReportCellMouseMotion => {
|
|
|
|
self.mode.remove(mode::TermMode::MOUSE_DRAG);
|
|
|
|
self.set_mouse_cursor(MouseCursor::Text);
|
|
|
|
},
|
2017-12-24 20:15:42 +00:00
|
|
|
ansi::Mode::ReportAllMouseMotion => {
|
|
|
|
self.mode.remove(mode::TermMode::MOUSE_MOTION);
|
|
|
|
self.set_mouse_cursor(MouseCursor::Text);
|
|
|
|
},
|
2017-10-12 01:52:23 +00:00
|
|
|
ansi::Mode::ReportFocusInOut => self.mode.remove(mode::TermMode::FOCUS_IN_OUT),
|
|
|
|
ansi::Mode::BracketedPaste => self.mode.remove(mode::TermMode::BRACKETED_PASTE),
|
|
|
|
ansi::Mode::SgrMouse => self.mode.remove(mode::TermMode::SGR_MOUSE),
|
|
|
|
ansi::Mode::LineWrap => self.mode.remove(mode::TermMode::LINE_WRAP),
|
|
|
|
ansi::Mode::LineFeedNewLine => self.mode.remove(mode::TermMode::LINE_FEED_NEW_LINE),
|
|
|
|
ansi::Mode::Origin => self.mode.remove(mode::TermMode::ORIGIN),
|
2017-04-19 16:03:33 +00:00
|
|
|
ansi::Mode::DECCOLM => self.deccolm(),
|
2017-10-12 01:52:23 +00:00
|
|
|
ansi::Mode::Insert => self.mode.remove(mode::TermMode::INSERT),
|
2016-06-08 04:17:48 +00:00
|
|
|
_ => {
|
2017-05-06 15:45:23 +00:00
|
|
|
trace!(".. ignoring unset_mode");
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-06-08 17:39:49 +00:00
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-07-04 04:58:35 +00:00
|
|
|
fn set_scrolling_region(&mut self, region: Range<Line>) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("set scroll region: {:?}", region);
|
2017-01-10 06:05:26 +00:00
|
|
|
self.scroll_region.start = min(region.start, self.grid.num_lines());
|
|
|
|
self.scroll_region.end = min(region.end, self.grid.num_lines());
|
2016-08-22 15:37:50 +00:00
|
|
|
self.goto(Line(0), Column(0));
|
2016-06-08 17:39:49 +00:00
|
|
|
}
|
2016-07-04 16:19:48 +00:00
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn set_keypad_application_mode(&mut self) {
|
2017-10-12 01:52:23 +00:00
|
|
|
trace!("set mode::TermMode::APP_KEYPAD");
|
|
|
|
self.mode.insert(mode::TermMode::APP_KEYPAD);
|
2016-07-04 16:19:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn unset_keypad_application_mode(&mut self) {
|
2017-10-12 01:52:23 +00:00
|
|
|
trace!("unset mode::TermMode::APP_KEYPAD");
|
|
|
|
self.mode.remove(mode::TermMode::APP_KEYPAD);
|
2016-07-04 16:19:48 +00:00
|
|
|
}
|
2017-01-09 20:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn configure_charset(&mut self, index: CharsetIndex, charset: StandardCharset) {
|
2017-01-23 16:50:24 +00:00
|
|
|
trace!("designate {:?} character set as {:?}", index, charset);
|
2017-02-01 17:55:23 +00:00
|
|
|
self.cursor.charsets[index] = charset;
|
2017-01-09 20:07:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn set_active_charset(&mut self, index: CharsetIndex) {
|
2017-01-23 16:50:24 +00:00
|
|
|
trace!("Activate {:?} character set", index);
|
2017-01-09 20:07:23 +00:00
|
|
|
self.active_charset = index;
|
|
|
|
}
|
2017-05-28 16:38:10 +00:00
|
|
|
|
|
|
|
#[inline]
|
2017-12-03 21:38:42 +00:00
|
|
|
fn set_cursor_style(&mut self, style: Option<CursorStyle>) {
|
2017-05-28 16:38:10 +00:00
|
|
|
trace!("set_cursor_style {:?}", style);
|
|
|
|
self.cursor_style = style;
|
|
|
|
}
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
2016-11-20 00:16:20 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
extern crate serde_json;
|
2016-11-25 20:03:11 +00:00
|
|
|
|
2017-04-20 17:18:05 +00:00
|
|
|
use super::{Cell, Term, SizeInfo};
|
2018-10-22 19:39:26 +00:00
|
|
|
use term::{cell, Search};
|
2016-11-20 00:16:20 +00:00
|
|
|
|
2018-09-02 00:30:03 +00:00
|
|
|
use grid::{Grid, Scroll};
|
2018-09-26 18:42:41 +00:00
|
|
|
use index::{Point, Line, Column, Side};
|
2018-09-02 00:30:03 +00:00
|
|
|
use ansi::{self, Handler, CharsetIndex, StandardCharset};
|
2017-01-15 01:53:48 +00:00
|
|
|
use selection::Selection;
|
|
|
|
use std::mem;
|
2018-07-21 17:17:41 +00:00
|
|
|
use input::FONT_SIZE_STEP;
|
|
|
|
use font::Size;
|
|
|
|
use config::Config;
|
2017-01-15 01:53:48 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn semantic_selection_works() {
|
|
|
|
let size = SizeInfo {
|
|
|
|
width: 21.0,
|
|
|
|
height: 51.0,
|
|
|
|
cell_width: 3.0,
|
|
|
|
cell_height: 3.0,
|
2017-05-06 15:45:23 +00:00
|
|
|
padding_x: 0.0,
|
|
|
|
padding_y: 0.0,
|
2018-11-10 16:08:48 +00:00
|
|
|
dpr: 1.0,
|
2017-01-15 01:53:48 +00:00
|
|
|
};
|
|
|
|
let mut term = Term::new(&Default::default(), size);
|
2018-03-09 21:49:47 +00:00
|
|
|
let mut grid: Grid<Cell> = Grid::new(Line(3), Column(5), 0, Cell::default());
|
2017-01-15 01:53:48 +00:00
|
|
|
for i in 0..5 {
|
|
|
|
for j in 0..2 {
|
|
|
|
grid[Line(j)][Column(i)].c = 'a';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
grid[Line(0)][Column(0)].c = '"';
|
|
|
|
grid[Line(0)][Column(3)].c = '"';
|
|
|
|
grid[Line(1)][Column(2)].c = '"';
|
2017-10-12 01:52:23 +00:00
|
|
|
grid[Line(0)][Column(4)].flags.insert(cell::Flags::WRAPLINE);
|
2017-01-15 01:53:48 +00:00
|
|
|
|
|
|
|
let mut escape_chars = String::from("\"");
|
|
|
|
|
|
|
|
mem::swap(&mut term.grid, &mut grid);
|
|
|
|
mem::swap(&mut term.semantic_escape_chars, &mut escape_chars);
|
|
|
|
|
|
|
|
{
|
2018-05-30 08:20:47 +00:00
|
|
|
*term.selection_mut() = Some(Selection::semantic(Point { line: 2, col: Column(1) }));
|
2018-03-09 21:49:47 +00:00
|
|
|
assert_eq!(term.selection_to_string(), Some(String::from("aa")));
|
2017-01-15 01:53:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2018-05-30 08:20:47 +00:00
|
|
|
*term.selection_mut() = Some(Selection::semantic(Point { line: 2, col: Column(4) }));
|
2018-03-09 21:49:47 +00:00
|
|
|
assert_eq!(term.selection_to_string(), Some(String::from("aaa")));
|
2017-01-15 01:53:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2018-05-30 08:20:47 +00:00
|
|
|
*term.selection_mut() = Some(Selection::semantic(Point { line: 1, col: Column(1) }));
|
2018-03-09 21:49:47 +00:00
|
|
|
assert_eq!(term.selection_to_string(), Some(String::from("aaa")));
|
2017-01-15 01:53:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn line_selection_works() {
|
|
|
|
let size = SizeInfo {
|
|
|
|
width: 21.0,
|
|
|
|
height: 51.0,
|
|
|
|
cell_width: 3.0,
|
|
|
|
cell_height: 3.0,
|
2017-05-06 15:45:23 +00:00
|
|
|
padding_x: 0.0,
|
|
|
|
padding_y: 0.0,
|
2018-11-10 16:08:48 +00:00
|
|
|
dpr: 1.0,
|
2017-01-15 01:53:48 +00:00
|
|
|
};
|
|
|
|
let mut term = Term::new(&Default::default(), size);
|
2018-03-09 21:49:47 +00:00
|
|
|
let mut grid: Grid<Cell> = Grid::new(Line(1), Column(5), 0, Cell::default());
|
2017-01-15 01:53:48 +00:00
|
|
|
for i in 0..5 {
|
|
|
|
grid[Line(0)][Column(i)].c = 'a';
|
|
|
|
}
|
|
|
|
grid[Line(0)][Column(0)].c = '"';
|
|
|
|
grid[Line(0)][Column(3)].c = '"';
|
|
|
|
|
|
|
|
|
|
|
|
mem::swap(&mut term.grid, &mut grid);
|
|
|
|
|
2018-03-09 21:49:47 +00:00
|
|
|
*term.selection_mut() = Some(Selection::lines(Point { line: 0, col: Column(3) }));
|
|
|
|
assert_eq!(term.selection_to_string(), Some(String::from("\"aa\"a\n")));
|
2017-01-15 01:53:48 +00:00
|
|
|
}
|
2016-11-20 00:16:20 +00:00
|
|
|
|
2018-09-26 18:42:41 +00:00
|
|
|
#[test]
|
|
|
|
fn selecting_empty_line() {
|
|
|
|
let size = SizeInfo {
|
|
|
|
width: 21.0,
|
|
|
|
height: 51.0,
|
|
|
|
cell_width: 3.0,
|
|
|
|
cell_height: 3.0,
|
|
|
|
padding_x: 0.0,
|
|
|
|
padding_y: 0.0,
|
2018-11-10 16:08:48 +00:00
|
|
|
dpr: 1.0,
|
2018-09-26 18:42:41 +00:00
|
|
|
};
|
|
|
|
let mut term = Term::new(&Default::default(), size);
|
|
|
|
let mut grid: Grid<Cell> = Grid::new(Line(3), Column(3), 0, Cell::default());
|
|
|
|
for l in 0..3 {
|
|
|
|
if l != 1 {
|
|
|
|
for c in 0..3 {
|
|
|
|
grid[Line(l)][Column(c)].c = 'a';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mem::swap(&mut term.grid, &mut grid);
|
|
|
|
|
|
|
|
let mut selection = Selection::simple(Point { line: 2, col: Column(0) }, Side::Left);
|
|
|
|
selection.update(Point { line: 0, col: Column(2) }, Side::Right);
|
|
|
|
*term.selection_mut() = Some(selection);
|
|
|
|
assert_eq!(term.selection_to_string(), Some("aaa\n\naaa\n".into()));
|
|
|
|
}
|
|
|
|
|
2016-11-20 00:16:20 +00:00
|
|
|
/// Check that the grid can be serialized back and forth losslessly
|
|
|
|
///
|
|
|
|
/// This test is in the term module as opposed to the grid since we want to
|
|
|
|
/// test this property with a T=Cell.
|
|
|
|
#[test]
|
|
|
|
fn grid_serde() {
|
2016-12-30 02:43:55 +00:00
|
|
|
let template = Cell::default();
|
2016-11-20 00:16:20 +00:00
|
|
|
|
2018-03-09 21:49:47 +00:00
|
|
|
let grid: Grid<Cell> = Grid::new(Line(24), Column(80), 0, template);
|
2016-11-20 00:16:20 +00:00
|
|
|
let serialized = serde_json::to_string(&grid).expect("ser");
|
|
|
|
let deserialized = serde_json::from_str::<Grid<Cell>>(&serialized)
|
|
|
|
.expect("de");
|
|
|
|
|
|
|
|
assert_eq!(deserialized, grid);
|
|
|
|
}
|
2016-11-25 20:03:11 +00:00
|
|
|
|
2017-01-09 20:07:23 +00:00
|
|
|
#[test]
|
|
|
|
fn input_line_drawing_character() {
|
|
|
|
let size = SizeInfo {
|
|
|
|
width: 21.0,
|
|
|
|
height: 51.0,
|
|
|
|
cell_width: 3.0,
|
|
|
|
cell_height: 3.0,
|
2017-05-06 15:45:23 +00:00
|
|
|
padding_x: 0.0,
|
|
|
|
padding_y: 0.0,
|
2018-11-10 16:08:48 +00:00
|
|
|
dpr: 1.0,
|
2017-01-09 20:07:23 +00:00
|
|
|
};
|
2017-01-09 03:18:39 +00:00
|
|
|
let mut term = Term::new(&Default::default(), size);
|
2017-01-09 20:07:23 +00:00
|
|
|
let cursor = Point::new(Line(0), Column(0));
|
|
|
|
term.configure_charset(CharsetIndex::G0,
|
|
|
|
StandardCharset::SpecialCharacterAndLineDrawing);
|
|
|
|
term.input('a');
|
|
|
|
|
|
|
|
assert_eq!(term.grid()[&cursor].c, '▒');
|
|
|
|
}
|
2018-07-21 17:17:41 +00:00
|
|
|
|
|
|
|
fn change_font_size_works(font_size: f32) {
|
|
|
|
let size = SizeInfo {
|
|
|
|
width: 21.0,
|
|
|
|
height: 51.0,
|
|
|
|
cell_width: 3.0,
|
|
|
|
cell_height: 3.0,
|
|
|
|
padding_x: 0.0,
|
|
|
|
padding_y: 0.0,
|
2018-11-10 16:08:48 +00:00
|
|
|
dpr: 1.0,
|
2018-07-21 17:17:41 +00:00
|
|
|
};
|
|
|
|
let config: Config = Default::default();
|
|
|
|
let mut term: Term = Term::new(&config, size);
|
|
|
|
term.change_font_size(font_size);
|
|
|
|
|
|
|
|
let expected_font_size: Size = config.font().size() + Size::new(font_size);
|
|
|
|
assert_eq!(term.font_size, expected_font_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn increase_font_size_works() {
|
|
|
|
change_font_size_works(10.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn decrease_font_size_works() {
|
|
|
|
change_font_size_works(-10.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn prevent_font_below_threshold_works() {
|
|
|
|
let size = SizeInfo {
|
|
|
|
width: 21.0,
|
|
|
|
height: 51.0,
|
|
|
|
cell_width: 3.0,
|
|
|
|
cell_height: 3.0,
|
|
|
|
padding_x: 0.0,
|
|
|
|
padding_y: 0.0,
|
2018-11-10 16:08:48 +00:00
|
|
|
dpr: 1.0,
|
2018-07-21 17:17:41 +00:00
|
|
|
};
|
|
|
|
let config: Config = Default::default();
|
|
|
|
let mut term: Term = Term::new(&config, size);
|
|
|
|
|
|
|
|
term.change_font_size(-100.0);
|
|
|
|
|
|
|
|
let expected_font_size: Size = Size::new(FONT_SIZE_STEP);
|
|
|
|
assert_eq!(term.font_size, expected_font_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn reset_font_size_works() {
|
|
|
|
let size = SizeInfo {
|
|
|
|
width: 21.0,
|
|
|
|
height: 51.0,
|
|
|
|
cell_width: 3.0,
|
|
|
|
cell_height: 3.0,
|
|
|
|
padding_x: 0.0,
|
|
|
|
padding_y: 0.0,
|
2018-11-10 16:08:48 +00:00
|
|
|
dpr: 1.0,
|
2018-07-21 17:17:41 +00:00
|
|
|
};
|
|
|
|
let config: Config = Default::default();
|
|
|
|
let mut term: Term = Term::new(&config, size);
|
|
|
|
|
|
|
|
term.change_font_size(10.0);
|
|
|
|
term.reset_font_size();
|
|
|
|
|
|
|
|
let expected_font_size: Size = config.font().size();
|
|
|
|
assert_eq!(term.font_size, expected_font_size);
|
|
|
|
}
|
2018-09-02 00:30:03 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn clear_saved_lines() {
|
|
|
|
let size = SizeInfo {
|
|
|
|
width: 21.0,
|
|
|
|
height: 51.0,
|
|
|
|
cell_width: 3.0,
|
|
|
|
cell_height: 3.0,
|
|
|
|
padding_x: 0.0,
|
|
|
|
padding_y: 0.0,
|
2018-11-10 16:08:48 +00:00
|
|
|
dpr: 1.0
|
2018-09-02 00:30:03 +00:00
|
|
|
};
|
|
|
|
let config: Config = Default::default();
|
|
|
|
let mut term: Term = Term::new(&config, size);
|
|
|
|
|
|
|
|
// Add one line of scrollback
|
|
|
|
term.grid.scroll_up(&(Line(0)..Line(1)), Line(1), &Cell::default());
|
|
|
|
|
|
|
|
// Clear the history
|
|
|
|
term.clear_screen(ansi::ClearMode::Saved);
|
|
|
|
|
|
|
|
// Make sure that scrolling does not change the grid
|
|
|
|
let mut scrolled_grid = term.grid.clone();
|
|
|
|
scrolled_grid.scroll_display(Scroll::Top);
|
|
|
|
assert_eq!(term.grid, scrolled_grid);
|
|
|
|
}
|
2018-10-22 19:39:26 +00:00
|
|
|
|
|
|
|
// `((ftp://a.de))` -> `Some("ftp://a.de")`
|
|
|
|
#[test]
|
|
|
|
fn url_trim_unmatched_parens() {
|
|
|
|
let size = SizeInfo {
|
|
|
|
width: 21.0,
|
|
|
|
height: 51.0,
|
|
|
|
cell_width: 3.0,
|
|
|
|
cell_height: 3.0,
|
|
|
|
padding_x: 0.0,
|
|
|
|
padding_y: 0.0,
|
2018-11-10 16:08:48 +00:00
|
|
|
dpr: 1.0,
|
2018-10-22 19:39:26 +00:00
|
|
|
};
|
|
|
|
let mut term = Term::new(&Default::default(), size);
|
|
|
|
let mut grid: Grid<Cell> = Grid::new(Line(1), Column(15), 0, Cell::default());
|
|
|
|
grid[Line(0)][Column(0)].c = '(';
|
|
|
|
grid[Line(0)][Column(1)].c = '(';
|
|
|
|
grid[Line(0)][Column(2)].c = 'f';
|
|
|
|
grid[Line(0)][Column(3)].c = 't';
|
|
|
|
grid[Line(0)][Column(4)].c = 'p';
|
|
|
|
grid[Line(0)][Column(5)].c = ':';
|
|
|
|
grid[Line(0)][Column(6)].c = '/';
|
|
|
|
grid[Line(0)][Column(7)].c = '/';
|
|
|
|
grid[Line(0)][Column(8)].c = 'a';
|
|
|
|
grid[Line(0)][Column(9)].c = '.';
|
|
|
|
grid[Line(0)][Column(10)].c = 'd';
|
|
|
|
grid[Line(0)][Column(11)].c = 'e';
|
|
|
|
grid[Line(0)][Column(12)].c = ')';
|
|
|
|
grid[Line(0)][Column(13)].c = ')';
|
|
|
|
mem::swap(&mut term.grid, &mut grid);
|
|
|
|
|
|
|
|
// Search for URL in grid
|
|
|
|
let url = term.url_search(Point::new(0, Column(4)));
|
|
|
|
|
|
|
|
assert_eq!(url, Some("ftp://a.de".into()));
|
|
|
|
}
|
|
|
|
|
|
|
|
// `ftp://a.de/()` -> `Some("ftp://a.de/()")`
|
|
|
|
#[test]
|
|
|
|
fn url_allow_matching_parens() {
|
|
|
|
let size = SizeInfo {
|
|
|
|
width: 21.0,
|
|
|
|
height: 51.0,
|
|
|
|
cell_width: 3.0,
|
|
|
|
cell_height: 3.0,
|
|
|
|
padding_x: 0.0,
|
|
|
|
padding_y: 0.0,
|
2018-11-10 16:08:48 +00:00
|
|
|
dpr: 1.0,
|
2018-10-22 19:39:26 +00:00
|
|
|
};
|
|
|
|
let mut term = Term::new(&Default::default(), size);
|
|
|
|
let mut grid: Grid<Cell> = Grid::new(Line(1), Column(15), 0, Cell::default());
|
|
|
|
grid[Line(0)][Column(0)].c = 'f';
|
|
|
|
grid[Line(0)][Column(1)].c = 't';
|
|
|
|
grid[Line(0)][Column(2)].c = 'p';
|
|
|
|
grid[Line(0)][Column(3)].c = ':';
|
|
|
|
grid[Line(0)][Column(4)].c = '/';
|
|
|
|
grid[Line(0)][Column(5)].c = '/';
|
|
|
|
grid[Line(0)][Column(6)].c = 'a';
|
|
|
|
grid[Line(0)][Column(7)].c = '.';
|
|
|
|
grid[Line(0)][Column(8)].c = 'd';
|
|
|
|
grid[Line(0)][Column(9)].c = 'e';
|
|
|
|
grid[Line(0)][Column(10)].c = '/';
|
|
|
|
grid[Line(0)][Column(11)].c = '(';
|
|
|
|
grid[Line(0)][Column(12)].c = ')';
|
|
|
|
mem::swap(&mut term.grid, &mut grid);
|
|
|
|
|
|
|
|
// Search for URL in grid
|
|
|
|
let url = term.url_search(Point::new(0, Column(4)));
|
|
|
|
|
|
|
|
assert_eq!(url, Some("ftp://a.de/()".into()));
|
|
|
|
}
|
|
|
|
|
|
|
|
// `aze` -> `None`
|
|
|
|
#[test]
|
|
|
|
fn url_skip_invalid() {
|
|
|
|
let size = SizeInfo {
|
|
|
|
width: 21.0,
|
|
|
|
height: 51.0,
|
|
|
|
cell_width: 3.0,
|
|
|
|
cell_height: 3.0,
|
|
|
|
padding_x: 0.0,
|
|
|
|
padding_y: 0.0,
|
2018-11-10 16:08:48 +00:00
|
|
|
dpr: 1.0,
|
2018-10-22 19:39:26 +00:00
|
|
|
};
|
|
|
|
let mut term = Term::new(&Default::default(), size);
|
|
|
|
let mut grid: Grid<Cell> = Grid::new(Line(1), Column(15), 0, Cell::default());
|
|
|
|
grid[Line(0)][Column(0)].c = 'a';
|
|
|
|
grid[Line(0)][Column(1)].c = 'z';
|
|
|
|
grid[Line(0)][Column(2)].c = 'e';
|
|
|
|
mem::swap(&mut term.grid, &mut grid);
|
|
|
|
|
|
|
|
// Search for URL in grid
|
|
|
|
let url = term.url_search(Point::new(0, Column(1)));
|
|
|
|
|
|
|
|
assert_eq!(url, None);
|
|
|
|
}
|
2016-11-20 00:16:20 +00:00
|
|
|
}
|
2016-11-28 22:13:11 +00:00
|
|
|
|
2017-02-03 04:50:48 +00:00
|
|
|
#[cfg(all(test, feature = "bench"))]
|
2016-12-30 02:51:17 +00:00
|
|
|
mod benches {
|
2016-11-28 22:13:11 +00:00
|
|
|
extern crate test;
|
|
|
|
extern crate serde_json as json;
|
|
|
|
|
|
|
|
use std::io::Read;
|
|
|
|
use std::fs::File;
|
|
|
|
use std::mem;
|
|
|
|
use std::path::Path;
|
|
|
|
|
|
|
|
use grid::Grid;
|
2017-05-28 16:38:10 +00:00
|
|
|
use config::Config;
|
2016-11-28 22:13:11 +00:00
|
|
|
|
|
|
|
use super::{SizeInfo, Term};
|
|
|
|
use super::cell::Cell;
|
|
|
|
|
|
|
|
fn read_string<P>(path: P) -> String
|
|
|
|
where P: AsRef<Path>
|
|
|
|
{
|
|
|
|
let mut res = String::new();
|
|
|
|
File::open(path.as_ref()).unwrap()
|
|
|
|
.read_to_string(&mut res).unwrap();
|
|
|
|
|
|
|
|
res
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Benchmark for the renderable cells iterator
|
|
|
|
///
|
2016-12-17 06:48:04 +00:00
|
|
|
/// The renderable cells iterator yields cells that require work to be
|
|
|
|
/// displayed (that is, not a an empty background cell). This benchmark
|
|
|
|
/// measures how long it takes to process the whole iterator.
|
2016-11-28 22:13:11 +00:00
|
|
|
///
|
2016-12-17 06:48:04 +00:00
|
|
|
/// When this benchmark was first added, it averaged ~78usec on my macbook
|
|
|
|
/// pro. The total render time for this grid is anywhere between ~1500 and
|
|
|
|
/// ~2000usec (measured imprecisely with the visual meter).
|
2016-11-28 22:13:11 +00:00
|
|
|
#[bench]
|
|
|
|
fn render_iter(b: &mut test::Bencher) {
|
|
|
|
// Need some realistic grid state; using one of the ref files.
|
|
|
|
let serialized_grid = read_string(
|
|
|
|
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/ref/vim_large_window_scroll/grid.json")
|
|
|
|
);
|
|
|
|
let serialized_size = read_string(
|
|
|
|
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/ref/vim_large_window_scroll/size.json")
|
|
|
|
);
|
|
|
|
|
|
|
|
let mut grid: Grid<Cell> = json::from_str(&serialized_grid).unwrap();
|
|
|
|
let size: SizeInfo = json::from_str(&serialized_size).unwrap();
|
|
|
|
|
2017-05-28 16:38:10 +00:00
|
|
|
let config = Config::default();
|
|
|
|
|
|
|
|
let mut terminal = Term::new(&config, size);
|
2016-11-28 22:13:11 +00:00
|
|
|
mem::swap(&mut terminal.grid, &mut grid);
|
|
|
|
|
|
|
|
b.iter(|| {
|
2018-07-21 17:17:41 +00:00
|
|
|
let iter = terminal.renderable_cells(&config, false);
|
2016-11-28 22:13:11 +00:00
|
|
|
for cell in iter {
|
|
|
|
test::black_box(cell);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|