srctree

Gregory Mullen parent 844b3f70 b3bb88c8
add basic readme markdown support

src/endpoints/repos.zig added: 227, removed: 31, total 196
@@ -27,7 +27,7 @@ const Humanize = @import("../humanize.zig");
const Ini = @import("../ini.zig");
const Repos = @import("../repos.zig");
const Git = @import("../git.zig");
const Highlighting = @import("../syntax-highlight.zig");
const Highlight = @import("../syntax-highlight.zig");
 
const Commits = @import("repos/commits.zig");
const Diffs = @import("repos/diffs.zig");
@@ -496,8 +496,8 @@ fn blame(ctx: *Context) Error!void {
try source_lines.append('\n');
}
 
const formatted = if (Highlighting.Language.guessFromFilename(blame_file)) |lang| fmt: {
var pre = try Highlighting.highlight(ctx.alloc, lang, source_lines.items);
const formatted = if (Highlight.Language.guessFromFilename(blame_file)) |lang| fmt: {
var pre = try Highlight.highlight(ctx.alloc, lang, source_lines.items);
break :fmt pre[28..][0 .. pre.len - 38];
} else Bleach.Html.sanitizeAlloc(ctx.alloc, source_lines.items) catch return error.Unknown;
 
@@ -602,8 +602,8 @@ fn blob(ctx: *Context, repo: *Git.Repo, pfiles: Git.Tree) Error!void {
var resolve = repo.loadBlob(ctx.alloc, blb.sha) catch return error.Unknown;
if (!resolve.isFile()) return error.Unknown;
var formatted: []const u8 = undefined;
if (Highlighting.Language.guessFromFilename(blb.name)) |lang| {
const pre = try Highlighting.highlight(ctx.alloc, lang, resolve.data.?);
if (Highlight.Language.guessFromFilename(blb.name)) |lang| {
const pre = try Highlight.highlight(ctx.alloc, lang, resolve.data.?);
formatted = pre[28..][0 .. pre.len - 38];
} else if (excludedExt(blb.name)) {
formatted = "This file type is currently unsupported";
@@ -653,10 +653,11 @@ fn htmlReadme(a: Allocator, readme: []const u8) ![]HTML.E {
dom = dom.open(HTML.element("readme", null, null));
dom.push(HTML.element("intro", "README.md", null));
dom = dom.open(HTML.element("code", null, null));
var litr = std.mem.splitScalar(u8, readme, '\n');
while (litr.next()) |dirty| {
const clean = Bleach.Html.sanitizeAlloc(a, dirty) catch return error.Unknown;
dom.push(HTML.element("ln", clean, null));
const clean = Bleach.Html.sanitizeAlloc(a, readme) catch return error.Unknown;
const translated = try Highlight.translate(a, .markdown, clean);
var litr = std.mem.splitScalar(u8, translated, '\n');
while (litr.next()) |line| {
dom.push(HTML.element("ln", line, null));
}
dom = dom.close();
dom = dom.close();
 
src/syntax-highlight.zig added: 227, removed: 31, total 196
@@ -3,6 +3,8 @@ const Allocator = std.mem.Allocator;
const endsWith = std.mem.endsWith;
const eql = std.mem.eql;
 
const Markdown = @import("syntax/markdown.zig");
 
pub const Language = enum {
c,
cpp,
@@ -11,6 +13,7 @@ pub const Language = enum {
ini,
kotlin,
lua,
markdown,
nginx,
python,
vim,
@@ -24,6 +27,7 @@ pub const Language = enum {
.ini => "ini",
.kotlin => "kotlin",
.lua => "lua",
.markdown => "markdown",
.nginx => "nginx",
.python => "python",
.vim => @tagName(l),
@@ -50,6 +54,10 @@ pub const Language = enum {
return .ini;
} else if (endsWith(u8, name, ".lua")) {
return .lua;
} else if (endsWith(u8, name, ".md") or
endsWith(u8, name, ".markdown"))
{
return .markdown;
} else if (eql(u8, name, "nginx.conf")) {
return .nginx;
} else if (endsWith(u8, name, ".py") or
@@ -70,6 +78,31 @@ pub const Language = enum {
}
};
 
pub fn translate(a: Allocator, lang: Language, text: []const u8) ![]u8 {
return switch (lang) {
.c,
.cpp,
.h,
.html,
.ini,
.kotlin,
.lua,
.nginx,
.python,
.vim,
.zig,
=> return error.NotSupported,
.markdown => translateInternal(a, lang, text),
};
}
 
pub fn translateInternal(a: Allocator, lang: Language, text: []const u8) ![]u8 {
return switch (lang) {
.markdown => try Markdown.translate(a, text),
else => unreachable,
};
}
 
pub fn highlight(a: Allocator, lang: Language, text: []const u8) ![]u8 {
return switch (lang) {
.c,
@@ -79,6 +112,7 @@ pub fn highlight(a: Allocator, lang: Language, text: []const u8) ![]u8 {
.ini,
.kotlin,
.lua,
.markdown,
.nginx,
.python,
.vim,
 
filename was Deleted added: 227, removed: 31, total 196
@@ -0,0 +1,138 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
 
pub fn highlight() void {}
 
pub fn translate(a: Allocator, blob: []const u8) ![]u8 {
var output = std.ArrayList(u8).init(a);
var newline: u8 = 255;
var idx: usize = 0;
var backtick: bool = false;
while (idx < blob.len) : (idx += 1) {
switch (blob[idx]) {
'\n' => |c| {
newline +|= 1;
if (newline % 2 == 0) {
try output.appendSlice("<br>");
}
try output.append(c);
},
'#' => |c| {
if (newline == 0) {
try output.append(c);
continue;
}
 
newline = 0;
var hlvl: u8 = 0;
while (idx < blob.len) : (idx += 1) {
switch (blob[idx]) {
'#' => hlvl +|= 1,
else => break,
}
}
const tag = switch (hlvl) {
1 => "<h1>",
2 => "<h2>",
3 => "<h3>",
4 => "<h4>",
5 => "<h5>",
6 => "<h6>",
else => t: {
for (0..hlvl) |_| try output.append('#');
break :t "";
},
};
 
try output.appendSlice(tag);
while (idx < blob.len and blob[idx] == ' ') idx += 1;
while (idx < blob.len and blob[idx] != '\n') : (idx += 1) {
try output.append(blob[idx]);
}
if (idx != blob.len) idx -= 1;
if (tag.len > 1) {
try output.appendSlice("</");
try output.appendSlice(tag[1..]);
}
},
'`' => {
if (backtick) {
backtick = false;
try output.appendSlice("</span>");
} else {
backtick = true;
try output.appendSlice("<span class=\"coderef\">");
}
},
else => |c| {
newline = 0;
try output.append(c);
},
}
}
 
return try output.toOwnedSlice();
}
 
test "title 0" {
const a = std.testing.allocator;
const blob = "# Title";
const expected = "<h1>Title</h1>";
 
const html = try translate(a, blob);
defer a.free(html);
 
try std.testing.expectEqualStrings(expected, html);
}
 
test "title 1" {
const a = std.testing.allocator;
const blob = "# Title Title Title\n\n\n";
const expected = "<h1>Title Title Title</h1>\n<br>\n\n";
 
const html = try translate(a, blob);
defer a.free(html);
 
try std.testing.expectEqualStrings(expected, html);
}
 
test "title 2" {
const a = std.testing.allocator;
const blob =
\\# Title
\\
\\## Fake Title
\\
\\# Other Title
\\
\\text
\\
;
 
const expected =
\\<h1>Title</h1>
\\<br>
\\<h2>Fake Title</h2>
\\<br>
\\<h1>Other Title</h1>
\\<br>
\\text
\\
;
 
const html = try translate(a, blob);
defer a.free(html);
 
try std.testing.expectEqualStrings(expected, html);
}
 
test "backtick" {
const a = std.testing.allocator;
const blob = "`backtick`";
const expected = "<span class=\"coderef\">backtick</span>";
 
const html = try translate(a, blob);
defer a.free(html);
 
try std.testing.expectEqualStrings(expected, html);
}
 
static/main.css added: 227, removed: 31, total 196
@@ -635,25 +635,48 @@ comment {
text-align: right;
float: right;
}
div.coderef {
border-top: 1px solid black;
border-left: 1px solid black;
border-radius: 6px;
border-bottom: 1px solid #737373;
border-right: 1px solid #737373;
margin: 0 10px;
padding: 2px 15px;
}
 
div.coderef {
border-top: 1px solid black;
border-left: 1px solid black;
border-radius: 6px;
border-bottom: 1px solid #737373;
border-right: 1px solid #737373;
margin: 0 10px;
padding: 2px 15px;
background: #333;
white-space: pre;
&.red {
background: #433;
}
&.green {
background: #343;
}
&.yellow {
background: #333;
}
}
 
span.coderef {
display: inline-block;
border-top: 1px solid black;
border-left: 1px solid black;
border-radius: 6px;
border-bottom: 1px solid #737373;
border-right: 1px solid #737373;
margin: 0;
padding: 1px 3px;
background: #333;
white-space: pre;
&.red {
background: #433;
}
&.green {
background: #343;
}
&.yellow {
background: #333;
white-space: pre;
&.red {
background: #433;
}
&.green {
background: #343;
}
&.yellow {
background: #333;
}
}
}