@@ 261,9 261,13 @@ fn renderFile(
};
const src_file = try targets.src.openFile(filename, .{});
defer src_file.close();
- const lines = try readLines(src_file.reader(), &arena.allocator);
- const doc = try parseDocument(lines, &arena.allocator, file_info);
- if (doc.info.private and !include_private) return;
+ const lines = blk: {
+ var lines_list = std.ArrayList([]const u8).init(&arena.allocator);
+ try readLines(src_file.reader(), &lines_list);
+ break :blk lines_list.toOwnedSlice();
+ };
+
+ const doc = (try parseDocument(lines, &arena.allocator, file_info)) orelse return;
const stdout = std.io.getStdOut().writer();
if (!silent) {
if (targets.dirname) |dirname| {
@@ 382,10 386,15 @@ fn parseDocument(
lines: []const []const u8,
allocator: *std.mem.Allocator,
file: FileInfo,
-) !Document {
+) !?Document {
const info_res = try parseInfo(lines, file.name, allocator);
+ if (info_res.data.private and !include_private) return null;
const context = ParseContext{ .file = file, .allocator = allocator, .info = info_res.data };
- const blocks = try parseBlocks(lines, info_res.new_pos + 1, context);
+
+ const blocks = if (info_res.new_pos + 1 < lines.len) blk: {
+ const expanded_lines = try expand(lines[info_res.new_pos + 1 ..], allocator, context.info.shell, file.name);
+ break :blk try parseBlocks(expanded_lines, 0, context);
+ } else &[_]Block{};
const result: Document = .{
.blocks = blocks,
@@ 488,13 497,7 @@ fn parseBlocks(
var blocks = std.ArrayList(Block).init(context.allocator);
var spans = std.ArrayList(Span).init(context.allocator);
while (index < lines.len) {
- if (try parseCommand(lines, index, context)) |res| {
- if (spans.items.len > 0) {
- try blocks.append(.{ .paragraph = spans.toOwnedSlice() });
- }
- try blocks.appendSlice(res.data);
- index = res.new_pos;
- } else if (try parseBlock(lines, index, context.allocator)) |res| {
+ if (try parseBlock(lines, index, context.allocator)) |res| {
if (spans.items.len > 0) {
try blocks.append(.{ .paragraph = spans.toOwnedSlice() });
}
@@ 516,68 519,6 @@ fn parseBlocks(
return blocks.toOwnedSlice();
}
-/// This is the fun stuff. Everything written inside of a command block is piped
-/// into the shell, and the output is then parsed as blocks like any other
-/// content. Not very much code and A LOT of power.
-fn parseCommand(
- lines: []const []const u8,
- start: usize,
- context: ParseContext,
-) !?ParseResult([]const Block) {
- if (try parsePrefixedLines(
- lines,
- start,
- ":",
- context.allocator,
- )) |res| {
- defer context.allocator.free(res.data);
- const shell = context.info.shell orelse
- try std.process.getEnvVarOwned(context.allocator, "SHELL");
-
- var process = try std.ChildProcess.init(
- &[_][]const u8{shell},
- context.allocator,
- );
- defer process.deinit();
- try env_map.put("FILE", context.file.name);
- process.env_map = &env_map;
- process.stdin_behavior = .Pipe;
- process.stdout_behavior = .Pipe;
-
- try process.spawn();
- errdefer _ = process.kill() catch |err| {
- logger.warn("Had trouble cleaning up process: {}", .{err});
- };
- const writer = process.stdin.?.writer();
- for (res.data) |line| {
- try writer.print("{s}\n", .{line});
- }
- process.stdin.?.close();
- process.stdin = null;
-
- const result_lines = try readLines(
- process.stdout.?.reader(),
- context.allocator,
- );
- switch (try process.wait()) {
- .Exited => |status| {
- if (status != 0) {
- logger.alert("Process ended unexpectedly on line {d}", .{
- res.new_pos,
- });
- return error.ProcessEndedUnexpectedly;
- }
- return ok(
- try parseBlocks(result_lines, 0, context),
- res.new_pos,
- );
- },
- else => return error.ProcessEndedUnexpectedly,
- }
- }
- return null;
-}
-
fn parseBlock(
lines: []const []const u8,
line: usize,
@@ 1428,10 1369,9 @@ fn readLine(reader: anytype, array_list: *std.ArrayList(u8)) !bool {
/// Caller owns result
fn readLines(
reader: anytype,
- allocator: *std.mem.Allocator,
-) ![]const []const u8 {
- var lines = std.ArrayList([]const u8).init(allocator);
- var current_line = std.ArrayList(u8).init(allocator);
+ lines: *std.ArrayList([]const u8),
+) !void {
+ var current_line = std.ArrayList(u8).init(lines.allocator);
while (try readLine(reader, ¤t_line)) {
if (std.mem.startsWith(u8, current_line.items, "; ")) {
if (std.mem.endsWith(u8, current_line.items, "\r")) {
@@ 1447,7 1387,82 @@ fn readLines(
try lines.append(current_line.toOwnedSlice());
}
}
- return lines.toOwnedSlice();
+}
+
+/// This is the fun stuff. Everything written inside of a command block is piped
+/// into the shell, and the output is then parsed as blocks like any other
+/// content. Not very much code and A LOT of power.
+fn expand(
+ original_lines: []const []const u8,
+ allocator: *std.mem.Allocator,
+ custom_shell: ?[]const u8,
+ filename: []const u8,
+) ![]const []const u8 {
+ // TODO this feels hacky still
+ var lines = original_lines;
+ var result = std.ArrayList([]const u8).init(allocator);
+ errdefer result.deinit();
+ while (true) {
+ var i: usize = 0;
+ defer {
+ const tmp = lines;
+ lines = result.toOwnedSlice();
+ if (tmp.ptr != original_lines.ptr) {
+ allocator.free(tmp);
+ }
+ }
+ var expanded = false;
+ while (i < lines.len) : (i += 1) {
+ const start = i;
+ while (i < lines.len) : (i += 1) {
+ if (!std.mem.startsWith(u8, lines[i], ": ")) break;
+ }
+ if (i > start) {
+ expanded = true;
+ const shell = custom_shell orelse
+ try std.process.getEnvVarOwned(allocator, "SHELL");
+
+ var process = try std.ChildProcess.init(
+ &[_][]const u8{shell},
+ allocator,
+ );
+ defer process.deinit();
+ try env_map.put("FILE", filename);
+ process.env_map = &env_map;
+ process.stdin_behavior = .Pipe;
+ process.stdout_behavior = .Pipe;
+
+ try process.spawn();
+ errdefer _ = process.kill() catch |err| {
+ logger.warn("Had trouble cleaning up process: {}", .{err});
+ };
+ const writer = process.stdin.?.writer();
+ logger.info("Running command:", .{});
+ for (lines[start..i]) |line| {
+ logger.info("{s}", .{line});
+ try writer.print("{s}\n", .{line[2..]});
+ }
+ process.stdin.?.close();
+ process.stdin = null;
+
+ try readLines(process.stdout.?.reader(), &result);
+
+ switch (try process.wait()) {
+ .Exited => |status| {
+ if (status != 0) {
+ logger.alert("Process ended unexpectedly", .{});
+ return error.ProcessEndedUnexpectedly;
+ }
+ },
+ else => return error.ProcessEndedUnexpectedly,
+ }
+ } else {
+ try result.append(lines[i]);
+ }
+ }
+ if (!expanded) break;
+ }
+ return lines;
}
fn parseLiteral(