mirror of
https://github.com/sbrow/thor.git
synced 2026-08-26 11:23:32 -04:00
feat: Added sectionate content processor.
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
- README.md
|
- README.md
|
||||||
- [ ] "Zero is beautiful"
|
- [ ] "Zero is beautiful"
|
||||||
- [ ] Content-hash fingerprinting for CSS and JS cache busting
|
- [ ] Content-hash fingerprinting for CSS and JS cache busting
|
||||||
|
- [ ] Enable configuration to opt-out of features, particular pre/post processors.
|
||||||
- [ ] proper date/time/now object.
|
- [ ] proper date/time/now object.
|
||||||
- [ ] Block attributes on code fences (`{ #ex-1 }`) — hello-world.md
|
- [ ] Block attributes on code fences (`{ #ex-1 }`) — hello-world.md
|
||||||
- [x] Emoji shortcodes (`:shrug:` etc.) — 2 instances
|
- [x] Emoji shortcodes (`:shrug:` etc.) — 2 instances
|
||||||
@@ -17,6 +18,10 @@
|
|||||||
guide
|
guide
|
||||||
- [ ] Search for grammars in multiple places
|
- [ ] Search for grammars in multiple places
|
||||||
- [ ] Download missing grammars.
|
- [ ] Download missing grammars.
|
||||||
|
- [ ] Syntax highlighting in production: CI (`nix build` on ubuntu-latest) has no grammar `.so`s and a machine-specific `QUERIES_PATH` nix store hash, so the deployed site renders unhighlighted. Provide grammars + queries as nix build inputs and pass paths to thor at runtime (env vars/flags).
|
||||||
|
- [ ] Durable highlight paths: read `GRAPHS_PATH`/`QUERIES_PATH` from env vars set by the flake instead of hardcoded nix store hashes, so they survive `nix flake update` and let the grammar/query version-mismatch detector fire automatically.
|
||||||
|
- [ ] Unit tests for highlighting helpers: `capture_name_to_css`, `escape_html`, `unescape_html`, `extract_query_token`, `helix_version_from_path`.
|
||||||
|
- [ ] Dark `<pre>` code-block background — atom-one-dark colors are tuned for a dark bg but currently render on the light `--color-body`. (See "Theme selector" item.)
|
||||||
- [ ] Review every file in thor
|
- [ ] Review every file in thor
|
||||||
- [ ] Review alerts.odin
|
- [ ] Review alerts.odin
|
||||||
- [ ] Review content.odin
|
- [ ] Review content.odin
|
||||||
|
|||||||
+18
-13
@@ -32,15 +32,15 @@ Page :: struct {
|
|||||||
// (or all pages if include_drafts is true).
|
// (or all pages if include_drafts is true).
|
||||||
//
|
//
|
||||||
// TODO: What is the lifetime of pages?
|
// TODO: What is the lifetime of pages?
|
||||||
walk_content :: proc(content_path: string, include_drafts: bool) -> []Page {
|
walk_content :: proc(content_path: string, include_drafts: bool, sectionate: bool) -> []Page {
|
||||||
pages: [dynamic]Page
|
pages: [dynamic]Page
|
||||||
|
|
||||||
collect_home(&pages, content_path)
|
collect_home(&pages, content_path, sectionate)
|
||||||
collect_standalone(&pages, content_path)
|
collect_standalone(&pages, content_path, sectionate)
|
||||||
|
|
||||||
posts_path := fmt.tprintf("%s/posts", content_path)
|
posts_path := fmt.tprintf("%s/posts", content_path)
|
||||||
if os.exists(posts_path) {
|
if os.exists(posts_path) {
|
||||||
collect_posts(&pages, posts_path)
|
collect_posts(&pages, posts_path, sectionate)
|
||||||
}
|
}
|
||||||
|
|
||||||
if include_drafts {
|
if include_drafts {
|
||||||
@@ -57,10 +57,10 @@ walk_content :: proc(content_path: string, include_drafts: bool) -> []Page {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
collect_home :: proc(pages: ^[dynamic]Page, content_path: string) {
|
collect_home :: proc(pages: ^[dynamic]Page, content_path: string, sectionate: bool) {
|
||||||
html_path := fmt.tprintf("%s/index.html", content_path)
|
html_path := fmt.tprintf("%s/index.html", content_path)
|
||||||
if os.exists(html_path) {
|
if os.exists(html_path) {
|
||||||
page, ok := load_page(html_path, .Home, "")
|
page, ok := load_page(html_path, .Home, "", sectionate)
|
||||||
if ok {
|
if ok {
|
||||||
page.permalink = "/"
|
page.permalink = "/"
|
||||||
append(pages, page)
|
append(pages, page)
|
||||||
@@ -70,7 +70,7 @@ collect_home :: proc(pages: ^[dynamic]Page, content_path: string) {
|
|||||||
|
|
||||||
md_path := fmt.tprintf("%s/index.md", content_path)
|
md_path := fmt.tprintf("%s/index.md", content_path)
|
||||||
if os.exists(md_path) {
|
if os.exists(md_path) {
|
||||||
page, ok := load_page(md_path, .Home, "")
|
page, ok := load_page(md_path, .Home, "", sectionate)
|
||||||
if ok {
|
if ok {
|
||||||
page.permalink = "/"
|
page.permalink = "/"
|
||||||
append(pages, page)
|
append(pages, page)
|
||||||
@@ -78,7 +78,7 @@ collect_home :: proc(pages: ^[dynamic]Page, content_path: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
collect_standalone :: proc(pages: ^[dynamic]Page, content_path: string) {
|
collect_standalone :: proc(pages: ^[dynamic]Page, content_path: string, sectionate: bool) {
|
||||||
entries, err := os.read_all_directory_by_path(content_path, context.allocator)
|
entries, err := os.read_all_directory_by_path(content_path, context.allocator)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.warnf("thor: cannot read %s: %v", content_path, err)
|
log.warnf("thor: cannot read %s: %v", content_path, err)
|
||||||
@@ -98,14 +98,14 @@ collect_standalone :: proc(pages: ^[dynamic]Page, content_path: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
slug := strip_extension(entry.name)
|
slug := strip_extension(entry.name)
|
||||||
page, ok := load_page(entry.fullpath, .Standalone, slug)
|
page, ok := load_page(entry.fullpath, .Standalone, slug, sectionate)
|
||||||
if ok {
|
if ok {
|
||||||
append(pages, page)
|
append(pages, page)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
collect_posts :: proc(pages: ^[dynamic]Page, posts_path: string) {
|
collect_posts :: proc(pages: ^[dynamic]Page, posts_path: string, sectionate: bool) {
|
||||||
entries, err := os.read_all_directory_by_path(posts_path, context.allocator)
|
entries, err := os.read_all_directory_by_path(posts_path, context.allocator)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.warnf("thor: cannot read %s: %v", posts_path, err)
|
log.warnf("thor: cannot read %s: %v", posts_path, err)
|
||||||
@@ -120,7 +120,7 @@ collect_posts :: proc(pages: ^[dynamic]Page, posts_path: string) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
slug := strip_extension(entry.name)
|
slug := strip_extension(entry.name)
|
||||||
page, ok := load_page(entry.fullpath, .Post, slug)
|
page, ok := load_page(entry.fullpath, .Post, slug, sectionate)
|
||||||
if ok {
|
if ok {
|
||||||
append(pages, page)
|
append(pages, page)
|
||||||
}
|
}
|
||||||
@@ -132,7 +132,7 @@ collect_posts :: proc(pages: ^[dynamic]Page, posts_path: string) {
|
|||||||
if !os.exists(index_path) {
|
if !os.exists(index_path) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
page, ok := load_page(index_path, .Post, entry.name)
|
page, ok := load_page(index_path, .Post, entry.name, sectionate)
|
||||||
if ok {
|
if ok {
|
||||||
page.bundle_dir = entry.fullpath
|
page.bundle_dir = entry.fullpath
|
||||||
append(pages, page)
|
append(pages, page)
|
||||||
@@ -146,6 +146,7 @@ load_page :: proc(
|
|||||||
file_path: string,
|
file_path: string,
|
||||||
page_type: Page_Type,
|
page_type: Page_Type,
|
||||||
slug: string,
|
slug: string,
|
||||||
|
sectionate: bool,
|
||||||
) -> (
|
) -> (
|
||||||
page: Page,
|
page: Page,
|
||||||
ok: bool,
|
ok: bool,
|
||||||
@@ -178,7 +179,11 @@ load_page :: proc(
|
|||||||
expanded := expand_emoji(body)
|
expanded := expand_emoji(body)
|
||||||
clean_body, defs := strip_definitions(expanded)
|
clean_body, defs := strip_definitions(expanded)
|
||||||
html := cm.markdown_to_html_from_string(clean_body, {.Unsafe})
|
html := cm.markdown_to_html_from_string(clean_body, {.Unsafe})
|
||||||
page.body_html = highlight_code(inject_alerts(inject_sidenotes(html, defs)), file_path)
|
html = highlight_code(inject_alerts(inject_sidenotes(html, defs)), file_path)
|
||||||
|
if sectionate {
|
||||||
|
html = wrap_sections(html)
|
||||||
|
}
|
||||||
|
page.body_html = html
|
||||||
}
|
}
|
||||||
|
|
||||||
switch page_type {
|
switch page_type {
|
||||||
|
|||||||
+11
-3
@@ -187,14 +187,22 @@ find_first_error_line :: proc(root: TSNode) -> int {
|
|||||||
|
|
||||||
capture_name_to_css :: proc(name: string) -> string {
|
capture_name_to_css :: proc(name: string) -> string {
|
||||||
sb := strings.builder_make()
|
sb := strings.builder_make()
|
||||||
strings.write_string(&sb, "hl-")
|
seg := strings.builder_make()
|
||||||
|
first := true
|
||||||
for i in 0..<len(name) {
|
for i in 0..<len(name) {
|
||||||
if name[i] == '.' {
|
if name[i] == '.' {
|
||||||
strings.write_byte(&sb, '-')
|
if !first do strings.write_byte(&sb, ' ')
|
||||||
|
first = false
|
||||||
|
strings.write_string(&sb, "hl-")
|
||||||
|
strings.write_string(&sb, strings.to_string(seg))
|
||||||
|
strings.write_byte(&seg, '-')
|
||||||
} else {
|
} else {
|
||||||
strings.write_byte(&sb, name[i])
|
strings.write_byte(&seg, name[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !first do strings.write_byte(&sb, ' ')
|
||||||
|
strings.write_string(&sb, "hl-")
|
||||||
|
strings.write_string(&sb, strings.to_string(seg))
|
||||||
return strings.to_string(sb)
|
return strings.to_string(sb)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ main :: proc() {
|
|||||||
init_site(&site, os.args)
|
init_site(&site, os.args)
|
||||||
defer destroy_site(&site)
|
defer destroy_site(&site)
|
||||||
|
|
||||||
pages := walk_content(site.content_dir, site.drafts)
|
pages := walk_content(site.content_dir, site.drafts, site.sectionate)
|
||||||
|
|
||||||
render_site(pages, site)
|
render_site(pages, site)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "core:strings"
|
||||||
|
|
||||||
|
wrap_sections :: proc(html: string) -> string {
|
||||||
|
H2 :: "<h2"
|
||||||
|
|
||||||
|
parts: [dynamic]string
|
||||||
|
defer delete(parts)
|
||||||
|
pos := 0
|
||||||
|
search_pos := 0
|
||||||
|
|
||||||
|
for {
|
||||||
|
rel := strings.index(html[search_pos:], H2)
|
||||||
|
if rel < 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
idx := search_pos + rel
|
||||||
|
|
||||||
|
if idx > pos {
|
||||||
|
append(&parts, "<section>")
|
||||||
|
append(&parts, html[pos:idx])
|
||||||
|
append(&parts, "</section>")
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = idx
|
||||||
|
search_pos = idx + len(H2)
|
||||||
|
}
|
||||||
|
|
||||||
|
if pos < len(html) {
|
||||||
|
append(&parts, "<section>")
|
||||||
|
append(&parts, html[pos:])
|
||||||
|
append(&parts, "</section>")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(parts) == 0 {
|
||||||
|
return html
|
||||||
|
}
|
||||||
|
return strings.join(parts[:], "")
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user