diff --git a/src/main.zig b/src/main.zig index 081efc5..c2e958c 100644 --- a/src/main.zig +++ b/src/main.zig @@ -363,15 +363,46 @@ fn readEditorInputAlloc(allocator: std.mem.Allocator, stdin: *std.Io.Reader) !?[ fn renderLocalFrame(allocator: std.mem.Allocator, io: std.Io, stdout: std.Io.File, client: *tui.Client, path: []const u8, is_dir: bool, interactive: bool) !void { const frame = try client.render(allocator); defer allocator.free(frame); - if (interactive) try stdout.writeStreamingAll(io, "\x1b[H\x1b[2J"); - try stdout.writeStreamingAll(io, frame); - try stdout.writeStreamingAll(io, "\n\n"); - try stdout.writeStreamingAll(io, "mim opened: "); - try stdout.writeStreamingAll(io, path); - try stdout.writeStreamingAll(io, if (is_dir) - "\nDirectory browser. Space opens commands; o/Enter opens panel items where available; q quits when clean.\n" - else - "\nEditor starts in insert mode. Esc or Space n enters normal mode; Space commands work in normal mode; Space w saves; Space q quits when clean.\n"); + if (interactive) { + try stdout.writeStreamingAll(io, "\x1b[H\x1b[2J"); + try writeTerminalText(allocator, io, stdout, frame); + try writeTerminalText(allocator, io, stdout, "\n\n"); + try writeTerminalText(allocator, io, stdout, "mim opened: "); + try writeTerminalText(allocator, io, stdout, path); + try writeTerminalText(allocator, io, stdout, if (is_dir) + "\nDirectory browser. Space opens commands; o/Enter opens panel items where available; q quits when clean.\n" + else + "\nEditor starts in insert mode. Esc or Space n enters normal mode; Space commands work in normal mode; Space w saves; Space q quits when clean.\n"); + } else { + try stdout.writeStreamingAll(io, frame); + try stdout.writeStreamingAll(io, "\n\n"); + try stdout.writeStreamingAll(io, "mim opened: "); + try stdout.writeStreamingAll(io, path); + try stdout.writeStreamingAll(io, if (is_dir) + "\nDirectory browser. Space opens commands; o/Enter opens panel items where available; q quits when clean.\n" + else + "\nEditor starts in insert mode. Esc or Space n enters normal mode; Space commands work in normal mode; Space w saves; Space q quits when clean.\n"); + } +} + +fn writeTerminalText(allocator: std.mem.Allocator, io: std.Io, stdout: std.Io.File, bytes: []const u8) !void { + const converted = try terminalCrlfAlloc(allocator, bytes); + defer allocator.free(converted); + try stdout.writeStreamingAll(io, converted); +} + +fn terminalCrlfAlloc(allocator: std.mem.Allocator, bytes: []const u8) ![]u8 { + var out = std.ArrayList(u8).empty; + errdefer out.deinit(allocator); + var start: usize = 0; + for (bytes, 0..) |byte, i| { + if (byte != '\n') continue; + if (start < i) try out.appendSlice(allocator, bytes[start..i]); + try out.appendSlice(allocator, "\r\n"); + start = i + 1; + } + if (start < bytes.len) try out.appendSlice(allocator, bytes[start..]); + return out.toOwnedSlice(allocator); } fn printLocalContext(allocator: std.mem.Allocator, io: std.Io, args: *std.process.Args.Iterator, stdout: std.Io.File) !void { @@ -568,3 +599,9 @@ test "regular: local editor client exposes save and dirty quit guards" { client.clearQuit(); try std.testing.expect(!client.requestedQuit()); } + +test "regular: raw terminal output converts newline to carriage-return newline" { + const converted = try terminalCrlfAlloc(std.testing.allocator, "a\nb\n"); + defer std.testing.allocator.free(converted); + try std.testing.expectEqualStrings("a\r\nb\r\n", converted); +}