summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CHANGELOG.md2
-rw-r--r--src/builtins.zig2
-rw-r--r--src/config.zig45
-rw-r--r--src/exec.zig6
-rw-r--r--src/mouse.zig3
-rw-r--r--src/ninep/ctl.zig4
-rw-r--r--src/pardes.zig18
7 files changed, 63 insertions, 17 deletions
diff --git a/src/CHANGELOG.md b/src/CHANGELOG.md
index 874082d8..142287b2 100644
--- a/src/CHANGELOG.md
+++ b/src/CHANGELOG.md
@@ -13,6 +13,8 @@
the text of a pane with room to spare, else halving the biggest pane; never
into a new column. `Placement pardes` brings back the old rules, which open a
first document in a column of its own.
+- A document dragged into the left column no longer closes the untouched
+ boot shell there; `BootShell replace` brings that back.
- Column tags are always shown: the `ColumnTags` setting is gone, and an
init file's `ColumnTags` line is ignored with a message.
- Carrying a pane or a column by its grip shows acme's box cursor in the SDL
diff --git a/src/builtins.zig b/src/builtins.zig
index c2b4d62f..7b34b376 100644
--- a/src/builtins.zig
+++ b/src/builtins.zig
@@ -169,7 +169,7 @@ pub const registry = struct {
inline for (comptime settingList(), manualBuiltinCount()..) |setting, i|
if (@intFromEnum(b) == i) return switch (setting.action) {
// a switch flips bare, and DumpDir bare is the default
- .toggle, .transition, .scene, .dump_dir, .placement => false,
+ .toggle, .transition, .scene, .dump_dir, .choice => false,
.shell, .theme, .font, .tagline_size, .window_opacity, .window_blur, .message_ms, .shader, .shader_animation => true,
};
unreachable;
diff --git a/src/config.zig b/src/config.zig
index 48f49d50..01a5daf8 100644
--- a/src/config.zig
+++ b/src/config.zig
@@ -74,6 +74,7 @@ pub const leader_path = paths: {
.Delcol = "cd",
.Joincol = "cj",
.Placement = "cp",
+ .BootShell = null,
.Debug = "td",
.Msg = null,
.Colors = "tc",
@@ -666,9 +667,16 @@ pub const Runtime = struct {
/// Where a new pane goes (exec.placeNew): `acme`, acme's makenewwindow,
/// or `pardes`, the rules pardes had before it.
placement: Placement = .acme,
+ /// A document dragged into the left column where a lone shell nobody
+ /// has typed into stands: `keep` leaves the shell, `replace` closes it
+ /// (exec.evictLonePristineTty).
+ boot_shell: BootShell = .keep,
pub const ShaderAnimation = enum { off, on, always };
pub const Placement = enum { acme, pardes };
+ pub const BootShell = enum { keep, replace };
+ /// A setting that is one of a few words, each an enum field of Runtime.
+ pub const Choice = enum { placement, boot_shell };
/// Each pass the bundled Crt or a Shadertoy file.
pub const Post = struct {
@@ -841,7 +849,7 @@ pub const Runtime = struct {
/// it of files.
shader,
shader_animation,
- placement,
+ choice: Choice,
};
pub const Setting = struct {
@@ -893,7 +901,8 @@ pub const Runtime = struct {
.{ .word = "Crt", .action = .{ .scene = .crt }, .availability = .scene_shaders },
.{ .word = "Shader", .action = .shader, .availability = .scene_shaders },
.{ .word = "ShaderAnimation", .action = .shader_animation, .availability = .scene_shaders },
- .{ .word = "Placement", .action = .placement },
+ .{ .word = "Placement", .action = .{ .choice = .placement } },
+ .{ .word = "BootShell", .action = .{ .choice = .boot_shell } },
};
pub fn find(name: []const u8) ?Setting {
@@ -1052,13 +1061,17 @@ pub const Runtime = struct {
const text = std.mem.trim(u8, argument orelse return false, " \t\r\n");
state.shader_animation = std.meta.stringToEnum(ShaderAnimation, text) orelse return false;
},
- // Bare, it flips between the two, as a tag word should.
- .placement => {
- const text = std.mem.trim(u8, argument orelse "", " \t\r\n");
- state.placement = if (text.len == 0)
- (if (state.placement == .acme) .pardes else .acme)
- else
- std.meta.stringToEnum(Placement, text) orelse return false;
+ // Bare, it steps to the next word, as a tag word should.
+ .choice => |which| switch (which) {
+ inline else => |field| {
+ const now = &@field(state, @tagName(field));
+ const T = @TypeOf(now.*);
+ const text = std.mem.trim(u8, argument orelse "", " \t\r\n");
+ now.* = if (text.len == 0)
+ @enumFromInt((@as(usize, @intFromEnum(now.*)) + 1) % std.enums.values(T).len)
+ else
+ std.meta.stringToEnum(T, text) orelse return false;
+ },
},
.theme, .font => return false,
}
@@ -1229,7 +1242,9 @@ pub const Runtime = struct {
try out.print("{s} {s}\n", .{ setting.word, @tagName(state.shader_animation) })
else
try out.print("{s} unsupported\n", .{setting.word}),
- .placement => try out.print("{s} {s}\n", .{ setting.word, @tagName(state.placement) }),
+ .choice => |which| switch (which) {
+ inline else => |field| try out.print("{s} {s}\n", .{ setting.word, @tagName(@field(state, @tagName(field))) }),
+ },
};
if (context.startup_config_path) |path|
@@ -1481,7 +1496,10 @@ pub const Runtime = struct {
.dump_dir => "~/dumps",
.shader => "~/crt.glsl",
.shader_animation => "always",
- .placement => "pardes",
+ .choice => |which| switch (which) {
+ .placement => "pardes",
+ .boot_shell => "replace",
+ },
else => null,
};
try std.testing.expect(apply(&state, setting, argument));
@@ -1531,7 +1549,10 @@ pub const Runtime = struct {
},
.shader => "Shader ~/crt.glsl\n",
.shader_animation => "ShaderAnimation always\n",
- .placement => "Placement pardes\n",
+ .choice => |which| switch (which) {
+ .placement => "Placement pardes\n",
+ .boot_shell => "BootShell replace\n",
+ },
.theme, .font => unreachable,
};
try std.testing.expect(std.mem.indexOf(u8, report, expected) != null);
diff --git a/src/exec.zig b/src/exec.zig
index 117970d9..22aae10a 100644
--- a/src/exec.zig
+++ b/src/exec.zig
@@ -326,9 +326,11 @@ fn spawnTtyWithMount(p: *Pardes, from: usize, v9fs: bool) ?*Pane {
return nt;
}
-/// when a doc lands in `col`, a lone pristine shell there is clutter — drop
-/// it; absorbVWeight hands its space to the doc
+/// With `BootShell replace`, a doc landing in `col` closes a lone pristine
+/// shell there, the boot's placeholder, and absorbVWeight hands its space to
+/// the doc. With `keep`, the default, the shell stays like any other pane.
pub fn evictLonePristineTty(p: *Pardes, col: usize, keep_id: usize) void {
+ if (p.settings.boot_shell == .keep) return;
var n_tty: usize = 0;
var tty_id: usize = 0;
for (0..p.col_n[col]) |k| {
diff --git a/src/mouse.zig b/src/mouse.zig
index 5f0a6c4b..ac72481a 100644
--- a/src/mouse.zig
+++ b/src/mouse.zig
@@ -788,7 +788,8 @@ fn dragRelease(p: *Pardes, button: Mouse.Button) void {
.move => |d| {
layout.movePane(p, d.id, d.cur_x, d.cur_y);
if (layout.findPane(p, d.id)) |f| p.setActiveColumn(f.col);
- // a file moved into the left column evicts a lone unused shell
+ // BootShell replace: a file moved into the left column closes a
+ // lone unused shell there
if (p.panes[d.id]) |mt| if (mt.file != null) {
if (layout.findPane(p, d.id)) |f| if (f.col == 0)
exec.evictLonePristineTty(p, 0, d.id);
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig
index b4ae7231..23d966e8 100644
--- a/src/ninep/ctl.zig
+++ b/src/ninep/ctl.zig
@@ -308,7 +308,9 @@ fn writeSettings(p: *Pardes, w: *std.Io.Writer) !void {
.tagline_size => try w.print("{s} {d}\n", .{ word, s.font.tagline_percent }),
.window_opacity => try w.print("{s} {d}\n", .{ word, s.window_opacity }),
.window_blur => try w.print("{s} {d}\n", .{ word, s.window_blur }),
- .placement => try w.print("{s} {s}\n", .{ word, @tagName(s.placement) }),
+ .choice => |which| switch (which) {
+ inline else => |field| try w.print("{s} {s}\n", .{ word, @tagName(@field(s, @tagName(field))) }),
+ },
.message_ms => |which| try w.print("{s} {d}\n", .{ word, @field(s, "message_" ++ @tagName(which) ++ "_ms") }),
// Bare, it names the default directory.
.dump_dir => if (s.dump_dir.len > 0) try w.print("{s} {s}\n", .{ word, s.dump_dir.get() }) else try w.print("{s}\n", .{word}),
diff --git a/src/pardes.zig b/src/pardes.zig
index e3f4bb60..23341904 100644
--- a/src/pardes.zig
+++ b/src/pardes.zig
@@ -1336,6 +1336,24 @@ test "an empty column shows its theme's empty_col under its tag: acme's white, e
}
}
+test "BootShell keep leaves the untouched boot shell a dragged document joins; replace closes it" {
+ inline for (.{ "keep", "replace" }) |word| {
+ const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 100, .rows = 30 });
+ defer p.deinit();
+ const shell = p.active;
+ try std.testing.expect(p.panes[shell].?.isTerminal());
+ try std.testing.expect(p.executeBuiltinLine(shell, "BootShell " ++ word));
+ _ = try p.openTutorView(1);
+ layout.insert(p, 0, 1, 1);
+ p.sync();
+ exec.evictLonePristineTty(p, 0, 1);
+ p.sync();
+ const kept = std.mem.eql(u8, word, "keep");
+ try std.testing.expectEqual(kept, p.panes[shell] != null);
+ try std.testing.expectEqual(@as(usize, if (kept) 2 else 1), p.col_n[0]);
+ }
+}
+
test "dragging a column's only pane to another leaves that column in place, empty" {
const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 160, .rows = 32 });
defer p.deinit();