Rename goto_split top/bottom directions to up/down. (#3427)

Renames the top/bottom directions of `goto_split` to up/down. I have
tested this on linux (nixos) but given that `goto_split` is broken on
linux anyway (#2866) there's not a whole lot to test.

I have no way to build on macOS so I can't verify that I've changed
everything correctly for that.

Closes #3237
This commit is contained in:
Mitchell Hashimoto
2025-01-02 07:08:16 -08:00
committed by GitHub
10 changed files with 77 additions and 36 deletions

View File

@ -375,9 +375,9 @@ typedef enum {
typedef enum { typedef enum {
GHOSTTY_GOTO_SPLIT_PREVIOUS, GHOSTTY_GOTO_SPLIT_PREVIOUS,
GHOSTTY_GOTO_SPLIT_NEXT, GHOSTTY_GOTO_SPLIT_NEXT,
GHOSTTY_GOTO_SPLIT_TOP, GHOSTTY_GOTO_SPLIT_UP,
GHOSTTY_GOTO_SPLIT_LEFT, GHOSTTY_GOTO_SPLIT_LEFT,
GHOSTTY_GOTO_SPLIT_BOTTOM, GHOSTTY_GOTO_SPLIT_DOWN,
GHOSTTY_GOTO_SPLIT_RIGHT, GHOSTTY_GOTO_SPLIT_RIGHT,
} ghostty_action_goto_split_e; } ghostty_action_goto_split_e;

View File

@ -358,8 +358,8 @@ class AppDelegate: NSObject,
syncMenuShortcut(config, action: "toggle_split_zoom", menuItem: self.menuZoomSplit) syncMenuShortcut(config, action: "toggle_split_zoom", menuItem: self.menuZoomSplit)
syncMenuShortcut(config, action: "goto_split:previous", menuItem: self.menuPreviousSplit) syncMenuShortcut(config, action: "goto_split:previous", menuItem: self.menuPreviousSplit)
syncMenuShortcut(config, action: "goto_split:next", menuItem: self.menuNextSplit) syncMenuShortcut(config, action: "goto_split:next", menuItem: self.menuNextSplit)
syncMenuShortcut(config, action: "goto_split:top", menuItem: self.menuSelectSplitAbove) syncMenuShortcut(config, action: "goto_split:up", menuItem: self.menuSelectSplitAbove)
syncMenuShortcut(config, action: "goto_split:bottom", menuItem: self.menuSelectSplitBelow) syncMenuShortcut(config, action: "goto_split:down", menuItem: self.menuSelectSplitBelow)
syncMenuShortcut(config, action: "goto_split:left", menuItem: self.menuSelectSplitLeft) syncMenuShortcut(config, action: "goto_split:left", menuItem: self.menuSelectSplitLeft)
syncMenuShortcut(config, action: "goto_split:right", menuItem: self.menuSelectSplitRight) syncMenuShortcut(config, action: "goto_split:right", menuItem: self.menuSelectSplitRight)
syncMenuShortcut(config, action: "resize_split:up,10", menuItem: self.menuMoveSplitDividerUp) syncMenuShortcut(config, action: "resize_split:up,10", menuItem: self.menuMoveSplitDividerUp)

View File

@ -540,11 +540,11 @@ class BaseTerminalController: NSWindowController,
} }
@IBAction func splitMoveFocusAbove(_ sender: Any) { @IBAction func splitMoveFocusAbove(_ sender: Any) {
splitMoveFocus(direction: .top) splitMoveFocus(direction: .up)
} }
@IBAction func splitMoveFocusBelow(_ sender: Any) { @IBAction func splitMoveFocusBelow(_ sender: Any) {
splitMoveFocus(direction: .bottom) splitMoveFocus(direction: .down)
} }
@IBAction func splitMoveFocusLeft(_ sender: Any) { @IBAction func splitMoveFocusLeft(_ sender: Any) {

View File

@ -51,7 +51,7 @@ extension Ghostty {
/// Returns the view that would prefer receiving focus in this tree. This is always the /// Returns the view that would prefer receiving focus in this tree. This is always the
/// top-left-most view. This is used when creating a split or closing a split to find the /// top-left-most view. This is used when creating a split or closing a split to find the
/// next view to send focus to. /// next view to send focus to.
func preferredFocus(_ direction: SplitFocusDirection = .top) -> SurfaceView { func preferredFocus(_ direction: SplitFocusDirection = .up) -> SurfaceView {
let container: Container let container: Container
switch (self) { switch (self) {
case .leaf(let leaf): case .leaf(let leaf):
@ -64,10 +64,10 @@ extension Ghostty {
let node: SplitNode let node: SplitNode
switch (direction) { switch (direction) {
case .previous, .top, .left: case .previous, .up, .left:
node = container.bottomRight node = container.bottomRight
case .next, .bottom, .right: case .next, .down, .right:
node = container.topLeft node = container.topLeft
} }
@ -431,12 +431,12 @@ extension Ghostty {
struct Neighbors { struct Neighbors {
var left: SplitNode? var left: SplitNode?
var right: SplitNode? var right: SplitNode?
var top: SplitNode? var up: SplitNode?
var bottom: SplitNode? var down: SplitNode?
/// These are the previous/next nodes. It will certainly be one of the above as well /// These are the previous/next nodes. It will certainly be one of the above as well
/// but we keep track of these separately because depending on the split direction /// but we keep track of these separately because depending on the split direction
/// of the containing node, previous may be left OR top (same for next). /// of the containing node, previous may be left OR up (same for next).
var previous: SplitNode? var previous: SplitNode?
var next: SplitNode? var next: SplitNode?
@ -448,8 +448,8 @@ extension Ghostty {
let map: [SplitFocusDirection : KeyPath<Self, SplitNode?>] = [ let map: [SplitFocusDirection : KeyPath<Self, SplitNode?>] = [
.previous: \.previous, .previous: \.previous,
.next: \.next, .next: \.next,
.top: \.top, .up: \.up,
.bottom: \.bottom, .down: \.down,
.left: \.left, .left: \.left,
.right: \.right, .right: \.right,
] ]

View File

@ -308,7 +308,7 @@ extension Ghostty {
resizeIncrements: .init(width: 1, height: 1), resizeIncrements: .init(width: 1, height: 1),
resizePublisher: container.resizeEvent, resizePublisher: container.resizeEvent,
left: { left: {
let neighborKey: WritableKeyPath<SplitNode.Neighbors, SplitNode?> = container.direction == .horizontal ? \.right : \.bottom let neighborKey: WritableKeyPath<SplitNode.Neighbors, SplitNode?> = container.direction == .horizontal ? \.right : \.down
TerminalSplitNested( TerminalSplitNested(
node: closeableTopLeft(), node: closeableTopLeft(),
@ -318,7 +318,7 @@ extension Ghostty {
]) ])
) )
}, right: { }, right: {
let neighborKey: WritableKeyPath<SplitNode.Neighbors, SplitNode?> = container.direction == .horizontal ? \.left : \.top let neighborKey: WritableKeyPath<SplitNode.Neighbors, SplitNode?> = container.direction == .horizontal ? \.left : \.up
TerminalSplitNested( TerminalSplitNested(
node: closeableBottomRight(), node: closeableBottomRight(),

View File

@ -66,7 +66,7 @@ extension Ghostty {
/// An enum that is used for the directions that a split focus event can change. /// An enum that is used for the directions that a split focus event can change.
enum SplitFocusDirection { enum SplitFocusDirection {
case previous, next, top, bottom, left, right case previous, next, up, down, left, right
/// Initialize from a Ghostty API enum. /// Initialize from a Ghostty API enum.
static func from(direction: ghostty_action_goto_split_e) -> Self? { static func from(direction: ghostty_action_goto_split_e) -> Self? {
@ -77,11 +77,11 @@ extension Ghostty {
case GHOSTTY_GOTO_SPLIT_NEXT: case GHOSTTY_GOTO_SPLIT_NEXT:
return .next return .next
case GHOSTTY_GOTO_SPLIT_TOP: case GHOSTTY_GOTO_SPLIT_UP:
return .top return .up
case GHOSTTY_GOTO_SPLIT_BOTTOM: case GHOSTTY_GOTO_SPLIT_DOWN:
return .bottom return .down
case GHOSTTY_GOTO_SPLIT_LEFT: case GHOSTTY_GOTO_SPLIT_LEFT:
return .left return .left
@ -102,11 +102,11 @@ extension Ghostty {
case .next: case .next:
return GHOSTTY_GOTO_SPLIT_NEXT return GHOSTTY_GOTO_SPLIT_NEXT
case .top: case .up:
return GHOSTTY_GOTO_SPLIT_TOP return GHOSTTY_GOTO_SPLIT_UP
case .bottom: case .down:
return GHOSTTY_GOTO_SPLIT_BOTTOM return GHOSTTY_GOTO_SPLIT_DOWN
case .left: case .left:
return GHOSTTY_GOTO_SPLIT_LEFT return GHOSTTY_GOTO_SPLIT_LEFT

View File

@ -332,9 +332,9 @@ pub const GotoSplit = enum(c_int) {
previous, previous,
next, next,
top, up,
left, left,
bottom, down,
right, right,
}; };

View File

@ -339,7 +339,7 @@ pub fn directionMap(self: *const Split, from: Side) DirectionMap {
// This behavior matches the behavior of macOS at the time of writing // This behavior matches the behavior of macOS at the time of writing
// this. There is an open issue (#524) to make this depend on the // this. There is an open issue (#524) to make this depend on the
// actual physical location of the current split. // actual physical location of the current split.
result.put(.top, prev.surface); result.put(.up, prev.surface);
result.put(.left, prev.surface); result.put(.left, prev.surface);
} }
} }
@ -347,7 +347,7 @@ pub fn directionMap(self: *const Split, from: Side) DirectionMap {
if (self.directionNext(from)) |next| { if (self.directionNext(from)) |next| {
result.put(.next, next.surface); result.put(.next, next.surface);
if (!next.wrapped) { if (!next.wrapped) {
result.put(.bottom, next.surface); result.put(.down, next.surface);
result.put(.right, next.surface); result.put(.right, next.surface);
} }
} }

View File

@ -2247,12 +2247,12 @@ pub fn default(alloc_gpa: Allocator) Allocator.Error!Config {
try result.keybind.set.put( try result.keybind.set.put(
alloc, alloc,
.{ .key = .{ .translated = .up }, .mods = .{ .ctrl = true, .alt = true } }, .{ .key = .{ .translated = .up }, .mods = .{ .ctrl = true, .alt = true } },
.{ .goto_split = .top }, .{ .goto_split = .up },
); );
try result.keybind.set.put( try result.keybind.set.put(
alloc, alloc,
.{ .key = .{ .translated = .down }, .mods = .{ .ctrl = true, .alt = true } }, .{ .key = .{ .translated = .down }, .mods = .{ .ctrl = true, .alt = true } },
.{ .goto_split = .bottom }, .{ .goto_split = .down },
); );
try result.keybind.set.put( try result.keybind.set.put(
alloc, alloc,
@ -2516,12 +2516,12 @@ pub fn default(alloc_gpa: Allocator) Allocator.Error!Config {
try result.keybind.set.put( try result.keybind.set.put(
alloc, alloc,
.{ .key = .{ .translated = .up }, .mods = .{ .super = true, .alt = true } }, .{ .key = .{ .translated = .up }, .mods = .{ .super = true, .alt = true } },
.{ .goto_split = .top }, .{ .goto_split = .up },
); );
try result.keybind.set.put( try result.keybind.set.put(
alloc, alloc,
.{ .key = .{ .translated = .down }, .mods = .{ .super = true, .alt = true } }, .{ .key = .{ .translated = .down }, .mods = .{ .super = true, .alt = true } },
.{ .goto_split = .bottom }, .{ .goto_split = .down },
); );
try result.keybind.set.put( try result.keybind.set.put(
alloc, alloc,

View File

@ -478,10 +478,42 @@ pub const Action = union(enum) {
previous, previous,
next, next,
top, up,
left, left,
bottom, down,
right, right,
pub fn parse(input: []const u8) !SplitFocusDirection {
return std.meta.stringToEnum(SplitFocusDirection, input) orelse {
// For backwards compatibility we map "top" and "bottom" onto the enum
// values "up" and "down"
if (std.mem.eql(u8, input, "top")) {
return .up;
} else if (std.mem.eql(u8, input, "bottom")) {
return .down;
} else {
return Error.InvalidFormat;
}
};
}
test "parse" {
const testing = std.testing;
try testing.expectEqual(.previous, try SplitFocusDirection.parse("previous"));
try testing.expectEqual(.next, try SplitFocusDirection.parse("next"));
try testing.expectEqual(.up, try SplitFocusDirection.parse("up"));
try testing.expectEqual(.left, try SplitFocusDirection.parse("left"));
try testing.expectEqual(.down, try SplitFocusDirection.parse("down"));
try testing.expectEqual(.right, try SplitFocusDirection.parse("right"));
try testing.expectEqual(.up, try SplitFocusDirection.parse("top"));
try testing.expectEqual(.down, try SplitFocusDirection.parse("bottom"));
try testing.expectError(error.InvalidFormat, SplitFocusDirection.parse(""));
try testing.expectError(error.InvalidFormat, SplitFocusDirection.parse("green"));
}
}; };
pub const SplitResizeDirection = enum { pub const SplitResizeDirection = enum {
@ -524,7 +556,16 @@ pub const Action = union(enum) {
comptime field: std.builtin.Type.UnionField, comptime field: std.builtin.Type.UnionField,
param: []const u8, param: []const u8,
) !field.type { ) !field.type {
return switch (@typeInfo(field.type)) { const field_info = @typeInfo(field.type);
// Fields can provide a custom "parse" function
if (field_info == .Struct or field_info == .Union or field_info == .Enum) {
if (@hasDecl(field.type, "parse") and @typeInfo(@TypeOf(field.type.parse)) == .Fn) {
return field.type.parse(param);
}
}
return switch (field_info) {
.Enum => try parseEnum(field.type, param), .Enum => try parseEnum(field.type, param),
.Int => try parseInt(field.type, param), .Int => try parseInt(field.type, param),
.Float => try parseFloat(field.type, param), .Float => try parseFloat(field.type, param),