Add coding symbol rail
This commit is contained in:
+95
-2
@@ -1,6 +1,7 @@
|
||||
const std = @import("std");
|
||||
const input = @import("input.zig");
|
||||
const session_mod = @import("session.zig");
|
||||
const symbol_mod = @import("symbol.zig");
|
||||
|
||||
// Space leader command grammar, separate from keyboard layout facts.
|
||||
// req: input/001, input/002, input/004, testing/001, testing/002, testing/003, testing/004
|
||||
@@ -19,6 +20,7 @@ pub const Action = union(enum) {
|
||||
save,
|
||||
quit,
|
||||
open: []u8,
|
||||
symbol: symbol_mod.Symbol,
|
||||
not_built: Feature,
|
||||
|
||||
pub fn deinit(self: Action, allocator: std.mem.Allocator) void {
|
||||
@@ -32,6 +34,7 @@ pub const Action = union(enum) {
|
||||
const Mode = enum {
|
||||
idle,
|
||||
rail,
|
||||
symbol_rail,
|
||||
open_prompt,
|
||||
};
|
||||
|
||||
@@ -54,6 +57,7 @@ pub const Leader = struct {
|
||||
switch (self.mode) {
|
||||
.idle => return self.handleIdle(event),
|
||||
.rail => return self.handleRail(event),
|
||||
.symbol_rail => return self.handleSymbolRail(event),
|
||||
.open_prompt => return self.handleOpenPrompt(event),
|
||||
}
|
||||
}
|
||||
@@ -62,7 +66,8 @@ pub const Leader = struct {
|
||||
if (self.message) |message| return message;
|
||||
return switch (self.mode) {
|
||||
.idle => "",
|
||||
.rail => "leader: s save q quit o open / search x close",
|
||||
.rail => "leader: s save q quit o open p symbols / search x close",
|
||||
.symbol_rail => symbol_mod.rail_status,
|
||||
.open_prompt => "open: type path, Enter opens, Esc cancels",
|
||||
};
|
||||
}
|
||||
@@ -104,6 +109,10 @@ pub const Leader = struct {
|
||||
self.mode = .open_prompt;
|
||||
return .none;
|
||||
}
|
||||
if (std.mem.eql(u8, text, "p")) {
|
||||
self.mode = .symbol_rail;
|
||||
return .none;
|
||||
}
|
||||
if (std.mem.eql(u8, text, "/")) {
|
||||
self.mode = .idle;
|
||||
self.message = "search is not built in this profile yet";
|
||||
@@ -139,6 +148,38 @@ pub const Leader = struct {
|
||||
}
|
||||
}
|
||||
|
||||
fn handleSymbolRail(self: *Leader, event: input.Event) Action {
|
||||
self.message = null;
|
||||
switch (event) {
|
||||
.text => |text| {
|
||||
if (symbol_mod.lookup(text)) |entry| {
|
||||
self.mode = .idle;
|
||||
return .{ .symbol = entry.symbol };
|
||||
}
|
||||
self.mode = .idle;
|
||||
self.message = "unknown symbol key";
|
||||
return .none;
|
||||
},
|
||||
.key => |key| switch (key) {
|
||||
.escape, .backspace => {
|
||||
self.mode = .idle;
|
||||
self.message = "symbol rail cancelled";
|
||||
return .none;
|
||||
},
|
||||
else => {
|
||||
self.mode = .idle;
|
||||
self.message = "unknown symbol key";
|
||||
return .none;
|
||||
},
|
||||
},
|
||||
.unknown => {
|
||||
self.mode = .idle;
|
||||
self.message = "unknown symbol key";
|
||||
return .none;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn handleOpenPrompt(self: *Leader, event: input.Event) !Action {
|
||||
self.message = null;
|
||||
switch (event) {
|
||||
@@ -188,7 +229,7 @@ test "regular: space opens a visible leader rail and save dispatches" {
|
||||
defer leader.deinit();
|
||||
|
||||
try expectActionTag(.none, try leader.handleEvent(input.normalize(" ")));
|
||||
try std.testing.expectEqualStrings("leader: s save q quit o open / search x close", leader.status());
|
||||
try std.testing.expectEqualStrings("leader: s save q quit o open p symbols / search x close", leader.status());
|
||||
|
||||
try expectActionTag(.save, try leader.handleEvent(input.normalize("s")));
|
||||
try std.testing.expect(!leader.isActive());
|
||||
@@ -263,3 +304,55 @@ test "adversarial: empty open and escape cancel without dispatching" {
|
||||
try expectActionTag(.none, try leader.handleEvent(input.normalize("\x1b")));
|
||||
try std.testing.expectEqualStrings("open cancelled", leader.status());
|
||||
}
|
||||
|
||||
test "regular: leader symbol rail dispatches coding symbols by letters" {
|
||||
var leader = Leader.init(std.testing.allocator);
|
||||
defer leader.deinit();
|
||||
|
||||
try expectActionTag(.none, try leader.handleEvent(input.normalize(" ")));
|
||||
try expectActionTag(.none, try leader.handleEvent(input.normalize("p")));
|
||||
try std.testing.expectEqualStrings(symbol_mod.rail_status, leader.status());
|
||||
|
||||
const action = try leader.handleEvent(input.normalize("c"));
|
||||
switch (action) {
|
||||
.symbol => |symbol| try std.testing.expectEqual(symbol_mod.Symbol.braces, symbol),
|
||||
else => return error.ExpectedSymbolAction,
|
||||
}
|
||||
}
|
||||
|
||||
test "regular: leader symbol rail uses letters for hard-to-reach punctuation" {
|
||||
var leader = Leader.init(std.testing.allocator);
|
||||
defer leader.deinit();
|
||||
|
||||
inline for (.{
|
||||
.{ "s", symbol_mod.Symbol.slash },
|
||||
.{ "v", symbol_mod.Symbol.pipe },
|
||||
.{ "q", symbol_mod.Symbol.double_quote },
|
||||
.{ "e", symbol_mod.Symbol.single_quote },
|
||||
.{ "t", symbol_mod.Symbol.backtick },
|
||||
.{ "u", symbol_mod.Symbol.underscore },
|
||||
}) |case| {
|
||||
try expectActionTag(.none, try leader.handleEvent(input.normalize(" ")));
|
||||
try expectActionTag(.none, try leader.handleEvent(input.normalize("p")));
|
||||
const action = try leader.handleEvent(input.normalize(case[0]));
|
||||
switch (action) {
|
||||
.symbol => |actual| try std.testing.expectEqual(case[1], actual),
|
||||
else => return error.ExpectedSymbolAction,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test "adversarial: unknown and cancelled symbol rail inputs do not dispatch" {
|
||||
var leader = Leader.init(std.testing.allocator);
|
||||
defer leader.deinit();
|
||||
|
||||
try expectActionTag(.none, try leader.handleEvent(input.normalize(" ")));
|
||||
try expectActionTag(.none, try leader.handleEvent(input.normalize("p")));
|
||||
try expectActionTag(.none, try leader.handleEvent(input.normalize("?")));
|
||||
try std.testing.expectEqualStrings("unknown symbol key", leader.status());
|
||||
|
||||
try expectActionTag(.none, try leader.handleEvent(input.normalize(" ")));
|
||||
try expectActionTag(.none, try leader.handleEvent(input.normalize("p")));
|
||||
try expectActionTag(.none, try leader.handleEvent(input.normalize("\x1b")));
|
||||
try std.testing.expectEqualStrings("symbol rail cancelled", leader.status());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user