mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
Merge pull request #656 from mitchellh/enable-lr
Enable Left/Right Margin Mode
This commit is contained in:
@ -717,6 +717,12 @@ pub fn print(self: *Terminal, c: u21) !void {
|
|||||||
self.insertBlanks(width);
|
self.insertBlanks(width);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Our right margin depends where our cursor is now.
|
||||||
|
const right_limit = if (self.screen.cursor.x > self.scrolling_region.right)
|
||||||
|
self.cols
|
||||||
|
else
|
||||||
|
self.scrolling_region.right + 1;
|
||||||
|
|
||||||
switch (width) {
|
switch (width) {
|
||||||
// Single cell is very easy: just write in the cell
|
// Single cell is very easy: just write in the cell
|
||||||
1 => _ = @call(.always_inline, printCell, .{ self, c }),
|
1 => _ = @call(.always_inline, printCell, .{ self, c }),
|
||||||
@ -725,11 +731,11 @@ pub fn print(self: *Terminal, c: u21) !void {
|
|||||||
// using two cells: the first is flagged "wide" and has the
|
// using two cells: the first is flagged "wide" and has the
|
||||||
// wide char. The second is guaranteed to be a spacer if
|
// wide char. The second is guaranteed to be a spacer if
|
||||||
// we're not at the end of the line.
|
// we're not at the end of the line.
|
||||||
2 => if (self.cols > 1) {
|
2 => if ((right_limit - self.scrolling_region.left) > 1) {
|
||||||
// If we don't have space for the wide char, we need
|
// If we don't have space for the wide char, we need
|
||||||
// to insert spacers and wrap. Then we just print the wide
|
// to insert spacers and wrap. Then we just print the wide
|
||||||
// char as normal.
|
// char as normal.
|
||||||
if (self.screen.cursor.x == self.cols - 1) {
|
if (self.screen.cursor.x == right_limit - 1) {
|
||||||
const spacer_head = self.printCell(' ');
|
const spacer_head = self.printCell(' ');
|
||||||
spacer_head.attrs.wide_spacer_head = true;
|
spacer_head.attrs.wide_spacer_head = true;
|
||||||
try self.printWrap();
|
try self.printWrap();
|
||||||
@ -757,7 +763,7 @@ pub fn print(self: *Terminal, c: u21) !void {
|
|||||||
// If we're at the column limit, then we need to wrap the next time.
|
// If we're at the column limit, then we need to wrap the next time.
|
||||||
// This is unlikely so we do the increment above and decrement here
|
// This is unlikely so we do the increment above and decrement here
|
||||||
// if we need to rather than check once.
|
// if we need to rather than check once.
|
||||||
if (self.screen.cursor.x == self.cols) {
|
if (self.screen.cursor.x == right_limit) {
|
||||||
self.screen.cursor.x -= 1;
|
self.screen.cursor.x -= 1;
|
||||||
self.screen.cursor.pending_wrap = true;
|
self.screen.cursor.pending_wrap = true;
|
||||||
}
|
}
|
||||||
@ -835,7 +841,7 @@ fn printWrap(self: *Terminal) !void {
|
|||||||
|
|
||||||
// Move to the next line
|
// Move to the next line
|
||||||
try self.index();
|
try self.index();
|
||||||
self.screen.cursor.x = 0;
|
self.screen.cursor.x = self.scrolling_region.left;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clearWideSpacerHead(self: *Terminal) void {
|
fn clearWideSpacerHead(self: *Terminal) void {
|
||||||
@ -1329,12 +1335,13 @@ pub fn cursorLeft(self: *Terminal, count_req: usize) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The margins we can move to.
|
// The margins we can move to.
|
||||||
// TODO: if cursor is left of the left margin, assume left margin to be 0.
|
|
||||||
// verified with xterm. don't forget when left margins are implemented!
|
|
||||||
const left_margin = 0;
|
|
||||||
const right_margin = self.cols - 1;
|
|
||||||
const top = self.scrolling_region.top;
|
const top = self.scrolling_region.top;
|
||||||
const bottom = self.scrolling_region.bottom;
|
const bottom = self.scrolling_region.bottom;
|
||||||
|
const right_margin = self.scrolling_region.right;
|
||||||
|
const left_margin = if (self.screen.cursor.x < self.scrolling_region.left)
|
||||||
|
0
|
||||||
|
else
|
||||||
|
self.scrolling_region.left;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
// We can move at most to the left margin.
|
// We can move at most to the left margin.
|
||||||
@ -1820,7 +1827,7 @@ pub fn setLeftAndRightMargin(self: *Terminal, left_req: usize, right_req: usize)
|
|||||||
if (!self.modes.get(.enable_left_and_right_margin)) return;
|
if (!self.modes.get(.enable_left_and_right_margin)) return;
|
||||||
|
|
||||||
const left = @max(1, left_req);
|
const left = @max(1, left_req);
|
||||||
const right = @min(self.rows, if (right_req == 0) self.rows else right_req);
|
const right = @min(self.cols, if (right_req == 0) self.cols else right_req);
|
||||||
if (left >= right) return;
|
if (left >= right) return;
|
||||||
|
|
||||||
self.scrolling_region.left = left - 1;
|
self.scrolling_region.left = left - 1;
|
||||||
@ -2227,6 +2234,57 @@ test "Terminal: print invoke charset single" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "Terminal: print right margin wrap" {
|
||||||
|
var t = try init(testing.allocator, 10, 5);
|
||||||
|
defer t.deinit(testing.allocator);
|
||||||
|
|
||||||
|
try t.printString("123456789");
|
||||||
|
t.modes.set(.enable_left_and_right_margin, true);
|
||||||
|
t.setLeftAndRightMargin(3, 5);
|
||||||
|
t.setCursorPos(1, 5);
|
||||||
|
try t.printString("XY");
|
||||||
|
|
||||||
|
{
|
||||||
|
var str = try t.plainString(testing.allocator);
|
||||||
|
defer testing.allocator.free(str);
|
||||||
|
try testing.expectEqualStrings("1234X6789\n Y", str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "Terminal: print right margin outside" {
|
||||||
|
var t = try init(testing.allocator, 10, 5);
|
||||||
|
defer t.deinit(testing.allocator);
|
||||||
|
|
||||||
|
try t.printString("123456789");
|
||||||
|
t.modes.set(.enable_left_and_right_margin, true);
|
||||||
|
t.setLeftAndRightMargin(3, 5);
|
||||||
|
t.setCursorPos(1, 6);
|
||||||
|
try t.printString("XY");
|
||||||
|
|
||||||
|
{
|
||||||
|
var str = try t.plainString(testing.allocator);
|
||||||
|
defer testing.allocator.free(str);
|
||||||
|
try testing.expectEqualStrings("12345XY89", str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "Terminal: print right margin outside wrap" {
|
||||||
|
var t = try init(testing.allocator, 10, 5);
|
||||||
|
defer t.deinit(testing.allocator);
|
||||||
|
|
||||||
|
try t.printString("123456789");
|
||||||
|
t.modes.set(.enable_left_and_right_margin, true);
|
||||||
|
t.setLeftAndRightMargin(3, 5);
|
||||||
|
t.setCursorPos(1, 10);
|
||||||
|
try t.printString("XY");
|
||||||
|
|
||||||
|
{
|
||||||
|
var str = try t.plainString(testing.allocator);
|
||||||
|
defer testing.allocator.free(str);
|
||||||
|
try testing.expectEqualStrings("123456789X\n Y", str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test "Terminal: linefeed and carriage return" {
|
test "Terminal: linefeed and carriage return" {
|
||||||
var t = try init(testing.allocator, 80, 80);
|
var t = try init(testing.allocator, 80, 80);
|
||||||
defer t.deinit(testing.allocator);
|
defer t.deinit(testing.allocator);
|
||||||
@ -2702,6 +2760,8 @@ test "Terminal: setLeftAndRightMargin left only" {
|
|||||||
try t.printString("GHI");
|
try t.printString("GHI");
|
||||||
t.modes.set(.enable_left_and_right_margin, true);
|
t.modes.set(.enable_left_and_right_margin, true);
|
||||||
t.setLeftAndRightMargin(2, 0);
|
t.setLeftAndRightMargin(2, 0);
|
||||||
|
try testing.expectEqual(@as(usize, 1), t.scrolling_region.left);
|
||||||
|
try testing.expectEqual(@as(usize, t.cols - 1), t.scrolling_region.right);
|
||||||
t.setCursorPos(1, 2);
|
t.setCursorPos(1, 2);
|
||||||
try t.insertLines(1);
|
try t.insertLines(1);
|
||||||
|
|
||||||
|
@ -184,6 +184,7 @@ const entries: []const ModeEntry = &.{
|
|||||||
.{ .name = "enable_mode_3", .value = 40 },
|
.{ .name = "enable_mode_3", .value = 40 },
|
||||||
.{ .name = "reverse_wrap", .value = 45 },
|
.{ .name = "reverse_wrap", .value = 45 },
|
||||||
.{ .name = "keypad_keys", .value = 66 },
|
.{ .name = "keypad_keys", .value = 66 },
|
||||||
|
.{ .name = "enable_left_and_right_margin", .value = 69 },
|
||||||
.{ .name = "mouse_event_normal", .value = 1000 },
|
.{ .name = "mouse_event_normal", .value = 1000 },
|
||||||
.{ .name = "mouse_event_button", .value = 1002 },
|
.{ .name = "mouse_event_button", .value = 1002 },
|
||||||
.{ .name = "mouse_event_any", .value = 1003 },
|
.{ .name = "mouse_event_any", .value = 1003 },
|
||||||
@ -200,10 +201,6 @@ const entries: []const ModeEntry = &.{
|
|||||||
.{ .name = "bracketed_paste", .value = 2004 },
|
.{ .name = "bracketed_paste", .value = 2004 },
|
||||||
.{ .name = "synchronized_output", .value = 2026 },
|
.{ .name = "synchronized_output", .value = 2026 },
|
||||||
.{ .name = "grapheme_cluster", .value = 2027 },
|
.{ .name = "grapheme_cluster", .value = 2027 },
|
||||||
|
|
||||||
// Disabled for now until we ensure we get left/right margins working
|
|
||||||
// correctly in all sequences.
|
|
||||||
.{ .name = "enable_left_and_right_margin", .value = 69, .disabled = true },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
test {
|
test {
|
||||||
|
@ -141,6 +141,12 @@ pub const ghostty: Source = .{
|
|||||||
.{ .name = "XR", .value = .{ .string = "\\E[>0q" } },
|
.{ .name = "XR", .value = .{ .string = "\\E[>0q" } },
|
||||||
.{ .name = "xr", .value = .{ .string = "\\EP>\\|[ -~]+a\\E\\\\" } },
|
.{ .name = "xr", .value = .{ .string = "\\EP>\\|[ -~]+a\\E\\\\" } },
|
||||||
|
|
||||||
|
// DECSLRM (Left/Right Margins)
|
||||||
|
.{ .name = "Enmg", .value = .{ .string = "\\E[?69h" } },
|
||||||
|
.{ .name = "Dsmg", .value = .{ .string = "\\E[?69l" } },
|
||||||
|
.{ .name = "Clmg", .value = .{ .string = "\\E[s" } },
|
||||||
|
.{ .name = "Cmg", .value = .{ .string = "\\E[%i%p1%d;%p2%ds" } },
|
||||||
|
|
||||||
// These are all capabilities that should be pretty straightforward
|
// These are all capabilities that should be pretty straightforward
|
||||||
// and map to input sequences.
|
// and map to input sequences.
|
||||||
.{ .name = "bel", .value = .{ .string = "^G" } },
|
.{ .name = "bel", .value = .{ .string = "^G" } },
|
||||||
|
Reference in New Issue
Block a user