mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
Merge pull request #1158 from mitchellh/grapheme-config
config: grapheme-width-method, default to "unicode"
This commit is contained in:
@ -162,6 +162,32 @@ const c = @cImport({
|
|||||||
@"adjust-strikethrough-position": ?MetricModifier = null,
|
@"adjust-strikethrough-position": ?MetricModifier = null,
|
||||||
@"adjust-strikethrough-thickness": ?MetricModifier = null,
|
@"adjust-strikethrough-thickness": ?MetricModifier = null,
|
||||||
|
|
||||||
|
/// The method to use for calculating the cell width of a grapheme cluster.
|
||||||
|
/// The default value is "unicode" which uses the Unicode standard to
|
||||||
|
/// determine grapheme width. This results in correct grapheme width but
|
||||||
|
/// may result in cursor-desync issues with some programs (such as shells)
|
||||||
|
/// that may use a legacy method such as "wcswidth".
|
||||||
|
///
|
||||||
|
/// Valid values are:
|
||||||
|
///
|
||||||
|
/// - "wcswidth" - Use the wcswidth function to determine grapheme width.
|
||||||
|
/// This maximizes compatibility with legacy programs but may result
|
||||||
|
/// in incorrect grapheme width for certain graphemes such as skin-tone
|
||||||
|
/// emoji, non-English characters, etc.
|
||||||
|
///
|
||||||
|
/// Note that this "wcswidth" functionality is based on the libc wcswidth,
|
||||||
|
/// not any other libraries with that name.
|
||||||
|
///
|
||||||
|
/// - "unicode" - Use the Unicode standard to determine grapheme width.
|
||||||
|
///
|
||||||
|
/// If a running program explicitly enables terminal mode 2027, then
|
||||||
|
/// "unicode" width will be forced regardless of this configuration. When
|
||||||
|
/// mode 2027 is reset, this configuration will be used again.
|
||||||
|
///
|
||||||
|
/// This configuration can be changed at runtime but will not affect existing
|
||||||
|
/// printed cells. Only new cells will use the new configuration.
|
||||||
|
@"grapheme-width-method": GraphemeWidthMethod = .unicode,
|
||||||
|
|
||||||
/// A named theme to use. The available themes are currently hardcoded to
|
/// A named theme to use. The available themes are currently hardcoded to
|
||||||
/// the themes that ship with Ghostty. On macOS, this list is in the
|
/// the themes that ship with Ghostty. On macOS, this list is in the
|
||||||
/// `Ghostty.app/Contents/Resources/themes` directory. On Linux, this
|
/// `Ghostty.app/Contents/Resources/themes` directory. On Linux, this
|
||||||
@ -2781,3 +2807,9 @@ pub const WindowSaveState = enum {
|
|||||||
never,
|
never,
|
||||||
always,
|
always,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// See grapheme-width-method
|
||||||
|
pub const GraphemeWidthMethod = enum {
|
||||||
|
wcswidth,
|
||||||
|
unicode,
|
||||||
|
};
|
||||||
|
@ -120,6 +120,9 @@ flags: packed struct {
|
|||||||
/// then we want to capture the shift key for the mouse protocol
|
/// then we want to capture the shift key for the mouse protocol
|
||||||
/// if the configuration allows it.
|
/// if the configuration allows it.
|
||||||
mouse_shift_capture: enum { null, false, true } = .null,
|
mouse_shift_capture: enum { null, false, true } = .null,
|
||||||
|
|
||||||
|
/// If true, we perform grapheme clustering even if mode 2027 is disabled.
|
||||||
|
default_grapheme_cluster: bool = false,
|
||||||
} = .{},
|
} = .{},
|
||||||
|
|
||||||
/// The event types that can be reported for mouse-related activities.
|
/// The event types that can be reported for mouse-related activities.
|
||||||
@ -724,6 +727,8 @@ pub fn print(self: *Terminal, c: u21) !void {
|
|||||||
const tracy = trace(@src());
|
const tracy = trace(@src());
|
||||||
defer tracy.end();
|
defer tracy.end();
|
||||||
|
|
||||||
|
// log.debug("print={x} y={} x={}", .{ c, self.screen.cursor.y, self.screen.cursor.x });
|
||||||
|
|
||||||
// If we're not on the main display, do nothing for now
|
// If we're not on the main display, do nothing for now
|
||||||
if (self.status_display != .main) return;
|
if (self.status_display != .main) return;
|
||||||
|
|
||||||
@ -738,7 +743,7 @@ pub fn print(self: *Terminal, c: u21) !void {
|
|||||||
// purposely ordered in least-likely to most-likely so we can drop out
|
// purposely ordered in least-likely to most-likely so we can drop out
|
||||||
// as quickly as possible.
|
// as quickly as possible.
|
||||||
if (c > 255 and
|
if (c > 255 and
|
||||||
self.modes.get(.grapheme_cluster) and
|
(self.modes.get(.grapheme_cluster) or self.flags.default_grapheme_cluster) and
|
||||||
self.screen.cursor.x > 0)
|
self.screen.cursor.x > 0)
|
||||||
grapheme: {
|
grapheme: {
|
||||||
const row = self.screen.getRow(.{ .active = self.screen.cursor.y });
|
const row = self.screen.getRow(.{ .active = self.screen.cursor.y });
|
||||||
@ -775,6 +780,7 @@ pub fn print(self: *Terminal, c: u21) !void {
|
|||||||
if (prev.cell.attrs.grapheme) {
|
if (prev.cell.attrs.grapheme) {
|
||||||
var it = row.codepointIterator(prev.x);
|
var it = row.codepointIterator(prev.x);
|
||||||
while (it.next()) |cp2| {
|
while (it.next()) |cp2| {
|
||||||
|
// log.debug("cp1={x} cp2={x}", .{ cp1, cp2 });
|
||||||
assert(!ziglyph.graphemeBreak(
|
assert(!ziglyph.graphemeBreak(
|
||||||
cp1,
|
cp1,
|
||||||
cp2,
|
cp2,
|
||||||
@ -785,6 +791,7 @@ pub fn print(self: *Terminal, c: u21) !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// log.debug("cp1={x} cp2={x} end", .{ cp1, c });
|
||||||
break :brk ziglyph.graphemeBreak(cp1, c, &state);
|
break :brk ziglyph.graphemeBreak(cp1, c, &state);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -868,7 +875,9 @@ pub fn print(self: *Terminal, c: u21) !void {
|
|||||||
// If we have grapheme clustering enabled, we don't blindly attach
|
// If we have grapheme clustering enabled, we don't blindly attach
|
||||||
// any zero width character to our cells and we instead just ignore
|
// any zero width character to our cells and we instead just ignore
|
||||||
// it.
|
// it.
|
||||||
if (self.modes.get(.grapheme_cluster)) return;
|
if (self.modes.get(.grapheme_cluster) or
|
||||||
|
self.flags.default_grapheme_cluster)
|
||||||
|
return;
|
||||||
|
|
||||||
// If we're at cell zero, then this is malformed data and we don't
|
// If we're at cell zero, then this is malformed data and we don't
|
||||||
// print anything or even store this. Zero-width characters are ALWAYS
|
// print anything or even store this. Zero-width characters are ALWAYS
|
||||||
|
@ -105,6 +105,7 @@ pub const DerivedConfig = struct {
|
|||||||
background: configpkg.Config.Color,
|
background: configpkg.Config.Color,
|
||||||
osc_color_report_format: configpkg.Config.OSCColorReportFormat,
|
osc_color_report_format: configpkg.Config.OSCColorReportFormat,
|
||||||
term: []const u8,
|
term: []const u8,
|
||||||
|
grapheme_width_method: configpkg.Config.GraphemeWidthMethod,
|
||||||
|
|
||||||
pub fn init(
|
pub fn init(
|
||||||
alloc_gpa: Allocator,
|
alloc_gpa: Allocator,
|
||||||
@ -122,6 +123,7 @@ pub const DerivedConfig = struct {
|
|||||||
.background = config.background,
|
.background = config.background,
|
||||||
.osc_color_report_format = config.@"osc-color-report-format",
|
.osc_color_report_format = config.@"osc-color-report-format",
|
||||||
.term = config.term,
|
.term = config.term,
|
||||||
|
.grapheme_width_method = config.@"grapheme-width-method",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,6 +148,7 @@ pub fn init(alloc: Allocator, opts: termio.Options) !Exec {
|
|||||||
errdefer term.deinit(alloc);
|
errdefer term.deinit(alloc);
|
||||||
term.default_palette = opts.config.palette;
|
term.default_palette = opts.config.palette;
|
||||||
term.color_palette.colors = opts.config.palette;
|
term.color_palette.colors = opts.config.palette;
|
||||||
|
term.flags.default_grapheme_cluster = opts.config.grapheme_width_method == .unicode;
|
||||||
|
|
||||||
// Set the image size limits
|
// Set the image size limits
|
||||||
try term.screen.kitty_images.setLimit(alloc, opts.config.image_storage_limit);
|
try term.screen.kitty_images.setLimit(alloc, opts.config.image_storage_limit);
|
||||||
@ -350,6 +353,8 @@ pub fn changeConfig(self: *Exec, config: *DerivedConfig) !void {
|
|||||||
// - command, working-directory: we never restart the underlying
|
// - command, working-directory: we never restart the underlying
|
||||||
// process so we don't care or need to know about these.
|
// process so we don't care or need to know about these.
|
||||||
|
|
||||||
|
self.terminal.flags.default_grapheme_cluster = config.grapheme_width_method == .unicode;
|
||||||
|
|
||||||
// Update the default palette. Note this will only apply to new colors drawn
|
// Update the default palette. Note this will only apply to new colors drawn
|
||||||
// since we decode all palette colors to RGB on usage.
|
// since we decode all palette colors to RGB on usage.
|
||||||
self.terminal.default_palette = config.palette;
|
self.terminal.default_palette = config.palette;
|
||||||
|
Reference in New Issue
Block a user