this repo has no description
13
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: add prettyPrint

Add a prettyPrint function which dumps the current screen to the tty,
not saving any state. This is useful for pretty printing text to stdout
in a streaming fashion

+270
+270
src/Vaxis.zig
··· 931 931 pub fn deviceStatusReport(_: Vaxis, tty: AnyWriter) !void { 932 932 try tty.writeAll(ctlseqs.device_status_report); 933 933 } 934 + 935 + /// prettyPrint is used to print the contents of the Screen to the tty. The state is not stored, and 936 + /// the cursor will be put on the next line after the last line is printed. This is useful to 937 + /// sequentially print data in a styled format to eg. stdout. This function returns an error if you 938 + /// are not in the alt screen. The cursor is always hidden, and mouse shapes are not available 939 + pub fn prettyPrint(self: *Vaxis, tty: AnyWriter) !void { 940 + if (self.state.alt_screen) return error.NotInPrimaryScreen; 941 + 942 + try tty.writeAll(ctlseqs.hide_cursor); 943 + try tty.writeAll(ctlseqs.sync_set); 944 + defer tty.writeAll(ctlseqs.sync_reset) catch {}; 945 + try tty.writeAll(ctlseqs.sgr_reset); 946 + 947 + var reposition: bool = false; 948 + var row: usize = 0; 949 + var col: usize = 0; 950 + var cursor: Style = .{}; 951 + var link: Hyperlink = .{}; 952 + var cursor_pos: struct { 953 + row: usize = 0, 954 + col: usize = 0, 955 + } = .{}; 956 + 957 + var i: usize = 0; 958 + while (i < self.screen.buf.len) { 959 + const cell = self.screen.buf[i]; 960 + const w = blk: { 961 + if (cell.char.width != 0) break :blk cell.char.width; 962 + 963 + const method: gwidth.Method = self.caps.unicode; 964 + const width = gwidth.gwidth(cell.char.grapheme, method, &self.unicode.width_data) catch 1; 965 + break :blk @max(1, width); 966 + }; 967 + defer { 968 + // advance by the width of this char mod 1 969 + std.debug.assert(w > 0); 970 + var j = i + 1; 971 + while (j < i + w) : (j += 1) { 972 + if (j >= self.screen_last.buf.len) break; 973 + self.screen_last.buf[j].skipped = true; 974 + } 975 + col += w; 976 + i += w; 977 + } 978 + if (col >= self.screen.width) { 979 + row += 1; 980 + col = 0; 981 + // Rely on terminal wrapping to reposition into next row instead of forcing it 982 + if (!cell.wrapped) 983 + reposition = true; 984 + } 985 + if (cell.default) { 986 + reposition = true; 987 + continue; 988 + } 989 + defer { 990 + cursor = cell.style; 991 + link = cell.link; 992 + } 993 + 994 + // reposition the cursor, if needed 995 + if (reposition) { 996 + reposition = false; 997 + link = .{}; 998 + if (cursor_pos.row == row) { 999 + const n = col - cursor_pos.col; 1000 + if (n > 0) 1001 + try tty.print(ctlseqs.cuf, .{n}); 1002 + } else { 1003 + const n = row - cursor_pos.row; 1004 + try tty.writeByteNTimes('\n', n); 1005 + try tty.writeByte('\r'); 1006 + if (col > 0) 1007 + try tty.print(ctlseqs.cuf, .{col}); 1008 + } 1009 + } 1010 + 1011 + if (cell.image) |img| { 1012 + try tty.print( 1013 + ctlseqs.kitty_graphics_preamble, 1014 + .{img.img_id}, 1015 + ); 1016 + if (img.options.pixel_offset) |offset| { 1017 + try tty.print( 1018 + ",X={d},Y={d}", 1019 + .{ offset.x, offset.y }, 1020 + ); 1021 + } 1022 + if (img.options.clip_region) |clip| { 1023 + if (clip.x) |x| 1024 + try tty.print(",x={d}", .{x}); 1025 + if (clip.y) |y| 1026 + try tty.print(",y={d}", .{y}); 1027 + if (clip.width) |width| 1028 + try tty.print(",w={d}", .{width}); 1029 + if (clip.height) |height| 1030 + try tty.print(",h={d}", .{height}); 1031 + } 1032 + if (img.options.size) |size| { 1033 + if (size.rows) |rows| 1034 + try tty.print(",r={d}", .{rows}); 1035 + if (size.cols) |cols| 1036 + try tty.print(",c={d}", .{cols}); 1037 + } 1038 + if (img.options.z_index) |z| { 1039 + try tty.print(",z={d}", .{z}); 1040 + } 1041 + try tty.writeAll(ctlseqs.kitty_graphics_closing); 1042 + } 1043 + 1044 + // something is different, so let's loop through everything and 1045 + // find out what 1046 + 1047 + // foreground 1048 + if (!Cell.Color.eql(cursor.fg, cell.style.fg)) { 1049 + switch (cell.style.fg) { 1050 + .default => try tty.writeAll(ctlseqs.fg_reset), 1051 + .index => |idx| { 1052 + switch (idx) { 1053 + 0...7 => try tty.print(ctlseqs.fg_base, .{idx}), 1054 + 8...15 => try tty.print(ctlseqs.fg_bright, .{idx - 8}), 1055 + else => { 1056 + switch (self.sgr) { 1057 + .standard => try tty.print(ctlseqs.fg_indexed, .{idx}), 1058 + .legacy => try tty.print(ctlseqs.fg_indexed_legacy, .{idx}), 1059 + } 1060 + }, 1061 + } 1062 + }, 1063 + .rgb => |rgb| { 1064 + switch (self.sgr) { 1065 + .standard => try tty.print(ctlseqs.fg_rgb, .{ rgb[0], rgb[1], rgb[2] }), 1066 + .legacy => try tty.print(ctlseqs.fg_rgb_legacy, .{ rgb[0], rgb[1], rgb[2] }), 1067 + } 1068 + }, 1069 + } 1070 + } 1071 + // background 1072 + if (!Cell.Color.eql(cursor.bg, cell.style.bg)) { 1073 + switch (cell.style.bg) { 1074 + .default => try tty.writeAll(ctlseqs.bg_reset), 1075 + .index => |idx| { 1076 + switch (idx) { 1077 + 0...7 => try tty.print(ctlseqs.bg_base, .{idx}), 1078 + 8...15 => try tty.print(ctlseqs.bg_bright, .{idx - 8}), 1079 + else => { 1080 + switch (self.sgr) { 1081 + .standard => try tty.print(ctlseqs.bg_indexed, .{idx}), 1082 + .legacy => try tty.print(ctlseqs.bg_indexed_legacy, .{idx}), 1083 + } 1084 + }, 1085 + } 1086 + }, 1087 + .rgb => |rgb| { 1088 + switch (self.sgr) { 1089 + .standard => try tty.print(ctlseqs.bg_rgb, .{ rgb[0], rgb[1], rgb[2] }), 1090 + .legacy => try tty.print(ctlseqs.bg_rgb_legacy, .{ rgb[0], rgb[1], rgb[2] }), 1091 + } 1092 + }, 1093 + } 1094 + } 1095 + // underline color 1096 + if (!Cell.Color.eql(cursor.ul, cell.style.ul)) { 1097 + switch (cell.style.ul) { 1098 + .default => try tty.writeAll(ctlseqs.ul_reset), 1099 + .index => |idx| { 1100 + switch (self.sgr) { 1101 + .standard => try tty.print(ctlseqs.ul_indexed, .{idx}), 1102 + .legacy => try tty.print(ctlseqs.ul_indexed_legacy, .{idx}), 1103 + } 1104 + }, 1105 + .rgb => |rgb| { 1106 + switch (self.sgr) { 1107 + .standard => try tty.print(ctlseqs.ul_rgb, .{ rgb[0], rgb[1], rgb[2] }), 1108 + .legacy => try tty.print(ctlseqs.ul_rgb_legacy, .{ rgb[0], rgb[1], rgb[2] }), 1109 + } 1110 + }, 1111 + } 1112 + } 1113 + // underline style 1114 + if (cursor.ul_style != cell.style.ul_style) { 1115 + const seq = switch (cell.style.ul_style) { 1116 + .off => ctlseqs.ul_off, 1117 + .single => ctlseqs.ul_single, 1118 + .double => ctlseqs.ul_double, 1119 + .curly => ctlseqs.ul_curly, 1120 + .dotted => ctlseqs.ul_dotted, 1121 + .dashed => ctlseqs.ul_dashed, 1122 + }; 1123 + try tty.writeAll(seq); 1124 + } 1125 + // bold 1126 + if (cursor.bold != cell.style.bold) { 1127 + const seq = switch (cell.style.bold) { 1128 + true => ctlseqs.bold_set, 1129 + false => ctlseqs.bold_dim_reset, 1130 + }; 1131 + try tty.writeAll(seq); 1132 + if (cell.style.dim) { 1133 + try tty.writeAll(ctlseqs.dim_set); 1134 + } 1135 + } 1136 + // dim 1137 + if (cursor.dim != cell.style.dim) { 1138 + const seq = switch (cell.style.dim) { 1139 + true => ctlseqs.dim_set, 1140 + false => ctlseqs.bold_dim_reset, 1141 + }; 1142 + try tty.writeAll(seq); 1143 + if (cell.style.bold) { 1144 + try tty.writeAll(ctlseqs.bold_set); 1145 + } 1146 + } 1147 + // dim 1148 + if (cursor.italic != cell.style.italic) { 1149 + const seq = switch (cell.style.italic) { 1150 + true => ctlseqs.italic_set, 1151 + false => ctlseqs.italic_reset, 1152 + }; 1153 + try tty.writeAll(seq); 1154 + } 1155 + // dim 1156 + if (cursor.blink != cell.style.blink) { 1157 + const seq = switch (cell.style.blink) { 1158 + true => ctlseqs.blink_set, 1159 + false => ctlseqs.blink_reset, 1160 + }; 1161 + try tty.writeAll(seq); 1162 + } 1163 + // reverse 1164 + if (cursor.reverse != cell.style.reverse) { 1165 + const seq = switch (cell.style.reverse) { 1166 + true => ctlseqs.reverse_set, 1167 + false => ctlseqs.reverse_reset, 1168 + }; 1169 + try tty.writeAll(seq); 1170 + } 1171 + // invisible 1172 + if (cursor.invisible != cell.style.invisible) { 1173 + const seq = switch (cell.style.invisible) { 1174 + true => ctlseqs.invisible_set, 1175 + false => ctlseqs.invisible_reset, 1176 + }; 1177 + try tty.writeAll(seq); 1178 + } 1179 + // strikethrough 1180 + if (cursor.strikethrough != cell.style.strikethrough) { 1181 + const seq = switch (cell.style.strikethrough) { 1182 + true => ctlseqs.strikethrough_set, 1183 + false => ctlseqs.strikethrough_reset, 1184 + }; 1185 + try tty.writeAll(seq); 1186 + } 1187 + 1188 + // url 1189 + if (!std.mem.eql(u8, link.uri, cell.link.uri)) { 1190 + var ps = cell.link.params; 1191 + if (cell.link.uri.len == 0) { 1192 + // Empty out the params no matter what if we don't have 1193 + // a url 1194 + ps = ""; 1195 + } 1196 + try tty.print(ctlseqs.osc8, .{ ps, cell.link.uri }); 1197 + } 1198 + try tty.writeAll(cell.char.grapheme); 1199 + cursor_pos.col = col + w; 1200 + cursor_pos.row = row; 1201 + } 1202 + try tty.writeAll("\r\n"); 1203 + }