Compare commits

...

4 Commits
0.1 ... main

Author SHA1 Message Date
moss 12e96454ff improve args handling 2025-01-25 18:15:36 -06:00
moss c88a2815fa remove stdio include and clean up 2025-01-25 18:02:56 -06:00
moss 013f468703 add build instructions 2025-01-25 01:34:38 +00:00
moss e8e4cd30e3 add dependencies section 2025-01-25 01:27:30 +00:00
2 changed files with 55 additions and 16 deletions

View File

@ -18,4 +18,26 @@ the following are currently supported:
- lists with `- list item` or `1. list item` (but numbered lists dont render as numbered yet) - lists with `- list item` or `1. list item` (but numbered lists dont render as numbered yet)
- task lists with `- [x] list item` or `- [ ] list item` where a checkbox is ticked if the `x` is present. - task lists with `- [x] list item` or `- [ ] list item` where a checkbox is ticked if the `x` is present.
- horizontal rules with `***` which span the entire terminal width. - horizontal rules with `***` which span the entire terminal width.
- strikethrough with `~~text~~` - strikethrough with `~~text~~`
## dependencies
- [md4c](https://github.com/mity/md4c) for markdown parsing
- [ansi-term](https://github.com/ziglibs/ansi-term) wraps ansi styling
- [zig-clap](https://github.com/Hejsil/zig-clap) for arg parsing
## building
1. install [zig 0.13.0](https://ziglang.org/download/#release-0.13.0)
2. clone repo
3. run `zig build`
if you want a release build:
4. run `zig build --release=small`
release optimize modes are:
- fast
- safe
- small
the mdcat binary will be in `zig-out/bin/mdcat`

View File

@ -2,9 +2,6 @@ const std = @import("std");
const clap = @import("clap"); const clap = @import("clap");
const md4c = @import("md4c_h.zig"); const md4c = @import("md4c_h.zig");
const ansi = @import("ansi-term"); const ansi = @import("ansi-term");
const cstdio = @cImport({
@cInclude("stdio.h");
});
pub fn main() !void { pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; var gpa = std.heap.GeneralPurposeAllocator(.{}){};
@ -12,7 +9,8 @@ pub fn main() !void {
const params = comptime clap.parseParamsComptime( const params = comptime clap.parseParamsComptime(
\\-h, --help display this help and exit \\-h, --help display this help and exit
\\<FILE> file to read from, if empty or "-" use stdin. \\-v, --version display the version and exit
\\<FILE> file to read from, empty or "-" use stdin
\\ \\
); );
@ -30,8 +28,14 @@ pub fn main() !void {
}; };
defer res.deinit(); defer res.deinit();
if (res.args.help != 0) return clap.help(std.io.getStdErr().writer(), clap.Help, &params, .{});
const version_string = "0.1.1\n";
if (res.args.version != 0) return std.debug.print(version_string, .{});
var reader: std.io.AnyReader = undefined; var reader: std.io.AnyReader = undefined;
if (res.positionals.len < 1) { if (res.positionals.len < 1 or std.mem.eql(u8, res.positionals[0], "-")) {
reader = std.io.getStdIn().reader().any(); reader = std.io.getStdIn().reader().any();
} else { } else {
const filename = res.positionals[0]; const filename = res.positionals[0];
@ -97,30 +101,36 @@ const State = struct {
}; };
fn enter_block(blocktype: md4c.MD_BLOCKTYPE, detail: ?*anyopaque, userdata: ?*anyopaque) callconv(.C) c_int { fn enter_block(blocktype: md4c.MD_BLOCKTYPE, detail: ?*anyopaque, userdata: ?*anyopaque) callconv(.C) c_int {
//std.debug.print("enter {d}\n", .{blocktype});
const state: *State = @ptrCast(@alignCast(userdata)); const state: *State = @ptrCast(@alignCast(userdata));
if (blocktype == md4c.MD_BLOCK_H) { if (blocktype == md4c.MD_BLOCK_H) {
just_write(state.writer, "\n"); just_write(state.writer, "\n");
const h_detail: *md4c.MD_BLOCK_H_DETAIL = @ptrCast(@alignCast(detail)); const h_detail: *md4c.MD_BLOCK_H_DETAIL = @ptrCast(@alignCast(detail));
var temp_style = state.last_style; var temp_style = state.last_style;
temp_style.font_style.dim = true; temp_style.font_style.dim = true;
temp_style.font_style.bold = true; temp_style.font_style.bold = true;
state.update_style(temp_style); state.update_style(temp_style);
for (0..h_detail.level) |_| { for (0..h_detail.level) |_| {
just_write(state.writer, "#"); just_write(state.writer, "#");
} }
temp_style.font_style.dim = false; temp_style.font_style.dim = false;
temp_style.font_style.bold = state.nested_bold_count > 0; temp_style.font_style.bold = state.nested_bold_count > 0;
state.update_style(temp_style); state.update_style(temp_style);
just_write(state.writer, " "); just_write(state.writer, " ");
} }
if (blocktype == md4c.MD_BLOCK_LI) { if (blocktype == md4c.MD_BLOCK_LI) {
const li_detail: *md4c.MD_BLOCK_LI_DETAIL = @ptrCast(@alignCast(detail)); const li_detail: *md4c.MD_BLOCK_LI_DETAIL = @ptrCast(@alignCast(detail));
var temp_style = state.last_style; var temp_style = state.last_style;
if (li_detail.is_task == 0) temp_style.font_style.dim = true; if (li_detail.is_task == 0) temp_style.font_style.dim = true;
temp_style.font_style.bold = true; temp_style.font_style.bold = true;
state.update_style(temp_style); state.update_style(temp_style);
if (li_detail.is_task == 0) { if (li_detail.is_task == 0) {
just_write(state.writer, ""); just_write(state.writer, "");
} else { } else {
@ -130,6 +140,7 @@ fn enter_block(blocktype: md4c.MD_BLOCKTYPE, detail: ?*anyopaque, userdata: ?*an
just_write(state.writer, ""); just_write(state.writer, "");
} }
} }
temp_style.font_style.dim = false; temp_style.font_style.dim = false;
temp_style.font_style.bold = state.nested_bold_count > 0; temp_style.font_style.bold = state.nested_bold_count > 0;
state.update_style(temp_style); state.update_style(temp_style);
@ -141,7 +152,6 @@ fn enter_block(blocktype: md4c.MD_BLOCKTYPE, detail: ?*anyopaque, userdata: ?*an
} }
fn leave_block(blocktype: md4c.MD_BLOCKTYPE, detail: ?*anyopaque, userdata: ?*anyopaque) callconv(.C) c_int { fn leave_block(blocktype: md4c.MD_BLOCKTYPE, detail: ?*anyopaque, userdata: ?*anyopaque) callconv(.C) c_int {
//std.debug.print("leave {d}\n", .{blocktype});
const state: *State = @ptrCast(@alignCast(userdata)); const state: *State = @ptrCast(@alignCast(userdata));
if (blocktype == md4c.MD_BLOCK_H) { if (blocktype == md4c.MD_BLOCK_H) {
@ -151,14 +161,15 @@ fn leave_block(blocktype: md4c.MD_BLOCKTYPE, detail: ?*anyopaque, userdata: ?*an
just_write(state.writer, "\n"); just_write(state.writer, "\n");
} }
if (blocktype == md4c.MD_BLOCK_HR) { if (blocktype == md4c.MD_BLOCK_HR) {
var winsize: std.c.winsize = undefined;
var temp_style = state.last_style; var temp_style = state.last_style;
temp_style.font_style.dim = true; temp_style.font_style.dim = true;
temp_style.font_style.bold = true; temp_style.font_style.bold = true;
state.update_style(temp_style); state.update_style(temp_style);
const term_name_c = cstdio.ctermid(null);
const term_name = std.mem.sliceTo(term_name_c, 0); const term_name = "/dev/tty";
const term_fd = std.posix.open(term_name, .{}, 0) catch errorexit(5, "failed to open terminal D:\n"); const term_fd = std.posix.open(term_name, .{}, 0) catch errorexit(5, "failed to open terminal D:\n");
var winsize: std.c.winsize = undefined;
if (std.c.ioctl(term_fd, std.c.T.IOCGWINSZ, &winsize) != -1) { if (std.c.ioctl(term_fd, std.c.T.IOCGWINSZ, &winsize) != -1) {
for (0..winsize.ws_col) |_| { for (0..winsize.ws_col) |_| {
just_write(state.writer, "-"); just_write(state.writer, "-");
@ -167,6 +178,7 @@ fn leave_block(blocktype: md4c.MD_BLOCKTYPE, detail: ?*anyopaque, userdata: ?*an
} else { } else {
just_write(state.writer, "---\n"); just_write(state.writer, "---\n");
} }
temp_style.font_style.dim = false; temp_style.font_style.dim = false;
temp_style.font_style.bold = state.nested_bold_count > 0; temp_style.font_style.bold = state.nested_bold_count > 0;
state.update_style(temp_style); state.update_style(temp_style);
@ -201,14 +213,16 @@ fn enter_span(spantype: md4c.MD_SPANTYPE, detail: ?*anyopaque, userdata: ?*anyop
temp_style.foreground = .Blue; temp_style.foreground = .Blue;
temp_style.font_style.underline = true; temp_style.font_style.underline = true;
state.update_style(temp_style); state.update_style(temp_style);
just_write(state.writer, "\x1b]8;;");
just_write(state.writer, "\x1b]8;;"); // OSC 8 link begin
const detail_a: *md4c.MD_SPAN_A_DETAIL = @ptrCast(@alignCast(detail)); const detail_a: *md4c.MD_SPAN_A_DETAIL = @ptrCast(@alignCast(detail));
const link_c = detail_a.href.text; const link_c = detail_a.href.text;
const link = link_c[0..detail_a.href.size]; const link = link_c[0..detail_a.href.size];
just_write(state.writer, link); just_write(state.writer, link);
just_write(state.writer, "\x07"); just_write(state.writer, "\x07"); // OSC 8 link seperate url and text
} }
// TODO emit ansi to the writer
return 0; // TODO what does the return value represent ???? return 0; // TODO what does the return value represent ????
} }
@ -232,13 +246,13 @@ fn leave_span(spantype: md4c.MD_SPANTYPE, detail: ?*anyopaque, userdata: ?*anyop
state.update_underline(); state.update_underline();
} }
if (spantype == md4c.MD_SPAN_A) { if (spantype == md4c.MD_SPAN_A) {
just_write(state.writer, "\x1b]8;;\x07"); just_write(state.writer, "\x1b]8;;\x07"); // OSC 8 link end
var temp_style = state.last_style; var temp_style = state.last_style;
temp_style.foreground = .Default; temp_style.foreground = .Default;
state.update_style(temp_style); state.update_style(temp_style);
state.update_underline(); state.update_underline();
} }
// TODO emit ansi to the writer
return 0; // TODO what does the return value represent ???? return 0; // TODO what does the return value represent ????
} }
@ -260,7 +274,9 @@ fn on_text(texttype: md4c.MD_TEXTTYPE, text: [*c]const md4c.MD_CHAR, size: md4c.
var temp_style = state.last_style; var temp_style = state.last_style;
temp_style.font_style.dim = true; temp_style.font_style.dim = true;
state.update_style(temp_style); state.update_style(temp_style);
just_write(state.writer, text_data); just_write(state.writer, text_data);
temp_style.font_style.dim = false; temp_style.font_style.dim = false;
state.update_style(temp_style); state.update_style(temp_style);
} }
@ -282,6 +298,7 @@ const parser: md4c.MD_PARSER = .{
}; };
fn errorexit(code: u8, comptime msg: []const u8) noreturn { fn errorexit(code: u8, comptime msg: []const u8) noreturn {
std.debug.print("mdcat exiting with error: ", .{});
std.debug.print(msg, .{}); std.debug.print(msg, .{});
std.process.exit(code); std.process.exit(code);
} }