diff --git a/.editorconfig b/.editorconfig index d68a9a4d..065542cc 100644 --- a/.editorconfig +++ b/.editorconfig @@ -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 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3eedddce..98903798 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -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 diff --git a/CHANGELOG.md b/CHANGELOG.md index cda212ed..4ca925ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e41b85f3..a40a81db 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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 diff --git a/Cargo.lock b/Cargo.lock index 7ee44996..b84ee05d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", ] diff --git a/INSTALL.md b/INSTALL.md index debe3f81..643f8dcc 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -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 diff --git a/Makefile b/Makefile index e8a86e34..289bbe7c 100644 --- a/Makefile +++ b/Makefile @@ -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)" diff --git a/README.md b/README.md index 9a8546ba..87a514b1 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/alacritty.yml b/alacritty.yml deleted file mode 100644 index 76047d2a..00000000 --- a/alacritty.yml +++ /dev/null @@ -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 diff --git a/alacritty/Cargo.toml b/alacritty/Cargo.toml index aa9e817e..e7980c77 100644 --- a/alacritty/Cargo.toml +++ b/alacritty/Cargo.toml @@ -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"] } diff --git a/alacritty/src/cli.rs b/alacritty/src/cli.rs index 7f39e527..7eb281d9 100644 --- a/alacritty/src/cli.rs +++ b/alacritty/src/cli.rs @@ -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, - /// 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, - /// 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, - /// 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, @@ -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, } @@ -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 { - 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 { 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, + + /// 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::("}"); 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] diff --git a/alacritty/src/config/bindings.rs b/alacritty/src/config/bindings.rs index 692cf7e9..8fd16361 100644 --- a/alacritty/src/config/bindings.rs +++ b/alacritty/src/config/bindings.rs @@ -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(::duplicate_field("key")); } - let val = map.next_value::()?; - if val.is_u64() { - let scancode = val.as_u64().unwrap(); - if scancode > u64::from(u32::MAX) { - return Err(::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::()?; + match value.as_integer() { + Some(scancode) => match u32::try_from(scancode) { + Ok(scancode) => key = Some(Key::Scancode(scancode)), + Err(_) => { + return Err(::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!( diff --git a/alacritty/src/config/color.rs b/alacritty/src/config/color.rs index 23c18e50..e08b08b4 100644 --- a/alacritty/src/config/color.rs +++ b/alacritty/src/config/color.rs @@ -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)] diff --git a/alacritty/src/config/mod.rs b/alacritty/src/config/mod.rs index 2f230b06..821e9b6b 100644 --- a/alacritty/src/config/mod.rs +++ b/alacritty/src/config/mod.rs @@ -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 = std::result::Result; @@ -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 for Error { } } -impl From for Error { - fn from(val: serde_yaml::Error) -> Self { +impl From for Error { + fn from(val: TomlError) -> Self { + Error::Toml(val) + } +} + +impl From for Error { + fn from(val: TomlSeError) -> Self { + Error::TomlSe(val) + } +} + +impl From 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 { 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 { 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 { 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, 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, 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>, 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 { +pub fn installed_config(suffix: &str) -> Option { + 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 { } #[cfg(windows)] -fn installed_config() -> Option { - dirs::config_dir().map(|path| path.join("alacritty\\alacritty.yml")).filter(|new| new.exists()) +pub fn installed_config(suffix: &str) -> Option { + 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::("").unwrap(); } } diff --git a/alacritty/src/config/mouse.rs b/alacritty/src/config/mouse.rs index 291e4c61..b6556a2c 100644 --- a/alacritty/src/config/mouse.rs +++ b/alacritty/src/config/mouse.rs @@ -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, + 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); + +impl Default for MouseBindings { + fn default() -> Self { + Self(bindings::default_mouse_bindings()) + } +} + +impl<'de> Deserialize<'de> for MouseBindings { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Ok(Self(ui_config::deserialize_bindings(deserializer, Self::default().0)?)) + } +} diff --git a/alacritty/src/config/serde_utils.rs b/alacritty/src/config/serde_utils.rs index beb9c36b..476133e0 100644 --- a/alacritty/src/config/serde_utils.rs +++ b/alacritty/src/config/serde_utils.rs @@ -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); } diff --git a/alacritty/src/config/ui_config.rs b/alacritty/src/config/ui_config.rs index 29ff2c4c..0933ea42 100644 --- a/alacritty/src/config/ui_config.rs +++ b/alacritty/src/config/ui_config.rs @@ -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, @@ -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, } 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); - -impl Default for MouseBindings { - fn default() -> Self { - Self(bindings::default_mouse_bindings()) - } -} - -impl<'de> Deserialize<'de> for MouseBindings { - fn deserialize(deserializer: D) -> Result - 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>, ) -> Result>, D::Error> @@ -192,7 +204,7 @@ where T: Copy + Eq, Binding: Deserialize<'a>, { - let values = Vec::::deserialize(deserializer)?; + let values = Vec::::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::()? { + while let Some((key, value)) = map.next_entry::()? { match key.as_str() { "regex" => match Option::::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}"), } } diff --git a/alacritty/src/config/window.rs b/alacritty/src/config/window.rs index db29fd85..e4236b99 100644 --- a/alacritty/src/config/window.rs +++ b/alacritty/src/config/window.rs @@ -232,7 +232,7 @@ impl<'de> Deserialize<'de> for Class { { let mut class = Self::Value::default(); - while let Some((key, value)) = map.next_entry::()? { + while let Some((key, value)) = map.next_entry::()? { 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}"), } } diff --git a/alacritty/src/display/content.rs b/alacritty/src/display/content.rs index da211094..72f71e9b 100644 --- a/alacritty/src/display/content.rs +++ b/alacritty/src/display/content.rs @@ -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, ) { diff --git a/alacritty/src/main.rs b/alacritty/src/main.rs index 29120b35..55f81502 100644 --- a/alacritty/src/main.rs +++ b/alacritty/src/main.rs @@ -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> { // 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. diff --git a/alacritty/src/migrate.rs b/alacritty/src/migrate.rs new file mode 100644 index 00000000..07a10ba4 --- /dev/null +++ b/alacritty/src/migrate.rs @@ -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 { + // 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, 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 { + 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" + ); + } +} diff --git a/alacritty/src/window_context.rs b/alacritty/src/window_context.rs index 885d71a4..a450c77d 100644 --- a/alacritty/src/window_context.rs +++ b/alacritty/src/window_context.rs @@ -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, } @@ -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, diff --git a/alacritty_config/Cargo.toml b/alacritty_config/Cargo.toml index 995edfa1..b7ed5658 100644 --- a/alacritty_config/Cargo.toml +++ b/alacritty_config/Cargo.toml @@ -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"] } diff --git a/alacritty_config/src/lib.rs b/alacritty_config/src/lib.rs index 5ed9e1a5..c9d73622 100644 --- a/alacritty_config/src/lib.rs +++ b/alacritty_config/src/lib.rs @@ -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>; diff --git a/alacritty_config_derive/Cargo.toml b/alacritty_config_derive/Cargo.toml index d50b6b1d..12ef2169 100644 --- a/alacritty_config_derive/Cargo.toml +++ b/alacritty_config_derive/Cargo.toml @@ -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" diff --git a/alacritty_config_derive/src/config_deserialize/de_struct.rs b/alacritty_config_derive/src/config_deserialize/de_struct.rs index 1846f925..d2a7dd82 100644 --- a/alacritty_config_derive/src/config_deserialize/de_struct.rs +++ b/alacritty_config_derive/src/config_deserialize/de_struct.rs @@ -42,20 +42,25 @@ pub fn derive_deserialize( { 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::()? { + while let Some((key, value)) = map.next_entry::()? { 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" => { diff --git a/alacritty_config_derive/src/serde_replace.rs b/alacritty_config_derive/src/serde_replace.rs index 4a0a6a99..7ca5ca7c 100644 --- a/alacritty_config_derive/src/serde_replace.rs +++ b/alacritty_config_derive/src/serde_replace.rs @@ -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> { + fn replace(&mut self, key: &str, value: toml::Value) -> Result<(), Box> { if !key.is_empty() { let error = format!("Fields \"{}\" do not exist", key); return Err(error.into()); @@ -53,7 +53,7 @@ pub fn derive_recursive( 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> { + fn replace(&mut self, key: &str, value: toml::Value) -> Result<(), Box> { if key.is_empty() { *self = serde::Deserialize::deserialize(value)?; return Ok(()); diff --git a/alacritty_config_derive/tests/config.rs b/alacritty_config_derive/tests/config.rs index bd449ff8..15bc62a9 100644 --- a/alacritty_config_derive/tests/config.rs +++ b/alacritty_config_derive/tests/config.rs @@ -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); diff --git a/alacritty_terminal/Cargo.toml b/alacritty_terminal/Cargo.toml index 7d354f39..9844939f 100644 --- a/alacritty_terminal/Cargo.toml +++ b/alacritty_terminal/Cargo.toml @@ -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"] } diff --git a/alacritty_terminal/src/ansi.rs b/alacritty_terminal/src/ansi.rs index b596cc82..5a28bda5 100644 --- a/alacritty_terminal/src/ansi.rs +++ b/alacritty_terminal/src/ansi.rs @@ -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> { + fn replace(&mut self, key: &str, value: toml::Value) -> Result<(), Box> { if !key.is_empty() { return Err(format!("Fields \"{0}\" do not exist", key).into()); } diff --git a/docs/features.md b/docs/features.md index a97c143d..c1fff46e 100644 --- a/docs/features.md +++ b/docs/features.md @@ -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 Shift to bypass that. -[configuration file]: ../alacritty.yml - ## Multi-Window Alacritty supports running multiple terminal emulators from the same Alacritty diff --git a/extra/alacritty.yml b/extra/alacritty.yml deleted file mode 120000 index 2db9ba40..00000000 --- a/extra/alacritty.yml +++ /dev/null @@ -1 +0,0 @@ -../alacritty.yml \ No newline at end of file diff --git a/extra/completions/_alacritty b/extra/completions/_alacritty index c4f586bb..6f21f450 100644 --- a/extra/completions/_alacritty +++ b/extra/completions/_alacritty @@ -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=( diff --git a/extra/completions/alacritty.bash b/extra/completions/alacritty.bash index bb70ef3f..f7f2caf1 100644 --- a/extra/completions/alacritty.bash +++ b/extra/completions/alacritty.bash @@ -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 diff --git a/extra/completions/alacritty.fish b/extra/completions/alacritty.fish index 2d80be62..fe02ea82 100644 --- a/extra/completions/alacritty.fish +++ b/extra/completions/alacritty.fish @@ -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' diff --git a/extra/man/alacritty-bindings.5.scd b/extra/man/alacritty-bindings.5.scd new file mode 100644 index 00000000..2db753b1 --- /dev/null +++ b/extra/man/alacritty-bindings.5.scd @@ -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 +- Kirill Chibisov diff --git a/extra/man/alacritty.1.scd b/extra/man/alacritty.1.scd index 4278eba1..46d34e7b 100644 --- a/extra/man/alacritty.1.scd +++ b/extra/man/alacritty.1.scd @@ -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* X11 window ID to embed Alacritty within (decimal or hexadecimal with _0x_ prefix) diff --git a/extra/man/alacritty.5.scd b/extra/man/alacritty.5.scd new file mode 100644 index 00000000..055cacfa --- /dev/null +++ b/extra/man/alacritty.5.scd @@ -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* [,] + + 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* | "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* + + Live config reload (changes require restart) + +*ipc_socket* _(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 = , lines = } + + 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 = , y = } + + 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* + + 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* + + 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* + + Window title + + Default: _"Alacritty"_ + +*dynamic_title* + + Allow terminal applications to change Alacritty's window title. + + Default: _true_ + +*class* { instance = , general = } _(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* + + 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* + + Maximum number of lines in the scrollback buffer.++ +Specifying _0_ will disable scrolling.++ +Limited to _100000_. + + Default: _10000_ + +*multiplier* + + Number of line scrolled for every input scroll increment. + + Default: _3_ + +# Font + +This section documents the *[font]* table of the configuration file. + +*normal* { family = , style = } + + Default: + Linux/BSD: _{ family = "monospace", style = "Regular" }_++ +Windows: _{ family = "Consolas", style = "Regular" }_++ +macOS: _{ family = "Menlo", style = "Regular" }_ + +*bold* { family = , style = } + + If the family is not specified, it will fall back to the value specified for + the normal font. + + Default: _{ style = "Bold" }_ + +*italic* { family = , style = } + + If the family is not specified, it will fall back to the value specified for + the normal font. + + Default: _{ style = "Italic" }_ + +*bold_italic* { family = , style = } + + If the family is not specified, it will fall back to the value specified for + the normal font. + + Default: _{ style = "Bold Italic" }_ + +*size* + + Font size in points. + + Default: _11.0_ + +*offset* { x = , y = } + + 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 = , y = } + + 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* + + 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* + + Default: _"#1d1f21"_ + + *background* + + Default: _"#c5c8c6"_ + + *dim_foreground* + + If this is not set, the color is automatically calculated based on the + foreground color. + + Default: _"#828482"_ + + *bright_foreground* + + 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 = , cursor = } + + 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 = , cursor = } + + 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 = , background = } + + Default: _{ foreground = "#000000", background: "#ffffff" }_ + + *focused_match* { foreground = , background = } + + Default: _{ foreground = "#ffffff", background: "#000000" }_ + +*hints* + + *start* { foreground = , background = } + + 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 = , background = } + + 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 = , background = } + + 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 = , background = } + + 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 = , background = } + + 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* + Default: _"#1d1f21"_ + *red* + Default: _"#cc6666"_ + *green* + Default: _"#b5bd68"_ + *yellow* + Default: _"#f0c674"_ + *blue* + Default: _"#81a2be"_ + *magenta* + Default: _"#b294bb"_ + *cyan* + Default: _"#8abeb7"_ + *white* + Default: _"#c5c8c6"_ + +*bright* + + *black* + Default: _"#666666"_ + *red* + Default: _"#d54e53"_ + *green* + Default: _"#b9ca4a"_ + *yellow* + Default: _"#e7c547"_ + *blue* + Default: _"#7aa6da"_ + *magenta* + Default: _"#c397d8"_ + *cyan* + Default: _"#70c0b1"_ + *white* + Default: _"#eaeaea"_ + +*dim* + + If the dim colors are not set, they will be calculated automatically based + on the _normal_ colors. + + *black* + Default: _"#131415"_ + *red* + Default: _"#864343"_ + *green* + Default: _"#777c44"_ + *yellow* + Default: _"#9e824c"_ + *blue* + Default: _"#556a7d"_ + *magenta* + Default: _"#75617b"_ + *cyan* + Default: _"#5b7d78"_ + *white* + Default: _"#828482"_ + +*indexed_colors* [{ index = , color = },] + + 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* + + 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* + + 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* + + Duration of the visual bell flash in milliseconds. A `duration` of `0` will + disable the visual bell animation. + + Default: _0_ + +*color* + + Visual bell animation color. + + Default: _"#ffffff"_ + +*command* | { program = , args = [,] } + + 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* + + This string contains all characters that are used as separators for + "semantic words" in Alacritty. + + Default: _",│`|:\"' ()[]{}<>\t"_ + +*save_to_clipboard* + + 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* + + Cursor blinking interval in milliseconds. + + Default: _750_ + +*blink_timeout* + + Time after which cursor stops blinking, in seconds. + + Specifying _0_ will disable timeout for blinking. + + Default: _5_ + +*unfocused_hollow* + + When this is _true_, the cursor will be rendered as a hollow box when the + window is not focused. + + Default: _true_ + +*thickness* + + Thickness of the cursor relative to the cell width as floating point number + from _0.0_ to _1.0_. + + Default: _0.15_ + +*shell* | { program = , args = [,] } + + 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 = } + + Set maximum time between double-clicks in milliseconds. + + Default: _300_ + +*triple_click* { threshold = } + + Set maximum time between triple-clicks in milliseconds. + + Default: _300_ + +*hide_when_typing* + + 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* + + Keys used for the hint labels. + + Default: _"jfkdls;ahgurieowpq"_ + +*enabled* [{ , , , , , },] + +Array with all available hints. + +Each hint must have at least one of _regex_ or _hyperlinks_ and either an +_action_ or a _command_. + + *regex* + + Regex each line will be compared against. + + *hyperlinks* + + When this is _true_, all OSC 8 escape sequence hyperlinks will be + included in the hints. + + *post_processing* + + 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* | { program = , args = [,] } + + 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 = , mods = , mode = } + + See _key_bindings_ for documentation on available values. + + This controls which key binding is used to start the keyboard hint + selection process. + + *mouse* { mods = , enabled = } + + 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* [{ , , , | chars = },] + + 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" | + + Mouse button which needs to be pressed to trigger this binding. + + *action* | "ExpandSelection" + + *ExpandSelection* + Expand the selection to the current mouse cursor location. + +*key_bindings* [{ , , , | chars = },] + + 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* + + 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* + + Display the time it takes to draw each frame. + + Default: _false_ + +*persistent_logging* + + 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* + + Log all received window events. + + Default: _false_ + +*highlight_damage* + + 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 +- Kirill Chibisov