Switch to TOML configuration format

This switches Alacritty's default configuration format from yaml to
toml. While yaml is still supported, it is done by converting it to toml
and should be removed entirely in the future.

All existing features were persisted based on my testing. Behavior
should not change much, though `--option` might have slightly different
behavior since the entire line is not interpreted as one line of toml.

A new `alacritty migrate` subcommand has been added which allows
automatic migration from yaml to toml. This also could be used as a
facility to automatically fix configuration file changes in the future.

Closes #6592.
This commit is contained in:
Christian Duerr 2023-06-12 02:23:41 +02:00 committed by GitHub
parent ea2c39e65d
commit bd4906722a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 2106 additions and 1236 deletions

View File

@ -10,10 +10,6 @@ insert_final_newline = true
indent_style = space
indent_size = 4
[*.yml]
indent_style = space
indent_size = 2
[Makefile]
indent_style = tab

View File

@ -76,6 +76,8 @@ jobs:
run: |
scdoc < extra/man/alacritty.1.scd | gzip -c > "./alacritty.1.gz"
scdoc < extra/man/alacritty-msg.1.scd | gzip -c > "./alacritty-msg.1.gz"
scdoc < extra/man/alacritty.5.scd | gzip -c > "./alacritty.5.gz"
scdoc < extra/man/alacritty-bindings.5.scd | gzip -c > "./alacritty-bindings.5.gz"
- name: Upload Assets
run: |
mv ./extra/logo/alacritty-term.svg ./Alacritty.svg
@ -87,4 +89,3 @@ jobs:
./.github/workflows/upload_asset.sh ./extra/completions/_alacritty $GITHUB_TOKEN
./.github/workflows/upload_asset.sh ./extra/linux/Alacritty.desktop $GITHUB_TOKEN
./.github/workflows/upload_asset.sh ./extra/alacritty.info $GITHUB_TOKEN
./.github/workflows/upload_asset.sh ./alacritty.yml $GITHUB_TOKEN

View File

@ -12,16 +12,32 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Minimum Rust version has been bumped to 1.65.0
- Manpages are now generated using `scdoc` (see `INSTALL.md`)
### Added
- Warnings for unused configuration file options
### Changed
- Mode-specific bindings can now be bound in any mode for easier macros
- `--help` output is more compact now and uses more neutral palette
- Configuration file now uses TOML instead of YAML
Run `alacritty migrate` to automatically convert all configuration files
- Deprecated config option `draw_bold_text_with_bright_colors`, use
`colors.draw_bold_text_with_bright_colors`
- Deprecated config option `key_bindings`, use `keyboard.bindings`
- Deprecated config option `mouse_bindings`, use `mouse.bindings`
### Fixed
- Hyperlink preview not being shown when the terminal has exactly 2 lines
- Crash on Windows when changing display scale factor
### Removed
- Config option `background_opacity`, use `window.background_opacity`
- Config option `colors.search.bar`, use `colors.footer_bar` instead
- Config option `mouse.url`, use the `hints` config section
## 0.12.1
### Fixed

View File

@ -86,8 +86,7 @@ Latency is another important factor for Alacritty. On X11, Windows, and macOS th
Code should be documented where appropriate. The existing code can be used as a guidance here and
the general `rustfmt` rules can be followed for formatting.
If any change has been made to the `config.rs` file, these changes should also be documented in the
example configuration file `alacritty.yml`.
If any change has been made to the `config.rs` file, it should also be documented in the man pages.
Changes compared to the latest Alacritty release which have a direct effect on the user (opposed to
things like code refactorings or documentation/tests) additionally need to be documented in the

34
Cargo.lock generated
View File

@ -38,6 +38,7 @@ dependencies = [
"serde",
"serde_json",
"serde_yaml",
"toml 0.7.4",
"unicode-width",
"wayland-client",
"windows-sys 0.48.0",
@ -52,7 +53,7 @@ version = "0.1.2-dev"
dependencies = [
"log",
"serde",
"serde_yaml",
"toml 0.7.4",
"winit",
]
@ -65,8 +66,8 @@ dependencies = [
"proc-macro2",
"quote",
"serde",
"serde_yaml",
"syn 2.0.18",
"toml 0.7.4",
]
[[package]]
@ -92,6 +93,7 @@ dependencies = [
"serde_yaml",
"signal-hook",
"signal-hook-mio",
"toml 0.7.4",
"unicode-width",
"vte",
"windows-sys 0.48.0",
@ -585,7 +587,7 @@ checksum = "e62abb876c07e4754fae5c14cafa77937841f01740637e17d78dc04352f32a5e"
dependencies = [
"cc",
"rustc_version",
"toml",
"toml 0.5.11",
"vswhom",
"winreg",
]
@ -1663,6 +1665,15 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_spanned"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93107647184f6027e3b7dcb2e11034cf95ffa1e3a682c67951963ac69c1c007d"
dependencies = [
"serde",
]
[[package]]
name = "serde_yaml"
version = "0.8.26"
@ -1886,11 +1897,26 @@ dependencies = [
"serde",
]
[[package]]
name = "toml"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6135d499e69981f9ff0ef2167955a5333c35e36f6937d382974566b3d5b94ec"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
]
[[package]]
name = "toml_datetime"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
@ -1899,6 +1925,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"winnow",
]

View File

@ -333,6 +333,8 @@ Installing the manual page requires the additional dependencies `gzip` and `scdo
sudo mkdir -p /usr/local/share/man/man1
scdoc < extra/man/alacritty.1.scd | gzip -c | sudo tee /usr/local/share/man/man1/alacritty.1.gz > /dev/null
scdoc < extra/man/alacritty-msg.1.scd | gzip -c | sudo tee /usr/local/share/man/man1/alacritty-msg.1.gz > /dev/null
scdoc < extra/man/alacritty.5.scd | gzip -c | sudo tee /usr/local/share/man/man5/alacritty.5.gz > /dev/null
scdoc < extra/man/alacritty-bindings.5.scd | gzip -c | sudo tee /usr/local/share/man/man5/alacritty-bindings.5.gz > /dev/null
```
### Shell completions

View File

@ -4,7 +4,8 @@ ASSETS_DIR = extra
RELEASE_DIR = target/release
MANPAGE = $(ASSETS_DIR)/man/alacritty.1.scd
MANPAGE-MSG = $(ASSETS_DIR)/man/alacritty-msg.1.scd
CONFIGFILE = alacritty.yml
MANPAGE-CONFIG = $(ASSETS_DIR)/man/alacritty.5.scd
MANPAGE-CONFIG-BINDINGS = $(ASSETS_DIR)/man/alacritty-bindings.5.scd
TERMINFO = $(ASSETS_DIR)/alacritty.info
COMPLETIONS_DIR = $(ASSETS_DIR)/completions
COMPLETIONS = $(COMPLETIONS_DIR)/_alacritty \
@ -48,10 +49,11 @@ $(APP_NAME)-%: $(TARGET)-%
@mkdir -p $(APP_COMPLETIONS_DIR)
@scdoc < $(MANPAGE) | gzip -c > $(APP_EXTRAS_DIR)/alacritty.1.gz
@scdoc < $(MANPAGE-MSG) | gzip -c > $(APP_EXTRAS_DIR)/alacritty-msg.1.gz
@scdoc < $(MANPAGE-CONFIG) | gzip -c > $(APP_EXTRAS_DIR)/alacritty.5.gz
@scdoc < $(MANPAGE-CONFIG-BINDINGS) | gzip -c > $(APP_EXTRAS_DIR)/alacritty-bindings.5.gz
@tic -xe alacritty,alacritty-direct -o $(APP_EXTRAS_DIR) $(TERMINFO)
@cp -fRp $(APP_TEMPLATE) $(APP_DIR)
@cp -fp $(APP_BINARY) $(APP_BINARY_DIR)
@cp -fp $(CONFIGFILE) $(APP_EXTRAS_DIR)/
@cp -fp $(COMPLETIONS) $(APP_COMPLETIONS_DIR)
@touch -r "$(APP_BINARY)" "$(APP_DIR)/$(APP_NAME)"
@codesign --remove-signature "$(APP_DIR)/$(APP_NAME)"

View File

