Minga.Core.Face (Minga v0.1.0)

Copy Markdown View Source

A named, inheritable bundle of visual attributes.

A face describes how text should look: foreground/background colors, bold/italic/underline, underline style and color, strikethrough, blend (opacity), and GUI-only font properties. Faces form an inheritance chain where nil attributes resolve up to the parent face.

Inheritance

Every face except "default" has a parent. When resolving a face's effective style, nil fields inherit from the parent. The resolution walks up the chain until it finds a non-nil value or reaches "default".

The inheritance chain follows tree-sitter capture name structure: "keyword.function" inherits from "keyword", which inherits from "default". This replaces the suffix-fallback lookup in Theme.style_for_capture/2 with proper structural inheritance.

GUI-only fields

font_family, font_weight, font_slant, and font_features are silently ignored in the TUI backend. The TUI uses bold/italic attrs from the base fields; the GUI uses the font fields to select specific font variants.

Blend

The blend field (0-100) controls opacity. In the GUI, this maps to alpha blending in the Metal shader. In the TUI, values below 50 map to the dim attribute; values 50-100 are rendered at full brightness. nil means inherit from parent (default is 100 = fully opaque).

Summary

Types

RGB color as a 24-bit integer (e.g., 0xFF6C6B).

Font slant for GUI rendering.

Font weight for GUI rendering.

t()

Underline rendering style.

Functions

The default face. All inheritance chains terminate here.

Creates a face from a style keyword list (as used in Theme syntax maps).

Infers the parent face name from a dotted capture name.

Creates an anonymous face from keyword attributes.

Resolves a face's effective attributes by walking the inheritance chain.

Converts a face to a style keyword list compatible with Protocol.style().

Types

color()

@type color() :: non_neg_integer()

RGB color as a 24-bit integer (e.g., 0xFF6C6B).

font_slant()

@type font_slant() :: :roman | :italic | :oblique

Font slant for GUI rendering.

font_weight()

@type font_weight() :: :thin | :light | :regular | :medium | :bold | :black

Font weight for GUI rendering.

t()

@type t() :: %Minga.Core.Face{
  bg: color() | nil,
  blend: 0..100 | nil,
  bold: boolean() | nil,
  fg: color() | nil,
  font_family: String.t() | nil,
  font_features: %{required(String.t()) => boolean()} | nil,
  font_slant: font_slant() | nil,
  font_weight: font_weight() | nil,
  inherit: String.t() | nil,
  italic: boolean() | nil,
  name: String.t(),
  reverse: boolean() | nil,
  strikethrough: boolean() | nil,
  underline: boolean() | nil,
  underline_color: color() | nil,
  underline_style: underline_style() | nil
}

underline_style()

@type underline_style() :: :line | :curl | :dashed | :dotted | :double

Underline rendering style.

Functions

default()

@spec default() :: t()

The default face. All inheritance chains terminate here.

Doom One colors, no decorations, fully opaque.

from_style(name, style, opts \\ [])

@spec from_style(String.t(), keyword(), keyword()) :: t()

Creates a face from a style keyword list (as used in Theme syntax maps).

This converts the existing [fg: 0xFF6C6B, bold: true] format into a Face struct. The name and optional inherit are provided separately.

Examples

iex> Face.from_style("keyword", [fg: 0xC678DD, bold: true])
%Face{name: "keyword", fg: 0xC678DD, bold: true, inherit: nil}

iex> Face.from_style("keyword.function", [fg: 0xC678DD, bold: true], inherit: "keyword")
%Face{name: "keyword.function", fg: 0xC678DD, bold: true, inherit: "keyword"}

infer_parent(name)

@spec infer_parent(String.t()) :: String.t() | nil

Infers the parent face name from a dotted capture name.

Strips the last .segment to produce the parent. Single-segment names (e.g., "keyword") return "default".

Examples

iex> Face.infer_parent("keyword.function.builtin")
"keyword.function"

iex> Face.infer_parent("keyword")
"default"

iex> Face.infer_parent("default")
nil

new(attrs \\ [])

@spec new(keyword()) :: t()

Creates an anonymous face from keyword attributes.

Convenience constructor for inline styles in renderer modules. Equivalent to from_style("_", attrs) but more concise.

Examples

iex> Face.new(fg: 0xFF6C6B, bold: true)
%Face{name: "_", fg: 0xFF6C6B, bold: true}

iex> Face.new()
%Face{name: "_"}

resolve(face, lookup)

@spec resolve(t(), (String.t() -> t() | nil)) :: t()

Resolves a face's effective attributes by walking the inheritance chain.

Takes a face and a lookup function (String.t() -> t() | nil) that retrieves parent faces by name. Returns a fully resolved face with no nil attribute values (every field has a concrete value from the nearest ancestor that defines it, bottoming out at default/0).

Examples

iex> comment = %Face{name: "comment", inherit: "default", fg: 0x5B6268, italic: true}
iex> resolved = Face.resolve(comment, fn "default" -> Face.default(); _ -> nil end)
iex> resolved.fg
0x5B6268
iex> resolved.italic
true
iex> resolved.bold
false

to_style(face, base \\ default())

@spec to_style(t(), t()) :: keyword()

Converts a face to a style keyword list compatible with Protocol.style().

This is the bridge between the face system and the existing render pipeline. Only includes fields that differ from the base face (defaults to default/0), matching the existing convention where styles are sparse (absent fields use the editor's default colors/attributes).

The keyword list may include: fg, bg, bold, italic, underline, strikethrough, underline_style, underline_color, blend.