Remove `alacritty_config` from alacritty_terminal
There's no need to force alacritty's user configuration on other users of the crate, thus provide the options actually used by alacritty_terminal itself.
This commit is contained in:
parent
3ffd6c8f26
commit
5060f8eeb8
|
@ -38,7 +38,7 @@ dependencies = [
|
|||
"alacritty_config",
|
||||
"alacritty_config_derive",
|
||||
"alacritty_terminal",
|
||||
"bitflags 2.4.0",
|
||||
"bitflags 2.4.1",
|
||||
"clap",
|
||||
"clap_complete",
|
||||
"cocoa 0.25.0",
|
||||
|
@ -93,10 +93,8 @@ dependencies = [
|
|||
name = "alacritty_terminal"
|
||||
version = "0.20.0-dev"
|
||||
dependencies = [
|
||||
"alacritty_config",
|
||||
"alacritty_config_derive",
|
||||
"base64",
|
||||
"bitflags 2.4.0",
|
||||
"bitflags 2.4.1",
|
||||
"home",
|
||||
"libc",
|
||||
"log",
|
||||
|
@ -108,9 +106,7 @@ dependencies = [
|
|||
"rustix-openpty",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"signal-hook",
|
||||
"toml 0.8.2",
|
||||
"unicode-width",
|
||||
"vte",
|
||||
"windows-sys 0.48.0",
|
||||
|
@ -123,7 +119,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "052ad56e336bcc615a214bffbeca6c181ee9550acec193f0327e0b103b033a4d"
|
||||
dependencies = [
|
||||
"android-properties",
|
||||
"bitflags 2.4.0",
|
||||
"bitflags 2.4.1",
|
||||
"cc",
|
||||
"cesu8",
|
||||
"jni",
|
||||
|
@ -235,9 +231,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
|||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.4.0"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
|
||||
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
@ -291,7 +287,7 @@ version = "0.12.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b50b5a44d59a98c55a9eeb518f39bf7499ba19fd98ee7d22618687f3f10adbf"
|
||||
dependencies = [
|
||||
"bitflags 2.4.0",
|
||||
"bitflags 2.4.1",
|
||||
"log",
|
||||
"polling",
|
||||
"rustix",
|
||||
|
@ -886,7 +882,7 @@ version = "0.31.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eca18d477e18c996c1fd1a50e04c6a745b67e2d512c7fb51f2757d9486a0e3ee"
|
||||
dependencies = [
|
||||
"bitflags 2.4.0",
|
||||
"bitflags 2.4.1",
|
||||
"cfg_aliases",
|
||||
"cgl",
|
||||
"core-foundation",
|
||||
|
@ -1206,7 +1202,7 @@ version = "0.8.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7"
|
||||
dependencies = [
|
||||
"bitflags 2.4.0",
|
||||
"bitflags 2.4.1",
|
||||
"jni-sys",
|
||||
"log",
|
||||
"ndk-sys",
|
||||
|
@ -1259,7 +1255,7 @@ version = "6.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d"
|
||||
dependencies = [
|
||||
"bitflags 2.4.0",
|
||||
"bitflags 2.4.1",
|
||||
"crossbeam-channel",
|
||||
"filetime",
|
||||
"fsevent-sys",
|
||||
|
@ -1554,7 +1550,7 @@ version = "0.38.20"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67ce50cb2e16c2903e30d1cbccfd8387a74b9d4c938b6a4c5ec6cc7556f7a8a0"
|
||||
dependencies = [
|
||||
"bitflags 2.4.0",
|
||||
"bitflags 2.4.1",
|
||||
"errno",
|
||||
"itoa",
|
||||
"libc",
|
||||
|
@ -1738,7 +1734,7 @@ version = "0.18.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60e3d9941fa3bacf7c2bf4b065304faa14164151254cd16ce1b1bc8fc381600f"
|
||||
dependencies = [
|
||||
"bitflags 2.4.0",
|
||||
"bitflags 2.4.1",
|
||||
"calloop",
|
||||
"calloop-wayland-source",
|
||||
"cursor-icon",
|
||||
|
@ -1983,7 +1979,7 @@ version = "0.12.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "401dc1020e10f74d38616c1f1ab92ccd85dc902705a29d0730e0fbea8534f91a"
|
||||
dependencies = [
|
||||
"bitflags 2.4.0",
|
||||
"bitflags 2.4.1",
|
||||
"cursor-icon",
|
||||
"log",
|
||||
"serde",
|
||||
|
@ -2103,7 +2099,7 @@ version = "0.31.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ca7d52347346f5473bf2f56705f360e8440873052e575e55890c4fa57843ed3"
|
||||
dependencies = [
|
||||
"bitflags 2.4.0",
|
||||
"bitflags 2.4.1",
|
||||
"nix",
|
||||
"wayland-backend",
|
||||
"wayland-scanner",
|
||||
|
@ -2115,7 +2111,7 @@ version = "0.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e"
|
||||
dependencies = [
|
||||
"bitflags 2.4.0",
|
||||
"bitflags 2.4.1",
|
||||
"cursor-icon",
|
||||
"wayland-backend",
|
||||
]
|
||||
|
@ -2137,7 +2133,7 @@ version = "0.31.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e253d7107ba913923dc253967f35e8561a3c65f914543e46843c88ddd729e21c"
|
||||
dependencies = [
|
||||
"bitflags 2.4.0",
|
||||
"bitflags 2.4.1",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
"wayland-scanner",
|
||||
|
@ -2149,7 +2145,7 @@ version = "0.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23803551115ff9ea9bce586860c5c5a971e360825a0309264102a9495a5ff479"
|
||||
dependencies = [
|
||||
"bitflags 2.4.0",
|
||||
"bitflags 2.4.1",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
"wayland-protocols",
|
||||
|
@ -2162,7 +2158,7 @@ version = "0.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6"
|
||||
dependencies = [
|
||||
"bitflags 2.4.0",
|
||||
"bitflags 2.4.1",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
"wayland-protocols",
|
||||
|
@ -2394,7 +2390,7 @@ dependencies = [
|
|||
"ahash",
|
||||
"android-activity",
|
||||
"atomic-waker",
|
||||
"bitflags 2.4.0",
|
||||
"bitflags 2.4.1",
|
||||
"bytemuck",
|
||||
"calloop",
|
||||
"cfg_aliases",
|
||||
|
@ -2529,7 +2525,7 @@ version = "0.4.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6924668544c48c0133152e7eec86d644a056ca3d09275eb8d5cdb9855f9d8699"
|
||||
dependencies = [
|
||||
"bitflags 2.4.0",
|
||||
"bitflags 2.4.1",
|
||||
"dlib",
|
||||
"log",
|
||||
"once_cell",
|
||||
|
|
|
@ -12,7 +12,6 @@ rust-version = "1.70.0"
|
|||
[dependencies.alacritty_terminal]
|
||||
path = "../alacritty_terminal"
|
||||
version = "0.20.0-dev"
|
||||
default-features = false
|
||||
|
||||
[dependencies.alacritty_config_derive]
|
||||
path = "../alacritty_config_derive"
|
||||
|
|
|
@ -6,8 +6,9 @@ use log::{self, error, LevelFilter};
|
|||
use serde::{Deserialize, Serialize};
|
||||
use toml::{Table, Value};
|
||||
|
||||
use alacritty_terminal::config::{Program, PtyConfig};
|
||||
use alacritty_terminal::tty::Options as PtyOptions;
|
||||
|
||||
use crate::config::ui_config::Program;
|
||||
use crate::config::window::{Class, Identity};
|
||||
use crate::config::{serde_utils, UiConfig};
|
||||
|
||||
|
@ -178,8 +179,8 @@ impl TerminalOptions {
|
|||
Some(Program::WithArgs { program: program.clone(), args: args.to_vec() })
|
||||
}
|
||||
|
||||
/// Override the [`PtyConfig`]'s fields with the [`TerminalOptions`].
|
||||
pub fn override_pty_config(&self, pty_config: &mut PtyConfig) {
|
||||
/// Override the [`PtyOptions`]'s fields with the [`TerminalOptions`].
|
||||
pub fn override_pty_config(&self, pty_config: &mut PtyOptions) {
|
||||
if let Some(working_directory) = &self.working_directory {
|
||||
if working_directory.is_dir() {
|
||||
pty_config.working_directory = Some(working_directory.to_owned());
|
||||
|
@ -189,18 +190,18 @@ impl TerminalOptions {
|
|||
}
|
||||
|
||||
if let Some(command) = self.command() {
|
||||
pty_config.shell = Some(command);
|
||||
pty_config.shell = Some(command.into());
|
||||
}
|
||||
|
||||
pty_config.hold |= self.hold;
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TerminalOptions> for PtyConfig {
|
||||
impl From<TerminalOptions> for PtyOptions {
|
||||
fn from(mut options: TerminalOptions) -> Self {
|
||||
PtyConfig {
|
||||
PtyOptions {
|
||||
working_directory: options.working_directory.take(),
|
||||
shell: options.command(),
|
||||
shell: options.command().map(Into::into),
|
||||
hold: options.hold,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,8 @@ use std::time::Duration;
|
|||
|
||||
use alacritty_config_derive::ConfigDeserialize;
|
||||
|
||||
use alacritty_terminal::config::Program;
|
||||
use alacritty_terminal::term::color::Rgb;
|
||||
use crate::config::ui_config::Program;
|
||||
use crate::display::color::Rgb;
|
||||
|
||||
#[derive(ConfigDeserialize, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct BellConfig {
|
||||
|
|
|
@ -14,11 +14,10 @@ use winit::platform::scancode::PhysicalKeyExtScancode;
|
|||
|
||||
use alacritty_config_derive::{ConfigDeserialize, SerdeReplace};
|
||||
|
||||
use alacritty_terminal::config::Program;
|
||||
use alacritty_terminal::term::TermMode;
|
||||
use alacritty_terminal::vi_mode::ViMotion;
|
||||
|
||||
use crate::config::ui_config::Hint;
|
||||
use crate::config::ui_config::{Hint, Program, StringVisitor};
|
||||
|
||||
/// Describes a state and action to take in that state.
|
||||
///
|
||||
|
@ -1103,8 +1102,9 @@ impl<'a> Deserialize<'a> for RawBinding {
|
|||
|
||||
action = if let Ok(vi_action) = ViAction::deserialize(value.clone()) {
|
||||
Some(vi_action.into())
|
||||
} else if let Ok(vi_motion) = ViMotion::deserialize(value.clone()) {
|
||||
Some(vi_motion.into())
|
||||
} else if let Ok(vi_motion) = SerdeViMotion::deserialize(value.clone())
|
||||
{
|
||||
Some(vi_motion.0.into())
|
||||
} else if let Ok(search_action) =
|
||||
SearchAction::deserialize(value.clone())
|
||||
{
|
||||
|
@ -1213,6 +1213,21 @@ impl<'a> Deserialize<'a> for KeyBinding {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(SerdeReplace, Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct SerdeViMotion(ViMotion);
|
||||
|
||||
impl<'de> Deserialize<'de> for SerdeViMotion {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let value = deserializer.deserialize_str(StringVisitor)?;
|
||||
ViMotion::deserialize(SerdeValue::String(value))
|
||||
.map(SerdeViMotion)
|
||||
.map_err(de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
/// Newtype for implementing deserialize on winit Mods.
|
||||
///
|
||||
/// Our deserialize impl wouldn't be covered by a derive(Deserialize); see the
|
||||
|
|
|
@ -2,7 +2,8 @@ use serde::de::Error as SerdeError;
|
|||
use serde::{Deserialize, Deserializer};
|
||||
|
||||
use alacritty_config_derive::ConfigDeserialize;
|
||||
use alacritty_terminal::term::color::{CellRgb, Rgb};
|
||||
|
||||
use crate::display::color::{CellRgb, Rgb};
|
||||
|
||||
#[derive(ConfigDeserialize, Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub struct Colors {
|
||||
|
|
|
@ -0,0 +1,156 @@
|
|||
use std::cmp;
|
||||
use std::time::Duration;
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
use alacritty_config_derive::{ConfigDeserialize, SerdeReplace};
|
||||
use alacritty_terminal::vte::ansi::{CursorShape as VteCursorShape, CursorStyle as VteCursorStyle};
|
||||
|
||||
use crate::config::ui_config::Percentage;
|
||||
|
||||
/// The minimum blink interval value in milliseconds.
|
||||
const MIN_BLINK_INTERVAL: u64 = 10;
|
||||
|
||||
/// The minimum number of blinks before pausing.
|
||||
const MIN_BLINK_CYCLES_BEFORE_PAUSE: u64 = 1;
|
||||
|
||||
#[derive(ConfigDeserialize, Copy, Clone, Debug, PartialEq)]
|
||||
pub struct Cursor {
|
||||
pub style: ConfigCursorStyle,
|
||||
pub vi_mode_style: Option<ConfigCursorStyle>,
|
||||
pub unfocused_hollow: bool,
|
||||
|
||||
thickness: Percentage,
|
||||
blink_interval: u64,
|
||||
blink_timeout: u8,
|
||||
}
|
||||
|
||||
impl Default for Cursor {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
thickness: Percentage::new(0.15),
|
||||
unfocused_hollow: true,
|
||||
blink_interval: 750,
|
||||
blink_timeout: 5,
|
||||
style: Default::default(),
|
||||
vi_mode_style: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Cursor {
|
||||
#[inline]
|
||||
pub fn thickness(self) -> f32 {
|
||||
self.thickness.as_f32()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn style(self) -> VteCursorStyle {
|
||||
self.style.into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn vi_mode_style(self) -> Option<VteCursorStyle> {
|
||||
self.vi_mode_style.map(Into::into)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn blink_interval(self) -> u64 {
|
||||
cmp::max(self.blink_interval, MIN_BLINK_INTERVAL)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn blink_timeout(self) -> Duration {
|
||||
if self.blink_timeout == 0 {
|
||||
Duration::ZERO
|
||||
} else {
|
||||
cmp::max(
|
||||
// Show/hide is what we consider a cycle, so multiply by `2`.
|
||||
Duration::from_millis(self.blink_interval * 2 * MIN_BLINK_CYCLES_BEFORE_PAUSE),
|
||||
Duration::from_secs(self.blink_timeout as u64),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(SerdeReplace, Deserialize, Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[serde(untagged, deny_unknown_fields)]
|
||||
pub enum ConfigCursorStyle {
|
||||
Shape(CursorShape),
|
||||
WithBlinking {
|
||||
#[serde(default)]
|
||||
shape: CursorShape,
|
||||
#[serde(default)]
|
||||
blinking: CursorBlinking,
|
||||
},
|
||||
}
|
||||
|
||||
impl Default for ConfigCursorStyle {
|
||||
fn default() -> Self {
|
||||
Self::Shape(CursorShape::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl ConfigCursorStyle {
|
||||
/// Check if blinking is force enabled/disabled.
|
||||
pub fn blinking_override(&self) -> Option<bool> {
|
||||
match self {
|
||||
Self::Shape(_) => None,
|
||||
Self::WithBlinking { blinking, .. } => blinking.blinking_override(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConfigCursorStyle> for VteCursorStyle {
|
||||
fn from(config_style: ConfigCursorStyle) -> Self {
|
||||
match config_style {
|
||||
ConfigCursorStyle::Shape(shape) => Self { shape: shape.into(), blinking: false },
|
||||
ConfigCursorStyle::WithBlinking { shape, blinking } => {
|
||||
Self { shape: shape.into(), blinking: blinking.into() }
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(ConfigDeserialize, Default, Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum CursorBlinking {
|
||||
Never,
|
||||
#[default]
|
||||
Off,
|
||||
On,
|
||||
Always,
|
||||
}
|
||||
|
||||
impl CursorBlinking {
|
||||
fn blinking_override(&self) -> Option<bool> {
|
||||
match self {
|
||||
Self::Never => Some(false),
|
||||
Self::Off | Self::On => None,
|
||||
Self::Always => Some(true),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CursorBlinking> for bool {
|
||||
fn from(blinking: CursorBlinking) -> bool {
|
||||
blinking == CursorBlinking::On || blinking == CursorBlinking::Always
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(ConfigDeserialize, Debug, Default, Eq, PartialEq, Copy, Clone, Hash)]
|
||||
pub enum CursorShape {
|
||||
#[default]
|
||||
Block,
|
||||
Underline,
|
||||
Beam,
|
||||
}
|
||||
|
||||
impl From<CursorShape> for VteCursorShape {
|
||||
fn from(value: CursorShape) -> Self {
|
||||
match value {
|
||||
CursorShape::Block => VteCursorShape::Block,
|
||||
CursorShape::Underline => VteCursorShape::Underline,
|
||||
CursorShape::Beam => VteCursorShape::Beam,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,14 +10,16 @@ use toml::de::Error as TomlError;
|
|||
use toml::ser::Error as TomlSeError;
|
||||
use toml::{Table, Value};
|
||||
|
||||
use alacritty_terminal::config::LOG_TARGET_CONFIG;
|
||||
|
||||
pub mod bell;
|
||||
pub mod color;
|
||||
pub mod cursor;
|
||||
pub mod debug;
|
||||
pub mod font;
|
||||
pub mod monitor;
|
||||
pub mod scrolling;
|
||||
pub mod selection;
|
||||
pub mod serde_utils;
|
||||
pub mod terminal;
|
||||
pub mod ui_config;
|
||||
pub mod window;
|
||||
|
||||
|
@ -31,6 +33,7 @@ pub use crate::config::bindings::{
|
|||
Action, BindingKey, BindingMode, MouseAction, SearchAction, ViAction,
|
||||
};
|
||||
pub use crate::config::ui_config::UiConfig;
|
||||
use crate::logging::LOG_TARGET_CONFIG;
|
||||
|
||||
/// Maximum number of depth for the configuration file imports.
|
||||
pub const IMPORT_RECURSION_LIMIT: usize = 5;
|
||||
|
|
|
@ -24,11 +24,6 @@ impl Scrolling {
|
|||
pub fn history(self) -> u32 {
|
||||
self.history.0
|
||||
}
|
||||
|
||||
// Update the history size, used in ref tests.
|
||||
pub fn set_history(&mut self, history: u32) {
|
||||
self.history = ScrollingHistory(history);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(SerdeReplace, Copy, Clone, Debug, PartialEq, Eq)]
|
|
@ -0,0 +1,17 @@
|
|||
use alacritty_config_derive::ConfigDeserialize;
|
||||
use alacritty_terminal::term::SEMANTIC_ESCAPE_CHARS;
|
||||
|
||||
#[derive(ConfigDeserialize, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Selection {
|
||||
pub semantic_escape_chars: String,
|
||||
pub save_to_clipboard: bool,
|
||||
}
|
||||
|
||||
impl Default for Selection {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
semantic_escape_chars: SEMANTIC_ESCAPE_CHARS.to_owned(),
|
||||
save_to_clipboard: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
use serde::{de, Deserialize, Deserializer};
|
||||
use toml::Value;
|
||||
|
||||
use alacritty_config_derive::{ConfigDeserialize, SerdeReplace};
|
||||
use alacritty_terminal::term::Osc52;
|
||||
|
||||
use crate::config::ui_config::StringVisitor;
|
||||
|
||||
#[derive(ConfigDeserialize, Default, Copy, Clone, Debug, PartialEq)]
|
||||
pub struct Terminal {
|
||||
/// OSC52 support mode.
|
||||
pub osc52: SerdeOsc52,
|
||||
}
|
||||
|
||||
#[derive(SerdeReplace, Default, Copy, Clone, Debug, PartialEq)]
|
||||
pub struct SerdeOsc52(pub Osc52);
|
||||
|
||||
impl<'de> Deserialize<'de> for SerdeOsc52 {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let value = deserializer.deserialize_str(StringVisitor)?;
|
||||
Osc52::deserialize(Value::String(value)).map(SerdeOsc52).map_err(de::Error::custom)
|
||||
}
|
||||
}
|
|
@ -1,8 +1,11 @@
|
|||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::{self, Formatter};
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
|
||||
use alacritty_terminal::term::Config as TermConfig;
|
||||
use alacritty_terminal::tty::{Options as PtyOptions, Shell};
|
||||
use log::{error, warn};
|
||||
use serde::de::{Error as SerdeError, MapAccess, Visitor};
|
||||
use serde::{self, Deserialize, Deserializer};
|
||||
|
@ -10,7 +13,6 @@ use unicode_width::UnicodeWidthChar;
|
|||
use winit::keyboard::{Key, KeyLocation, ModifiersState};
|
||||
|
||||
use alacritty_config_derive::{ConfigDeserialize, SerdeReplace};
|
||||
use alacritty_terminal::config::{Config as TerminalConfig, Program, LOG_TARGET_CONFIG};
|
||||
use alacritty_terminal::term::search::RegexSearch;
|
||||
|
||||
use crate::config::bell::BellConfig;
|
||||
|
@ -18,10 +20,15 @@ use crate::config::bindings::{
|
|||
self, Action, Binding, BindingKey, KeyBinding, ModeWrapper, ModsWrapper, MouseBinding,
|
||||
};
|
||||
use crate::config::color::Colors;
|
||||
use crate::config::cursor::Cursor;
|
||||
use crate::config::debug::Debug;
|
||||
use crate::config::font::Font;
|
||||
use crate::config::mouse::{Mouse, MouseBindings};
|
||||
use crate::config::scrolling::Scrolling;
|
||||
use crate::config::selection::Selection;
|
||||
use crate::config::terminal::Terminal;
|
||||
use crate::config::window::WindowConfig;
|
||||
use crate::config::LOG_TARGET_CONFIG;
|
||||
|
||||
/// Regex used for the default URL hint.
|
||||
#[rustfmt::skip]
|
||||
|
@ -30,6 +37,18 @@ const URL_REGEX: &str = "(ipfs:|ipns:|magnet:|mailto:|gemini://|gopher://|https:
|
|||
|
||||
#[derive(ConfigDeserialize, Clone, Debug, PartialEq)]
|
||||
pub struct UiConfig {
|
||||
/// Extra environment variables.
|
||||
pub env: HashMap<String, String>,
|
||||
|
||||
/// How much scrolling history to keep.
|
||||
pub scrolling: Scrolling,
|
||||
|
||||
/// Cursor configuration.
|
||||
pub cursor: Cursor,
|
||||
|
||||
/// Selection configuration.
|
||||
pub selection: Selection,
|
||||
|
||||
/// Font configuration.
|
||||
pub font: Font,
|
||||
|
||||
|
@ -68,8 +87,13 @@ pub struct UiConfig {
|
|||
pub ipc_socket: bool,
|
||||
|
||||
/// Config for the alacritty_terminal itself.
|
||||
#[config(flatten)]
|
||||
pub terminal_config: TerminalConfig,
|
||||
pub terminal: Terminal,
|
||||
|
||||
/// Path to a shell program to run on startup.
|
||||
pub shell: Option<Program>,
|
||||
|
||||
/// Shell startup directory.
|
||||
pub working_directory: Option<PathBuf>,
|
||||
|
||||
/// Keyboard configuration.
|
||||
keyboard: Keyboard,
|
||||
|
@ -100,25 +124,48 @@ impl Default for UiConfig {
|
|||
#[cfg(unix)]
|
||||
ipc_socket: true,
|
||||
draw_bold_text_with_bright_colors: Default::default(),
|
||||
terminal_config: Default::default(),
|
||||
working_directory: Default::default(),
|
||||
mouse_bindings: Default::default(),
|
||||
config_paths: Default::default(),
|
||||
key_bindings: Default::default(),
|
||||
alt_send_esc: Default::default(),
|
||||
scrolling: Default::default(),
|
||||
selection: Default::default(),
|
||||
keyboard: Default::default(),
|
||||
terminal: Default::default(),
|
||||
import: Default::default(),
|
||||
cursor: Default::default(),
|
||||
window: Default::default(),
|
||||
colors: Default::default(),
|
||||
shell: Default::default(),
|
||||
mouse: Default::default(),
|
||||
debug: Default::default(),
|
||||
hints: Default::default(),
|
||||
font: Default::default(),
|
||||
bell: Default::default(),
|
||||
env: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UiConfig {
|
||||
/// Derive [`TermConfig`] from the config.
|
||||
pub fn term_options(&self) -> TermConfig {
|
||||
TermConfig {
|
||||
scrolling_history: self.scrolling.history() as usize,
|
||||
default_cursor_style: self.cursor.style(),
|
||||
vi_mode_cursor_style: self.cursor.vi_mode_style(),
|
||||
semantic_escape_chars: self.selection.semantic_escape_chars.clone(),
|
||||
osc52: self.terminal.osc52.0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Derive [`PtyOptions`] from the config.
|
||||
pub fn pty_config(&self) -> PtyOptions {
|
||||
let shell = self.shell.clone().map(Into::into);
|
||||
PtyOptions { shell, working_directory: self.working_directory.clone(), hold: false }
|
||||
}
|
||||
|
||||
/// Generate key bindings for all keyboard hints.
|
||||
pub fn generate_hint_bindings(&mut self) {
|
||||
// Check which key bindings is most likely to be the user's configuration.
|
||||
|
@ -551,6 +598,78 @@ impl PartialEq for LazyRegexVariant {
|
|||
}
|
||||
impl Eq for LazyRegexVariant {}
|
||||
|
||||
/// Wrapper around f32 that represents a percentage value between 0.0 and 1.0.
|
||||
#[derive(SerdeReplace, Deserialize, Clone, Copy, Debug, PartialEq)]
|
||||
pub struct Percentage(f32);
|
||||
|
||||
impl Default for Percentage {
|
||||
fn default() -> Self {
|
||||
Percentage(1.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Percentage {
|
||||
pub fn new(value: f32) -> Self {
|
||||
Percentage(value.clamp(0., 1.))
|
||||
}
|
||||
|
||||
pub fn as_f32(self) -> f32 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
#[serde(untagged, deny_unknown_fields)]
|
||||
pub enum Program {
|
||||
Just(String),
|
||||
WithArgs {
|
||||
program: String,
|
||||
#[serde(default)]
|
||||
args: Vec<String>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Program {
|
||||
pub fn program(&self) -> &str {
|
||||
match self {
|
||||
Program::Just(program) => program,
|
||||
Program::WithArgs { program, .. } => program,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn args(&self) -> &[String] {
|
||||
match self {
|
||||
Program::Just(_) => &[],
|
||||
Program::WithArgs { args, .. } => args,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Program> for Shell {
|
||||
fn from(value: Program) -> Self {
|
||||
match value {
|
||||
Program::Just(program) => Shell::new(program, Vec::new()),
|
||||
Program::WithArgs { program, args } => Shell::new(program, args),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct StringVisitor;
|
||||
impl<'de> serde::de::Visitor<'de> for StringVisitor {
|
||||
type Value = String;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
formatter.write_str("a string")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Ok(s.to_lowercase())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
@ -9,10 +9,9 @@ use winit::window::{Fullscreen, Theme};
|
|||
use winit::platform::macos::OptionAsAlt as WinitOptionAsAlt;
|
||||
|
||||
use alacritty_config_derive::{ConfigDeserialize, SerdeReplace};
|
||||
use alacritty_terminal::config::{Percentage, LOG_TARGET_CONFIG};
|
||||
use alacritty_terminal::index::Column;
|
||||
|
||||
use crate::config::ui_config::Delta;
|
||||
use crate::config::ui_config::{Delta, Percentage};
|
||||
use crate::config::LOG_TARGET_CONFIG;
|
||||
|
||||
/// Default Alacritty name, used for window title and class.
|
||||
pub const DEFAULT_NAME: &str = "Alacritty";
|
||||
|
@ -90,7 +89,7 @@ impl Default for WindowConfig {
|
|||
impl WindowConfig {
|
||||
#[inline]
|
||||
pub fn dimensions(&self) -> Option<Dimensions> {
|
||||
let (lines, columns) = (self.dimensions.lines, self.dimensions.columns.0);
|
||||
let (lines, columns) = (self.dimensions.lines, self.dimensions.columns);
|
||||
let (lines_is_non_zero, columns_is_non_zero) = (lines != 0, columns != 0);
|
||||
|
||||
if lines_is_non_zero && columns_is_non_zero {
|
||||
|
@ -194,7 +193,7 @@ pub enum Decorations {
|
|||
#[derive(ConfigDeserialize, Default, Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Dimensions {
|
||||
/// Window width in character columns.
|
||||
pub columns: Column,
|
||||
pub columns: usize,
|
||||
|
||||
/// Window Height in character lines.
|
||||
pub lines: usize,
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
use std::ops::{Index, IndexMut};
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::ops::{Add, Deref, Index, IndexMut, Mul};
|
||||
use std::str::FromStr;
|
||||
|
||||
use log::trace;
|
||||
use serde::de::{Error as SerdeError, Visitor};
|
||||
use serde::{Deserialize, Deserializer};
|
||||
|
||||
use alacritty_terminal::ansi::NamedColor;
|
||||
use alacritty_terminal::term::color::{Rgb, COUNT};
|
||||
use alacritty_config_derive::SerdeReplace;
|
||||
use alacritty_terminal::term::color::COUNT;
|
||||
use alacritty_terminal::vte::ansi::{NamedColor, Rgb as VteRgb};
|
||||
|
||||
use crate::config::color::Colors;
|
||||
|
||||
|
@ -165,3 +170,191 @@ impl IndexMut<NamedColor> for List {
|
|||
&mut self.0[idx as usize]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(SerdeReplace, Debug, Eq, PartialEq, Copy, Clone, Default)]
|
||||
pub struct Rgb(pub VteRgb);
|
||||
|
||||
impl Rgb {
|
||||
#[inline]
|
||||
pub const fn new(r: u8, g: u8, b: u8) -> Self {
|
||||
Self(VteRgb { r, g, b })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_tuple(self) -> (u8, u8, u8) {
|
||||
(self.0.r, self.0.g, self.0.b)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<VteRgb> for Rgb {
|
||||
fn from(value: VteRgb) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Rgb {
|
||||
type Target = VteRgb;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<f32> for Rgb {
|
||||
type Output = Rgb;
|
||||
|
||||
fn mul(self, rhs: f32) -> Self::Output {
|
||||
Rgb(self.0 * rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<Rgb> for Rgb {
|
||||
type Output = Rgb;
|
||||
|
||||
fn add(self, rhs: Rgb) -> Self::Output {
|
||||
Rgb(self.0 + rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Deserialize an Rgb from a hex string.
|
||||
///
|
||||
/// This is *not* the deserialize impl for Rgb since we want a symmetric
|
||||
/// serialize/deserialize impl for ref tests.
|
||||
impl<'de> Deserialize<'de> for Rgb {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct RgbVisitor;
|
||||
|
||||
// Used for deserializing reftests.
|
||||
#[derive(Deserialize)]
|
||||
struct RgbDerivedDeser {
|
||||
r: u8,
|
||||
g: u8,
|
||||
b: u8,
|
||||
}
|
||||
|
||||
impl<'a> Visitor<'a> for RgbVisitor {
|
||||
type Value = Rgb;
|
||||
|
||||
fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("hex color like #ff00ff")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Rgb, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Rgb::from_str(value).map_err(|_| {
|
||||
E::custom(format!(
|
||||
"failed to parse rgb color {value}; expected hex color like #ff00ff"
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Return an error if the syntax is incorrect.
|
||||
let value = toml::Value::deserialize(deserializer)?;
|
||||
|
||||
// Attempt to deserialize from struct form.
|
||||
if let Ok(RgbDerivedDeser { r, g, b }) = RgbDerivedDeser::deserialize(value.clone()) {
|
||||
return Ok(Rgb::new(r, g, b));
|
||||
}
|
||||
|
||||
// Deserialize from hex notation (either 0xff00ff or #ff00ff).
|
||||
value.deserialize_str(RgbVisitor).map_err(D::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Rgb {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "#{:02x}{:02x}{:02x}", self.r, self.g, self.b)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Rgb {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<Rgb, ()> {
|
||||
let chars = if s.starts_with("0x") && s.len() == 8 {
|
||||
&s[2..]
|
||||
} else if s.starts_with('#') && s.len() == 7 {
|
||||
&s[1..]
|
||||
} else {
|
||||
return Err(());
|
||||
};
|
||||
|
||||
match u32::from_str_radix(chars, 16) {
|
||||
Ok(mut color) => {
|
||||
let b = (color & 0xff) as u8;
|
||||
color >>= 8;
|
||||
let g = (color & 0xff) as u8;
|
||||
color >>= 8;
|
||||
let r = color as u8;
|
||||
Ok(Rgb::new(r, g, b))
|
||||
},
|
||||
Err(_) => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// RGB color optionally referencing the cell's foreground or background.
|
||||
#[derive(SerdeReplace, Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum CellRgb {
|
||||
CellForeground,
|
||||
CellBackground,
|
||||
Rgb(Rgb),
|
||||
}
|
||||
|
||||
impl CellRgb {
|
||||
pub fn color(self, foreground: Rgb, background: Rgb) -> Rgb {
|
||||
match self {
|
||||
Self::CellForeground => foreground,
|
||||
Self::CellBackground => background,
|
||||
Self::Rgb(rgb) => rgb,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for CellRgb {
|
||||
fn default() -> Self {
|
||||
Self::Rgb(Rgb::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for CellRgb {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
const EXPECTING: &str = "CellForeground, CellBackground, or hex color like #ff00ff";
|
||||
|
||||
struct CellRgbVisitor;
|
||||
impl<'a> Visitor<'a> for CellRgbVisitor {
|
||||
type Value = CellRgb;
|
||||
|
||||
fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(EXPECTING)
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<CellRgb, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
// Attempt to deserialize as enum constants.
|
||||
match value {
|
||||
"CellForeground" => return Ok(CellRgb::CellForeground),
|
||||
"CellBackground" => return Ok(CellRgb::CellBackground),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
Rgb::from_str(value).map(CellRgb::Rgb).map_err(|_| {
|
||||
E::custom(format!("failed to parse color {value}; expected {EXPECTING}"))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_str(CellRgbVisitor).map_err(D::Error::custom)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,18 +2,17 @@ use std::borrow::Cow;
|
|||
use std::ops::Deref;
|
||||
use std::{cmp, mem};
|
||||
|
||||
use alacritty_terminal::ansi::{Color, CursorShape, NamedColor};
|
||||
use alacritty_terminal::event::EventListener;
|
||||
use alacritty_terminal::grid::{Dimensions, Indexed};
|
||||
use alacritty_terminal::index::{Column, Line, Point};
|
||||
use alacritty_terminal::selection::SelectionRange;
|
||||
use alacritty_terminal::term::cell::{Cell, Flags, Hyperlink};
|
||||
use alacritty_terminal::term::color::{CellRgb, Rgb};
|
||||
use alacritty_terminal::term::search::{Match, RegexSearch};
|
||||
use alacritty_terminal::term::{self, RenderableContent as TerminalContent, Term, TermMode};
|
||||
use alacritty_terminal::vte::ansi::{Color, CursorShape, NamedColor};
|
||||
|
||||
use crate::config::UiConfig;
|
||||
use crate::display::color::{List, DIM_FACTOR};
|
||||
use crate::display::color::{CellRgb, List, Rgb, DIM_FACTOR};
|
||||
use crate::display::hint::{self, HintState};
|
||||
use crate::display::{Display, SizeInfo};
|
||||
use crate::event::SearchState;
|
||||
|
@ -55,7 +54,7 @@ impl<'a> RenderableContent<'a> {
|
|||
|| display.ime.preedit().is_some()
|
||||
{
|
||||
CursorShape::Hidden
|
||||
} else if !term.is_focused && config.terminal_config.cursor.unfocused_hollow {
|
||||
} else if !term.is_focused && config.cursor.unfocused_hollow {
|
||||
CursorShape::HollowBlock
|
||||
} else {
|
||||
terminal_content.cursor.shape
|
||||
|
@ -102,7 +101,7 @@ impl<'a> RenderableContent<'a> {
|
|||
|
||||
/// Get the RGB value for a color index.
|
||||
pub fn color(&self, color: usize) -> Rgb {
|
||||
self.terminal_content.colors[color].unwrap_or(self.colors[color])
|
||||
self.terminal_content.colors[color].map(Rgb).unwrap_or(self.colors[color])
|
||||
}
|
||||
|
||||
pub fn selection_range(&self) -> Option<SelectionRange> {
|
||||
|
@ -117,8 +116,8 @@ impl<'a> RenderableContent<'a> {
|
|||
} else {
|
||||
self.config.colors.cursor
|
||||
};
|
||||
let cursor_color =
|
||||
self.terminal_content.colors[NamedColor::Cursor].map_or(color.background, CellRgb::Rgb);
|
||||
let cursor_color = self.terminal_content.colors[NamedColor::Cursor]
|
||||
.map_or(color.background, |c| CellRgb::Rgb(Rgb(c)));
|
||||
let text_color = color.foreground;
|
||||
|
||||
let insufficient_contrast = (!matches!(cursor_color, CellRgb::Rgb(_))
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
//! Convert a cursor into an iterator of rects.
|
||||
|
||||
use alacritty_terminal::ansi::CursorShape;
|
||||
use alacritty_terminal::term::color::Rgb;
|
||||
use alacritty_terminal::vte::ansi::CursorShape;
|
||||
|
||||
use crate::display::color::Rgb;
|
||||
use crate::display::content::RenderableCursor;
|
||||
use crate::display::SizeInfo;
|
||||
use crate::renderer::rects::RenderRect;
|
||||
|
|
|
@ -590,9 +590,9 @@ impl<'a, T> Iterator for HintPostProcessor<'a, T> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use alacritty_terminal::ansi::Handler;
|
||||
use alacritty_terminal::index::{Column, Line};
|
||||
use alacritty_terminal::term::test::mock_term;
|
||||
use alacritty_terminal::vte::ansi::Handler;
|
||||
|
||||
use super::*;
|
||||
|
||||
|
|
|
@ -23,23 +23,22 @@ use winit::window::CursorIcon;
|
|||
use crossfont::{self, Rasterize, Rasterizer};
|
||||
use unicode_width::UnicodeWidthChar;
|
||||
|
||||
use alacritty_terminal::ansi::{CursorShape, NamedColor};
|
||||
use alacritty_terminal::config::MAX_SCROLLBACK_LINES;
|
||||
use alacritty_terminal::event::{EventListener, OnResize, WindowSize};
|
||||
use alacritty_terminal::grid::Dimensions as TermDimensions;
|
||||
use alacritty_terminal::index::{Column, Direction, Line, Point};
|
||||
use alacritty_terminal::selection::{Selection, SelectionRange};
|
||||
use alacritty_terminal::term::cell::Flags;
|
||||
use alacritty_terminal::term::color::Rgb;
|
||||
use alacritty_terminal::term::{self, Term, TermDamage, TermMode, MIN_COLUMNS, MIN_SCREEN_LINES};
|
||||
use alacritty_terminal::vte::ansi::{CursorShape, NamedColor};
|
||||
|
||||
use crate::config::font::Font;
|
||||
use crate::config::scrolling::MAX_SCROLLBACK_LINES;
|
||||
use crate::config::window::Dimensions;
|
||||
#[cfg(not(windows))]
|
||||
use crate::config::window::StartupMode;
|
||||
use crate::config::UiConfig;
|
||||
use crate::display::bell::VisualBell;
|
||||
use crate::display::color::List;
|
||||
use crate::display::color::{List, Rgb};
|
||||
use crate::display::content::{RenderableContent, RenderableCursor};
|
||||
use crate::display::cursor::IntoRects;
|
||||
use crate::display::damage::RenderDamageIterator;
|
||||
|
@ -53,13 +52,13 @@ use crate::renderer::{self, GlyphCache, Renderer};
|
|||
use crate::scheduler::{Scheduler, TimerId, Topic};
|
||||
use crate::string::{ShortenDirection, StrShortener};
|
||||
|
||||
pub mod color;
|
||||
pub mod content;
|
||||
pub mod cursor;
|
||||
pub mod hint;
|
||||
pub mod window;
|
||||
|
||||
mod bell;
|
||||
mod color;
|
||||
mod damage;
|
||||
mod meter;
|
||||
|
||||
|
@ -862,7 +861,7 @@ impl Display {
|
|||
};
|
||||
|
||||
// Draw cursor.
|
||||
rects.extend(cursor.rects(&size_info, config.terminal_config.cursor.thickness()));
|
||||
rects.extend(cursor.rects(&size_info, config.cursor.thickness()));
|
||||
|
||||
// Push visual bell after url/underline/strikeout rects.
|
||||
let visual_bell_intensity = self.visual_bell.intensity();
|
||||
|
@ -900,9 +899,7 @@ impl Display {
|
|||
let fg = config.colors.footer_bar_foreground();
|
||||
let shape = CursorShape::Underline;
|
||||
let cursor = RenderableCursor::new(Point::new(line, column), shape, fg, false);
|
||||
rects.extend(
|
||||
cursor.rects(&size_info, config.terminal_config.cursor.thickness()),
|
||||
);
|
||||
rects.extend(cursor.rects(&size_info, config.cursor.thickness()));
|
||||
}
|
||||
|
||||
Some(Point::new(line, column))
|
||||
|
@ -1144,9 +1141,7 @@ impl Display {
|
|||
let cursor_point = Point::new(point.line, cursor_column);
|
||||
let cursor =
|
||||
RenderableCursor::new(cursor_point, CursorShape::HollowBlock, fg, is_wide);
|
||||
rects.extend(
|
||||
cursor.rects(&self.size_info, config.terminal_config.cursor.thickness()),
|
||||
);
|
||||
rects.extend(cursor.rects(&self.size_info, config.cursor.thickness()));
|
||||
cursor_point
|
||||
},
|
||||
_ => end,
|
||||
|
@ -1622,7 +1617,7 @@ fn window_size(
|
|||
) -> PhysicalSize<u32> {
|
||||
let padding = config.window.padding(scale_factor);
|
||||
|
||||
let grid_width = cell_width * dimensions.columns.0.max(MIN_COLUMNS) as f32;
|
||||
let grid_width = cell_width * dimensions.columns.max(MIN_COLUMNS) as f32;
|
||||
let grid_height = cell_height * dimensions.lines.max(MIN_SCREEN_LINES) as f32;
|
||||
|
||||
let width = (padding.0).mul_add(2., grid_width).floor();
|
||||
|
|
|
@ -27,7 +27,6 @@ use winit::event_loop::{
|
|||
};
|
||||
use winit::window::WindowId;
|
||||
|
||||
use alacritty_terminal::config::LOG_TARGET_CONFIG;
|
||||
use alacritty_terminal::event::{Event as TerminalEvent, EventListener, Notify};
|
||||
use alacritty_terminal::event_loop::Notifier;
|
||||
use alacritty_terminal::grid::{BidirectionalIterator, Dimensions, Scroll};
|
||||
|
@ -45,10 +44,12 @@ use crate::config::{self, UiConfig};
|
|||
#[cfg(not(windows))]
|
||||
use crate::daemon::foreground_process_path;
|
||||
use crate::daemon::spawn_daemon;
|
||||
use crate::display::color::Rgb;
|
||||
use crate::display::hint::HintMatch;
|
||||
use crate::display::window::Window;
|
||||
use crate::display::{Display, Preedit, SizeInfo};
|
||||
use crate::input::{self, ActionContext as _, FONT_SIZE_STEP};
|
||||
use crate::logging::LOG_TARGET_CONFIG;
|
||||
use crate::message_bar::{Message, MessageBuffer};
|
||||
use crate::scheduler::{Scheduler, TimerId, Topic};
|
||||
use crate::window_context::WindowContext;
|
||||
|
@ -284,8 +285,7 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
|
|||
None => return,
|
||||
};
|
||||
|
||||
if ty == ClipboardType::Selection && self.config.terminal_config.selection.save_to_clipboard
|
||||
{
|
||||
if ty == ClipboardType::Selection && self.config.selection.save_to_clipboard {
|
||||
self.clipboard.store(ClipboardType::Clipboard, text.clone());
|
||||
}
|
||||
self.clipboard.store(ty, text);
|
||||
|
@ -1033,10 +1033,10 @@ impl<'a, N: Notify + 'a, T: EventListener> ActionContext<'a, N, T> {
|
|||
/// Update the cursor blinking state.
|
||||
fn update_cursor_blinking(&mut self) {
|
||||
// Get config cursor style.
|
||||
let mut cursor_style = self.config.terminal_config.cursor.style;
|
||||
let mut cursor_style = self.config.cursor.style;
|
||||
let vi_mode = self.terminal.mode().contains(TermMode::VI);
|
||||
if vi_mode {
|
||||
cursor_style = self.config.terminal_config.cursor.vi_mode_style.unwrap_or(cursor_style);
|
||||
cursor_style = self.config.cursor.vi_mode_style.unwrap_or(cursor_style);
|
||||
}
|
||||
|
||||
// Check terminal cursor style.
|
||||
|
@ -1066,23 +1066,21 @@ impl<'a, N: Notify + 'a, T: EventListener> ActionContext<'a, N, T> {
|
|||
let window_id = self.display.window.id();
|
||||
let timer_id = TimerId::new(Topic::BlinkCursor, window_id);
|
||||
let event = Event::new(EventType::BlinkCursor, window_id);
|
||||
let blinking_interval =
|
||||
Duration::from_millis(self.config.terminal_config.cursor.blink_interval());
|
||||
let blinking_interval = Duration::from_millis(self.config.cursor.blink_interval());
|
||||
self.scheduler.schedule(event, blinking_interval, true, timer_id);
|
||||
}
|
||||
|
||||
fn schedule_blinking_timeout(&mut self) {
|
||||
let blinking_timeout = self.config.terminal_config.cursor.blink_timeout();
|
||||
if blinking_timeout == 0 {
|
||||
let blinking_timeout = self.config.cursor.blink_timeout();
|
||||
if blinking_timeout == Duration::ZERO {
|
||||
return;
|
||||
}
|
||||
|
||||
let window_id = self.display.window.id();
|
||||
let blinking_timeout_interval = Duration::from_secs(blinking_timeout);
|
||||
let event = Event::new(EventType::BlinkCursorTimeout, window_id);
|
||||
let timer_id = TimerId::new(Topic::BlinkTimeout, window_id);
|
||||
|
||||
self.scheduler.schedule(event, blinking_timeout_interval, false, timer_id);
|
||||
self.scheduler.schedule(event, blinking_timeout, false, timer_id);
|
||||
}
|
||||
|
||||
/// Perform vi mode inline search in the specified direction.
|
||||
|
@ -1324,8 +1322,9 @@ impl input::Processor<EventProxy, ActionContext<'_, Notifier, EventProxy>> {
|
|||
},
|
||||
TerminalEvent::ColorRequest(index, format) => {
|
||||
let color = self.ctx.terminal().colors()[index]
|
||||
.map(Rgb)
|
||||
.unwrap_or(self.ctx.display.colors[index]);
|
||||
self.ctx.write_to_pty(format(color).into_bytes());
|
||||
self.ctx.write_to_pty(format(color.0).into_bytes());
|
||||
},
|
||||
TerminalEvent::TextAreaSizeRequest(format) => {
|
||||
let text = format(self.ctx.size_info().into());
|
||||
|
@ -1386,7 +1385,7 @@ impl input::Processor<EventProxy, ActionContext<'_, Notifier, EventProxy>> {
|
|||
self.ctx.terminal.is_focused = is_focused;
|
||||
|
||||
// When the unfocused hollow is used we must redraw on focus change.
|
||||
if self.ctx.config.terminal_config.cursor.unfocused_hollow {
|
||||
if self.ctx.config.cursor.unfocused_hollow {
|
||||
*self.ctx.dirty = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,6 @@ use winit::platform::macos::{EventLoopWindowTargetExtMacOS, OptionAsAlt};
|
|||
use winit::platform::modifier_supplement::KeyEventExtModifierSupplement;
|
||||
use winit::window::CursorIcon;
|
||||
|
||||
use alacritty_terminal::ansi::{ClearMode, Handler};
|
||||
use alacritty_terminal::event::EventListener;
|
||||
use alacritty_terminal::grid::{Dimensions, Scroll};
|
||||
use alacritty_terminal::index::{Boundary, Column, Direction, Point, Side};
|
||||
|
@ -37,6 +36,7 @@ use alacritty_terminal::selection::SelectionType;
|
|||
use alacritty_terminal::term::search::Match;
|
||||
use alacritty_terminal::term::{ClipboardType, Term, TermMode};
|
||||
use alacritty_terminal::vi_mode::ViMotion;
|
||||
use alacritty_terminal::vte::ansi::{ClearMode, Handler};
|
||||
|
||||
use crate::clipboard::Clipboard;
|
||||
use crate::config::{
|
||||
|
@ -701,7 +701,7 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> {
|
|||
}
|
||||
|
||||
pub fn mouse_wheel_input(&mut self, delta: MouseScrollDelta, phase: TouchPhase) {
|
||||
let multiplier = self.ctx.config().terminal_config.scrolling.multiplier;
|
||||
let multiplier = self.ctx.config().scrolling.multiplier;
|
||||
match delta {
|
||||
MouseScrollDelta::LineDelta(columns, lines) => {
|
||||
let new_scroll_px_x = columns * self.ctx.size_info().cell_width();
|
||||
|
@ -1382,7 +1382,7 @@ mod tests {
|
|||
false,
|
||||
);
|
||||
|
||||
let mut terminal = Term::new(&cfg.terminal_config, &size, MockEventProxy);
|
||||
let mut terminal = Term::new(cfg.term_options(), &size, MockEventProxy);
|
||||
|
||||
let mut mouse = Mouse {
|
||||
click_state: $initial_state,
|
||||
|
|
|
@ -16,8 +16,6 @@ use log::{self, Level, LevelFilter};
|
|||
use once_cell::sync::Lazy;
|
||||
use winit::event_loop::EventLoopProxy;
|
||||
|
||||
use alacritty_terminal::config::LOG_TARGET_CONFIG;
|
||||
|
||||
use crate::cli::Options;
|
||||
use crate::event::{Event, EventType};
|
||||
use crate::message_bar::{Message, MessageType};
|
||||
|
@ -28,6 +26,9 @@ pub const LOG_TARGET_IPC_CONFIG: &str = "alacritty_log_ipc_config";
|
|||
/// Name for the environment variable containing the log file's path.
|
||||
const ALACRITTY_LOG_ENV: &str = "ALACRITTY_LOG";
|
||||
|
||||
/// Logging target for config error messages.
|
||||
pub const LOG_TARGET_CONFIG: &str = "alacritty_config_derive";
|
||||
|
||||
/// Name for the environment variable containing extra logging targets.
|
||||
///
|
||||
/// The targets are semicolon separated.
|
||||
|
|
|
@ -12,13 +12,11 @@
|
|||
#[cfg(not(any(feature = "x11", feature = "wayland", target_os = "macos", windows)))]
|
||||
compile_error!(r#"at least one of the "x11"/"wayland" features must be enabled"#);
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
use std::fmt::Write as _;
|
||||
use std::fs;
|
||||
use std::io::{self, Write};
|
||||
use std::path::PathBuf;
|
||||
use std::{env, fs};
|
||||
|
||||
use log::info;
|
||||
#[cfg(windows)]
|
||||
|
@ -147,8 +145,13 @@ fn alacritty(options: Options) -> Result<(), Box<dyn Error>> {
|
|||
// Update the log level from config.
|
||||
log::set_max_level(config.debug.log_level);
|
||||
|
||||
// Set environment variables.
|
||||
tty::setup_env(&config.terminal_config);
|
||||
// Set tty environment variables.
|
||||
tty::setup_env();
|
||||
|
||||
// Set env vars from config.
|
||||
for (key, value) in config.env.iter() {
|
||||
env::set_var(key, value);
|
||||
}
|
||||
|
||||
// Switch to home directory.
|
||||
#[cfg(target_os = "macos")]
|
||||
|
|
|
@ -13,9 +13,9 @@ use unicode_width::UnicodeWidthChar;
|
|||
|
||||
use alacritty_terminal::index::Point;
|
||||
use alacritty_terminal::term::cell::Flags;
|
||||
use alacritty_terminal::term::color::Rgb;
|
||||
|
||||
use crate::config::debug::RendererPreference;
|
||||
use crate::display::color::Rgb;
|
||||
use crate::display::content::RenderableCell;
|
||||
use crate::display::SizeInfo;
|
||||
use crate::gl;
|
||||
|
|
|
@ -7,8 +7,8 @@ use crossfont::Metrics;
|
|||
use alacritty_terminal::grid::Dimensions;
|
||||
use alacritty_terminal::index::{Column, Point};
|
||||
use alacritty_terminal::term::cell::Flags;
|
||||
use alacritty_terminal::term::color::Rgb;
|
||||
|
||||
use crate::display::color::Rgb;
|
||||
use crate::display::content::RenderableCell;
|
||||
use crate::display::SizeInfo;
|
||||
use crate::gl;
|
||||
|
|
|
@ -174,7 +174,7 @@ impl WindowContext {
|
|||
options: WindowOptions,
|
||||
proxy: EventLoopProxy<Event>,
|
||||
) -> Result<Self, Box<dyn Error>> {
|
||||
let mut pty_config = config.terminal_config.pty_config.clone();
|
||||
let mut pty_config = config.pty_config();
|
||||
options.terminal_options.override_pty_config(&mut pty_config);
|
||||
|
||||
let preserve_title = options.window_identity.title.is_some();
|
||||
|
@ -192,7 +192,7 @@ impl WindowContext {
|
|||
// This object contains all of the state about what's being displayed. It's
|
||||
// wrapped in a clonable mutex since both the I/O loop and display need to
|
||||
// access it.
|
||||
let terminal = Term::new(&config.terminal_config, &display.size_info, event_proxy.clone());
|
||||
let terminal = Term::new(config.term_options(), &display.size_info, event_proxy.clone());
|
||||
let terminal = Arc::new(FairMutex::new(terminal));
|
||||
|
||||
// Create the PTY.
|
||||
|
@ -229,7 +229,7 @@ impl WindowContext {
|
|||
let _io_thread = event_loop.spawn();
|
||||
|
||||
// Start cursor blinking, in case `Focused` isn't sent on startup.
|
||||
if config.terminal_config.cursor.style().blinking {
|
||||
if config.cursor.style().blinking {
|
||||
event_proxy.send_event(TerminalEvent::CursorBlinkingChange.into());
|
||||
}
|
||||
|
||||
|
@ -289,14 +289,10 @@ impl WindowContext {
|
|||
}
|
||||
|
||||
self.display.update_config(&self.config);
|
||||
self.terminal.lock().update_config(&self.config.terminal_config);
|
||||
self.terminal.lock().set_options(self.config.term_options());
|
||||
|
||||
// Reload cursor if its thickness has changed.
|
||||
if (old_config.terminal_config.cursor.thickness()
|
||||
- self.config.terminal_config.cursor.thickness())
|
||||
.abs()
|
||||
> f32::EPSILON
|
||||
{
|
||||
if (old_config.cursor.thickness() - self.config.cursor.thickness()).abs() > f32::EPSILON {
|
||||
self.display.pending_update.set_cursor_dirty();
|
||||
}
|
||||
|
||||
|
|
|
@ -9,28 +9,22 @@ homepage = "https://github.com/alacritty/alacritty"
|
|||
edition = "2021"
|
||||
rust-version = "1.70.0"
|
||||
|
||||
[dependencies.alacritty_config_derive]
|
||||
path = "../alacritty_config_derive"
|
||||
version = "0.2.2-dev"
|
||||
|
||||
[dependencies.alacritty_config]
|
||||
path = "../alacritty_config"
|
||||
version = "0.1.2-dev"
|
||||
[features]
|
||||
default = ["serde"]
|
||||
serde = ["dep:serde", "bitflags/serde", "vte/serde"]
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.21.3"
|
||||
bitflags = { version = "2.2.1", features = ["serde"] }
|
||||
bitflags = "2.4.1"
|
||||
home = "0.5.5"
|
||||
libc = "0.2"
|
||||
log = "0.4"
|
||||
parking_lot = "0.12.0"
|
||||
polling = "3.0.0"
|
||||
regex-automata = "0.3.6"
|
||||
serde = { version = "1", features = ["derive", "rc"] }
|
||||
serde_yaml = "0.9.25"
|
||||
toml = "0.8.2"
|
||||
unicode-width = "0.1"
|
||||
vte = { version = "0.12.0", default-features = false, features = ["ansi", "serde"] }
|
||||
serde = { version = "1", features = ["derive", "rc"], optional = true }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
rustix-openpty = "0.1.1"
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
//! ANSI Terminal Stream Parsing.
|
||||
|
||||
pub use vte::ansi::*;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
|
||||
pub struct CursorShapeShim(CursorShape);
|
||||
|
||||
impl Default for CursorShapeShim {
|
||||
fn default() -> CursorShapeShim {
|
||||
CursorShapeShim(CursorShape::Block)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CursorShapeShim> for CursorShape {
|
||||
fn from(value: CursorShapeShim) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
struct CursorShapeVisitor;
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for CursorShapeVisitor {
|
||||
type Value = CursorShapeShim;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
formatter.write_str("one of `Block`, `Underline`, `Beam`")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
match s.to_lowercase().as_str() {
|
||||
"block" => Ok(CursorShapeShim(CursorShape::Block)),
|
||||
"underline" => Ok(CursorShapeShim(CursorShape::Underline)),
|
||||
"beam" => Ok(CursorShapeShim(CursorShape::Beam)),
|
||||
_ => Err(E::custom(format!(
|
||||
"unknown variant `{0}`, expected {1}",
|
||||
s, "one of `Block`, `Underline`, `Beam`"
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde::Deserialize<'de> for CursorShapeShim {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_str(CursorShapeVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl alacritty_config::SerdeReplace for CursorShapeShim {
|
||||
fn replace(&mut self, value: toml::Value) -> Result<(), Box<dyn std::error::Error>> {
|
||||
*self = serde::Deserialize::deserialize(value)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -1,263 +0,0 @@
|
|||
use std::cmp;
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
use alacritty_config_derive::{ConfigDeserialize, SerdeReplace};
|
||||
|
||||
mod scrolling;
|
||||
|
||||
use crate::ansi::{CursorShapeShim, CursorStyle};
|
||||
|
||||
pub use crate::config::scrolling::{Scrolling, MAX_SCROLLBACK_LINES};
|
||||
|
||||
/// Logging target for config error messages.
|
||||
pub const LOG_TARGET_CONFIG: &str = "alacritty_config_derive";
|
||||
|
||||
const MIN_BLINK_INTERVAL: u64 = 10;
|
||||
|
||||
/// Top-level config type.
|
||||
#[derive(ConfigDeserialize, Clone, Debug, PartialEq, Default)]
|
||||
pub struct Config {
|
||||
/// TERM env variable.
|
||||
pub env: HashMap<String, String>,
|
||||
|
||||
pub selection: Selection,
|
||||
|
||||
/// How much scrolling history to keep.
|
||||
pub scrolling: Scrolling,
|
||||
|
||||
/// Cursor configuration.
|
||||
pub cursor: Cursor,
|
||||
|
||||
/// Terminal specific settings.
|
||||
pub terminal: Terminal,
|
||||
|
||||
#[config(flatten)]
|
||||
pub pty_config: PtyConfig,
|
||||
}
|
||||
|
||||
#[derive(ConfigDeserialize, Clone, Debug, PartialEq, Eq, Default)]
|
||||
pub struct Terminal {
|
||||
// OSC 52 handling (clipboard handling).
|
||||
pub osc52: Osc52,
|
||||
}
|
||||
|
||||
#[derive(ConfigDeserialize, Clone, Debug, PartialEq, Eq, Default)]
|
||||
pub enum Osc52 {
|
||||
/// The handling of the escape sequence is disabled.
|
||||
Disabled,
|
||||
/// Only copy sequence is accepted.
|
||||
///
|
||||
/// This option is the default as a compromise between entirely
|
||||
/// disabling it (the most secure) and allowing `paste` (the less secure).
|
||||
#[default]
|
||||
OnlyCopy,
|
||||
/// Only paste sequence is accepted.
|
||||
OnlyPaste,
|
||||
/// Both are accepted.
|
||||
CopyPaste,
|
||||
}
|
||||
|
||||
#[derive(ConfigDeserialize, Clone, Debug, PartialEq, Eq, Default)]
|
||||
pub struct PtyConfig {
|
||||
/// Path to a shell program to run on startup.
|
||||
pub shell: Option<Program>,
|
||||
|
||||
/// Shell startup directory.
|
||||
pub working_directory: Option<PathBuf>,
|
||||
|
||||
/// Remain open after child process exits.
|
||||
#[config(skip)]
|
||||
pub hold: bool,
|
||||
}
|
||||
|
||||
impl PtyConfig {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(ConfigDeserialize, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Selection {
|
||||
pub semantic_escape_chars: String,
|
||||
pub save_to_clipboard: bool,
|
||||
}
|
||||
|
||||
impl Default for Selection {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
semantic_escape_chars: String::from(",│`|:\"' ()[]{}<>\t"),
|
||||
save_to_clipboard: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(ConfigDeserialize, Copy, Clone, Debug, PartialEq)]
|
||||
pub struct Cursor {
|
||||
pub style: ConfigCursorStyle,
|
||||
pub vi_mode_style: Option<ConfigCursorStyle>,
|
||||
pub unfocused_hollow: bool,
|
||||
|
||||
thickness: Percentage,
|
||||
blink_interval: u64,
|
||||
blink_timeout: u8,
|
||||
}
|
||||
|
||||
impl Default for Cursor {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
thickness: Percentage(0.15),
|
||||
unfocused_hollow: true,
|
||||
blink_interval: 750,
|
||||
blink_timeout: 5,
|
||||
style: Default::default(),
|
||||
vi_mode_style: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Cursor {
|
||||
#[inline]
|
||||
pub fn thickness(self) -> f32 {
|
||||
self.thickness.as_f32()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn style(self) -> CursorStyle {
|
||||
self.style.into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn vi_mode_style(self) -> Option<CursorStyle> {
|
||||
self.vi_mode_style.map(From::from)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn blink_interval(self) -> u64 {
|
||||
cmp::max(self.blink_interval, MIN_BLINK_INTERVAL)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn blink_timeout(self) -> u64 {
|
||||
const MILLIS_IN_SECOND: u64 = 1000;
|
||||
match self.blink_timeout {
|
||||
0 => 0,
|
||||
blink_timeout => {
|
||||
cmp::max(self.blink_interval * 5 / MILLIS_IN_SECOND, blink_timeout as u64)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(SerdeReplace, Deserialize, Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[serde(untagged, deny_unknown_fields)]
|
||||
pub enum ConfigCursorStyle {
|
||||
Shape(CursorShapeShim),
|
||||
WithBlinking {
|
||||
#[serde(default)]
|
||||
shape: CursorShapeShim,
|
||||
#[serde(default)]
|
||||
blinking: CursorBlinking,
|
||||
},
|
||||
}
|
||||
|
||||
impl Default for ConfigCursorStyle {
|
||||
fn default() -> Self {
|
||||
Self::Shape(CursorShapeShim::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl ConfigCursorStyle {
|
||||
/// Check if blinking is force enabled/disabled.
|
||||
pub fn blinking_override(&self) -> Option<bool> {
|
||||
match self {
|
||||
Self::Shape(_) => None,
|
||||
Self::WithBlinking { blinking, .. } => blinking.blinking_override(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConfigCursorStyle> for CursorStyle {
|
||||
fn from(config_style: ConfigCursorStyle) -> Self {
|
||||
match config_style {
|
||||
ConfigCursorStyle::Shape(shape) => Self { shape: shape.into(), blinking: false },
|
||||
ConfigCursorStyle::WithBlinking { shape, blinking } => {
|
||||
Self { shape: shape.into(), blinking: blinking.into() }
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(ConfigDeserialize, Default, Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum CursorBlinking {
|
||||
Never,
|
||||
#[default]
|
||||
Off,
|
||||
On,
|
||||
Always,
|
||||
}
|
||||
|
||||
impl CursorBlinking {
|
||||
fn blinking_override(&self) -> Option<bool> {
|
||||
match self {
|
||||
Self::Never => Some(false),
|
||||
Self::Off | Self::On => None,
|
||||
Self::Always => Some(true),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CursorBlinking> for bool {
|
||||
fn from(blinking: CursorBlinking) -> bool {
|
||||
blinking == CursorBlinking::On || blinking == CursorBlinking::Always
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
#[serde(untagged, deny_unknown_fields)]
|
||||
pub enum Program {
|
||||
Just(String),
|
||||
WithArgs {
|
||||
program: String,
|
||||
#[serde(default)]
|
||||
args: Vec<String>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Program {
|
||||
pub fn program(&self) -> &str {
|
||||
match self {
|
||||
Program::Just(program) => program,
|
||||
Program::WithArgs { program, .. } => program,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn args(&self) -> &[String] {
|
||||
match self {
|
||||
Program::Just(_) => &[],
|
||||
Program::WithArgs { args, .. } => args,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper around f32 that represents a percentage value between 0.0 and 1.0.
|
||||
#[derive(SerdeReplace, Deserialize, Clone, Copy, Debug, PartialEq)]
|
||||
pub struct Percentage(f32);
|
||||
|
||||
impl Default for Percentage {
|
||||
fn default() -> Self {
|
||||
Percentage(1.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Percentage {
|
||||
pub fn new(value: f32) -> Self {
|
||||
Percentage(value.clamp(0., 1.))
|
||||
}
|
||||
|
||||
pub fn as_f32(self) -> f32 {
|
||||
self.0
|
||||
}
|
||||
}
|
|
@ -2,8 +2,8 @@ use std::borrow::Cow;
|
|||
use std::fmt::{self, Debug, Formatter};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::term::color::Rgb;
|
||||
use crate::term::ClipboardType;
|
||||
use crate::vte::ansi::Rgb;
|
||||
|
||||
/// Terminal event.
|
||||
///
|
||||
|
|
|
@ -17,7 +17,8 @@ use polling::{Event as PollingEvent, Events, PollMode};
|
|||
use crate::event::{self, Event, EventListener, WindowSize};
|
||||
use crate::sync::FairMutex;
|
||||
use crate::term::Term;
|
||||
use crate::{ansi, thread, tty};
|
||||
use crate::vte::ansi;
|
||||
use crate::{thread, tty};
|
||||
|
||||
/// Max bytes to read from the PTY before forced terminal synchronization.
|
||||
pub(crate) const READ_BUFFER_SIZE: usize = 0x10_0000;
|
||||
|
|
|
@ -3,11 +3,12 @@
|
|||
use std::cmp::{max, min};
|
||||
use std::ops::{Bound, Deref, Index, IndexMut, Range, RangeBounds};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::ansi::{CharsetIndex, StandardCharset};
|
||||
use crate::index::{Column, Line, Point};
|
||||
use crate::term::cell::{Flags, ResetDiscriminant};
|
||||
use crate::vte::ansi::{CharsetIndex, StandardCharset};
|
||||
|
||||
pub mod resize;
|
||||
mod row;
|
||||
|
@ -104,14 +105,15 @@ pub enum Scroll {
|
|||
/// ^
|
||||
/// columns
|
||||
/// ```
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct Grid<T> {
|
||||
/// Current cursor for writing data.
|
||||
#[serde(skip)]
|
||||
#[cfg_attr(feature = "serde", serde(skip))]
|
||||
pub cursor: Cursor<T>,
|
||||
|
||||
/// Last saved cursor.
|
||||
#[serde(skip)]
|
||||
#[cfg_attr(feature = "serde", serde(skip))]
|
||||
pub saved_cursor: Cursor<T>,
|
||||
|
||||
/// Lines in the grid. Each row holds a list of cells corresponding to the
|
||||
|
|
|
@ -4,6 +4,7 @@ use std::cmp::{max, min};
|
|||
use std::ops::{Index, IndexMut, Range, RangeFrom, RangeFull, RangeTo, RangeToInclusive};
|
||||
use std::{ptr, slice};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::grid::GridCell;
|
||||
|
@ -11,7 +12,8 @@ use crate::index::Column;
|
|||
use crate::term::cell::ResetDiscriminant;
|
||||
|
||||
/// A row in the grid.
|
||||
#[derive(Serialize, Deserialize, Default, Clone, Debug)]
|
||||
#[derive(Default, Clone, Debug)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct Row<T> {
|
||||
inner: Vec<T>,
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ use std::mem;
|
|||
use std::mem::MaybeUninit;
|
||||
use std::ops::{Index, IndexMut};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::Row;
|
||||
|
@ -27,7 +28,8 @@ const MAX_CACHE_SIZE: usize = 1_000;
|
|||
/// [`slice::rotate_left`]: https://doc.rust-lang.org/std/primitive.slice.html#method.rotate_left
|
||||
/// [`Deref`]: std::ops::Deref
|
||||
/// [`zero`]: #structfield.zero
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct Storage<T> {
|
||||
inner: Vec<Row<T>>,
|
||||
|
||||
|
|
|
@ -5,10 +5,9 @@ use std::cmp::{max, min, Ord, Ordering};
|
|||
use std::fmt;
|
||||
use std::ops::{Add, AddAssign, Deref, Sub, SubAssign};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use alacritty_config_derive::SerdeReplace;
|
||||
|
||||
use crate::grid::Dimensions;
|
||||
|
||||
/// The side of a cell.
|
||||
|
@ -46,7 +45,8 @@ pub enum Boundary {
|
|||
}
|
||||
|
||||
/// Index in the grid using row, column notation.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, Default, Eq, PartialEq)]
|
||||
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct Point<L = Line, C = Column> {
|
||||
pub line: L,
|
||||
pub column: C,
|
||||
|
@ -131,7 +131,8 @@ impl<L: Ord, C: Ord> Ord for Point<L, C> {
|
|||
/// A line.
|
||||
///
|
||||
/// Newtype to avoid passing values incorrectly.
|
||||
#[derive(Serialize, Deserialize, Debug, Copy, Clone, Eq, PartialEq, Default, Ord, PartialOrd)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Ord, PartialOrd)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct Line(pub i32);
|
||||
|
||||
impl Line {
|
||||
|
@ -224,19 +225,8 @@ impl PartialEq<usize> for Line {
|
|||
/// A column.
|
||||
///
|
||||
/// Newtype to avoid passing values incorrectly.
|
||||
#[derive(
|
||||
SerdeReplace,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Debug,
|
||||
Copy,
|
||||
Clone,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Default,
|
||||
Ord,
|
||||
PartialOrd,
|
||||
)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Ord, PartialOrd)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct Column(pub usize);
|
||||
|
||||
impl fmt::Display for Column {
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
#![deny(clippy::all, clippy::if_not_else, clippy::enum_glob_use)]
|
||||
#![cfg_attr(feature = "cargo-clippy", deny(warnings))]
|
||||
|
||||
pub mod ansi;
|
||||
pub mod config;
|
||||
pub mod event;
|
||||
pub mod event_loop;
|
||||
pub mod grid;
|
||||
|
@ -19,3 +17,4 @@ pub mod vi_mode;
|
|||
|
||||
pub use crate::grid::Grid;
|
||||
pub use crate::term::Term;
|
||||
pub use vte;
|
||||
|
|
|
@ -9,11 +9,11 @@ use std::cmp::min;
|
|||
use std::mem;
|
||||
use std::ops::{Bound, Range, RangeBounds};
|
||||
|
||||
use crate::ansi::CursorShape;
|
||||
use crate::grid::{Dimensions, GridCell, Indexed};
|
||||
use crate::index::{Boundary, Column, Line, Point, Side};
|
||||
use crate::term::cell::{Cell, Flags};
|
||||
use crate::term::Term;
|
||||
use crate::vte::ansi::CursorShape;
|
||||
|
||||
/// A Point and side within that point.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
|
@ -395,14 +395,13 @@ impl Selection {
|
|||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::index::{Column, Point, Side};
|
||||
use crate::term::test::TermSize;
|
||||
use crate::term::Term;
|
||||
use crate::term::{Config, Term};
|
||||
|
||||
fn term(height: usize, width: usize) -> Term<()> {
|
||||
let size = TermSize::new(width, height);
|
||||
Term::new(&Config::default(), &size, ())
|
||||
Term::new(Config::default(), &size, ())
|
||||
}
|
||||
|
||||
/// Test case of single cell selection.
|
||||
|
|
|
@ -2,15 +2,16 @@ use std::sync::atomic::{AtomicU32, Ordering};
|
|||
use std::sync::Arc;
|
||||
|
||||
use bitflags::bitflags;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
use vte::ansi::Hyperlink as VteHyperlink;
|
||||
|
||||
use crate::ansi::{Color, NamedColor};
|
||||
use crate::grid::{self, GridCell};
|
||||
use crate::index::Column;
|
||||
use crate::vte::ansi::{Color, Hyperlink as VteHyperlink, NamedColor};
|
||||
|
||||
bitflags! {
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct Flags: u16 {
|
||||
const INVERSE = 0b0000_0000_0000_0001;
|
||||
const BOLD = 0b0000_0000_0000_0010;
|
||||
|
@ -38,7 +39,8 @@ bitflags! {
|
|||
/// Counter for hyperlinks without explicit ID.
|
||||
static HYPERLINK_ID_SUFFIX: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct Hyperlink {
|
||||
inner: Arc<HyperlinkInner>,
|
||||
}
|
||||
|
@ -70,7 +72,8 @@ impl From<Hyperlink> for VteHyperlink {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
struct HyperlinkInner {
|
||||
/// Identifier for the given hyperlink.
|
||||
id: String,
|
||||
|
@ -117,7 +120,8 @@ impl ResetDiscriminant<Color> for Cell {
|
|||
/// This storage is reserved for cell attributes which are rarely set. This allows reducing the
|
||||
/// allocation required ahead of time for every cell, with some additional overhead when the extra
|
||||
/// storage is actually required.
|
||||
#[derive(Serialize, Deserialize, Default, Debug, Clone, Eq, PartialEq)]
|
||||
#[derive(Default, Debug, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct CellExtra {
|
||||
zerowidth: Vec<char>,
|
||||
|
||||
|
@ -127,7 +131,8 @@ pub struct CellExtra {
|
|||
}
|
||||
|
||||
/// Content and attributes of a single cell in the terminal grid.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct Cell {
|
||||
pub c: char,
|
||||
pub fg: Color,
|
||||
|
|
|
@ -1,208 +1,10 @@
|
|||
use std::fmt::{self, Display, Formatter};
|
||||
use std::ops::{Add, Deref, Index, IndexMut, Mul};
|
||||
use std::str::FromStr;
|
||||
use std::ops::{Index, IndexMut};
|
||||
|
||||
use serde::de::{Error as _, Visitor};
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use serde_yaml::Value;
|
||||
|
||||
use alacritty_config_derive::SerdeReplace;
|
||||
|
||||
use vte::ansi::Rgb as VteRgb;
|
||||
|
||||
use crate::ansi::NamedColor;
|
||||
use crate::vte::ansi::{NamedColor, Rgb};
|
||||
|
||||
/// Number of terminal colors.
|
||||
pub const COUNT: usize = 269;
|
||||
|
||||
#[derive(SerdeReplace, Debug, Eq, PartialEq, Copy, Clone, Default, Serialize)]
|
||||
pub struct Rgb(VteRgb);
|
||||
|
||||
impl Rgb {
|
||||
#[inline]
|
||||
pub const fn new(r: u8, g: u8, b: u8) -> Self {
|
||||
Self(VteRgb { r, g, b })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_tuple(self) -> (u8, u8, u8) {
|
||||
(self.0.r, self.0.g, self.0.b)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<VteRgb> for Rgb {
|
||||
fn from(value: VteRgb) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Rgb {
|
||||
type Target = VteRgb;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<f32> for Rgb {
|
||||
type Output = Rgb;
|
||||
|
||||
fn mul(self, rhs: f32) -> Self::Output {
|
||||
Rgb(self.0 * rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<Rgb> for Rgb {
|
||||
type Output = Rgb;
|
||||
|
||||
fn add(self, rhs: Rgb) -> Self::Output {
|
||||
Rgb(self.0 + rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Deserialize an Rgb from a hex string.
|
||||
///
|
||||
/// This is *not* the deserialize impl for Rgb since we want a symmetric
|
||||
/// serialize/deserialize impl for ref tests.
|
||||
impl<'de> Deserialize<'de> for Rgb {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct RgbVisitor;
|
||||
|
||||
// Used for deserializing reftests.
|
||||
#[derive(Deserialize)]
|
||||
struct RgbDerivedDeser {
|
||||
r: u8,
|
||||
g: u8,
|
||||
b: u8,
|
||||
}
|
||||
|
||||
impl<'a> Visitor<'a> for RgbVisitor {
|
||||
type Value = Rgb;
|
||||
|
||||
fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("hex color like #ff00ff")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Rgb, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Rgb::from_str(value).map_err(|_| {
|
||||
E::custom(format!(
|
||||
"failed to parse rgb color {value}; expected hex color like #ff00ff"
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Return an error if the syntax is incorrect.
|
||||
let value = Value::deserialize(deserializer)?;
|
||||
|
||||
// Attempt to deserialize from struct form.
|
||||
if let Ok(RgbDerivedDeser { r, g, b }) = RgbDerivedDeser::deserialize(value.clone()) {
|
||||
return Ok(Rgb::new(r, g, b));
|
||||
}
|
||||
|
||||
// Deserialize from hex notation (either 0xff00ff or #ff00ff).
|
||||
value.deserialize_str(RgbVisitor).map_err(D::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Rgb {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "#{:02x}{:02x}{:02x}", self.r, self.g, self.b)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Rgb {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<Rgb, ()> {
|
||||
let chars = if s.starts_with("0x") && s.len() == 8 {
|
||||
&s[2..]
|
||||
} else if s.starts_with('#') && s.len() == 7 {
|
||||
&s[1..]
|
||||
} else {
|
||||
return Err(());
|
||||
};
|
||||
|
||||
match u32::from_str_radix(chars, 16) {
|
||||
Ok(mut color) => {
|
||||
let b = (color & 0xff) as u8;
|
||||
color >>= 8;
|
||||
let g = (color & 0xff) as u8;
|
||||
color >>= 8;
|
||||
let r = color as u8;
|
||||
Ok(Rgb::new(r, g, b))
|
||||
},
|
||||
Err(_) => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// RGB color optionally referencing the cell's foreground or background.
|
||||
#[derive(SerdeReplace, Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum CellRgb {
|
||||
CellForeground,
|
||||
CellBackground,
|
||||
Rgb(Rgb),
|
||||
}
|
||||
|
||||
impl CellRgb {
|
||||
pub fn color(self, foreground: Rgb, background: Rgb) -> Rgb {
|
||||
match self {
|
||||
Self::CellForeground => foreground,
|
||||
Self::CellBackground => background,
|
||||
Self::Rgb(rgb) => rgb,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for CellRgb {
|
||||
fn default() -> Self {
|
||||
Self::Rgb(Rgb::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for CellRgb {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
const EXPECTING: &str = "CellForeground, CellBackground, or hex color like #ff00ff";
|
||||
|
||||
struct CellRgbVisitor;
|
||||
impl<'a> Visitor<'a> for CellRgbVisitor {
|
||||
type Value = CellRgb;
|
||||
|
||||
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(EXPECTING)
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<CellRgb, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
// Attempt to deserialize as enum constants.
|
||||
match value {
|
||||
"CellForeground" => return Ok(CellRgb::CellForeground),
|
||||
"CellBackground" => return Ok(CellRgb::CellBackground),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
Rgb::from_str(value).map(CellRgb::Rgb).map_err(|_| {
|
||||
E::custom(format!("failed to parse color {value}; expected {EXPECTING}"))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_str(CellRgbVisitor).map_err(D::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
/// Array of indexed colors.
|
||||
///
|
||||
/// | Indices | Description |
|
||||
|
|
|
@ -4,17 +4,15 @@ use std::ops::{Index, IndexMut, Range};
|
|||
use std::sync::Arc;
|
||||
use std::{cmp, mem, ptr, slice, str};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use base64::engine::general_purpose::STANDARD as Base64;
|
||||
use base64::Engine;
|
||||
use bitflags::bitflags;
|
||||
use log::{debug, trace};
|
||||
use unicode_width::UnicodeWidthChar;
|
||||
use vte::ansi::{Hyperlink as VteHyperlink, Rgb as VteRgb};
|
||||
|
||||
use crate::ansi::{
|
||||
self, Attr, CharsetIndex, Color, CursorShape, CursorStyle, Handler, NamedColor, StandardCharset,
|
||||
};
|
||||
use crate::config::{Config, Osc52, Terminal};
|
||||
use crate::event::{Event, EventListener};
|
||||
use crate::grid::{Dimensions, Grid, GridIterator, Scroll};
|
||||
use crate::index::{self, Boundary, Column, Direction, Line, Point, Side};
|
||||
|
@ -22,6 +20,10 @@ use crate::selection::{Selection, SelectionRange, SelectionType};
|
|||
use crate::term::cell::{Cell, Flags, LineLength};
|
||||
use crate::term::color::Colors;
|
||||
use crate::vi_mode::{ViModeCursor, ViMotion};
|
||||
use crate::vte::ansi::{
|
||||
self, Attr, CharsetIndex, Color, CursorShape, CursorStyle, Handler, Hyperlink, NamedColor, Rgb,
|
||||
StandardCharset,
|
||||
};
|
||||
|
||||
pub mod cell;
|
||||
pub mod color;
|
||||
|
@ -38,6 +40,9 @@ pub const MIN_SCREEN_LINES: usize = 1;
|
|||
/// Max size of the window title stack.
|
||||
const TITLE_STACK_MAX_DEPTH: usize = 4096;
|
||||
|
||||
/// Default semantic escape characters.
|
||||
pub const SEMANTIC_ESCAPE_CHARS: &str = ",│`|:\"' ()[]{}<>\t";
|
||||
|
||||
/// Default tab interval, corresponding to terminfo `it` value.
|
||||
const INITIAL_TABSTOPS: usize = 8;
|
||||
|
||||
|
@ -280,20 +285,12 @@ pub struct Term<T> {
|
|||
/// Range going from top to bottom of the terminal, indexed from the top of the viewport.
|
||||
scroll_region: Range<Line>,
|
||||
|
||||
semantic_escape_chars: String,
|
||||
|
||||
/// Modified terminal colors.
|
||||
colors: Colors,
|
||||
|
||||
/// Current style of the cursor.
|
||||
cursor_style: Option<CursorStyle>,
|
||||
|
||||
/// Default style for resetting the cursor.
|
||||
default_cursor_style: CursorStyle,
|
||||
|
||||
/// Style of the vi mode cursor.
|
||||
vi_mode_cursor_style: Option<CursorStyle>,
|
||||
|
||||
/// Proxy for sending events to the event loop.
|
||||
event_proxy: T,
|
||||
|
||||
|
@ -308,7 +305,58 @@ pub struct Term<T> {
|
|||
damage: TermDamageState,
|
||||
|
||||
/// Config directly for the terminal.
|
||||
config: Terminal,
|
||||
config: Config,
|
||||
}
|
||||
|
||||
/// Configuration options for the [`Term`].
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Config {
|
||||
/// The maximum amount of scrolling history.
|
||||
pub scrolling_history: usize,
|
||||
|
||||
/// Default cursor style to reset the cursor to.
|
||||
pub default_cursor_style: CursorStyle,
|
||||
|
||||
/// Cursor style for Vi mode.
|
||||
pub vi_mode_cursor_style: Option<CursorStyle>,
|
||||
|
||||
/// The characters which terminate semantic selection.
|
||||
///
|
||||
/// The default value is [`SEMANTIC_ESCAPE_CHARS`].
|
||||
pub semantic_escape_chars: String,
|
||||
|
||||
/// OSC52 support mode.
|
||||
pub osc52: Osc52,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
scrolling_history: 10000,
|
||||
semantic_escape_chars: SEMANTIC_ESCAPE_CHARS.to_owned(),
|
||||
default_cursor_style: Default::default(),
|
||||
vi_mode_cursor_style: Default::default(),
|
||||
osc52: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// OSC 52 behavior.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "lowercase"))]
|
||||
pub enum Osc52 {
|
||||
/// The handling of the escape sequence is disabled.
|
||||
Disabled,
|
||||
/// Only copy sequence is accepted.
|
||||
///
|
||||
/// This option is the default as a compromise between entirely
|
||||
/// disabling it (the most secure) and allowing `paste` (the less secure).
|
||||
#[default]
|
||||
OnlyCopy,
|
||||
/// Only paste sequence is accepted.
|
||||
OnlyPaste,
|
||||
/// Both are accepted.
|
||||
CopyPaste,
|
||||
}
|
||||
|
||||
impl<T> Term<T> {
|
||||
|
@ -334,11 +382,11 @@ impl<T> Term<T> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn new<D: Dimensions>(config: &Config, dimensions: &D, event_proxy: T) -> Term<T> {
|
||||
pub fn new<D: Dimensions>(options: Config, dimensions: &D, event_proxy: T) -> Term<T> {
|
||||
let num_cols = dimensions.columns();
|
||||
let num_lines = dimensions.screen_lines();
|
||||
|
||||
let history_size = config.scrolling.history() as usize;
|
||||
let history_size = options.scrolling_history;
|
||||
let grid = Grid::new(num_lines, num_cols, history_size);
|
||||
let alt = Grid::new(num_lines, num_cols, 0);
|
||||
|
||||
|
@ -358,17 +406,14 @@ impl<T> Term<T> {
|
|||
mode: Default::default(),
|
||||
scroll_region,
|
||||
colors: color::Colors::default(),
|
||||
semantic_escape_chars: config.selection.semantic_escape_chars.to_owned(),
|
||||
cursor_style: None,
|
||||
default_cursor_style: config.cursor.style(),
|
||||
vi_mode_cursor_style: config.cursor.vi_mode_style(),
|
||||
event_proxy,
|
||||
is_focused: true,
|
||||
title: None,
|
||||
title_stack: Vec::new(),
|
||||
selection: None,
|
||||
damage,
|
||||
config: config.terminal.clone(),
|
||||
config: options,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -446,13 +491,12 @@ impl<T> Term<T> {
|
|||
self.damage.damage_line(line, left, right);
|
||||
}
|
||||
|
||||
pub fn update_config(&mut self, config: &Config)
|
||||
/// Set new options for the [`Term`].
|
||||
pub fn set_options(&mut self, options: Config)
|
||||
where
|
||||
T: EventListener,
|
||||
{
|
||||
self.semantic_escape_chars = config.selection.semantic_escape_chars.to_owned();
|
||||
self.default_cursor_style = config.cursor.style();
|
||||
self.vi_mode_cursor_style = config.cursor.vi_mode_style();
|
||||
self.config = options;
|
||||
|
||||
let title_event = match &self.title {
|
||||
Some(title) => Event::Title(title.clone()),
|
||||
|
@ -462,13 +506,11 @@ impl<T> Term<T> {
|
|||
self.event_proxy.send_event(title_event);
|
||||
|
||||
if self.mode.contains(TermMode::ALT_SCREEN) {
|
||||
self.inactive_grid.update_history(config.scrolling.history() as usize);
|
||||
self.inactive_grid.update_history(self.config.scrolling_history);
|
||||
} else {
|
||||
self.grid.update_history(config.scrolling.history() as usize);
|
||||
self.grid.update_history(self.config.scrolling_history);
|
||||
}
|
||||
|
||||
self.config = config.terminal.clone();
|
||||
|
||||
// Damage everything on config updates.
|
||||
self.mark_fully_damaged();
|
||||
}
|
||||
|
@ -870,7 +912,7 @@ impl<T> Term<T> {
|
|||
|
||||
#[inline]
|
||||
pub fn semantic_escape_chars(&self) -> &str {
|
||||
&self.semantic_escape_chars
|
||||
&self.config.semantic_escape_chars
|
||||
}
|
||||
|
||||
/// Active terminal cursor style.
|
||||
|
@ -878,10 +920,10 @@ impl<T> Term<T> {
|
|||
/// While vi mode is active, this will automatically return the vi mode cursor style.
|
||||
#[inline]
|
||||
pub fn cursor_style(&self) -> CursorStyle {
|
||||
let cursor_style = self.cursor_style.unwrap_or(self.default_cursor_style);
|
||||
let cursor_style = self.cursor_style.unwrap_or(self.config.default_cursor_style);
|
||||
|
||||
if self.mode.contains(TermMode::VI) {
|
||||
self.vi_mode_cursor_style.unwrap_or(cursor_style)
|
||||
self.config.vi_mode_cursor_style.unwrap_or(cursor_style)
|
||||
} else {
|
||||
cursor_style
|
||||
}
|
||||
|
@ -1501,11 +1543,9 @@ impl<T: EventListener> Handler for Term<T> {
|
|||
|
||||
/// Set the indexed color value.
|
||||
#[inline]
|
||||
fn set_color(&mut self, index: usize, color: VteRgb) {
|
||||
fn set_color(&mut self, index: usize, color: Rgb) {
|
||||
trace!("Setting color[{}] = {:?}", index, color);
|
||||
|
||||
let color = color.into();
|
||||
|
||||
// Damage terminal if the color changed and it's not the cursor.
|
||||
if index != NamedColor::Cursor as usize && self.colors[index] != Some(color) {
|
||||
self.mark_fully_damaged();
|
||||
|
@ -1713,7 +1753,7 @@ impl<T: EventListener> Handler for Term<T> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn set_hyperlink(&mut self, hyperlink: Option<VteHyperlink>) {
|
||||
fn set_hyperlink(&mut self, hyperlink: Option<Hyperlink>) {
|
||||
trace!("Setting hyperlink: {:?}", hyperlink);
|
||||
self.grid.cursor.template.set_hyperlink(hyperlink.map(|e| e.into()));
|
||||
}
|
||||
|
@ -1818,7 +1858,7 @@ impl<T: EventListener> Handler for Term<T> {
|
|||
ansi::Mode::ColumnMode => self.deccolm(),
|
||||
ansi::Mode::Insert => self.mode.insert(TermMode::INSERT),
|
||||
ansi::Mode::BlinkingCursor => {
|
||||
let style = self.cursor_style.get_or_insert(self.default_cursor_style);
|
||||
let style = self.cursor_style.get_or_insert(self.config.default_cursor_style);
|
||||
style.blinking = true;
|
||||
self.event_proxy.send_event(Event::CursorBlinkingChange);
|
||||
},
|
||||
|
@ -1863,7 +1903,7 @@ impl<T: EventListener> Handler for Term<T> {
|
|||
self.mark_fully_damaged();
|
||||
},
|
||||
ansi::Mode::BlinkingCursor => {
|
||||
let style = self.cursor_style.get_or_insert(self.default_cursor_style);
|
||||
let style = self.cursor_style.get_or_insert(self.config.default_cursor_style);
|
||||
style.blinking = false;
|
||||
self.event_proxy.send_event(Event::CursorBlinkingChange);
|
||||
},
|
||||
|
@ -1932,7 +1972,7 @@ impl<T: EventListener> Handler for Term<T> {
|
|||
fn set_cursor_shape(&mut self, shape: CursorShape) {
|
||||
trace!("Setting cursor shape {:?}", shape);
|
||||
|
||||
let style = self.cursor_style.get_or_insert(self.default_cursor_style);
|
||||
let style = self.cursor_style.get_or_insert(self.config.default_cursor_style);
|
||||
style.shape = shape;
|
||||
}
|
||||
|
||||
|
@ -2118,14 +2158,14 @@ impl<'a> RenderableContent<'a> {
|
|||
pub mod test {
|
||||
use super::*;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
use unicode_width::UnicodeWidthChar;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::event::VoidListener;
|
||||
use crate::index::Column;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct TermSize {
|
||||
pub columns: usize,
|
||||
pub screen_lines: usize,
|
||||
|
@ -2180,7 +2220,7 @@ pub mod test {
|
|||
|
||||
// Create terminal with the appropriate dimensions.
|
||||
let size = TermSize::new(num_cols, lines.len());
|
||||
let mut term = Term::new(&Config::default(), &size, VoidListener);
|
||||
let mut term = Term::new(Config::default(), &size, VoidListener);
|
||||
|
||||
// Fill terminal with content.
|
||||
for (line, text) in lines.iter().enumerate() {
|
||||
|
@ -2214,19 +2254,18 @@ mod tests {
|
|||
|
||||
use std::mem;
|
||||
|
||||
use crate::ansi::{self, CharsetIndex, Handler, StandardCharset};
|
||||
use crate::config::Config;
|
||||
use crate::event::VoidListener;
|
||||
use crate::grid::{Grid, Scroll};
|
||||
use crate::index::{Column, Point, Side};
|
||||
use crate::selection::{Selection, SelectionType};
|
||||
use crate::term::cell::{Cell, Flags};
|
||||
use crate::term::test::TermSize;
|
||||
use crate::vte::ansi::{self, CharsetIndex, Handler, StandardCharset};
|
||||
|
||||
#[test]
|
||||
fn scroll_display_page_up() {
|
||||
let size = TermSize::new(5, 10);
|
||||
let mut term = Term::new(&Config::default(), &size, VoidListener);
|
||||
let mut term = Term::new(Config::default(), &size, VoidListener);
|
||||
|
||||
// Create 11 lines of scrollback.
|
||||
for _ in 0..20 {
|
||||
|
@ -2252,7 +2291,7 @@ mod tests {
|
|||
#[test]
|
||||
fn scroll_display_page_down() {
|
||||
let size = TermSize::new(5, 10);
|
||||
let mut term = Term::new(&Config::default(), &size, VoidListener);
|
||||
let mut term = Term::new(Config::default(), &size, VoidListener);
|
||||
|
||||
// Create 11 lines of scrollback.
|
||||
for _ in 0..20 {
|
||||
|
@ -2282,7 +2321,7 @@ mod tests {
|
|||
#[test]
|
||||
fn simple_selection_works() {
|
||||
let size = TermSize::new(5, 5);
|
||||
let mut term = Term::new(&Config::default(), &size, VoidListener);
|
||||
let mut term = Term::new(Config::default(), &size, VoidListener);
|
||||
let grid = term.grid_mut();
|
||||
for i in 0..4 {
|
||||
if i == 1 {
|
||||
|
@ -2328,7 +2367,7 @@ mod tests {
|
|||
#[test]
|
||||
fn semantic_selection_works() {
|
||||
let size = TermSize::new(5, 3);
|
||||
let mut term = Term::new(&Config::default(), &size, VoidListener);
|
||||
let mut term = Term::new(Config::default(), &size, VoidListener);
|
||||
let mut grid: Grid<Cell> = Grid::new(3, 5, 0);
|
||||
for i in 0..5 {
|
||||
for j in 0..2 {
|
||||
|
@ -2343,7 +2382,7 @@ mod tests {
|
|||
let mut escape_chars = String::from("\"");
|
||||
|
||||
mem::swap(&mut term.grid, &mut grid);
|
||||
mem::swap(&mut term.semantic_escape_chars, &mut escape_chars);
|
||||
mem::swap(&mut term.config.semantic_escape_chars, &mut escape_chars);
|
||||
|
||||
{
|
||||
term.selection = Some(Selection::new(
|
||||
|
@ -2376,7 +2415,7 @@ mod tests {
|
|||
#[test]
|
||||
fn line_selection_works() {
|
||||
let size = TermSize::new(5, 1);
|
||||
let mut term = Term::new(&Config::default(), &size, VoidListener);
|
||||
let mut term = Term::new(Config::default(), &size, VoidListener);
|
||||
let mut grid: Grid<Cell> = Grid::new(1, 5, 0);
|
||||
for i in 0..5 {
|
||||
grid[Line(0)][Column(i)].c = 'a';
|
||||
|
@ -2397,7 +2436,7 @@ mod tests {
|
|||
#[test]
|
||||
fn block_selection_works() {
|
||||
let size = TermSize::new(5, 5);
|
||||
let mut term = Term::new(&Config::default(), &size, VoidListener);
|
||||
let mut term = Term::new(Config::default(), &size, VoidListener);
|
||||
let grid = term.grid_mut();
|
||||
for i in 1..4 {
|
||||
grid[Line(i)][Column(0)].c = '"';
|
||||
|
@ -2453,7 +2492,7 @@ mod tests {
|
|||
#[test]
|
||||
fn input_line_drawing_character() {
|
||||
let size = TermSize::new(7, 17);
|
||||
let mut term = Term::new(&Config::default(), &size, VoidListener);
|
||||
let mut term = Term::new(Config::default(), &size, VoidListener);
|
||||
let cursor = Point::new(Line(0), Column(0));
|
||||
term.configure_charset(CharsetIndex::G0, StandardCharset::SpecialCharacterAndLineDrawing);
|
||||
term.input('a');
|
||||
|
@ -2464,7 +2503,7 @@ mod tests {
|
|||
#[test]
|
||||
fn clearing_viewport_keeps_history_position() {
|
||||
let size = TermSize::new(10, 20);
|
||||
let mut term = Term::new(&Config::default(), &size, VoidListener);
|
||||
let mut term = Term::new(Config::default(), &size, VoidListener);
|
||||
|
||||
// Create 10 lines of scrollback.
|
||||
for _ in 0..29 {
|
||||
|
@ -2485,7 +2524,7 @@ mod tests {
|
|||
#[test]
|
||||
fn clearing_viewport_with_vi_mode_keeps_history_position() {
|
||||
let size = TermSize::new(10, 20);
|
||||
let mut term = Term::new(&Config::default(), &size, VoidListener);
|
||||
let mut term = Term::new(Config::default(), &size, VoidListener);
|
||||
|
||||
// Create 10 lines of scrollback.
|
||||
for _ in 0..29 {
|
||||
|
@ -2511,7 +2550,7 @@ mod tests {
|
|||
#[test]
|
||||
fn clearing_scrollback_resets_display_offset() {
|
||||
let size = TermSize::new(10, 20);
|
||||
let mut term = Term::new(&Config::default(), &size, VoidListener);
|
||||
let mut term = Term::new(Config::default(), &size, VoidListener);
|
||||
|
||||
// Create 10 lines of scrollback.
|
||||
for _ in 0..29 {
|
||||
|
@ -2532,7 +2571,7 @@ mod tests {
|
|||
#[test]
|
||||
fn clearing_scrollback_sets_vi_cursor_into_viewport() {
|
||||
let size = TermSize::new(10, 20);
|
||||
let mut term = Term::new(&Config::default(), &size, VoidListener);
|
||||
let mut term = Term::new(Config::default(), &size, VoidListener);
|
||||
|
||||
// Create 10 lines of scrollback.
|
||||
for _ in 0..29 {
|
||||
|
@ -2558,7 +2597,7 @@ mod tests {
|
|||
#[test]
|
||||
fn clear_saved_lines() {
|
||||
let size = TermSize::new(7, 17);
|
||||
let mut term = Term::new(&Config::default(), &size, VoidListener);
|
||||
let mut term = Term::new(Config::default(), &size, VoidListener);
|
||||
|
||||
// Add one line of scrollback.
|
||||
term.grid.scroll_up(&(Line(0)..Line(1)), 1);
|
||||
|
@ -2580,7 +2619,7 @@ mod tests {
|
|||
#[test]
|
||||
fn vi_cursor_keep_pos_on_scrollback_buffer() {
|
||||
let size = TermSize::new(5, 10);
|
||||
let mut term = Term::new(&Config::default(), &size, VoidListener);
|
||||
let mut term = Term::new(Config::default(), &size, VoidListener);
|
||||
|
||||
// Create 11 lines of scrollback.
|
||||
for _ in 0..20 {
|
||||
|
@ -2600,7 +2639,7 @@ mod tests {
|
|||
#[test]
|
||||
fn grow_lines_updates_active_cursor_pos() {
|
||||
let mut size = TermSize::new(100, 10);
|
||||
let mut term = Term::new(&Config::default(), &size, VoidListener);
|
||||
let mut term = Term::new(Config::default(), &size, VoidListener);
|
||||
|
||||
// Create 10 lines of scrollback.
|
||||
for _ in 0..19 {
|
||||
|
@ -2620,7 +2659,7 @@ mod tests {
|
|||
#[test]
|
||||
fn grow_lines_updates_inactive_cursor_pos() {
|
||||
let mut size = TermSize::new(100, 10);
|
||||
let mut term = Term::new(&Config::default(), &size, VoidListener);
|
||||
let mut term = Term::new(Config::default(), &size, VoidListener);
|
||||
|
||||
// Create 10 lines of scrollback.
|
||||
for _ in 0..19 {
|
||||
|
@ -2646,7 +2685,7 @@ mod tests {
|
|||
#[test]
|
||||
fn shrink_lines_updates_active_cursor_pos() {
|
||||
let mut size = TermSize::new(100, 10);
|
||||
let mut term = Term::new(&Config::default(), &size, VoidListener);
|
||||
let mut term = Term::new(Config::default(), &size, VoidListener);
|
||||
|
||||
// Create 10 lines of scrollback.
|
||||
for _ in 0..19 {
|
||||
|
@ -2666,7 +2705,7 @@ mod tests {
|
|||
#[test]
|
||||
fn shrink_lines_updates_inactive_cursor_pos() {
|
||||
let mut size = TermSize::new(100, 10);
|
||||
let mut term = Term::new(&Config::default(), &size, VoidListener);
|
||||
let mut term = Term::new(Config::default(), &size, VoidListener);
|
||||
|
||||
// Create 10 lines of scrollback.
|
||||
for _ in 0..19 {
|
||||
|
@ -2692,7 +2731,7 @@ mod tests {
|
|||
#[test]
|
||||
fn damage_public_usage() {
|
||||
let size = TermSize::new(10, 10);
|
||||
let mut term = Term::new(&Config::default(), &size, VoidListener);
|
||||
let mut term = Term::new(Config::default(), &size, VoidListener);
|
||||
// Reset terminal for partial damage tests since it's initialized as fully damaged.
|
||||
term.reset_damage();
|
||||
|
||||
|
@ -2785,7 +2824,7 @@ mod tests {
|
|||
#[test]
|
||||
fn damage_cursor_movements() {
|
||||
let size = TermSize::new(10, 10);
|
||||
let mut term = Term::new(&Config::default(), &size, VoidListener);
|
||||
let mut term = Term::new(Config::default(), &size, VoidListener);
|
||||
let num_cols = term.columns();
|
||||
// Reset terminal for partial damage tests since it's initialized as fully damaged.
|
||||
term.reset_damage();
|
||||
|
@ -2883,7 +2922,7 @@ mod tests {
|
|||
#[test]
|
||||
fn full_damage() {
|
||||
let size = TermSize::new(100, 10);
|
||||
let mut term = Term::new(&Config::default(), &size, VoidListener);
|
||||
let mut term = Term::new(Config::default(), &size, VoidListener);
|
||||
|
||||
assert!(term.damage.is_fully_damaged);
|
||||
for _ in 0..20 {
|
||||
|
@ -2904,7 +2943,7 @@ mod tests {
|
|||
assert!(!term.damage.is_fully_damaged);
|
||||
term.reset_damage();
|
||||
|
||||
term.update_config(&Config::default());
|
||||
term.set_options(Config::default());
|
||||
assert!(term.damage.is_fully_damaged);
|
||||
term.reset_damage();
|
||||
|
||||
|
@ -2930,12 +2969,12 @@ mod tests {
|
|||
term.reset_damage();
|
||||
|
||||
let color_index = 257;
|
||||
term.set_color(color_index, VteRgb::default());
|
||||
term.set_color(color_index, Rgb::default());
|
||||
assert!(term.damage.is_fully_damaged);
|
||||
term.reset_damage();
|
||||
|
||||
// Setting the same color once again shouldn't trigger full damage.
|
||||
term.set_color(color_index, VteRgb::default());
|
||||
term.set_color(color_index, Rgb::default());
|
||||
assert!(!term.damage.is_fully_damaged);
|
||||
|
||||
term.reset_color(color_index);
|
||||
|
@ -2943,7 +2982,7 @@ mod tests {
|
|||
term.reset_damage();
|
||||
|
||||
// We shouldn't trigger fully damage when cursor gets update.
|
||||
term.set_color(NamedColor::Cursor as usize, VteRgb::default());
|
||||
term.set_color(NamedColor::Cursor as usize, Rgb::default());
|
||||
assert!(!term.damage.is_fully_damaged);
|
||||
|
||||
// However requesting terminal damage should mark terminal as fully damaged in `Insert`
|
||||
|
@ -2969,7 +3008,7 @@ mod tests {
|
|||
#[test]
|
||||
fn window_title() {
|
||||
let size = TermSize::new(7, 17);
|
||||
let mut term = Term::new(&Config::default(), &size, VoidListener);
|
||||
let mut term = Term::new(Config::default(), &size, VoidListener);
|
||||
|
||||
// Title None by default.
|
||||
assert_eq!(term.title, None);
|
||||
|
|
|
@ -515,7 +515,7 @@ impl<T> Term<T> {
|
|||
/// Find left end of semantic block.
|
||||
#[must_use]
|
||||
pub fn semantic_search_left(&self, point: Point) -> Point {
|
||||
match self.inline_search_left(point, &self.semantic_escape_chars) {
|
||||
match self.inline_search_left(point, self.semantic_escape_chars()) {
|
||||
Ok(point) => self.grid.iter_from(point).next().map_or(point, |cell| cell.point),
|
||||
Err(point) => point,
|
||||
}
|
||||
|
@ -524,7 +524,7 @@ impl<T> Term<T> {
|
|||
/// Find right end of semantic block.
|
||||
#[must_use]
|
||||
pub fn semantic_search_right(&self, point: Point) -> Point {
|
||||
match self.inline_search_right(point, &self.semantic_escape_chars) {
|
||||
match self.inline_search_right(point, self.semantic_escape_chars()) {
|
||||
Ok(point) => self.grid.iter_from(point).prev().map_or(point, |cell| cell.point),
|
||||
Err(point) => point,
|
||||
}
|
||||
|
@ -676,9 +676,9 @@ impl<'a, T> Iterator for RegexIter<'a, T> {
|
|||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::index::{Column, Line};
|
||||
use crate::term::test::{mock_term, TermSize};
|
||||
use crate::term::Config;
|
||||
|
||||
#[test]
|
||||
fn regex_right() {
|
||||
|
@ -1052,7 +1052,7 @@ mod tests {
|
|||
#[test]
|
||||
fn wide_without_spacer() {
|
||||
let size = TermSize::new(2, 2);
|
||||
let mut term = Term::new(&Config::default(), &size, ());
|
||||
let mut term = Term::new(Config::default(), &size, ());
|
||||
term.grid[Line(0)][Column(0)].c = 'x';
|
||||
term.grid[Line(0)][Column(1)].c = '字';
|
||||
term.grid[Line(0)][Column(1)].flags = Flags::WIDE_CHAR;
|
||||
|
|
|
@ -4,8 +4,6 @@ use std::path::PathBuf;
|
|||
use std::sync::Arc;
|
||||
use std::{env, io};
|
||||
|
||||
use crate::config::Config;
|
||||
|
||||
use polling::{Event, PollMode, Poller};
|
||||
|
||||
#[cfg(not(windows))]
|
||||
|
@ -18,8 +16,38 @@ pub mod windows;
|
|||
#[cfg(windows)]
|
||||
pub use self::windows::*;
|
||||
|
||||
/// Configuration for the `Pty` interface.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Default)]
|
||||
pub struct Options {
|
||||
/// Shell options.
|
||||
///
|
||||
/// [`None`] will use the default shell.
|
||||
pub shell: Option<Shell>,
|
||||
|
||||
/// Shell startup directory.
|
||||
pub working_directory: Option<PathBuf>,
|
||||
|
||||
/// Remain open after child process exits.
|
||||
pub hold: bool,
|
||||
}
|
||||
|
||||
/// Shell options.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Default)]
|
||||
pub struct Shell {
|
||||
/// Path to a shell program to run on startup.
|
||||
pub(crate) program: String,
|
||||
/// Arguments passed to shell.
|
||||
pub(crate) args: Vec<String>,
|
||||
}
|
||||
|
||||
impl Shell {
|
||||
pub fn new(program: String, args: Vec<String>) -> Self {
|
||||
Self { program, args }
|
||||
}
|
||||
}
|
||||
|
||||
/// This trait defines the behaviour needed to read and/or write to a stream.
|
||||
/// It defines an abstraction over mio's interface in order to allow either one
|
||||
/// It defines an abstraction over polling's interface in order to allow either one
|
||||
/// read/write object or a separate read and write object.
|
||||
pub trait EventedReadWrite {
|
||||
type Reader: io::Read;
|
||||
|
@ -56,7 +84,7 @@ pub trait EventedPty: EventedReadWrite {
|
|||
}
|
||||
|
||||
/// Setup environment variables.
|
||||
pub fn setup_env(config: &Config) {
|
||||
pub fn setup_env() {
|
||||
// Default to 'alacritty' terminfo if it is available, otherwise
|
||||
// default to 'xterm-256color'. May be overridden by user's config
|
||||
// below.
|
||||
|
@ -69,11 +97,6 @@ pub fn setup_env(config: &Config) {
|
|||
// Prevent child processes from inheriting startup notification env.
|
||||
env::remove_var("DESKTOP_STARTUP_ID");
|
||||
env::remove_var("XDG_ACTIVATION_TOKEN");
|
||||
|
||||
// Set env vars from config.
|
||||
for (key, value) in config.env.iter() {
|
||||
env::set_var(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if a terminfo entry exists on the system.
|
||||
|
|
|
@ -22,9 +22,8 @@ use rustix_openpty::rustix::termios::{self, InputModes, OptionalActions};
|
|||
use signal_hook::consts as sigconsts;
|
||||
use signal_hook::low_level::pipe as signal_pipe;
|
||||
|
||||
use crate::config::PtyConfig;
|
||||
use crate::event::{OnResize, WindowSize};
|
||||
use crate::tty::{ChildEvent, EventedPty, EventedReadWrite};
|
||||
use crate::tty::{ChildEvent, EventedPty, EventedReadWrite, Options};
|
||||
|
||||
// Interest in PTY read/writes.
|
||||
pub(crate) const PTY_READ_WRITE_TOKEN: usize = 0;
|
||||
|
@ -194,7 +193,7 @@ fn default_shell_command(shell: &str, user: &str) -> Command {
|
|||
}
|
||||
|
||||
/// Create a new TTY and return a handle to interact with it.
|
||||
pub fn new(config: &PtyConfig, window_size: WindowSize, window_id: u64) -> Result<Pty> {
|
||||
pub fn new(config: &Options, window_size: WindowSize, window_id: u64) -> Result<Pty> {
|
||||
let (master, slave) = make_pty(window_size.to_winsize())?;
|
||||
let master_fd = master.as_raw_fd();
|
||||
let slave_fd = slave.as_raw_fd();
|
||||
|
@ -209,8 +208,8 @@ pub fn new(config: &PtyConfig, window_size: WindowSize, window_id: u64) -> Resul
|
|||
let user = ShellUser::from_env()?;
|
||||
|
||||
let mut builder = if let Some(shell) = config.shell.as_ref() {
|
||||
let mut cmd = Command::new(shell.program());
|
||||
cmd.args(shell.args());
|
||||
let mut cmd = Command::new(&shell.program);
|
||||
cmd.args(shell.args.as_slice());
|
||||
cmd
|
||||
} else {
|
||||
default_shell_command(&user.shell, &user.user)
|
||||
|
|
|
@ -17,11 +17,11 @@ use windows_sys::Win32::System::Threading::{
|
|||
STARTF_USESTDHANDLES, STARTUPINFOEXW, STARTUPINFOW,
|
||||
};
|
||||
|
||||
use crate::config::PtyConfig;
|
||||
use crate::event::{OnResize, WindowSize};
|
||||
use crate::tty::windows::blocking::{UnblockedReader, UnblockedWriter};
|
||||
use crate::tty::windows::child::ChildExitWatcher;
|
||||
use crate::tty::windows::{cmdline, win32_string, Pty};
|
||||
use crate::tty::Options;
|
||||
|
||||
const PIPE_CAPACITY: usize = crate::event_loop::READ_BUFFER_SIZE;
|
||||
|
||||
|
@ -104,7 +104,7 @@ impl Drop for Conpty {
|
|||
// The ConPTY handle can be sent between threads.
|
||||
unsafe impl Send for Conpty {}
|
||||
|
||||
pub fn new(config: &PtyConfig, window_size: WindowSize) -> Option<Pty> {
|
||||
pub fn new(config: &Options, window_size: WindowSize) -> Option<Pty> {
|
||||
let api = ConptyApi::new();
|
||||
let mut pty_handle: HPCON = 0;
|
||||
|
||||
|
|
|
@ -5,10 +5,9 @@ use std::os::windows::ffi::OsStrExt;
|
|||
use std::sync::mpsc::TryRecvError;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::config::{Program, PtyConfig};
|
||||
use crate::event::{OnResize, WindowSize};
|
||||
use crate::tty::windows::child::ChildExitWatcher;
|
||||
use crate::tty::{ChildEvent, EventedPty, EventedReadWrite};
|
||||
use crate::tty::{ChildEvent, EventedPty, EventedReadWrite, Options, Shell};
|
||||
|
||||
mod blocking;
|
||||
mod child;
|
||||
|
@ -34,7 +33,7 @@ pub struct Pty {
|
|||
child_watcher: ChildExitWatcher,
|
||||
}
|
||||
|
||||
pub fn new(config: &PtyConfig, window_size: WindowSize, _window_id: u64) -> Result<Pty> {
|
||||
pub fn new(config: &Options, window_size: WindowSize, _window_id: u64) -> Result<Pty> {
|
||||
conpty::new(config, window_size)
|
||||
.ok_or_else(|| Error::new(ErrorKind::Other, "failed to spawn conpty"))
|
||||
}
|
||||
|
@ -123,12 +122,12 @@ impl OnResize for Pty {
|
|||
}
|
||||
}
|
||||
|
||||
fn cmdline(config: &PtyConfig) -> String {
|
||||
let default_shell = Program::Just("powershell".to_owned());
|
||||
fn cmdline(config: &Options) -> String {
|
||||
let default_shell = Shell::new("powershell".to_owned(), Vec::new());
|
||||
let shell = config.shell.as_ref().unwrap_or(&default_shell);
|
||||
|
||||
once(shell.program())
|
||||
.chain(shell.args().iter().map(|a| a.as_ref()))
|
||||
once(shell.program.as_str())
|
||||
.chain(shell.args.iter().map(|s| s.as_str()))
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use std::cmp::min;
|
||||
|
||||
use alacritty_config_derive::ConfigDeserialize;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::event::EventListener;
|
||||
use crate::grid::{Dimensions, GridCell};
|
||||
|
@ -9,7 +10,8 @@ use crate::term::cell::Flags;
|
|||
use crate::term::Term;
|
||||
|
||||
/// Possible vi mode motion movements.
|
||||
#[derive(ConfigDeserialize, Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "lowercase"))]
|
||||
pub enum ViMotion {
|
||||
/// Move up.
|
||||
Up,
|
||||
|
@ -379,16 +381,15 @@ fn is_boundary<T>(term: &Term<T>, point: Point, direction: Direction) -> bool {
|
|||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use crate::ansi::Handler;
|
||||
use crate::config::Config;
|
||||
use crate::event::VoidListener;
|
||||
use crate::index::{Column, Line};
|
||||
use crate::term::test::TermSize;
|
||||
use crate::term::Term;
|
||||
use crate::term::{Config, Term};
|
||||
use crate::vte::ansi::Handler;
|
||||
|
||||
fn term() -> Term<VoidListener> {
|
||||
let size = TermSize::new(20, 20);
|
||||
Term::new(&Config::default(), &size, VoidListener)
|
||||
Term::new(Config::default(), &size, VoidListener)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -5,14 +5,13 @@ use std::fs::{self, File};
|
|||
use std::io::Read;
|
||||
use std::path::Path;
|
||||
|
||||
use alacritty_terminal::ansi;
|
||||
use alacritty_terminal::config::Config;
|
||||
use alacritty_terminal::event::{Event, EventListener};
|
||||
use alacritty_terminal::grid::{Dimensions, Grid};
|
||||
use alacritty_terminal::index::{Column, Line};
|
||||
use alacritty_terminal::term::cell::Cell;
|
||||
use alacritty_terminal::term::test::TermSize;
|
||||
use alacritty_terminal::term::Term;
|
||||
use alacritty_terminal::term::{Config, Term};
|
||||
use alacritty_terminal::vte::ansi;
|
||||
|
||||
macro_rules! ref_tests {
|
||||
($($name:ident)*) => {
|
||||
|
@ -105,10 +104,10 @@ fn ref_test(dir: &Path) {
|
|||
let grid: Grid<Cell> = json::from_str(&serialized_grid).unwrap();
|
||||
let ref_config: RefConfig = json::from_str(&serialized_cfg).unwrap();
|
||||
|
||||
let mut config = Config::default();
|
||||
config.scrolling.set_history(ref_config.history_size);
|
||||
let mut options = Config::default();
|
||||
options.scrolling_history = ref_config.history_size as usize;
|
||||
|
||||
let mut terminal = Term::new(&config, &size, Mock);
|
||||
let mut terminal = Term::new(options, &size, Mock);
|
||||
let mut parser: ansi::Processor = ansi::Processor::new();
|
||||
|
||||
for byte in recording {
|
||||
|
|
Loading…
Reference in New Issue