@ -52,22 +52,25 @@ For everyone else, the detailed instructions to install Alacritty can be found
## Configuration
You can find the default configuration file with documentation for all available
fields on the [GitHub releases page](https://github.com/alacritty/alacritty/releases) for each release.
You can find the documentation for Alacritty's configuration in `man 5
alacritty`, or by looking at [the scdoc file] if you do not have the manpages
installed.
[the scdoc file]: ./extra/man/alacritty.5.scd
Alacritty doesn't create the config file for you, but it looks for one in the
following locations:
1. `$XDG_CONFIG_HOME/alacritty/alacritty.yml`
2. `$XDG_CONFIG_HOME/alacritty.yml`
3. `$HOME/.config/alacritty/alacritty.yml`
4. `$HOME/.alacritty.yml`
1. `$XDG_CONFIG_HOME/alacritty/alacritty.toml`
2. `$XDG_CONFIG_HOME/alacritty.toml`
3. `$HOME/.config/alacritty/alacritty.toml`
4. `$HOME/.alacritty.toml`
### Windows
On Windows, the config file should be located at:
`%APPDATA%\alacritty\alacritty.yml`
`%APPDATA%\alacritty\alacritty.toml`
## Contributing

View File

@ -1,911 +0,0 @@
# Configuration for Alacritty, the GPU enhanced terminal emulator.
# Import additional configuration files
#
# Imports are loaded in order, skipping all missing files, with the importing
# file being loaded last. If a field is already present in a previous import, it
# will be replaced.
#
# All imports must either be absolute paths starting with `/`, or paths relative
# to the user's home directory starting with `~/`.
#import:
# - /path/to/alacritty.yml
# Any items in the `env` entry below will be added as
# environment variables. Some entries may override variables
# set by alacritty itself.
#env:
# TERM variable
#
# This value is used to set the `$TERM` environment variable for
# each instance of Alacritty. If it is not present, alacritty will
# check the local terminfo database and use `alacritty` if it is
# available, otherwise `xterm-256color` is used.
#TERM: alacritty
#window:
# Window dimensions (changes require restart)
#
# Number of lines/columns (not pixels) in the terminal. Both lines and columns
# must be non-zero for this to take effect. The number of columns must be at
# least `2`, while using a value of `0` for columns and lines will fall back
# to the window manager's recommended size
#dimensions:
# columns: 0
# lines: 0
# Window position (changes require restart)
#
# Specified in number of pixels.
# If the position is not set, the window manager will handle the placement.
#position:
# x: 0
# y: 0
# Window padding (changes require restart)
#
# Blank space added around the window in pixels. This padding is scaled
# by DPI and the specified value is always added at both opposing sides.
#padding:
# x: 0
# y: 0
# Spread additional padding evenly around the terminal content.
#dynamic_padding: false
# Window decorations
#
# Values for `decorations`:
# - full: Borders and title bar
# - none: Neither borders nor title bar
#
# Values for `decorations` (macOS only):
# - transparent: Title bar, transparent background and title bar buttons
# - buttonless: Title bar, transparent background and no title bar buttons
#decorations: full
# Background opacity
#
# Window opacity as a floating point number from `0.0` to `1.0`.
# The value `0.0` is completely transparent and `1.0` is opaque.
#opacity: 1.0
# Startup Mode (changes require restart)
#
# Values for `startup_mode`:
# - Windowed
# - Maximized
# - Fullscreen
#
# Values for `startup_mode` (macOS only):
# - SimpleFullscreen
#startup_mode: Windowed
# Window title
#title: Alacritty
# Allow terminal applications to change Alacritty's window title.
#dynamic_title: true
# Window class (Linux/BSD only):
#class:
# Application instance name
#instance: Alacritty
# General application class
#general: Alacritty
# Decorations theme variant
#
# Override the variant of the System theme/GTK theme/Wayland client side
# decorations. Commonly supported values are `Dark`, `Light`, and `None` for
# auto pick-up. Set this to `None` to use the default theme variant.
#decorations_theme_variant: None
# Resize increments
#
# Prefer resizing window by discrete steps equal to cell dimensions.
#resize_increments: false
# Make `Option` key behave as `Alt` (macOS only):
# - OnlyLeft
# - OnlyRight
# - Both
# - None (default)
#option_as_alt: None
#scrolling:
# Maximum number of lines in the scrollback buffer.
# Specifying '0' will disable scrolling.
#history: 10000
# Scrolling distance multiplier.
#multiplier: 3
# Font configuration
#font:
# Normal (roman) font face
#normal:
# Font family
#
# Default:
# - (macOS) Menlo
# - (Linux/BSD) monospace
# - (Windows) Consolas
#family: monospace
# The `style` can be specified to pick a specific face.
#style: Regular
# Bold font face
#bold:
# Font family
#
# If the bold family is not specified, it will fall back to the
# value specified for the normal font.
#family: monospace
# The `style` can be specified to pick a specific face.
#style: Bold
# Italic font face
#italic:
# Font family
#
# If the italic family is not specified, it will fall back to the
# value specified for the normal font.
#family: monospace
# The `style` can be specified to pick a specific face.
#style: Italic
# Bold italic font face
#bold_italic:
# Font family
#
# If the bold italic family is not specified, it will fall back to the
# value specified for the normal font.
#family: monospace
# The `style` can be specified to pick a specific face.
#style: Bold Italic
# Point size
#size: 11.0
# Offset is the extra space around each character. `offset.y` can be thought
# of as modifying the line spacing, and `offset.x` as modifying the letter
# spacing.
#offset:
# x: 0
# y: 0
# Glyph offset determines the locations of the glyphs within their cells with
# the default being at the bottom. Increasing `x` moves the glyph to the
# right, increasing `y` moves the glyph upward.
#glyph_offset:
# x: 0
# y: 0
# Use built-in font for box drawing characters.
#
# If `true`, Alacritty will use a custom built-in font for box drawing
# characters (Unicode points 2500 - 259f).
#
#builtin_box_drawing: true
# If `true`, bold text is drawn using the bright color variants.
#draw_bold_text_with_bright_colors: false
# Colors (Tomorrow Night)
#colors:
# Default colors
#primary:
# background: '#1d1f21'
# foreground: '#c5c8c6'
# Bright and dim foreground colors
#
# The dimmed foreground color is calculated automatically if it is not
# present. If the bright foreground color is not set, or
# `draw_bold_text_with_bright_colors` is `false`, the normal foreground
# color will be used.
#dim_foreground: '#828482'
#bright_foreground: '#eaeaea'
# Cursor colors
#
# Colors which should be used to draw the terminal cursor.
#
# Allowed values are CellForeground/CellBackground, which reference the
# affected cell, or hexadecimal colors like #ff00ff.
#cursor:
# text: CellBackground
# cursor: CellForeground
# Vi mode cursor colors
#
# Colors for the cursor when the vi mode is active.
#
# Allowed values are CellForeground/CellBackground, which reference the
# affected cell, or hexadecimal colors like #ff00ff.
#vi_mode_cursor:
# text: CellBackground
# cursor: CellForeground
# Search colors
#
# Colors used for the search bar and match highlighting.
#search:
# Allowed values are CellForeground/CellBackground, which reference the
# affected cell, or hexadecimal colors like #ff00ff.
#matches:
# foreground: '#000000'
# background: '#ffffff'
#focused_match:
# foreground: '#ffffff'
# background: '#000000'
# Keyboard hints
#hints:
# First character in the hint label
#
# Allowed values are CellForeground/CellBackground, which reference the
# affected cell, or hexadecimal colors like #ff00ff.
#start:
# foreground: '#1d1f21'
# background: '#e9ff5e'
# All characters after the first one in the hint label
#
# Allowed values are CellForeground/CellBackground, which reference the
# affected cell, or hexadecimal colors like #ff00ff.
#end:
# foreground: '#e9ff5e'
# background: '#1d1f21'
# Line indicator
#
# Color used for the indicator displaying the position in history during
# search and vi mode.
#
# By default, these will use the opposing primary color.
#line_indicator:
# foreground: None
# background: None
# Footer bar
#
# Color used for the footer bar on the bottom, used by search regex input,
# hyperlink URI preview, etc.
#
#footer_bar:
# background: '#c5c8c6'
# foreground: '#1d1f21'
# Selection colors
#
# Colors which should be used to draw the selection area.
#
# Allowed values are CellForeground/CellBackground, which reference the
# affected cell, or hexadecimal colors like #ff00ff.
#selection:
# text: CellBackground
# background: CellForeground
# Normal colors
#normal:
# black: '#1d1f21'
# red: '#cc6666'
# green: '#b5bd68'
# yellow: '#f0c674'
# blue: '#81a2be'
# magenta: '#b294bb'
# cyan: '#8abeb7'
# white: '#c5c8c6'
# Bright colors
#bright:
# black: '#666666'
# red: '#d54e53'
# green: '#b9ca4a'
# yellow: '#e7c547'
# blue: '#7aa6da'
# magenta: '#c397d8'
# cyan: '#70c0b1'
# white: '#eaeaea'
# Dim colors
#
# If the dim colors are not set, they will be calculated automatically based
# on the `normal` colors.
#dim:
# black: '#131415'
# red: '#864343'
# green: '#777c44'
# yellow: '#9e824c'
# blue: '#556a7d'
# magenta: '#75617b'
# cyan: '#5b7d78'
# white: '#828482'
# Indexed Colors
#
# The indexed colors include all colors from 16 to 256.
# When these are not set, they're filled with sensible defaults.
#
# Example:
# `- { index: 16, color: '#ff00ff' }`
#
#indexed_colors: []
# Transparent cell backgrounds
#
# Whether or not `window.opacity` applies to all cell backgrounds or only to
# the default background. When set to `true` all cells will be transparent
# regardless of their background color.
#transparent_background_colors: false
# Bell
#
# The bell is rung every time the BEL control character is received.
#bell:
# Visual Bell Animation
#
# Animation effect for flashing the screen when the visual bell is rung.
#
# Values for `animation`:
# - Ease
# - EaseOut
# - EaseOutSine
# - EaseOutQuad
# - EaseOutCubic
# - EaseOutQuart
# - EaseOutQuint
# - EaseOutExpo
# - EaseOutCirc
# - Linear
#animation: EaseOutExpo
# Duration of the visual bell flash in milliseconds. A `duration` of `0` will
# disable the visual bell animation.
#duration: 0
# Visual bell animation color.
#color: '#ffffff'
# Bell Command
#
# This program is executed whenever the bell is rung.
#
# When set to `command: None`, no command will be executed.
#
# Example:
# command:
# program: notify-send
# args: ["Hello, World!"]
#
#command: None
#selection:
# This string contains all characters that are used as separators for
# "semantic words" in Alacritty.
#semantic_escape_chars: ",│`|:\"' ()[]{}<>\t"
# When set to `true`, selected text will be copied to the primary clipboard.
#save_to_clipboard: false
#cursor:
# Cursor style
#style:
# Cursor shape
#
# Values for `shape`:
# - ▇ Block
# - _ Underline
# - | Beam
#shape: Block
# Cursor blinking state
#
# Values for `blinking`:
# - Never: Prevent the cursor from ever blinking
# - Off: Disable blinking by default
# - On: Enable blinking by default
# - Always: Force the cursor to always blink
#blinking: Off
# Vi mode cursor style
#
# If the vi mode cursor style is `None` or not specified, it will fall back to
# the style of the active value of the normal cursor.
#
# See `cursor.style` for available options.
#vi_mode_style: None
# Cursor blinking interval in milliseconds.
#blink_interval: 750
# Time after which cursor stops blinking, in seconds.
#
# Specifying '0' will disable timeout for blinking.
#blink_timeout: 5
# If this is `true`, the cursor will be rendered as a hollow box when the
# window is not focused.
#unfocused_hollow: true
# Thickness of the cursor relative to the cell width as floating point number
# from `0.0` to `1.0`.
#thickness: 0.15
# Live config reload (changes require restart)
#live_config_reload: true
# Shell
#
# You can set `shell.program` to the path of your favorite shell, e.g.
# `/bin/fish`. Entries in `shell.args` are passed unmodified as arguments to the
# shell.
#
# Default:
# - (Linux/BSD/macOS) `$SHELL` or the user's login shell, if `$SHELL` is unset
# - (Windows) powershell
#shell:
# program: /bin/bash
# args:
# - --login
# Startup directory
#
# Directory the shell is started in. If this is unset, or `None`, the working
# directory of the parent process will be used.
#working_directory: None
# Offer IPC using `alacritty msg` (unix only)
#ipc_socket: true
#mouse:
# Click settings
#
# The `double_click` and `triple_click` settings control the time
# alacritty should wait for accepting multiple clicks as one double
# or triple click.
#double_click: { threshold: 300 }
#triple_click: { threshold: 300 }
# If this is `true`, the cursor is temporarily hidden when typing.
#hide_when_typing: false
# Hints
#
# Terminal hints can be used to find text or hyperlink in the visible part of
# the terminal and pipe it to other applications.
#hints:
# Keys used for the hint labels.
#alphabet: "jfkdls;ahgurieowpq"
# List with all available hints
#
# Each hint must have any of `regex` or `hyperlinks` field and either an
# `action` or a `command` field. The fields `mouse`, `binding` and
# `post_processing` are optional.
#
# The `hyperlinks` option will cause OSC 8 escape sequence hyperlinks to be
# highlighted.
#
# The fields `command`, `binding.key`, `binding.mods`, `binding.mode` and
# `mouse.mods` accept the same values as they do in the `key_bindings` section.
#
# The `mouse.enabled` field controls if the hint should be underlined while
# the mouse with all `mouse.mods` keys held or the vi mode cursor is above it.
#
# If the `post_processing` field is set to `true`, heuristics will be used to
# shorten the match if there are characters likely not to be part of the hint
# (e.g. a trailing `.`). This is most useful for URIs and applies only to
# `regex` matches.
#
# Values for `action`:
# - Copy
# Copy the hint's text to the clipboard.
# - Paste
# Paste the hint's text to the terminal or search.
# - Select
# Select the hint's text.
# - MoveViModeCursor
# Move the vi mode cursor to the beginning of the hint.
#enabled:
# - regex: "(ipfs:|ipns:|magnet:|mailto:|gemini:|gopher:|https:|http:|news:|file:|git:|ssh:|ftp:)\
# [^\u0000-\u001F\u007F-\u009F<>\"\\s{-}\\^⟨⟩`]+"
# hyperlinks: true
# command: xdg-open
# post_processing: true
# mouse:
# enabled: true
# mods: None
# binding:
# key: U
# mods: Control|Shift
# Mouse bindings
#
# Mouse bindings are specified as a list of objects, much like the key
# bindings further below.
#
# To trigger mouse bindings when an application running within Alacritty
# captures the mouse, the `Shift` modifier is automatically added as a
# requirement.
#
# Each mouse binding will specify a:
#
# - `mouse`:
#
# - Middle
# - Left
# - Right
# - Numeric identifier such as `5`
#
# - `action` (see key bindings for actions not exclusive to mouse mode)
#
# - Mouse exclusive actions:
#
# - ExpandSelection
# Expand the selection to the current mouse cursor location.
#
# And optionally:
#
# - `mods` (see key bindings)
#mouse_bindings:
# - { mouse: Right, action: ExpandSelection }
# - { mouse: Right, mods: Control, action: ExpandSelection }
# - { mouse: Middle, mode: ~Vi, action: PasteSelection }
# Key bindings
#
# Key bindings are specified as a list of objects. For example, this is the
# default paste binding:
#
# `- { key: V, mods: Control|Shift, action: Paste }`
#
# Each key binding will specify a:
#
# - `key`: Identifier of the key pressed
#
# - A-Z
# - F1-F24
# - Key0-Key9
#
# A full list with available key codes can be found here:
# https://docs.rs/winit/*/winit/event/enum.VirtualKeyCode.html#variants
#
# Instead of using the name of the keys, the `key` field also supports using
# the scancode of the desired key. Scancodes have to be specified as a
# decimal number. This command will allow you to display the hex scancodes
# for certain keys:
#
# `showkey --scancodes`.
#
# Then exactly one of:
#
# - `chars`: Send a byte sequence to the running application
#
# The `chars` field writes the specified string to the terminal. This makes
# it possible to pass escape sequences. To find escape codes for bindings
# like `PageUp` (`"\x1b[5~"`), you can run the command `showkey -a` outside
# of tmux. Note that applications use terminfo to map escape sequences back
# to keys. It is therefore required to update the terminfo when changing an
# escape sequence.
#
# - `action`: Execute a predefined action
#
# - ToggleViMode
# - SearchForward
# Start searching toward the right of the search origin.
# - SearchBackward
# Start searching toward the left of the search origin.
# - Copy
# - Paste
# - IncreaseFontSize
# - DecreaseFontSize
# - ResetFontSize
# - ScrollPageUp
# - ScrollPageDown
# - ScrollHalfPageUp
# - ScrollHalfPageDown
# - ScrollLineUp
# - ScrollLineDown
# - ScrollToTop
# - ScrollToBottom
# - ClearHistory
# Remove the terminal's scrollback history.
# - Hide
# Hide the Alacritty window.
# - Minimize
# Minimize the Alacritty window.
# - Quit
# Quit Alacritty.
# - ToggleFullscreen
# - ToggleMaximized
# - SpawnNewInstance
# Spawn a new instance of Alacritty.
# - CreateNewWindow
# Create a new Alacritty window from the current process.
# - ClearLogNotice
# Clear Alacritty's UI warning and error notice.
# - ClearSelection
# Remove the active selection.
# - ReceiveChar
# - None
#
# - Vi mode exclusive actions:
#
# - Open
# Perform the action of the first matching hint under the vi mode cursor
# with `mouse.enabled` set to `true`.
# - ToggleNormalSelection
# - ToggleLineSelection
# - ToggleBlockSelection
# - ToggleSemanticSelection
# Toggle semantic selection based on `selection.semantic_escape_chars`.
# - CenterAroundViCursor
# Center view around vi mode cursor
#
# - Vi mode exclusive cursor motion actions:
#
# - Up
# One line up.
# - Down
# One line down.
# - Left
# One character left.
# - Right
# One character right.
# - First
# First column, or beginning of the line when already at the first column.
# - Last
# Last column, or beginning of the line when already at the last column.
# - FirstOccupied
# First non-empty cell in this terminal row, or first non-empty cell of
# the line when already at the first cell of the row.
# - High
# Top of the screen.
# - Middle
# Center of the screen.
# - Low
# Bottom of the screen.
# - SemanticLeft
# Start of the previous semantically separated word.
# - SemanticRight
# Start of the next semantically separated word.
# - SemanticLeftEnd
# End of the previous semantically separated word.
# - SemanticRightEnd
# End of the next semantically separated word.
# - WordLeft
# Start of the previous whitespace separated word.
# - WordRight
# Start of the next whitespace separated word.
# - WordLeftEnd
# End of the previous whitespace separated word.
# - WordRightEnd
# End of the next whitespace separated word.
# - Bracket
# Character matching the bracket at the cursor's location.
# - SearchNext
# Beginning of the next match.
# - SearchPrevious
# Beginning of the previous match.
# - SearchStart
# Start of the match to the left of the vi mode cursor.
# - SearchEnd
# End of the match to the right of the vi mode cursor.
#
# - Search mode exclusive actions:
# - SearchFocusNext
# Move the focus to the next search match.
# - SearchFocusPrevious
# Move the focus to the previous search match.
# - SearchConfirm
# - SearchCancel
# - SearchClear
# Reset the search regex.
# - SearchDeleteWord
# Delete the last word in the search regex.
# - SearchHistoryPrevious
# Go to the previous regex in the search history.
# - SearchHistoryNext
# Go to the next regex in the search history.
#
# - macOS exclusive actions:
# - ToggleSimpleFullscreen
# Enter fullscreen without occupying another space.
#
# - Linux/BSD exclusive actions:
#
# - CopySelection
# Copy from the selection buffer.
# - PasteSelection
# Paste from the selection buffer.
#
# - `command`: Fork and execute a specified command plus arguments
#
# The `command` field must be a map containing a `program` string and an
# `args` array of command line parameter strings. For example:
# `{ program: "alacritty", args: ["-e", "vttest"] }`
#
# And optionally:
#
# - `mods`: Key modifiers to filter binding actions
#
# - Command
# - Control
# - Option
# - Super
# - Shift
# - Alt
#
# Multiple `mods` can be combined using `|` like this:
# `mods: Control|Shift`.
# Whitespace and capitalization are relevant and must match the example.
#
# - `mode`: Indicate a binding for only specific terminal reported modes
#
# This is mainly used to send applications the correct escape sequences
# when in different modes.
#
# - AppCursor
# - AppKeypad
# - Search
# - Alt
# - Vi
#
# A `~` operator can be used before a mode to apply the binding whenever
# the mode is *not* active, e.g. `~Alt`.
#
# Bindings are always filled by default, but will be replaced when a new
# binding with the same triggers is defined. To unset a default binding, it can
# be mapped to the `ReceiveChar` action. Alternatively, you can use `None` for
# a no-op if you do not wish to receive input characters for that binding.
#
# If the same trigger is assigned to multiple actions, all of them are executed
# in the order they were defined in.
#key_bindings:
#- { key: Paste, action: Paste }
#- { key: Copy, action: Copy }
#- { key: L, mods: Control, action: ClearLogNotice }
#- { key: L, mods: Control, mode: ~Vi|~Search, chars: "\x0c" }
#- { key: PageUp, mods: Shift, mode: ~Alt, action: ScrollPageUp }
#- { key: PageDown, mods: Shift, mode: ~Alt, action: ScrollPageDown }
#- { key: Home, mods: Shift, mode: ~Alt, action: ScrollToTop }
#- { key: End, mods: Shift, mode: ~Alt, action: ScrollToBottom }
# Vi Mode
#- { key: Space, mods: Shift|Control, mode: ~Search, action: ToggleViMode }
#- { key: Space, mods: Shift|Control, mode: Vi|~Search, action: ScrollToBottom }
#- { key: Escape, mode: Vi|~Search, action: ClearSelection }
#- { key: I, mode: Vi|~Search, action: ToggleViMode }
#- { key: I, mode: Vi|~Search, action: ScrollToBottom }
#- { key: C, mods: Control, mode: Vi|~Search, action: ToggleViMode }
#- { key: Y, mods: Control, mode: Vi|~Search, action: ScrollLineUp }
#- { key: E, mods: Control, mode: Vi|~Search, action: ScrollLineDown }
#- { key: G, mode: Vi|~Search, action: ScrollToTop }
#- { key: G, mods: Shift, mode: Vi|~Search, action: ScrollToBottom }
#- { key: B, mods: Control, mode: Vi|~Search, action: ScrollPageUp }
#- { key: F, mods: Control, mode: Vi|~Search, action: ScrollPageDown }
#- { key: U, mods: Control, mode: Vi|~Search, action: ScrollHalfPageUp }
#- { key: D, mods: Control, mode: Vi|~Search, action: ScrollHalfPageDown }
#- { key: Y, mode: Vi|~Search, action: Copy }
#- { key: Y, mode: Vi|~Search, action: ClearSelection }
#- { key: Copy, mode: Vi|~Search, action: ClearSelection }
#- { key: V, mode: Vi|~Search, action: ToggleNormalSelection }
#- { key: V, mods: Shift, mode: Vi|~Search, action: ToggleLineSelection }
#- { key: V, mods: Control, mode: Vi|~Search, action: ToggleBlockSelection }
#- { key: V, mods: Alt, mode: Vi|~Search, action: ToggleSemanticSelection }
#- { key: Return, mode: Vi|~Search, action: Open }
#- { key: Z, mode: Vi|~Search, action: CenterAroundViCursor }
#- { key: K, mode: Vi|~Search, action: Up }
#- { key: J, mode: Vi|~Search, action: Down }
#- { key: H, mode: Vi|~Search, action: Left }
#- { key: L, mode: Vi|~Search, action: Right }
#- { key: Up, mode: Vi|~Search, action: Up }
#- { key: Down, mode: Vi|~Search, action: Down }
#- { key: Left, mode: Vi|~Search, action: Left }
#- { key: Right, mode: Vi|~Search, action: Right }
#- { key: Key0, mode: Vi|~Search, action: First }
#- { key: Key4, mods: Shift, mode: Vi|~Search, action: Last }
#- { key: Key6, mods: Shift, mode: Vi|~Search, action: FirstOccupied }
#- { key: H, mods: Shift, mode: Vi|~Search, action: High }
#- { key: M, mods: Shift, mode: Vi|~Search, action: Middle }
#- { key: L, mods: Shift, mode: Vi|~Search, action: Low }
#- { key: B, mode: Vi|~Search, action: SemanticLeft }
#- { key: W, mode: Vi|~Search, action: SemanticRight }
#- { key: E, mode: Vi|~Search, action: SemanticRightEnd }
#- { key: B, mods: Shift, mode: Vi|~Search, action: WordLeft }
#- { key: W, mods: Shift, mode: Vi|~Search, action: WordRight }
#- { key: E, mods: Shift, mode: Vi|~Search, action: WordRightEnd }
#- { key: Key5, mods: Shift, mode: Vi|~Search, action: Bracket }
#- { key: Slash, mode: Vi|~Search, action: SearchForward }
#- { key: Slash, mods: Shift, mode: Vi|~Search, action: SearchBackward }
#- { key: N, mode: Vi|~Search, action: SearchNext }
#- { key: N, mods: Shift, mode: Vi|~Search, action: SearchPrevious }
# Search Mode
#- { key: Return, mode: Search|Vi, action: SearchConfirm }
#- { key: Escape, mode: Search, action: SearchCancel }
#- { key: C, mods: Control, mode: Search, action: SearchCancel }
#- { key: U, mods: Control, mode: Search, action: SearchClear }
#- { key: W, mods: Control, mode: Search, action: SearchDeleteWord }
#- { key: P, mods: Control, mode: Search, action: SearchHistoryPrevious }
#- { key: N, mods: Control, mode: Search, action: SearchHistoryNext }
#- { key: Up, mode: Search, action: SearchHistoryPrevious }
#- { key: Down, mode: Search, action: SearchHistoryNext }
#- { key: Return, mode: Search|~Vi, action: SearchFocusNext }
#- { key: Return, mods: Shift, mode: Search|~Vi, action: SearchFocusPrevious }
# (Windows, Linux, and BSD only)
#- { key: V, mods: Control|Shift, mode: ~Vi, action: Paste }
#- { key: C, mods: Control|Shift, action: Copy }
#- { key: F, mods: Control|Shift, mode: ~Search, action: SearchForward }
#- { key: B, mods: Control|Shift, mode: ~Search, action: SearchBackward }
#- { key: C, mods: Control|Shift, mode: Vi|~Search, action: ClearSelection }
#- { key: Insert, mods: Shift, action: PasteSelection }
#- { key: Key0, mods: Control, action: ResetFontSize }
#- { key: Equals, mods: Control, action: IncreaseFontSize }
#- { key: Plus, mods: Control, action: IncreaseFontSize }
#- { key: NumpadAdd, mods: Control, action: IncreaseFontSize }
#- { key: Minus, mods: Control, action: DecreaseFontSize }
#- { key: NumpadSubtract, mods: Control, action: DecreaseFontSize }
# (Windows only)
#- { key: Return, mods: Alt, action: ToggleFullscreen }
# (macOS only)
#- { key: K, mods: Command, mode: ~Vi|~Search, chars: "\x0c" }
#- { key: K, mods: Command, mode: ~Vi|~Search, action: ClearHistory }
#- { key: Key0, mods: Command, action: ResetFontSize }
#- { key: Equals, mods: Command, action: IncreaseFontSize }
#- { key: Plus, mods: Command, action: IncreaseFontSize }
#- { key: NumpadAdd, mods: Command, action: IncreaseFontSize }
#- { key: Minus, mods: Command, action: DecreaseFontSize }
#- { key: NumpadSubtract, mods: Command, action: DecreaseFontSize }
#- { key: V, mods: Command, action: Paste }
#- { key: C, mods: Command, action: Copy }
#- { key: C, mods: Command, mode: Vi|~Search, action: ClearSelection }
#- { key: H, mods: Command, action: Hide }
#- { key: H, mods: Command|Alt, action: HideOtherApplications }
#- { key: M, mods: Command, action: Minimize }
#- { key: Q, mods: Command, action: Quit }
#- { key: W, mods: Command, action: Quit }
#- { key: N, mods: Command, action: CreateNewWindow }
#- { key: F, mods: Command|Control, action: ToggleFullscreen }
#- { key: F, mods: Command, mode: ~Search, action: SearchForward }
#- { key: B, mods: Command, mode: ~Search, action: SearchBackward }
#debug:
# Display the time it takes to redraw each frame.
#render_timer: false
# Keep the log file after quitting Alacritty.
#persistent_logging: false
# Log level
#
# Values for `log_level`:
# - Off
# - Error
# - Warn
# - Info
# - Debug
# - Trace
#log_level: Warn
# Renderer override.
# - glsl3
# - gles2
# - gles2_pure
#renderer: None
# Print all received window events.
#print_events: false
# Highlight window damage information.
#highlight_damage: false

View File

@ -39,6 +39,7 @@ raw-window-handle = "0.5.0"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_yaml = "0.8"
toml = "0.7.1"
unicode-width = "0.1"
winit = { version = "0.28.2", default-features = false, features = ["serde"] }

View File

@ -2,12 +2,10 @@ use std::cmp::max;
use std::os::raw::c_ulong;
use std::path::PathBuf;
#[cfg(unix)]
use clap::Subcommand;
use clap::{ArgAction, Args, Parser, ValueHint};
use clap::{ArgAction, Args, Parser, Subcommand, ValueHint};
use log::{self, error, LevelFilter};
use serde::{Deserialize, Serialize};
use serde_yaml::Value;
use toml::{Table, Value};
use alacritty_terminal::config::{Program, PtyConfig};
@ -30,17 +28,18 @@ pub struct Options {
#[clap(long)]
pub embed: Option<String>,
/// Specify alternative configuration file [default: $XDG_CONFIG_HOME/alacritty/alacritty.yml].
/// Specify alternative configuration file [default:
/// $XDG_CONFIG_HOME/alacritty/alacritty.toml].
#[cfg(not(any(target_os = "macos", windows)))]
#[clap(long, value_hint = ValueHint::FilePath)]
pub config_file: Option<PathBuf>,
/// Specify alternative configuration file [default: %APPDATA%\alacritty\alacritty.yml].
/// Specify alternative configuration file [default: %APPDATA%\alacritty\alacritty.toml].
#[cfg(windows)]
#[clap(long, value_hint = ValueHint::FilePath)]
pub config_file: Option<PathBuf>,
/// Specify alternative configuration file [default: $HOME/.config/alacritty/alacritty.yml].
/// Specify alternative configuration file [default: $HOME/.config/alacritty/alacritty.toml].
#[cfg(target_os = "macos")]
#[clap(long, value_hint = ValueHint::FilePath)]
pub config_file: Option<PathBuf>,
@ -64,14 +63,13 @@ pub struct Options {
/// CLI options for config overrides.
#[clap(skip)]
pub config_options: Value,
pub config_options: TomlValue,
/// Options which can be passed via IPC.
#[clap(flatten)]
pub window_options: WindowOptions,
/// Subcommand passed to the CLI.
#[cfg(unix)]
#[clap(subcommand)]
pub subcommands: Option<Subcommands>,
}
@ -81,7 +79,7 @@ impl Options {
let mut options = Self::parse();
// Convert `--option` flags into serde `Value`.
options.config_options = options_as_value(&options.option);
options.config_options = TomlValue(options_as_value(&options.option));
options
}
@ -125,9 +123,9 @@ impl Options {
}
}
/// Combine multiple options into a [`serde_yaml::Value`].
/// Combine multiple options into a [`toml::Value`].
pub fn options_as_value(options: &[String]) -> Value {
options.iter().fold(Value::default(), |value, option| match option_as_value(option) {
options.iter().fold(Value::Table(Table::new()), |value, option| match toml::from_str(option) {
Ok(new_value) => serde_utils::merge(value, new_value),
Err(_) => {
eprintln!("Ignoring invalid option: {:?}", option);
@ -136,31 +134,6 @@ pub fn options_as_value(options: &[String]) -> Value {
})
}
/// Parse an option in the format of `parent.field=value` as a serde Value.
fn option_as_value(option: &str) -> Result<Value, serde_yaml::Error> {
let mut yaml_text = String::with_capacity(option.len());
let mut closing_brackets = String::new();
for (i, c) in option.chars().enumerate() {
match c {
'=' => {
yaml_text.push_str(": ");
yaml_text.push_str(&option[i + 1..]);
break;
},
'.' => {
yaml_text.push_str(": {");
closing_brackets.push('}');
},
_ => yaml_text.push(c),
}
}
yaml_text += &closing_brackets;
serde_yaml::from_str(&yaml_text)
}
/// Parse the class CLI parameter.
fn parse_class(input: &str) -> Result<Class, String> {
let (general, instance) = match input.split_once(',') {
@ -259,10 +232,11 @@ impl WindowIdentity {
}
/// Available CLI subcommands.
#[cfg(unix)]
#[derive(Subcommand, Debug)]
pub enum Subcommands {
#[cfg(unix)]
Msg(MessageOptions),
Migrate(MigrateOptions),
}
/// Send a message to the Alacritty socket.
@ -289,6 +263,30 @@ pub enum SocketMessage {
Config(IpcConfig),
}
/// Migrate the configuration file.
#[derive(Args, Clone, Debug)]
pub struct MigrateOptions {
/// Path to the configuration file.
#[clap(short, long, value_hint = ValueHint::FilePath)]
pub config_file: Option<PathBuf>,
/// Only output TOML config to stdout.
#[clap(short, long)]
pub dry_run: bool,
/// Do not recurse over imports.
#[clap(short = 'i', long)]
pub skip_imports: bool,
/// Do not move renamed fields to their new location.
#[clap(long)]
pub skip_renames: bool,
#[clap(short, long)]
/// Do not output to STDOUT.
pub silent: bool,
}
/// Subset of options that we pass to 'create-window' IPC subcommand.
#[derive(Serialize, Deserialize, Args, Default, Clone, Debug, PartialEq, Eq)]
pub struct WindowOptions {
@ -320,6 +318,16 @@ pub struct IpcConfig {
pub reset: bool,
}
/// Toml value with default implementation.
#[derive(Debug)]
pub struct TomlValue(pub Value);
impl Default for TomlValue {
fn default() -> Self {
Self(Value::Table(Table::new()))
}
}
#[cfg(test)]
mod tests {
use super::*;
@ -333,7 +341,7 @@ mod tests {
use clap::CommandFactory;
#[cfg(target_os = "linux")]
use clap_complete::Shell;
use serde_yaml::mapping::Mapping;
use toml::Table;
#[test]
fn dynamic_title_ignoring_options_by_default() {
@ -371,38 +379,38 @@ mod tests {
#[test]
fn valid_option_as_value() {
// Test with a single field.
let value = option_as_value("field=true").unwrap();
let value: Value = toml::from_str("field=true").unwrap();
let mut mapping = Mapping::new();
mapping.insert(Value::String(String::from("field")), Value::Bool(true));
let mut table = Table::new();
table.insert(String::from("field"), Value::Boolean(true));
assert_eq!(value, Value::Mapping(mapping));
assert_eq!(value, Value::Table(table));
// Test with nested fields
let value = option_as_value("parent.field=true").unwrap();
let value: Value = toml::from_str("parent.field=true").unwrap();
let mut parent_mapping = Mapping::new();
parent_mapping.insert(Value::String(String::from("field")), Value::Bool(true));
let mut mapping = Mapping::new();
mapping.insert(Value::String(String::from("parent")), Value::Mapping(parent_mapping));
let mut parent_table = Table::new();
parent_table.insert(String::from("field"), Value::Boolean(true));
let mut table = Table::new();
table.insert(String::from("parent"), Value::Table(parent_table));
assert_eq!(value, Value::Mapping(mapping));
assert_eq!(value, Value::Table(table));
}
#[test]
fn invalid_option_as_value() {
let value = option_as_value("}");
let value = toml::from_str::<Value>("}");
assert!(value.is_err());
}
#[test]
fn float_option_as_value() {
let value = option_as_value("float=3.4").unwrap();
let value: Value = toml::from_str("float=3.4").unwrap();
let mut expected = Mapping::new();
expected.insert(Value::String(String::from("float")), Value::Number(3.4.into()));
let mut expected = Table::new();
expected.insert(String::from("float"), Value::Float(3.4));
assert_eq!(value, Value::Mapping(expected));
assert_eq!(value, Value::Table(expected));
}
#[test]

View File

@ -5,7 +5,7 @@ use std::fmt::{self, Debug, Display};
use bitflags::bitflags;
use serde::de::{self, Error as SerdeError, MapAccess, Unexpected, Visitor};
use serde::{Deserialize, Deserializer};
use serde_yaml::Value as SerdeValue;
use toml::Value as SerdeValue;
use winit::event::VirtualKeyCode::*;
use winit::event::{ModifiersState, MouseButton, VirtualKeyCode};
@ -1011,19 +1011,20 @@ impl<'a> Deserialize<'a> for RawBinding {
return Err(<V::Error as Error>::duplicate_field("key"));
}
let val = map.next_value::<SerdeValue>()?;
if val.is_u64() {
let scancode = val.as_u64().unwrap();
if scancode > u64::from(u32::MAX) {
return Err(<V::Error as Error>::custom(format!(
"Invalid key binding, scancode too big: {}",
scancode
)));
}
key = Some(Key::Scancode(scancode as u32));
} else {
let k = Key::deserialize(val).map_err(V::Error::custom)?;
key = Some(k);
let value = map.next_value::<SerdeValue>()?;
match value.as_integer() {
Some(scancode) => match u32::try_from(scancode) {
Ok(scancode) => key = Some(Key::Scancode(scancode)),
Err(_) => {
return Err(<V::Error as Error>::custom(format!(
"Invalid key binding, scancode is too big: {}",
scancode
)));
},
},
None => {
key = Some(Key::deserialize(value).map_err(V::Error::custom)?);
},
}
},
Field::Mods => {
@ -1066,15 +1067,6 @@ impl<'a> Deserialize<'a> for RawBinding {
Err(err) => {
let value = match value {
SerdeValue::String(string) => string,
SerdeValue::Mapping(map) if map.len() == 1 => {
match map.into_iter().next() {
Some((
SerdeValue::String(string),
SerdeValue::Null,
)) => string,
_ => return Err(err),
}
},
_ => return Err(err),
};
return Err(V::Error::custom(format!(

View File

@ -18,16 +18,17 @@ pub struct Colors {
pub line_indicator: LineIndicatorColors,
pub hints: HintColors,
pub transparent_background_colors: bool,
pub draw_bold_text_with_bright_colors: bool,
footer_bar: BarColors,
}
impl Colors {
pub fn footer_bar_foreground(&self) -> Rgb {
self.search.bar.foreground.or(self.footer_bar.foreground).unwrap_or(self.primary.background)
self.footer_bar.foreground.unwrap_or(self.primary.background)
}
pub fn footer_bar_background(&self) -> Rgb {
self.search.bar.background.or(self.footer_bar.background).unwrap_or(self.primary.foreground)
self.footer_bar.background.unwrap_or(self.primary.foreground)
}
}
@ -126,8 +127,6 @@ impl Default for InvertedCellColors {
pub struct SearchColors {
pub focused_match: FocusedMatchColors,
pub matches: MatchColors,
#[config(deprecated = "use `colors.footer_bar` instead")]
bar: BarColors,
}
#[derive(ConfigDeserialize, Debug, Copy, Clone, PartialEq, Eq)]

View File

@ -1,11 +1,14 @@
use std::fmt::{self, Display, Formatter};
use std::path::{Path, PathBuf};
use std::result::Result as StdResult;
use std::{env, fs, io};
use log::{debug, error, info};
use log::{debug, error, info, warn};
use serde::Deserialize;
use serde_yaml::mapping::Mapping;
use serde_yaml::Value;
use serde_yaml::Error as YamlError;
use toml::de::Error as TomlError;
use toml::ser::Error as TomlSeError;
use toml::{Table, Value};
use alacritty_terminal::config::LOG_TARGET_CONFIG;
@ -30,7 +33,7 @@ pub use crate::config::mouse::{ClickHandler, Mouse};
pub use crate::config::ui_config::UiConfig;
/// Maximum number of depth for the configuration file imports.
const IMPORT_RECURSION_LIMIT: usize = 5;
pub const IMPORT_RECURSION_LIMIT: usize = 5;
/// Result from config loading.
pub type Result<T> = std::result::Result<T, Error>;
@ -47,8 +50,14 @@ pub enum Error {
/// io error reading file.
Io(io::Error),
/// Not valid yaml or missing parameters.
Yaml(serde_yaml::Error),
/// Invalid toml.
Toml(TomlError),
/// Failed toml serialization.
TomlSe(TomlSeError),
/// Invalid yaml.
Yaml(YamlError),
}
impl std::error::Error for Error {
@ -57,6 +66,8 @@ impl std::error::Error for Error {
Error::NotFound => None,
Error::ReadingEnvHome(err) => err.source(),
Error::Io(err) => err.source(),
Error::Toml(err) => err.source(),
Error::TomlSe(err) => err.source(),
Error::Yaml(err) => err.source(),
}
}
@ -70,6 +81,8 @@ impl Display for Error {
write!(f, "Unable to read $HOME environment variable: {}", err)
},
Error::Io(err) => write!(f, "Error reading config file: {}", err),
Error::Toml(err) => write!(f, "Config error: {}", err),
Error::TomlSe(err) => write!(f, "Yaml conversion error: {}", err),
Error::Yaml(err) => write!(f, "Config error: {}", err),
}
}
@ -91,16 +104,32 @@ impl From<io::Error> for Error {
}
}
impl From<serde_yaml::Error> for Error {
fn from(val: serde_yaml::Error) -> Self {
impl From<TomlError> for Error {
fn from(val: TomlError) -> Self {
Error::Toml(val)
}
}
impl From<TomlSeError> for Error {
fn from(val: TomlSeError) -> Self {
Error::TomlSe(val)
}
}
impl From<YamlError> for Error {
fn from(val: YamlError) -> Self {
Error::Yaml(val)
}
}
/// Load the configuration file.
pub fn load(options: &Options) -> UiConfig {
let config_options = options.config_options.clone();
let config_path = options.config_file.clone().or_else(installed_config);
let config_options = options.config_options.0.clone();
let config_path = options
.config_file
.clone()
.or_else(|| installed_config("toml"))
.or_else(|| installed_config("yml"));
// Load the config using the following fallback behavior:
// - Config path + CLI overrides
@ -128,7 +157,7 @@ pub fn reload(config_path: &Path, options: &Options) -> Result<UiConfig> {
debug!("Reloading configuration file: {:?}", config_path);
// Load config, propagating errors.
let config_options = options.config_options.clone();
let config_options = options.config_options.0.clone();
let mut config = load_from(config_path, config_options)?;
after_loading(&mut config, options);
@ -179,6 +208,16 @@ fn parse_config(
) -> Result<Value> {
config_paths.push(path.to_owned());
// Deserialize the configuration file.
let config = deserialize_config(path)?;
// Merge config with imports.
let imports = load_imports(&config, config_paths, recursion_limit);
Ok(serde_utils::merge(imports, config))
}
/// Deserialize a configuration file.
pub fn deserialize_config(path: &Path) -> Result<Value> {
let mut contents = fs::read_to_string(path)?;
// Remove UTF-8 BOM.
@ -186,60 +225,43 @@ fn parse_config(
contents = contents.split_off(3);
}
// Load configuration file as Value.
let config: Value = match serde_yaml::from_str(&contents) {
Ok(config) => config,
Err(error) => {
// Prevent parsing error with an empty string and commented out file.
if error.to_string() == "EOF while parsing a value" {
Value::Mapping(Mapping::new())
} else {
return Err(Error::Yaml(error));
}
},
};
// Convert YAML to TOML as a transitionary fallback mechanism.
let extension = path.extension().unwrap_or_default();
if (extension == "yaml" || extension == "yml") && !contents.trim().is_empty() {
warn!("YAML config {path:?} is deprecated, please migrate to TOML");
// Merge config with imports.
let imports = load_imports(&config, config_paths, recursion_limit);
Ok(serde_utils::merge(imports, config))
let value: serde_yaml::Value = serde_yaml::from_str(&contents)?;
contents = toml::to_string(&value)?;
}
// Load configuration file as Value.
let config: Value = toml::from_str(&contents)?;
Ok(config)
}
/// Load all referenced configuration files.
fn load_imports(config: &Value, config_paths: &mut Vec<PathBuf>, recursion_limit: usize) -> Value {
let imports = match config.get("import") {
Some(Value::Sequence(imports)) => imports,
Some(_) => {
error!(target: LOG_TARGET_CONFIG, "Invalid import type: expected a sequence");
return Value::Null;
// Get paths for all imports.
let import_paths = match imports(config, recursion_limit) {
Ok(import_paths) => import_paths,
Err(err) => {
error!(target: LOG_TARGET_CONFIG, "{err}");
return Value::Table(Table::new());
},
None => return Value::Null,
};
// Limit recursion to prevent infinite loops.
if !imports.is_empty() && recursion_limit == 0 {
error!(target: LOG_TARGET_CONFIG, "Exceeded maximum configuration import depth");
return Value::Null;
}
let mut merged = Value::Null;
for import in imports {
let mut path = match import {
Value::String(path) => PathBuf::from(path),
_ => {
error!(
target: LOG_TARGET_CONFIG,
"Invalid import element type: expected path string"
);
// Parse configs for all imports recursively.
let mut merged = Value::Table(Table::new());
for import_path in import_paths {
let path = match import_path {
Ok(path) => path,
Err(err) => {
error!(target: LOG_TARGET_CONFIG, "{err}");
continue;
},
};
// Resolve paths relative to user's home directory.
if let (Ok(stripped), Some(home_dir)) = (path.strip_prefix("~/"), home::home_dir()) {
path = home_dir.join(stripped);
}
if !path.exists() {
info!(target: LOG_TARGET_CONFIG, "Config import not found:\n {:?}", path.display());
continue;
@ -256,33 +278,76 @@ fn load_imports(config: &Value, config_paths: &mut Vec<PathBuf>, recursion_limit
merged
}
// TODO: Merge back with `load_imports` once `alacritty migrate` is dropped.
//
/// Get all import paths for a configuration.
pub fn imports(
config: &Value,
recursion_limit: usize,
) -> StdResult<Vec<StdResult<PathBuf, String>>, String> {
let imports = match config.get("import") {
Some(Value::Array(imports)) => imports,
Some(_) => return Err("Invalid import type: expected a sequence".into()),
None => return Ok(Vec::new()),
};
// Limit recursion to prevent infinite loops.
if !imports.is_empty() && recursion_limit == 0 {
return Err("Exceeded maximum configuration import depth".into());
}
let mut import_paths = Vec::new();
for import in imports {
let mut path = match import {
Value::String(path) => PathBuf::from(path),
_ => {
import_paths.push(Err("Invalid import element type: expected path string".into()));
continue;
},
};
// Resolve paths relative to user's home directory.
if let (Ok(stripped), Some(home_dir)) = (path.strip_prefix("~/"), home::home_dir()) {
path = home_dir.join(stripped);
}
import_paths.push(Ok(path));
}
Ok(import_paths)
}
/// Get the location of the first found default config file paths
/// according to the following order:
///
/// 1. $XDG_CONFIG_HOME/alacritty/alacritty.yml
/// 2. $XDG_CONFIG_HOME/alacritty.yml
/// 3. $HOME/.config/alacritty/alacritty.yml
/// 4. $HOME/.alacritty.yml
/// 1. $XDG_CONFIG_HOME/alacritty/alacritty.toml
/// 2. $XDG_CONFIG_HOME/alacritty.toml
/// 3. $HOME/.config/alacritty/alacritty.toml
/// 4. $HOME/.alacritty.toml
#[cfg(not(windows))]
fn installed_config() -> Option<PathBuf> {
pub fn installed_config(suffix: &str) -> Option<PathBuf> {
let file_name = format!("alacritty.{suffix}");
// Try using XDG location by default.
xdg::BaseDirectories::with_prefix("alacritty")
.ok()
.and_then(|xdg| xdg.find_config_file("alacritty.yml"))
.and_then(|xdg| xdg.find_config_file(&file_name))
.or_else(|| {
xdg::BaseDirectories::new()
.ok()
.and_then(|fallback| fallback.find_config_file("alacritty.yml"))
.and_then(|fallback| fallback.find_config_file(&file_name))
})
.or_else(|| {
if let Ok(home) = env::var("HOME") {
// Fallback path: $HOME/.config/alacritty/alacritty.yml.
let fallback = PathBuf::from(&home).join(".config/alacritty/alacritty.yml");
// Fallback path: $HOME/.config/alacritty/alacritty.toml.
let fallback = PathBuf::from(&home).join(".config/alacritty").join(&file_name);
if fallback.exists() {
return Some(fallback);
}
// Fallback path: $HOME/.alacritty.yml.
let fallback = PathBuf::from(&home).join(".alacritty.yml");
// Fallback path: $HOME/.alacritty.toml.
let hidden_name = format!(".{file_name}");
let fallback = PathBuf::from(&home).join(hidden_name);
if fallback.exists() {
return Some(fallback);
}
@ -292,22 +357,17 @@ fn installed_config() -> Option<PathBuf> {
}
#[cfg(windows)]
fn installed_config() -> Option<PathBuf> {
dirs::config_dir().map(|path| path.join("alacritty\\alacritty.yml")).filter(|new| new.exists())
pub fn installed_config(suffix: &str) -> Option<PathBuf> {
let file_name = format!("alacritty.{suffix}");
dirs::config_dir().map(|path| path.join("alacritty").join(file_name)).filter(|new| new.exists())
}
#[cfg(test)]
mod tests {
use super::*;
static DEFAULT_ALACRITTY_CONFIG: &str =
concat!(env!("CARGO_MANIFEST_DIR"), "/../alacritty.yml");
#[test]
fn config_read_eof() {
let config_path: PathBuf = DEFAULT_ALACRITTY_CONFIG.into();
let mut config = read_config(&config_path, Value::Null).unwrap();
config.config_paths = Vec::new();
assert_eq!(config, UiConfig::default());
fn empty_config() {
toml::from_str::<UiConfig>("").unwrap();
}
}

View File

@ -1,14 +1,18 @@
use std::time::Duration;
use alacritty_config_derive::ConfigDeserialize;
use serde::{Deserialize, Deserializer};
use alacritty_config_derive::{ConfigDeserialize, SerdeReplace};
use crate::config::bindings::{self, MouseBinding};
use crate::config::ui_config;
#[derive(ConfigDeserialize, Default, Clone, Debug, PartialEq, Eq)]
pub struct Mouse {
pub double_click: ClickHandler,
pub triple_click: ClickHandler,
pub hide_when_typing: bool,
#[config(deprecated = "use `hints` section instead")]
pub url: Option<serde_yaml::Value>,
pub bindings: MouseBindings,
}
#[derive(ConfigDeserialize, Clone, Debug, PartialEq, Eq)]
@ -27,3 +31,21 @@ impl ClickHandler {
Duration::from_millis(self.threshold as u64)
}
}
#[derive(SerdeReplace, Clone, Debug, PartialEq, Eq)]
pub struct MouseBindings(pub Vec<MouseBinding>);
impl Default for MouseBindings {
fn default() -> Self {
Self(bindings::default_mouse_bindings())
}
}
impl<'de> Deserialize<'de> for MouseBindings {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(Self(ui_config::deserialize_bindings(deserializer, Self::default().0)?))
}
}

View File

@ -1,7 +1,6 @@
//! Serde helpers.
use serde_yaml::mapping::Mapping;
use serde_yaml::Value;
use toml::{Table, Value};
/// Merge two serde structures.
///
@ -9,20 +8,19 @@ use serde_yaml::Value;
/// `replacement`.
pub fn merge(base: Value, replacement: Value) -> Value {
match (base, replacement) {
(Value::Sequence(mut base), Value::Sequence(mut replacement)) => {
(Value::Array(mut base), Value::Array(mut replacement)) => {
base.append(&mut replacement);
Value::Sequence(base)
Value::Array(base)
},
(Value::Mapping(base), Value::Mapping(replacement)) => {
Value::Mapping(merge_mapping(base, replacement))
(Value::Table(base), Value::Table(replacement)) => {
Value::Table(merge_tables(base, replacement))
},
(value, Value::Null) => value,
(_, value) => value,
}
}
/// Merge two key/value mappings.
fn merge_mapping(mut base: Mapping, replacement: Mapping) -> Mapping {
/// Merge two key/value tables.
fn merge_tables(mut base: Table, replacement: Table) -> Table {
for (key, value) in replacement {
let value = match base.remove(&key) {
Some(base_value) => merge(base_value, value),
@ -40,54 +38,54 @@ mod tests {
#[test]
fn merge_primitive() {
let base = Value::Null;
let replacement = Value::Bool(true);
let base = Value::Table(Table::new());
let replacement = Value::Boolean(true);
assert_eq!(merge(base, replacement.clone()), replacement);
let base = Value::Bool(false);
let replacement = Value::Bool(true);
let base = Value::Boolean(false);
let replacement = Value::Boolean(true);
assert_eq!(merge(base, replacement.clone()), replacement);
let base = Value::Number(0.into());
let replacement = Value::Number(1.into());
let base = Value::Integer(0.into());
let replacement = Value::Integer(1.into());
assert_eq!(merge(base, replacement.clone()), replacement);
let base = Value::String(String::new());
let replacement = Value::String(String::from("test"));
assert_eq!(merge(base, replacement.clone()), replacement);
let base = Value::Mapping(Mapping::new());
let replacement = Value::Null;
let base = Value::Table(Table::new());
let replacement = Value::Table(Table::new());
assert_eq!(merge(base.clone(), replacement), base);
}
#[test]
fn merge_sequence() {
let base = Value::Sequence(vec![Value::Null]);
let replacement = Value::Sequence(vec![Value::Bool(true)]);
let expected = Value::Sequence(vec![Value::Null, Value::Bool(true)]);
let base = Value::Array(vec![Value::Table(Table::new())]);
let replacement = Value::Array(vec![Value::Boolean(true)]);
let expected = Value::Array(vec![Value::Table(Table::new()), Value::Boolean(true)]);
assert_eq!(merge(base, replacement), expected);
}
#[test]
fn merge_mapping() {
let mut base_mapping = Mapping::new();
base_mapping.insert(Value::String(String::from("a")), Value::Bool(true));
base_mapping.insert(Value::String(String::from("b")), Value::Bool(false));
let base = Value::Mapping(base_mapping);
fn merge_tables() {
let mut base_table = Table::new();
base_table.insert(String::from("a"), Value::Boolean(true));
base_table.insert(String::from("b"), Value::Boolean(false));
let base = Value::Table(base_table);
let mut replacement_mapping = Mapping::new();
replacement_mapping.insert(Value::String(String::from("a")), Value::Bool(true));
replacement_mapping.insert(Value::String(String::from("c")), Value::Bool(false));
let replacement = Value::Mapping(replacement_mapping);
let mut replacement_table = Table::new();
replacement_table.insert(String::from("a"), Value::Boolean(true));
replacement_table.insert(String::from("c"), Value::Boolean(false));
let replacement = Value::Table(replacement_table);
let merged = merge(base, replacement);
let mut expected_mapping = Mapping::new();
expected_mapping.insert(Value::String(String::from("b")), Value::Bool(false));
expected_mapping.insert(Value::String(String::from("a")), Value::Bool(true));
expected_mapping.insert(Value::String(String::from("c")), Value::Bool(false));
let expected = Value::Mapping(expected_mapping);
let mut expected_table = Table::new();
expected_table.insert(String::from("b"), Value::Boolean(false));
expected_table.insert(String::from("a"), Value::Boolean(true));
expected_table.insert(String::from("c"), Value::Boolean(false));
let expected = Value::Table(expected_table);
assert_eq!(merged, expected);
}

View File

@ -3,16 +3,14 @@ use std::fmt::{self, Formatter};
use std::path::PathBuf;
use std::rc::Rc;
use log::error;
use log::{error, warn};
use serde::de::{Error as SerdeError, MapAccess, Visitor};
use serde::{self, Deserialize, Deserializer};
use unicode_width::UnicodeWidthChar;
use winit::event::{ModifiersState, VirtualKeyCode};
use alacritty_config_derive::{ConfigDeserialize, SerdeReplace};
use alacritty_terminal::config::{
Config as TerminalConfig, Percentage, Program, LOG_TARGET_CONFIG,
};
use alacritty_terminal::config::{Config as TerminalConfig, Program, LOG_TARGET_CONFIG};
use alacritty_terminal::term::search::RegexSearch;
use crate::config::bell::BellConfig;
@ -22,7 +20,7 @@ use crate::config::bindings::{
use crate::config::color::Colors;
use crate::config::debug::Debug;
use crate::config::font::Font;
use crate::config::mouse::Mouse;
use crate::config::mouse::{Mouse, MouseBindings};
use crate::config::window::WindowConfig;
/// Regex used for the default URL hint.
@ -38,6 +36,7 @@ pub struct UiConfig {
/// Window configuration.
pub window: WindowConfig,
/// Mouse configuration.
pub mouse: Mouse,
/// Debug options.
@ -57,9 +56,6 @@ pub struct UiConfig {
/// RGB values for colors.
pub colors: Colors,
/// Should draw bold text with brighter colors instead of bold font.
pub draw_bold_text_with_bright_colors: bool,
/// Path where config was loaded from.
#[config(skip)]
pub config_paths: Vec<PathBuf>,
@ -75,37 +71,42 @@ pub struct UiConfig {
#[config(flatten)]
pub terminal_config: TerminalConfig,
/// Keyboard configuration.
keyboard: Keyboard,
/// Should draw bold text with brighter colors instead of bold font.
#[config(deprecated = "use colors.draw_bold_text_with_bright_colors instead")]
draw_bold_text_with_bright_colors: bool,
/// Keybindings.
#[config(deprecated = "use keyboard.bindings instead")]
key_bindings: KeyBindings,
/// Bindings for the mouse.
#[config(deprecated = "use mouse.bindings instead")]
mouse_bindings: MouseBindings,
/// Background opacity from 0.0 to 1.0.
#[config(deprecated = "use window.opacity instead")]
background_opacity: Option<Percentage>,
}
impl Default for UiConfig {
fn default() -> Self {
Self {
live_config_reload: true,
alt_send_esc: Default::default(),
#[cfg(unix)]
ipc_socket: true,
font: Default::default(),
window: Default::default(),
mouse: Default::default(),
debug: Default::default(),
draw_bold_text_with_bright_colors: Default::default(),
terminal_config: Default::default(),
mouse_bindings: Default::default(),
config_paths: Default::default(),
key_bindings: Default::default(),
mouse_bindings: Default::default(),
terminal_config: Default::default(),
background_opacity: Default::default(),
bell: Default::default(),
alt_send_esc: Default::default(),
keyboard: Default::default(),
window: Default::default(),
colors: Default::default(),
draw_bold_text_with_bright_colors: Default::default(),
mouse: Default::default(),
debug: Default::default(),
hints: Default::default(),
font: Default::default(),
bell: Default::default(),
}
}
}
@ -113,6 +114,15 @@ impl Default for UiConfig {
impl UiConfig {
/// 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.
//
// Both will be non-empty due to the presence of the default keybindings.
let key_bindings = if self.keyboard.bindings.0.len() >= self.key_bindings.0.len() {
&mut self.keyboard.bindings.0
} else {
&mut self.key_bindings.0
};
for hint in &self.hints.enabled {
let binding = match hint.binding {
Some(binding) => binding,
@ -127,24 +137,44 @@ impl UiConfig {
action: Action::Hint(hint.clone()),
};
self.key_bindings.0.push(binding);
key_bindings.push(binding);
}
}
#[inline]
pub fn window_opacity(&self) -> f32 {
self.background_opacity.unwrap_or(self.window.opacity).as_f32()
self.window.opacity.as_f32()
}
#[inline]
pub fn key_bindings(&self) -> &[KeyBinding] {
self.key_bindings.0.as_slice()
if self.keyboard.bindings.0.len() >= self.key_bindings.0.len() {
self.keyboard.bindings.0.as_slice()
} else {
self.key_bindings.0.as_slice()
}
}
#[inline]
pub fn mouse_bindings(&self) -> &[MouseBinding] {
self.mouse_bindings.0.as_slice()
if self.mouse.bindings.0.len() >= self.mouse_bindings.0.len() {
self.mouse.bindings.0.as_slice()
} else {
self.mouse_bindings.0.as_slice()
}
}
#[inline]
pub fn draw_bold_text_with_bright_colors(&self) -> bool {
self.colors.draw_bold_text_with_bright_colors || self.draw_bold_text_with_bright_colors
}
}
/// Keyboard configuration.
#[derive(ConfigDeserialize, Default, Clone, Debug, PartialEq)]
struct Keyboard {
/// Keybindings.
bindings: KeyBindings,
}
#[derive(SerdeReplace, Clone, Debug, PartialEq, Eq)]
@ -165,25 +195,7 @@ impl<'de> Deserialize<'de> for KeyBindings {
}
}
#[derive(SerdeReplace, Clone, Debug, PartialEq, Eq)]
struct MouseBindings(Vec<MouseBinding>);
impl Default for MouseBindings {
fn default() -> Self {
Self(bindings::default_mouse_bindings())
}
}
impl<'de> Deserialize<'de> for MouseBindings {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(Self(deserialize_bindings(deserializer, Self::default().0)?))
}
}
fn deserialize_bindings<'a, D, T>(
pub fn deserialize_bindings<'a, D, T>(
deserializer: D,
mut default: Vec<Binding<T>>,
) -> Result<Vec<Binding<T>>, D::Error>
@ -192,7 +204,7 @@ where
T: Copy + Eq,
Binding<T>: Deserialize<'a>,
{
let values = Vec::<serde_yaml::Value>::deserialize(deserializer)?;
let values = Vec::<toml::Value>::deserialize(deserializer)?;
// Skip all invalid values.
let mut bindings = Vec::with_capacity(values.len());
@ -388,7 +400,7 @@ impl<'de> Deserialize<'de> for HintContent {
{
let mut content = Self::Value::default();
while let Some((key, value)) = map.next_entry::<String, serde_yaml::Value>()? {
while let Some((key, value)) = map.next_entry::<String, toml::Value>()? {
match key.as_str() {
"regex" => match Option::<LazyRegex>::deserialize(value) {
Ok(regex) => content.regex = regex,
@ -408,7 +420,8 @@ impl<'de> Deserialize<'de> for HintContent {
);
},
},
_ => (),
"command" | "action" => (),
key => warn!(target: LOG_TARGET_CONFIG, "Unrecognized hint field: {key}"),
}
}

View File

@ -232,7 +232,7 @@ impl<'de> Deserialize<'de> for Class {
{
let mut class = Self::Value::default();
while let Some((key, value)) = map.next_entry::<String, serde_yaml::Value>()? {
while let Some((key, value)) = map.next_entry::<String, toml::Value>()? {
match key.as_str() {
"instance" => match String::deserialize(value) {
Ok(instance) => class.instance = instance,
@ -252,7 +252,7 @@ impl<'de> Deserialize<'de> for Class {
);
},
},
_ => (),
key => warn!(target: LOG_TARGET_CONFIG, "Unrecognized class field: {key}"),
}
}

View File

@ -314,7 +314,7 @@ impl RenderableCell {
_ => rgb.into(),
},
Color::Named(ansi) => {
match (config.draw_bold_text_with_bright_colors, flags & Flags::DIM_BOLD) {
match (config.draw_bold_text_with_bright_colors(), flags & Flags::DIM_BOLD) {
// If no bright foreground is set, treat it like the BOLD flag doesn't exist.
(_, Flags::DIM_BOLD)
if ansi == NamedColor::Foreground
@ -334,7 +334,7 @@ impl RenderableCell {
},
Color::Indexed(idx) => {
let idx = match (
config.draw_bold_text_with_bright_colors,
config.draw_bold_text_with_bright_colors(),
flags & Flags::DIM_BOLD,
idx,
) {

View File

@ -42,6 +42,7 @@ mod logging;
#[cfg(target_os = "macos")]
mod macos;
mod message_bar;
mod migrate;
#[cfg(windows)]
mod panic;
mod renderer;
@ -54,9 +55,9 @@ mod gl {
include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs"));
}
use crate::cli::Options;
#[cfg(unix)]
use crate::cli::{MessageOptions, Subcommands};
use crate::cli::MessageOptions;
use crate::cli::{Options, Subcommands};
use crate::config::{monitor, UiConfig};
use crate::event::{Event, Processor};
#[cfg(target_os = "macos")]
@ -77,14 +78,14 @@ fn main() -> Result<(), Box<dyn Error>> {
// Load command line options.
let options = Options::new();
#[cfg(unix)]
match options.subcommands {
Some(Subcommands::Msg(options)) => msg(options),
None => alacritty(options),
#[cfg(unix)]
Some(Subcommands::Msg(options)) => msg(options)?,
Some(Subcommands::Migrate(options)) => migrate::migrate(options),
None => alacritty(options)?,
}
#[cfg(not(unix))]
alacritty(options)
Ok(())
}
/// `msg` subcommand entrypoint.

249
alacritty/src/migrate.rs Normal file
View File

@ -0,0 +1,249 @@
//! Configuration file migration.
use std::fs;
use std::path::Path;
use toml::map::Entry;
use toml::{Table, Value};
use crate::cli::MigrateOptions;
use crate::config;
/// Handle migration.
pub fn migrate(options: MigrateOptions) {
// Find configuration file path.
let config_path = options
.config_file
.clone()
.or_else(|| config::installed_config("toml"))
.or_else(|| config::installed_config("yml"));
// Abort if system has no installed configuration.
let config_path = match config_path {
Some(config_path) => config_path,
None => {
eprintln!("No configuration file found");
std::process::exit(1);
},
};
// If we're doing a wet run, perform a dry run first for safety.
if !options.dry_run {
let mut options = options.clone();
options.silent = true;
options.dry_run = true;
if let Err(err) = migrate_config(&options, &config_path, config::IMPORT_RECURSION_LIMIT) {
eprintln!("Configuration file migration failed:");
eprintln!(" {config_path:?}: {err}");
std::process::exit(1);
}
}
// Migrate the root config.
match migrate_config(&options, &config_path, config::IMPORT_RECURSION_LIMIT) {
Ok(new_path) => {
if !options.silent {
println!("Successfully migrated {config_path:?} to {new_path:?}");
}
},
Err(err) => {
eprintln!("Configuration file migration failed:");
eprintln!(" {config_path:?}: {err}");
std::process::exit(1);
},
}
}
/// Migrate a specific configuration file.
fn migrate_config(
options: &MigrateOptions,
path: &Path,
recursion_limit: usize,
) -> Result<String, String> {
// Ensure configuration file has an extension.
let path_str = path.to_string_lossy();
let (prefix, suffix) = match path_str.rsplit_once('.') {
Some((prefix, suffix)) => (prefix, suffix),
None => return Err("missing file extension".to_string()),
};
// Abort if config is already toml.
if suffix == "toml" {
return Err("already in TOML format".to_string());
}
// Try to parse the configuration file.
let mut config = match config::deserialize_config(path) {
Ok(config) => config,
Err(err) => return Err(format!("parsing error: {err}")),
};
// Migrate config imports.
if !options.skip_imports {
migrate_imports(options, &mut config, recursion_limit)?;
}
// Migrate deprecated field names to their new location.
if !options.skip_renames {
migrate_renames(&mut config)?;
}
// Convert to TOML format.
let toml = toml::to_string(&config).map_err(|err| format!("conversion error: {err}"))?;
let new_path = format!("{prefix}.toml");
if options.dry_run && !options.silent {
// Output new content to STDOUT.
println!(
"\nv-----Start TOML for {path:?}-----v\n\n{toml}\n^-----End TOML for {path:?}-----^"
);
} else if !options.dry_run {
// Write the new toml configuration.
fs::write(&new_path, toml).map_err(|err| format!("filesystem error: {err}"))?;
}
Ok(new_path)
}
/// Migrate the imports of a config.
fn migrate_imports(
options: &MigrateOptions,
config: &mut Value,
recursion_limit: usize,
) -> Result<(), String> {
let imports = match config::imports(config, recursion_limit) {
Ok(imports) => imports,
Err(err) => return Err(format!("import error: {err}")),
};
// Migrate the individual imports.
let mut new_imports = Vec::new();
for import in imports {
let import = match import {
Ok(import) => import,
Err(err) => return Err(format!("import error: {err}")),
};
let new_path = migrate_config(options, &import, recursion_limit - 1)?;
new_imports.push(Value::String(new_path));
}
// Update the imports field.
if let Some(import) = config.get_mut("import") {
*import = Value::Array(new_imports);
}
Ok(())
}
/// Migrate deprecated fields.
fn migrate_renames(config: &mut Value) -> Result<(), String> {
let config_table = match config.as_table_mut() {
Some(config_table) => config_table,
None => return Ok(()),
};
// draw_bold_text_with_bright_colors -> colors.draw_bold_text_with_bright_colors
move_value(config_table, &["draw_bold_text_with_bright_colors"], &[
"colors",
"draw_bold_text_with_bright_colors",
])?;
// key_bindings -> keyboard.bindings
move_value(config_table, &["key_bindings"], &["keyboard", "bindings"])?;
// mouse_bindings -> keyboard.bindings
move_value(config_table, &["mouse_bindings"], &["mouse", "bindings"])?;
Ok(())
}
/// Move a toml value from one map to another.
fn move_value(config_table: &mut Table, origin: &[&str], target: &[&str]) -> Result<(), String> {
if let Some(value) = remove_node(config_table, origin)? {
if !insert_node_if_empty(config_table, target, value)? {
return Err(format!(
"conflict: both `{}` and `{}` are set",
origin.join("."),
target.join(".")
));
}
}
Ok(())
}
/// Remove a node from a tree of tables.
fn remove_node(table: &mut Table, path: &[&str]) -> Result<Option<Value>, String> {
if path.len() == 1 {
Ok(table.remove(path[0]))
} else {
let next_table_value = match table.get_mut(path[0]) {
Some(next_table_value) => next_table_value,
None => return Ok(None),
};
let next_table = match next_table_value.as_table_mut() {
Some(next_table) => next_table,
None => return Err(format!("invalid `{}` table", path[0])),
};
remove_node(next_table, &path[1..])
}
}
/// Try to insert a node into a tree of tables.
///
/// Returns `false` if the node already exists.
fn insert_node_if_empty(table: &mut Table, path: &[&str], node: Value) -> Result<bool, String> {
if path.len() == 1 {
match table.entry(path[0]) {
Entry::Vacant(vacant_entry) => {
vacant_entry.insert(node);
Ok(true)
},
Entry::Occupied(_) => Ok(false),
}
} else {
let next_table_value = table.entry(path[0]).or_insert_with(|| Value::Table(Table::new()));
let next_table = match next_table_value.as_table_mut() {
Some(next_table) => next_table,
None => return Err(format!("invalid `{}` table", path[0])),
};
insert_node_if_empty(next_table, &path[1..], node)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn move_values() {
let input = r#"
root_value = 3
[table]
table_value = 5
[preexisting]
not_moved = 9
"#;
let mut value: Value = toml::from_str(input).unwrap();
let table = value.as_table_mut().unwrap();
move_value(table, &["root_value"], &["new_table", "root_value"]).unwrap();
move_value(table, &["table", "table_value"], &["preexisting", "subtable", "new_name"])
.unwrap();
let output = toml::to_string(table).unwrap();
assert_eq!(
output,
"[new_table]\nroot_value = 3\n\n[preexisting]\nnot_moved = \
9\n\n[preexisting.subtable]\nnew_name = 5\n\n[table]\n"
);
}
}

View File

@ -70,7 +70,7 @@ pub struct WindowContext {
master_fd: RawFd,
#[cfg(not(windows))]
shell_pid: u32,
ipc_config: Vec<(String, serde_yaml::Value)>,
ipc_config: Vec<(String, toml::Value)>,
config: Rc<UiConfig>,
}
@ -379,8 +379,8 @@ impl WindowContext {
},
};
// Try and parse value as yaml.
match serde_yaml::from_str(value) {
// Try and parse value as toml.
match toml::from_str(value) {
Ok(value) => self.ipc_config.push((key.to_owned(), value)),
Err(err) => error!(
target: LOG_TARGET_IPC_CONFIG,

View File

@ -10,8 +10,8 @@ rust-version = "1.65.0"
[dependencies]
log = { version = "0.4.17", features = ["serde"] }
serde_yaml = "0.8.24"
serde = "1.0.163"
toml = "0.7.1"
[target.'cfg(target_os = "macos")'.dependencies]
winit = { version = "0.28.2", default-features = false, features = ["serde"] }

View File

@ -3,7 +3,7 @@ use std::error::Error;
use log::LevelFilter;
use serde::Deserialize;
use serde_yaml::Value;
use toml::Value;
pub trait SerdeReplace {
fn replace(&mut self, key: &str, value: Value) -> Result<(), Box<dyn Error>>;

View File

@ -12,15 +12,15 @@ rust-version = "1.65.0"
proc-macro = true
[dependencies]
syn = { version = "2.0.16", features = ["derive", "parsing", "proc-macro", "printing"], default-features = false }
proc-macro2 = "1.0.24"
quote = "1.0.7"
syn = { version = "2.0.16", features = ["derive", "parsing", "proc-macro", "printing"], default-features = false }
[dev-dependencies.alacritty_config]
path = "../alacritty_config"
version = "0.1.2-dev"
[dev-dependencies]
serde = { version = "1.0.117", features = ["derive"] }
serde_yaml = "0.8.14"
log = "0.4.11"
serde = { version = "1.0.117", features = ["derive"] }
toml = "0.7.1"

View File

@ -42,20 +42,25 @@ pub fn derive_deserialize<T>(
{
let mut config = Self::Value::default();
// NOTE: This could be used to print unused keys.
let mut unused = serde_yaml::Mapping::new();
// Unused keys for flattening and warning.
let mut unused = toml::Table::new();
while let Some((key, value)) = map.next_entry::<String, serde_yaml::Value>()? {
while let Some((key, value)) = map.next_entry::<String, toml::Value>()? {
match key.as_str() {
#match_assignments
_ => {
unused.insert(serde_yaml::Value::String(key), value);
unused.insert(key, value);
},
}
}
#flatten
// Warn about unused keys.
for key in unused.keys() {
log::warn!(target: #LOG_TARGET, "Unused config key: {}", key);
}
Ok(config)
}
}
@ -109,7 +114,12 @@ fn field_deserializer(field_streams: &mut FieldStreams, field: &Field) -> Result
match serde::Deserialize::deserialize(value) {
Ok(value) => config.#ident = value,
Err(err) => {
log::error!(target: #LOG_TARGET, "Config error: {}: {}", #literal, err);
log::error!(
target: #LOG_TARGET,
"Config error: {}: {}",
#literal,
err.to_string().trim(),
);
},
}
};
@ -133,8 +143,10 @@ fn field_deserializer(field_streams: &mut FieldStreams, field: &Field) -> Result
// Create the tokens to deserialize the flattened struct from the unused fields.
field_streams.flatten.extend(quote! {
let unused = serde_yaml::Value::Mapping(unused);
config.#ident = serde::Deserialize::deserialize(unused).unwrap_or_default();
// Drain unused fields since they will be used for flattening.
let flattened = std::mem::replace(&mut unused, toml::Table::new());
config.#ident = serde::Deserialize::deserialize(flattened).unwrap_or_default();
});
},
"deprecated" | "removed" => {

View File

@ -28,7 +28,7 @@ pub fn derive(input: TokenStream) -> TokenStream {
pub fn derive_direct(ident: Ident, generics: Generics) -> TokenStream2 {
quote! {
impl <#generics> alacritty_config::SerdeReplace for #ident <#generics> {
fn replace(&mut self, key: &str, value: serde_yaml::Value) -> Result<(), Box<dyn std::error::Error>> {
fn replace(&mut self, key: &str, value: toml::Value) -> Result<(), Box<dyn std::error::Error>> {
if !key.is_empty() {
let error = format!("Fields \"{}\" do not exist", key);
return Err(error.into());
@ -53,7 +53,7 @@ pub fn derive_recursive<T>(
quote! {
#[allow(clippy::extra_unused_lifetimes)]
impl <'de, #constrained> alacritty_config::SerdeReplace for #ident <#unconstrained> {
fn replace(&mut self, key: &str, value: serde_yaml::Value) -> Result<(), Box<dyn std::error::Error>> {
fn replace(&mut self, key: &str, value: toml::Value) -> Result<(), Box<dyn std::error::Error>> {
if key.is_empty() {
*self = serde::Deserialize::deserialize(value)?;
return Ok(());

View File

@ -86,20 +86,22 @@ fn config_deserialize() {
log::set_logger(logger).unwrap();
log::set_max_level(log::LevelFilter::Warn);
let test: Test = serde_yaml::from_str(
let test: Test = toml::from_str(
r#"
field1: 3
field3: 32
nesting:
field1: "testing"
field2: None
field3: 99
aliased: 8
flatty: 123
enom_small: "one"
enom_big: "THREE"
enom_error: "HugaBuga"
gone: false
field1 = 3
field3 = 32
flatty = 123
enom_small = "one"
enom_big = "THREE"
enom_error = "HugaBuga"
gone = false
[nesting]
field1 = "testing"
field2 = "None"
field3 = 99
aliased = 8
"#,
)
.unwrap();
@ -121,15 +123,16 @@ fn config_deserialize() {
// Verify all log messages are correct.
let error_logs = logger.error_logs.lock().unwrap();
assert_eq!(error_logs.as_slice(), [
"Config error: field1: invalid type: string \"testing\", expected usize",
"Config error: enom_error: unknown variant `HugaBuga`, expected one of `One`, `Two`, \
`Three`",
"Config error: field1: invalid type: string \"testing\", expected usize",
]);
let warn_logs = logger.warn_logs.lock().unwrap();
assert_eq!(warn_logs.as_slice(), [
"Config warning: field1 has been deprecated; use field2 instead",
"Config warning: enom_error has been deprecated",
"Config warning: gone has been removed; it's gone",
"Unused config key: field3",
]);
}
@ -170,7 +173,7 @@ impl Log for Logger {
fn field_replacement() {
let mut test = Test::default();
let value = serde_yaml::to_value(13).unwrap();
let value = toml::Value::Integer(13);
test.replace("nesting.field2", value).unwrap();
assert_eq!(test.nesting.field2, Some(13));
@ -180,7 +183,7 @@ fn field_replacement() {
fn replace_derive() {
let mut test = Test::default();
let value = serde_yaml::to_value(9).unwrap();
let value = toml::Value::Integer(9);
test.replace("nesting.newtype", value).unwrap();
assert_eq!(test.nesting.newtype, NewType(9));
@ -190,7 +193,7 @@ fn replace_derive() {
fn replace_flatten() {
let mut test = Test::default();
let value = serde_yaml::to_value(7).unwrap();
let value = toml::Value::Integer(7);
test.replace("flatty", value).unwrap();
assert_eq!(test.flatten.flatty, 7);

View File

@ -29,6 +29,7 @@ parking_lot = "0.12.0"
regex-automata = "0.1.9"
serde = { version = "1", features = ["derive", "rc"] }
serde_yaml = "0.8"
toml = "0.7.1"
unicode-width = "0.1"
vte = { version = "0.11.1", default-features = false, features = ["ansi", "serde"] }

View File

@ -52,11 +52,7 @@ impl<'de> serde::Deserialize<'de> for CursorShapeShim {
}
impl alacritty_config::SerdeReplace for CursorShapeShim {
fn replace(
&mut self,
key: &str,
value: serde_yaml::Value,
) -> Result<(), Box<dyn std::error::Error>> {
fn replace(&mut self, key: &str, value: toml::Value) -> Result<(), Box<dyn std::error::Error>> {
if !key.is_empty() {
return Err(format!("Fields \"{0}\" do not exist", key).into());
}

View File

@ -14,8 +14,8 @@ opening URLs with the keyboard. By default you can launch it using
### Motion
The cursor motions are setup by default to mimic vi, however they are fully
configurable. If you don't like vi's bindings, take a look at the [configuration
file] to change the various movements.
configurable. If you don't like vi's bindings, take a look at the [onfiguration
file to change the various movements.
### Selection
@ -77,8 +77,6 @@ file. If an application captures your mouse clicks, which is indicated by a
change in mouse cursor shape, you're required to hold <kbd>Shift</kbd> to bypass
that.
[configuration file]: ../alacritty.yml
## Multi-Window
Alacritty supports running multiple terminal emulators from the same Alacritty

View File

@ -1 +0,0 @@
../alacritty.yml

View File

@ -16,7 +16,7 @@ _alacritty() {
local context curcontext="$curcontext" state line
_arguments "${_arguments_options[@]}" \
'--embed=[X11 window ID to embed Alacritty within (decimal or hexadecimal with "0x" prefix)]:EMBED: ' \
'--config-file=[Specify alternative configuration file \[default\: \$XDG_CONFIG_HOME/alacritty/alacritty.yml\]]:CONFIG_FILE:_files' \
'--config-file=[Specify alternative configuration file \[default\: \$XDG_CONFIG_HOME/alacritty/alacritty.toml\]]:CONFIG_FILE:_files' \
'--socket=[Path for IPC socket creation]:SOCKET:_files' \
'*-o+[Override configuration file options \[example\: cursor.style=Beam\]]:OPTION: ' \
'*--option=[Override configuration file options \[example\: cursor.style=Beam\]]:OPTION: ' \
@ -116,6 +116,21 @@ esac
;;
esac
;;
(migrate)
_arguments "${_arguments_options[@]}" \
'-c+[Path to the configuration file]:CONFIG_FILE:_files' \
'--config-file=[Path to the configuration file]:CONFIG_FILE:_files' \
'-d[Only output TOML config to stdout]' \
'--dry-run[Only output TOML config to stdout]' \
'-i[Do not recurse over imports]' \
'--skip-imports[Do not recurse over imports]' \
'--skip-renames[Do not move renamed fields to their new location]' \
'-s[Do not output to STDOUT]' \
'--silent[Do not output to STDOUT]' \
'-h[Print help]' \
'--help[Print help]' \
&& ret=0
;;
(help)
_arguments "${_arguments_options[@]}" \
":: :_alacritty__help_commands" \
@ -152,6 +167,10 @@ _arguments "${_arguments_options[@]}" \
;;
esac
;;
(migrate)
_arguments "${_arguments_options[@]}" \
&& ret=0
;;
(help)
_arguments "${_arguments_options[@]}" \
&& ret=0
@ -169,6 +188,7 @@ esac
_alacritty_commands() {
local commands; commands=(
'msg:Send a message to the Alacritty socket' \
'migrate:Migrate the configuration file' \
'help:Print this message or the help of the given subcommand(s)' \
)
_describe -t commands 'alacritty commands' commands "$@"
@ -207,6 +227,7 @@ _alacritty__msg__help__create-window_commands() {
_alacritty__help_commands() {
local commands; commands=(
'msg:Send a message to the Alacritty socket' \
'migrate:Migrate the configuration file' \
'help:Print this message or the help of the given subcommand(s)' \
)
_describe -t commands 'alacritty help commands' commands "$@"
@ -230,6 +251,16 @@ _alacritty__msg__help__help_commands() {
local commands; commands=()
_describe -t commands 'alacritty msg help help commands' commands "$@"
}
(( $+functions[_alacritty__help__migrate_commands] )) ||
_alacritty__help__migrate_commands() {
local commands; commands=()
_describe -t commands 'alacritty help migrate commands' commands "$@"
}
(( $+functions[_alacritty__migrate_commands] )) ||
_alacritty__migrate_commands() {
local commands; commands=()
_describe -t commands 'alacritty migrate commands' commands "$@"
}
(( $+functions[_alacritty__help__msg_commands] )) ||
_alacritty__help__msg_commands() {
local commands; commands=(

View File

@ -15,12 +15,18 @@ _alacritty() {
alacritty,help)
cmd="alacritty__help"
;;
alacritty,migrate)
cmd="alacritty__migrate"
;;
alacritty,msg)
cmd="alacritty__msg"
;;
alacritty__help,help)
cmd="alacritty__help__help"
;;
alacritty__help,migrate)
cmd="alacritty__help__migrate"
;;
alacritty__help,msg)
cmd="alacritty__help__msg"
;;
@ -55,7 +61,7 @@ _alacritty() {
case "${cmd}" in
alacritty)
opts="-q -v -o -e -T -h -V --print-events --ref-test --embed --config-file --socket --option --working-directory --hold --command --title --class --help --version msg help"
opts="-q -v -o -e -T -h -V --print-events --ref-test --embed --config-file --socket --option --working-directory --hold --command --title --class --help --version msg migrate help"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
@ -113,7 +119,7 @@ _alacritty() {
return 0
;;
alacritty__help)
opts="msg help"
opts="msg migrate help"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
@ -140,6 +146,20 @@ _alacritty() {
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
alacritty__help__migrate)
opts=""
if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
alacritty__help__msg)
opts="create-window config"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
@ -182,6 +202,28 @@ _alacritty() {
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
alacritty__migrate)
opts="-c -d -i -s -h --config-file --dry-run --skip-imports --skip-renames --silent --help"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
--config-file)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-c)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
alacritty__msg)
opts="-s -h --socket --help create-window config help"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then

View File

@ -1,5 +1,5 @@
complete -c alacritty -n "__fish_use_subcommand" -l embed -d 'X11 window ID to embed Alacritty within (decimal or hexadecimal with "0x" prefix)' -r
complete -c alacritty -n "__fish_use_subcommand" -l config-file -d 'Specify alternative configuration file [default: $XDG_CONFIG_HOME/alacritty/alacritty.yml]' -r -F
complete -c alacritty -n "__fish_use_subcommand" -l config-file -d 'Specify alternative configuration file [default: $XDG_CONFIG_HOME/alacritty/alacritty.toml]' -r -F
complete -c alacritty -n "__fish_use_subcommand" -l socket -d 'Path for IPC socket creation' -r -F
complete -c alacritty -n "__fish_use_subcommand" -s o -l option -d 'Override configuration file options [example: cursor.style=Beam]' -r
complete -c alacritty -n "__fish_use_subcommand" -l working-directory -d 'Start the shell in the specified working directory' -r -F
@ -14,6 +14,7 @@ complete -c alacritty -n "__fish_use_subcommand" -l hold -d 'Remain open after c
complete -c alacritty -n "__fish_use_subcommand" -s h -l help -d 'Print help'
complete -c alacritty -n "__fish_use_subcommand" -s V -l version -d 'Print version'
complete -c alacritty -n "__fish_use_subcommand" -f -a "msg" -d 'Send a message to the Alacritty socket'
complete -c alacritty -n "__fish_use_subcommand" -f -a "migrate" -d 'Migrate the configuration file'
complete -c alacritty -n "__fish_use_subcommand" -f -a "help" -d 'Print this message or the help of the given subcommand(s)'
complete -c alacritty -n "__fish_seen_subcommand_from msg; and not __fish_seen_subcommand_from create-window; and not __fish_seen_subcommand_from config; and not __fish_seen_subcommand_from help" -s s -l socket -d 'IPC socket connection path override' -r -F
complete -c alacritty -n "__fish_seen_subcommand_from msg; and not __fish_seen_subcommand_from create-window; and not __fish_seen_subcommand_from config; and not __fish_seen_subcommand_from help" -s h -l help -d 'Print help'
@ -32,7 +33,14 @@ complete -c alacritty -n "__fish_seen_subcommand_from msg; and __fish_seen_subco
complete -c alacritty -n "__fish_seen_subcommand_from msg; and __fish_seen_subcommand_from help; and not __fish_seen_subcommand_from create-window; and not __fish_seen_subcommand_from config; and not __fish_seen_subcommand_from help" -f -a "create-window" -d 'Create a new window in the same Alacritty process'
complete -c alacritty -n "__fish_seen_subcommand_from msg; and __fish_seen_subcommand_from help; and not __fish_seen_subcommand_from create-window; and not __fish_seen_subcommand_from config; and not __fish_seen_subcommand_from help" -f -a "config" -d 'Update the Alacritty configuration'
complete -c alacritty -n "__fish_seen_subcommand_from msg; and __fish_seen_subcommand_from help; and not __fish_seen_subcommand_from create-window; and not __fish_seen_subcommand_from config; and not __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)'
complete -c alacritty -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from msg; and not __fish_seen_subcommand_from help" -f -a "msg" -d 'Send a message to the Alacritty socket'
complete -c alacritty -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from msg; and not __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)'
complete -c alacritty -n "__fish_seen_subcommand_from migrate" -s c -l config-file -d 'Path to the configuration file' -r -F
complete -c alacritty -n "__fish_seen_subcommand_from migrate" -s d -l dry-run -d 'Only output TOML config to stdout'
complete -c alacritty -n "__fish_seen_subcommand_from migrate" -s i -l skip-imports -d 'Do not recurse over imports'
complete -c alacritty -n "__fish_seen_subcommand_from migrate" -l skip-renames -d 'Do not move renamed fields to their new location'
complete -c alacritty -n "__fish_seen_subcommand_from migrate" -s s -l silent -d 'Do not output to STDOUT'
complete -c alacritty -n "__fish_seen_subcommand_from migrate" -s h -l help -d 'Print help'
complete -c alacritty -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from msg; and not __fish_seen_subcommand_from migrate; and not __fish_seen_subcommand_from help" -f -a "msg" -d 'Send a message to the Alacritty socket'
complete -c alacritty -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from msg; and not __fish_seen_subcommand_from migrate; and not __fish_seen_subcommand_from help" -f -a "migrate" -d 'Migrate the configuration file'
complete -c alacritty -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from msg; and not __fish_seen_subcommand_from migrate; and not __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)'
complete -c alacritty -n "__fish_seen_subcommand_from help; and __fish_seen_subcommand_from msg; and not __fish_seen_subcommand_from create-window; and not __fish_seen_subcommand_from config" -f -a "create-window" -d 'Create a new window in the same Alacritty process'
complete -c alacritty -n "__fish_seen_subcommand_from help; and __fish_seen_subcommand_from msg; and not __fish_seen_subcommand_from create-window; and not __fish_seen_subcommand_from config" -f -a "config" -d 'Update the Alacritty configuration'

View File

@ -0,0 +1,478 @@
ALACRITTY-BINDINGS(5)
# NAME
Alacritty Bindings - Default configuration file bindings
# SYNOPSIS
This page documents all key and mouse bindings for the default Alacritty
configuration. See *alacritty*(5) for full configuration format documentation.
# Mouse Bindings
[[ *mouse*
:[ *mods*
:[ *action*
| _"Right"_
:[
: _"ExpandSelection"_
| _"Right"_
: _"Control"_
: _"ExpandSelection"_
| _"Middle"_
: _"~Vi"_
: _"PasteSelection"_
# Key Bindings
[[ *key*
:[ *mods*
:[ *mode*
:[ *action* / *chars*
| _"Paste"_
:[
:[
: _"Paste"_
| _"Copy"_
:[
:[
: _"Copy"_
| _"L"_
: _"Control"_
:[
: _"ClearLogNotice"_
| _"L"_
: _"Control"_
: _"~Vi|~Search"_
: *chars*: _"\\u000c"_
| _"PageUp"_
: _"Shift"_
: _"~Alt"_
: _"ScrollPageUp"_
| _"PageDown"_
: _"Shift"_
: _"~Alt"_
: _"ScrollPageDown"_
| _"Home"_
: _"Shift"_
: _"~Alt"_
: _"ScrollToTop"_
| _"End"_
: _"Shift"_
: _"~Alt"_
: _"ScrollToBottom"_
## Vi Mode
[[ *key*
:[ *mods*
:[ *mode*
:[ *action*
| _"Space"_
: _"Shift|Control"_
: _"~Search"_
: _"ToggleViMode"_
| _"Space"_
: _"Shift|Control"_
: _"Vi|~Search"_
: _"ScrollToBottom"_
| _"Escape"_
:[
: _"Vi|~Search"_
: _"ClearSelection"_
| _"I"_
:[
: _"Vi|~Search"_
: _"ToggleViMode"_
| _"I"_
:[
: _"Vi|~Search"_
: _"ScrollToBottom"_
| _"C"_
: _"Control"_
: _"Vi|~Search"_
: _"ToggleViMode"_
| _"Y"_
: _"Control"_
: _"Vi|~Search"_
: _"ScrollLineUp"_
| _"E"_
: _"Control"_
: _"Vi|~Search"_
: _"ScrollLineDown"_
| _"G"_
:[
: _"Vi|~Search"_
: _"ScrollToTop"_
| _"G"_
: _"Shift"_
: _"Vi|~Search"_
: _"ScrollToBottom"_
| _"B"_
: _"Control"_
: _"Vi|~Search"_
: _"ScrollPageUp"_
| _"F"_
: _"Control"_
: _"Vi|~Search"_
: _"ScrollPageDown"_
| _"U"_
: _"Control"_
: _"Vi|~Search"_
: _"ScrollHalfPageUp"_
| _"D"_
: _"Control"_
: _"Vi|~Search"_
: _"ScrollHalfPageDown"_
| _"Y"_
:[
: _"Vi|~Search"_
: _"Copy"_
| _"Y"_
:[
: _"Vi|~Search"_
: _"ClearSelection"_
| _"Copy"_
:[
: _"Vi|~Search"_
: _"ClearSelection"_
| _"V"_
:[
: _"Vi|~Search"_
: _"ToggleNormalSelection"_
| _"V"_
: _"Shift"_
: _"Vi|~Search"_
: _"ToggleLineSelection"_
| _"V"_
: _"Control"_
: _"Vi|~Search"_
: _"ToggleBlockSelection"_
| _"V"_
: _"Alt"_
: _"Vi|~Search"_
: _"ToggleSemanticSelection"_
| _"Return"_
:[
: _"Vi|~Search"_
: _"Open"_
| _"Z"_
:[
: _"Vi|~Search"_
: _"CenterAroundViCursor"_
| _"K"_
:[
: _"Vi|~Search"_
: _"Up"_
| _"J"_
:[
: _"Vi|~Search"_
: _"Down"_
| _"H"_
:[
: _"Vi|~Search"_
: _"Left"_
| _"L"_
:[
: _"Vi|~Search"_
: _"Right"_
| _"Up"_
:[
: _"Vi|~Search"_
: _"Up"_
| _"Down"_
:[
: _"Vi|~Search"_
: _"Down"_
| _"Left"_
:[
: _"Vi|~Search"_
: _"Left"_
| _"Right"_
:[
: _"Vi|~Search"_
: _"Right"_
| _"Key0"_
:[
: _"Vi|~Search"_
: _"First"_
| _"Key4"_
: _"Shift"_
: _"Vi|~Search"_
: _"Last"_
| _"Key6"_
: _"Shift"_
: _"Vi|~Search"_
: _"FirstOccupied"_
| _"H"_
: _"Shift"_
: _"Vi|~Search"_
: _"High"_
| _"M"_
: _"Shift"_
: _"Vi|~Search"_
: _"Middle"_
| _"L"_
: _"Shift"_
: _"Vi|~Search"_
: _"Low"_
| _"B"_
:[
: _"Vi|~Search"_
: _"SemanticLeft"_
| _"W"_
:[
: _"Vi|~Search"_
: _"SemanticRight"_
| _"E"_
:[
: _"Vi|~Search"_
: _"SemanticRightEnd"_
| _"B"_
: _"Shift"_
: _"Vi|~Search"_
: _"WordLeft"_
| _"W"_
: _"Shift"_
: _"Vi|~Search"_
: _"WordRight"_
| _"E"_
: _"Shift"_
: _"Vi|~Search"_
: _"WordRightEnd"_
| _"Key5"_
: _"Shift"_
: _"Vi|~Search"_
: _"Bracket"_
| _"Slash"_
:[
: _"Vi|~Search"_
: _"SearchForward"_
| _"Slash"_
: _"Shift"_
: _"Vi|~Search"_
: _"SearchBackward"_
| _"N"_
:[
: _"Vi|~Search"_
: _"SearchNext"_
| _"N"_
: _"Shift"_
: _"Vi|~Search"_
: _"SearchPrevious"_
## Search Mode
[[ *key*
:[ *mods*
:[ *mode*
:[ *action*
| _"Return"_
:[
: _"Search|Vi"_
: _"SearchConfirm"_
| _"Escape"_
:[
: _"Search"_
: _"SearchCancel"_
| _"C"_
: _"Control"_
: _"Search"_
: _"SearchCancel"_
| _"U"_
: _"Control"_
: _"Search"_
: _"SearchClear"_
| _"W"_
: _"Control"_
: _"Search"_
: _"SearchDeleteWord"_
| _"P"_
: _"Control"_
: _"Search"_
: _"SearchHistoryPrevious"_
| _"N"_
: _"Control"_
: _"Search"_
: _"SearchHistoryNext"_
| _"Up"_
:[
: _"Search"_
: _"SearchHistoryPrevious"_
| _"Down"_
:[
: _"Search"_
: _"SearchHistoryNext"_
| _"Return"_
:[
: _"Search|~Vi"_
: _"SearchFocusNext"_
## Windows, Linux, and BSD only
[[ *key*
:[ *mods*
:[ *mode*
:[ *action*
| _"V"_
: _"Control|Shift"_
: _"~Vi"_
: _"Paste"_
| _"C"_
: _"Control|Shift"_
:[
: _"Copy"_
| _"F"_
: _"Control|Shift"_
: _"~Search"_
: _"SearchForward"_
| _"B"_
: _"Control|Shift"_
: _"~Search"_
: _"SearchBackward"_
| _"C"_
: _"Control|Shift"_
: _"Vi|~Search"_
: _"ClearSelection"_
| _"Insert"_
: _"Shift"_
:[
: _"PasteSelection"_
| _"Key0"_
: _"Control"_
:[
: _"ResetFontSize"_
| _"Equals"_
: _"Control"_
:[
: _"IncreaseFontSize"_
| _"Plus"_
: _"Control"_
:[
: _"IncreaseFontSize"_
| _"NumpadAdd"_
: _"Control"_
:[
: _"IncreaseFontSize"_
| _"Minus"_
: _"Control"_
:[
: _"DecreaseFontSize"_
| _"NumpadSubtract"_
: _"Control"_
:[
: _"DecreaseFontSize"_
## Windows only
[[ *key*
:[ *mods*
:[ *mode*
:[ *action*
| _"Return"_
: _"Alt"_
:[
: _"ToggleFullscreen"_
## macOS only
[[ *key*
:[ *mods*
:[ *mode*
:[ *action* / *chars*
| _"K"_
: _"Command"_
: _"~Vi|~Search"_
: *chars*: _"\\u000c"_
| _"K"_
: _"Command"_
: _"~Vi|~Search"_
: _"ClearHistory"_
| _"Key0"_
: _"Command"_
:[
: _"ResetFontSize"_
| _"Equals"_
: _"Command"_
:[
: _"IncreaseFontSize"_
| _"Plus"_
: _"Command"_
:[
: _"IncreaseFontSize"_
| _"NumpadAdd"_
: _"Command"_
:[
: _"IncreaseFontSize"_
| _"Minus"_
: _"Command"_
:[
: _"DecreaseFontSize"_
| _"NumpadSubtract"_
: _"Command"_
:[
: _"DecreaseFontSize"_
| _"V"_
: _"Command"_
:[
: _"Paste"_
| _"C"_
: _"Command"_
:[
: _"Copy"_
| _"C"_
: _"Command"_
: _"Vi|~Search"_
: _"ClearSelection"_
| _"H"_
: _"Command"_
:[
: _"Hide"_
| _"H"_
: _"Command|Alt"_
:[
: _"HideOtherApplications"_
| _"M"_
: _"Command"_
:[
: _"Minimize"_
| _"Q"_
: _"Command"_
:[
: _"Quit"_
| _"W"_
: _"Command"_
:[
: _"Quit"_
| _"N"_
: _"Command"_
:[
: _"CreateNewWindow"_
| _"F"_
: _"Command|Control"_
:[
: _"ToggleFullscreen"_
| _"F"_
: _"Command"_
: _"~Search"_
: _"SearchForward"_
| _"B"_
: _"Command"_
: _"~Search"_
: _"SearchBackward"_
# SEE ALSO
See the alacritty github repository at https://github.com/alacritty/alacritty
for the full documentation.
# BUGS
Found a bug? Please report it at https://github.com/alacritty/alacritty/issues.
# MAINTAINERS
- Christian Duerr <contact@christianduerr.com>
- Kirill Chibisov <contact@kchibisov.com>

View File

@ -50,12 +50,12 @@ set of features with high performance.
Specify alternative configuration file
Alacritty looks for the configuration file at the following paths:
. $XDG_CONFIG_HOME/alacritty/alacritty.yml
. $XDG_CONFIG_HOME/alacritty.yml
. $HOME/.config/alacritty/alacritty.yml
. $HOME/.alacritty.yml
. $XDG_CONFIG_HOME/alacritty/alacritty.toml
. $XDG_CONFIG_HOME/alacritty.toml
. $HOME/.config/alacritty/alacritty.toml
. $HOME/.alacritty.toml
On Windows, the configuration file is located at %APPDATA%\\alacritty\\alacritty.yml.
On Windows, the configuration file is located at %APPDATA%\\alacritty\\alacritty.toml
*--embed* <parent>
X11 window ID to embed Alacritty within (decimal or hexadecimal with _0x_ prefix)

823
extra/man/alacritty.5.scd Normal file
View File

@ -0,0 +1,823 @@
ALACRITTY(5)
# NAME
Alacritty - TOML configuration file format
# SYNTAX
Alacritty's configuration file uses the TOML format. The format's specification
can be found at https://toml.io/en/v1.0.0.
# GENERAL
This section documents the root level of the configuration file.
*import* [<string>,]
Import additional configuration files
Imports are loaded in order, skipping all missing files, with the importing
file being loaded last. If a field is already present in a previous import,
it will be replaced.
All imports must either be absolute paths starting with _/_, or paths
relative to the user's home directory starting with _~/_.
*working_directory* <string> | "None"
Directory the shell is started in. When this is unset, or _None_, the
working directory of the parent process will be used.
Default: _None_
*live_config_reload* <boolean>
Live config reload (changes require restart)
*ipc_socket* <boolean> _(unix only)_
Offer IPC using _alacritty msg_
# ENV
All key-value pairs in the *env* section will be added as environment variables
for any process spawned by Alacritty, including its shell. Some entries may
override variables set by alacritty itself.
```
[env]
TERM = "alacritty"
```
# WINDOW
This section documents the *[window]* table of the configuration file.
*dimensions* { columns = <integer>, lines = <integer> }
Window dimensions (changes require restart)
Number of lines/columns (not pixels) in the terminal. Both lines and columns
must be non-zero for this to take effect. The number of columns must be at
least _2_, while using a value of _0_ for columns and lines will fall back
to the window manager's recommended size
Default: _{ column = 0, lines = 0 }_
*padding* { x = <integer>, y = <integer> }
Blank space added around the window in pixels. This padding is scaled
by DPI and the specified value is always added at both opposing sides.
Default: _{ x = 0, y = 0 }_
*dynamic_padding* <boolean>
Spread additional padding evenly around the terminal content.
Default: _false_
*decorations* "Full" | "None" | "Transparent" | "Buttonless"
Window decorations
*Full*
Borders and title bar
*None*
Neither borders nor title bar
*Transparent* _(macOS only)_
Title bar, transparent background and title bar buttons
*Buttonless* _(macOS only)_
Title bar, transparent background and no title bar buttons
Default: _"Full"_
*opacity* <float>
Background opacity as a floating point number from _0.0_ to _1.0_. The value
\_0.0_ is completely transparent and _1.0_ is opaque.
Default: _1.0_
*startup_mode* "Windowed" | "Maximized" | "Fullscreen" | "SimpleFullscreen"
Startup mode (changes require restart)
Default: _"Windowed"_
*title* <string>
Window title
Default: _"Alacritty"_
*dynamic_title* <boolean>
Allow terminal applications to change Alacritty's window title.
Default: _true_
*class* { instance = <string>, general = <string> } _(Linux/BSD only)_
Window class
On Wayland, *general* is used as _app\_id_ and *instance* is ignored.
Default: _{ instance = "Alacritty", general = "Alacritty" }_
*decoration_theme_variant* "Dark" | "Light" | "None"
Override the variant of the System theme/GTK theme/Wayland client side
decorations. Set this to _"None"_ to use the system's default theme variant.
Default: _"None"_
*resize_increments* <boolean>
Prefer resizing window by discrete steps equal to cell dimensions.
Default: _false_
*option_as_alt* "OnlyLeft" | "OnlyRight" | "Both" | "None" _(macos only)_
Make _Option_ key behave as _Alt_
Default: _"None"_
# Scrolling
This section documents the *[scrolling]* table of the configuration file.
*history* <integer>
Maximum number of lines in the scrollback buffer.++
Specifying _0_ will disable scrolling.++
Limited to _100000_.
Default: _10000_
*multiplier* <integer>
Number of line scrolled for every input scroll increment.
Default: _3_
# Font
This section documents the *[font]* table of the configuration file.
*normal* { family = <string>, style = <string> }
Default:
Linux/BSD: _{ family = "monospace", style = "Regular" }_++
Windows: _{ family = "Consolas", style = "Regular" }_++
macOS: _{ family = "Menlo", style = "Regular" }_
*bold* { family = <string>, style = <string> }
If the family is not specified, it will fall back to the value specified for
the normal font.
Default: _{ style = "Bold" }_
*italic* { family = <string>, style = <string> }
If the family is not specified, it will fall back to the value specified for
the normal font.
Default: _{ style = "Italic" }_
*bold_italic* { family = <string>, style = <string> }
If the family is not specified, it will fall back to the value specified for
the normal font.
Default: _{ style = "Bold Italic" }_
*size* <float>
Font size in points.
Default: _11.0_
*offset* { x = <integer>, y = <integer> }
Offset is the extra space around each character. _y_ can be thought of as
modifying the line spacing, and _x_ as modifying the letter spacing.
Default: _{ x = 0, y = 0 }_
*glyph_offset* { x = <integer>, y = <integer> }
Glyph offset determines the locations of the glyphs within their cells with
the default being at the bottom. Increasing _x_ moves the glyph to the
right, increasing _y_ moves the glyph upward.
*builtin_box_drawing* <boolean>
When _true_, Alacritty will use a custom built-in font for box drawing
characters (Unicode points _U+2500_ - _U+259f_).
Default: _true_
# Colors
This section documents the *[colors]* table of the configuration file.
Colors are specified using their hexadecimal values with a _#_ prefix: _#RRGGBB_
*primary*
*foreground* <string>
Default: _"#1d1f21"_
*background* <string>
Default: _"#c5c8c6"_
*dim_foreground* <string>
If this is not set, the color is automatically calculated based on the
foreground color.
Default: _"#828482"_
*bright_foreground* <string>
This color is only used when _draw\_bold\_text\_with\_bright\_colors_ is
_true_.
If this is not set, the normal foreground will be used.
Default: _"None"_
*cursor* { text = <string>, cursor = <string> }
Colors which should be used to draw the terminal cursor.
Allowed values are hexadecimal colors like _#ff00ff_, or
_CellForeground_/_CellBackground_, which references the affected cell.
Default: _{ text = "CellBackground", cursor: "CellForeground" }_
*vi_mode_cursor* { text = <string>, cursor = <string> }
Colors for the cursor when the vi mode is active.
Allowed values are hexadecimal colors like _#ff00ff_, or
_CellForeground_/_CellBackground_, which references the affected cell.
Default: _{ text = "CellBackground", cursor: "CellForeground" }_
*search*
Colors used for the search bar and match highlighting.
Allowed values are hexadecimal colors like _#ff00ff_, or
_CellForeground_/_CellBackground_, which references the affected cell.
*matches* { foreground = <string>, background = <string> }
Default: _{ foreground = "#000000", background: "#ffffff" }_
*focused_match* { foreground = <string>, background = <string> }
Default: _{ foreground = "#ffffff", background: "#000000" }_
*hints*
*start* { foreground = <string>, background = <string> }
First character in the hint label
Allowed values are hexadecimal colors like _#ff00ff_, or
_CellForeground_/_CellBackground_, which references the affected cell.
Default: _{ foreground = "#1d1f21", background = "#e9ff5e" }_
*end* { foreground = <string>, background = <string> }
All characters after the first one in the hint label
Allowed values are hexadecimal colors like _#ff00ff_, or
_CellForeground_/_CellBackground_, which references the affected cell.
Default: _{ foreground = "#e9ff5e", background = "#1d1f21" }_
*line_indicator* { foreground = <string>, background = <string> }
Color used for the indicator displaying the position in history during
search and vi mode.
Setting this to _"None"_ will use the opposing primary color.
Default: _{ foreground = "None", background = "None" }_
*footer_bar* { foreground = <string>, background = <string> }
Color used for the footer bar on the bottom, used by search regex input,
hyperlink URI preview, etc.
Default: _{ foreground = "#c5c8c6", background = "#1d1f21" }_
*selection* { text = <string>, background = <string> }
Colors used for drawing selections.
Allowed values are hexadecimal colors like _#ff00ff_, or
_CellForeground_/_CellBackground_, which references the affected cell.
Default: _{ text = "CellBackground", background = "CellForeground" }_
*normal*
*black* <string>
Default: _"#1d1f21"_
*red* <string>
Default: _"#cc6666"_
*green* <string>
Default: _"#b5bd68"_
*yellow* <string>
Default: _"#f0c674"_
*blue* <string>
Default: _"#81a2be"_
*magenta* <string>
Default: _"#b294bb"_
*cyan* <string>
Default: _"#8abeb7"_
*white* <string>
Default: _"#c5c8c6"_
*bright*
*black* <string>
Default: _"#666666"_
*red* <string>
Default: _"#d54e53"_
*green* <string>
Default: _"#b9ca4a"_
*yellow* <string>
Default: _"#e7c547"_
*blue* <string>
Default: _"#7aa6da"_
*magenta* <string>
Default: _"#c397d8"_
*cyan* <string>
Default: _"#70c0b1"_
*white* <string>
Default: _"#eaeaea"_
*dim*
If the dim colors are not set, they will be calculated automatically based
on the _normal_ colors.
*black* <string>
Default: _"#131415"_
*red* <string>
Default: _"#864343"_
*green* <string>
Default: _"#777c44"_
*yellow* <string>
Default: _"#9e824c"_
*blue* <string>
Default: _"#556a7d"_
*magenta* <string>
Default: _"#75617b"_
*cyan* <string>
Default: _"#5b7d78"_
*white* <string>
Default: _"#828482"_
*indexed_colors* [{ index = <integer>, color = <string> },]
The indexed colors include all colors from 16 to 256.
When these are not set, they're filled with sensible defaults.
Default: _[]_
*transparent_background_colors* <boolean>
Whether or not _window.opacity_ applies to all cell backgrounds, or only to
the default background. When set to _true_ all cells will be transparent
regardless of their background color.
Default: _false_
*draw_bold_text_with_bright_colors* <boolean>
When _true_, bold text is drawn using the bright color variants.
# Bell
This section documents the *[bell]* table of the configuration file.
*animation* "Ease" | "EaseOut" | "EaseOutSine" | "EaseOutQuad" | "EaseOutCubic"
\| "EaseOutQuart" | "EaseOutQuint" | "EaseOutExpo" | "EaseOutCirc" | "Linear"
Visual bell animation effect for flashing the screen when the visual bell is rung.
Default: _"EaseOutExpo"_
*duration* <integer>
Duration of the visual bell flash in milliseconds. A `duration` of `0` will
disable the visual bell animation.
Default: _0_
*color* <string>
Visual bell animation color.
Default: _"#ffffff"_
*command* <string> | { program = <string>, args = [<string>,] }
This program is executed whenever the bell is rung.
When set to _"None"_, no command will be executed.
Default: _"None"_
# Selection
This section documents the *[selection]* table of the configuration file.
*semantic_escape_chars* <string>
This string contains all characters that are used as separators for
"semantic words" in Alacritty.
Default: _",│`|:\"' ()[]{}<>\t"_
*save_to_clipboard* <boolean>
When set to _true_, selected text will be copied to the primary clipboard.
Default: _false_
# Cursor
This section documents the *[cursor]* table of the configuration file.
*style*
*shape* "Block" | "Underline" | "Beam"
Default: _"Block"_
*blinking* "Never" | "Off" | "On" | "Always"
*Never*
Prevent the cursor from ever blinking
*Off*
Disable blinking by default
*On*
Enable blinking by default
*Always*
Force the cursor to always blink
Default: _"Off"_
*vi_mode_style*
If the vi mode cursor style is _None_ or not specified, it will fall back to
the active value of the normal cursor.
See _cursor.style_ for available options.
Default: _None_
*blink_interval* <integer>
Cursor blinking interval in milliseconds.
Default: _750_
*blink_timeout* <integer>
Time after which cursor stops blinking, in seconds.
Specifying _0_ will disable timeout for blinking.
Default: _5_
*unfocused_hollow* <boolean>
When this is _true_, the cursor will be rendered as a hollow box when the
window is not focused.
Default: _true_
*thickness* <float>
Thickness of the cursor relative to the cell width as floating point number
from _0.0_ to _1.0_.
Default: _0.15_
*shell* <string> | { program = <string>, args = [<string>,] }
You can set _shell.program_ to the path of your favorite shell, e.g.
_/bin/zsh_. Entries in _shell.args_ are passed as arguments to the shell.
Default:
Linux/BSD/macOS: _$SHELL_ or the user's login shell, if _$SHELL_ is unset++
Windows: _"powershell"_
# Mouse
This section documents the *[mouse]* table of the configuration file.
*double_click* { threshold = <integer> }
Set maximum time between double-clicks in milliseconds.
Default: _300_
*triple_click* { threshold = <integer> }
Set maximum time between triple-clicks in milliseconds.
Default: _300_
*hide_when_typing* <boolean>
When this is _true_, the cursor is temporarily hidden when typing.
Default: _false_
# Hints
This section documents the *[hints]* table of the configuration file.
Terminal hints can be used to find text or hyperlinks in the visible part of the
terminal and pipe it to other applications.
*alphabet* <string>
Keys used for the hint labels.
Default: _"jfkdls;ahgurieowpq"_
*enabled* [{ <regex>, <hyperlinks>, <post_processing>, <action>, <command> <binding>, <mouse> },]
Array with all available hints.
Each hint must have at least one of _regex_ or _hyperlinks_ and either an
_action_ or a _command_.
*regex* <string>
Regex each line will be compared against.
*hyperlinks* <boolean>
When this is _true_, all OSC 8 escape sequence hyperlinks will be
included in the hints.
*post_processing* <boolean>
When this is _true_, heuristics will be used to shorten the match if
there are characters likely not to be part of the hint (e.g. a trailing
_._). This is most useful for URIs and applies only to _regex_ matches.
*action* "Copy" | "Paste" | "Select" | "MoveViModeCursor"
*Copy*
Copy the hint's text to the clipboard.
*Paste*
Paste the hint's text to the terminal or search.
*Select*
Select the hint's text.
*MoveViModeCursor*
Move the vi mode cursor to the beginning of the hint.
*command* <string> | { program = <string>, args = [<string>,] }
Command which will be executed when the hint is clicked or selected with
the _binding_.
The hint's text is always attached as the last argument.
*binding* { key = <string>, mods = <string>, mode = <string> }
See _key_bindings_ for documentation on available values.
This controls which key binding is used to start the keyboard hint
selection process.
*mouse* { mods = <string>, enabled = <boolean> }
See _key_bindings_ for documentation on available _mods_.
The _enabled_ field controls if the hint should be underlined when
hovering over the hint text with all _mods_ pressed.
Default: _[{
regex = "(mailto:|gemini:|gopher:|https:|http:|news:|file:|git:|ssh:|ftp:)[^\\u0000-\\u001F\\u007F-\\u009F<>\\"\\\\s{-}\\\\^⟨⟩`]+",++
hyperlinks = true,++
post_processing = true,++
mouse = { enabled = true },++
binding = { key = "U", mods = "Control | Shift" },
}]_
# Bindings
This section documents the *mouse_bindings* and *key_bindings* at the root level
of the configuration file.
*mouse_bindings* [{ <mouse>, <mods>, <mode>, <action> | chars = <string> },]
See _key_bindings_ for full documentation on _mods_, _mode_, _action_, and
_chars_.
To trigger mouse bindings when an application running within Alacritty
captures the mouse, the `Shift` modifier is automatically added as a
requirement.
*mouse* "Middle" | "Left" | "Right" | <number>
Mouse button which needs to be pressed to trigger this binding.
*action* <key_bindings.action> | "ExpandSelection"
*ExpandSelection*
Expand the selection to the current mouse cursor location.
*key_bindings* [{ <key>, <mods>, <mode>, <action> | chars = <string> },]
To unset a default binding, you can use the action _"ReceiveChar"_ to remove
it or _"None"_ to inhibit any action.
Multiple keybindings can be triggered by a single key press and will be
executed in the order they are defined in.
*key* <string>
Identifier of the binding's key, for example: _"A"_, _"F1"_, or
_"Key0"_.
A full list with available key codes can be found here:++
https://docs.rs/winit/\*/winit/event/enum.VirtualKeyCode.html#variants
The _key_ field also supports using scancodes, which are specified as a
decimal number.
*mods* "Command" | "Control" | "Option" | "Super" | "Shift" | "Alt"
Multiple modifiers can be combined using _|_, like this: _"Control |
Shift"_.
*mode* "AppCursor" | "AppKeypad" | "Search" | "Alt" | "Vi"
This defines a terminal mode which must be active for this binding to
have an effect.
Prepending _~_ to a mode will require the mode to *not* be active for
the binding to take effect.
Multiple modes can be combined using _|_, like this: _"~Vi|Search"_.
*chars*
Writes the specified string to the terminal.
*action*
*Up*
One line up.
*Down*
One line down.
*Left*
One character left.
*Right*
One character right.
*First*
First column, or beginning of the line when already at the first column.
*Last*
Last column, or beginning of the line when already at the last column.
*FirstOccupied*
First non-empty cell in this terminal row, or first non-empty cell
of the line when already at the first cell of the row.
*High*
Top of the screen.
*Middle*
Center of the screen.
*Low*
Bottom of the screen.
*SemanticLeft*
the previous semantically separated word.
*SemanticRight*
he next semantically separated word.
*SemanticLeftEnd*
revious semantically separated word.
*SemanticRightEnd*
End of the next semantically separated word.
*WordLeft*
Start of the previous whitespace separated word.
*WordRight*
Start of the next whitespace separated word.
*WordLeftEnd*
End of the previous whitespace separated word.
*WordRightEnd*
End of the next whitespace separated word.
*Bracket*
Character matching the bracket at the cursor's location.
*SearchNext*
Beginning of the next match.
*SearchPrevious*
Beginning of the previous match.
*SearchStart*
Start of the match to the left of the vi mode cursor.
*SearchEnd*
End of the match to the right of the vi mode cursor.
_Search mode exclusive:_
*SearchFocusNext*
Move the focus to the next search match.
*SearchFocusPrevious*
Move the focus to the previous search match.
*SearchConfirm*
*SearchCancel*
*SearchClear*
Reset the search regex.
*SearchDeleteWord*
Delete the last word in the search regex.
*SearchHistoryPrevious*
Go to the previous regex in the search history.
*SearchHistoryNext*
Go to the next regex in the search history.
_macOS exclusive:_
*ToggleSimpleFullscreen*
Enter fullscreen without occupying another space.
_Linux/BSD exclusive:_
*CopySelection*
Copy from the selection buffer.
*PasteSelection*
Paste from the selection buffer.
# Debug
This section documents the *[debug]* table of the configuration file.
Debug options are meant to help troubleshoot issues with Alacritty. These can
change or be removed entirely without warning, so their stability shouldn't be
relied upon.
*render_timer* <boolean>
Display the time it takes to draw each frame.
Default: _false_
*persistent_logging* <boolean>
Keep the log file after quitting Alacritty.
Default: _false_
*log_level* "Off" | "Error" | "Warn" | "Info" | "Debug" | "Trace"
Default: _"Warn"_
*renderer* "glsl3" | "gles2" | "gles2_pure" | "None"
Force use of a specific renderer, _"None"_ will use the highest available
one.
Default: _"None"_
*print_events* <boolean>
Log all received window events.
Default: _false_
*highlight_damage* <boolean>
Highlight window damage information.
Default: _false_
# SEE ALSO
See the alacritty github repository at https://github.com/alacritty/alacritty
for the full documentation.
# BUGS
Found a bug? Please report it at https://github.com/alacritty/alacritty/issues.
# MAINTAINERS
- Christian Duerr <contact@christianduerr.com>
- Kirill Chibisov <contact@kchibisov.com>