diff --git a/TODOS.md b/TODOS.md index c69062e..9bad994 100644 --- a/TODOS.md +++ b/TODOS.md @@ -46,16 +46,21 @@ - [ ] Import/export packages. Hugo, jekyll, WordPress, etc. - [ ] Review every file in thor - [ ] Review assets.odin - - [ ] Review alerts.odin - [ ] Review content.odin - - [ ] Review emoji.odin + - [ ] Review defaults.odin - [ ] Review feed.odin - - [ ] Review footnotes.odin - [ ] Review frontmatter.odin - - [ ] Review highlight.odin - [ ] Review main.odin - - [ ] Review minifiy.odin + - [ ] Review minify.odin + - [ ] Review `markdown/` + - [ ] Review alerts.odin + - [ ] Review emoji.odin + - [ ] Review footnotes.odin + - [ ] Review footnotes_test.odin + - [ ] Review highlight.odin + - [ ] Review markdown.odin + - [ ] Review sectionate.odin - [ ] Review render.odin - - [ ] Review sectionate.odin - [x] Review site.odin - - [ ] Review tree_sitter.odin + - [ ] Review treesitter/treesitter.odin + - [ ] Review vfs.odin diff --git a/content.odin b/content.odin index e4f748f..0baa811 100644 --- a/content.odin +++ b/content.odin @@ -1,6 +1,6 @@ package main -import cm "vendor:commonmark" +import md "markdown" import "core:fmt" import "core:log" @@ -101,7 +101,7 @@ load_page :: proc( section: string, slug: string, is_index: bool, - ext: bit_set[Markdown_Extension], + ext: bit_set[md.Extension], ) -> ( page: Page, ok: bool, @@ -132,29 +132,7 @@ load_page :: proc( if strings.has_suffix(file_path, ".html") { page.body_html = strings.clone(body) } else { - sn_defs := make(map[string]string) - mn_defs := make(map[string]string) - clean_body := body - if .Sidenotes in ext { - clean_body, sn_defs, mn_defs = strip_definitions(body) - } - html := cm.markdown_to_html_from_string(clean_body, {.Unsafe}) - if .Emoji in ext { - html = expand_emoji(html) - } - if .Sidenotes in ext { - html = inject_notes(html, sn_defs, mn_defs) - } - if .Alerts in ext { - html = inject_alerts(html) - } - if .Highlight in ext { - html = highlight_code(html, file_path) - } - if .Sections in ext { - html = wrap_sections(html) - } - page.body_html = html + page.body_html = md.process(body, ext, file_path) } if section == "" && is_index { diff --git a/alerts.odin b/markdown/alerts.odin similarity index 99% rename from alerts.odin rename to markdown/alerts.odin index 852a7f4..483c765 100644 --- a/alerts.odin +++ b/markdown/alerts.odin @@ -1,5 +1,5 @@ #+feature dynamic-literals -package main +package markdown import "core:fmt" import "core:strings" diff --git a/emoji.odin b/markdown/emoji.odin similarity index 99% rename from emoji.odin rename to markdown/emoji.odin index eb8fab1..3d98729 100644 --- a/emoji.odin +++ b/markdown/emoji.odin @@ -1,5 +1,5 @@ #+feature dynamic-literals -package main +package markdown import "core:strings" diff --git a/footnotes.odin b/markdown/footnotes.odin similarity index 99% rename from footnotes.odin rename to markdown/footnotes.odin index f15f9a0..3248508 100644 --- a/footnotes.odin +++ b/markdown/footnotes.odin @@ -1,4 +1,4 @@ -package main +package markdown import cm "vendor:commonmark" diff --git a/footnotes_test.odin b/markdown/footnotes_test.odin similarity index 99% rename from footnotes_test.odin rename to markdown/footnotes_test.odin index 811a678..a4775cb 100644 --- a/footnotes_test.odin +++ b/markdown/footnotes_test.odin @@ -1,6 +1,6 @@ #+feature dynamic-literals #+test -package main +package markdown import "core:strings" import "core:testing" diff --git a/highlight.odin b/markdown/highlight.odin similarity index 99% rename from highlight.odin rename to markdown/highlight.odin index 4d3ffef..b7e415d 100644 --- a/highlight.odin +++ b/markdown/highlight.odin @@ -1,6 +1,6 @@ -package main +package markdown -import ts "treesitter" +import ts "../treesitter" import "core:fmt" import "core:log" diff --git a/markdown/markdown.odin b/markdown/markdown.odin new file mode 100644 index 0000000..771a64e --- /dev/null +++ b/markdown/markdown.odin @@ -0,0 +1,83 @@ +package markdown + +import cm "vendor:commonmark" + +import "core:encoding/json" +import "core:strings" + +Extension :: enum { + Emoji, + Sidenotes, + Alerts, + Highlight, + Sections, +} + +DEFAULT_EXTENSIONS :: bit_set[Extension]{.Emoji, .Sidenotes, .Alerts} + +process :: proc(body: string, ext: bit_set[Extension], file_path: string) -> string { + side_notes := make(map[string]string) + margin_notes := make(map[string]string) + clean_body := body + if .Sidenotes in ext { + clean_body, side_notes, margin_notes = strip_definitions(body) + } + html := cm.markdown_to_html_from_string(clean_body, {.Unsafe}) + if .Emoji in ext { + html = expand_emoji(html) + } + if .Sidenotes in ext { + html = inject_notes(html, side_notes, margin_notes) + } + if .Alerts in ext { + html = inject_alerts(html) + } + if .Highlight in ext { + html = highlight_code(html, file_path) + } + if .Sections in ext { + html = wrap_sections(html) + } + return html +} + +// Convert a ',' separated list of case-insensitive extension names to a bit set. +parse_extension_list :: proc(s: string) -> (result: bit_set[Extension]) { + for part in strings.split(s, ",", allocator = context.temp_allocator) { + name := strings.to_lower(strings.trim_space(part), allocator = context.temp_allocator) + switch name { + case "emoji": + result += {.Emoji} + case "sidenotes": + result += {.Sidenotes} + case "alerts": + result += {.Alerts} + case "highlight": + result += {.Highlight} + case "sections": + result += {.Sections} + } + } + return result +} + +// Given a map[Extension]bool, apply it to ext. +apply_extension_config :: proc(ext: ^bit_set[Extension], config: json.Object) { + for name, val in config { + // TODO: Silently discards invalid values. + enabled := val.(json.Boolean) or_continue + switch name { + case "emoji": + if enabled {ext^ += {.Emoji}} else {ext^ -= {.Emoji}} + case "sidenotes": + if enabled {ext^ += {.Sidenotes}} else {ext^ -= {.Sidenotes}} + case "alerts": + if enabled {ext^ += {.Alerts}} else {ext^ -= {.Alerts}} + case "highlight": + if enabled {ext^ += {.Highlight}} else {ext^ -= {.Highlight}} + case "sections": + if enabled {ext^ += {.Sections}} else {ext^ -= {.Sections}} + } + } +} + diff --git a/sectionate.odin b/markdown/sectionate.odin similarity index 97% rename from sectionate.odin rename to markdown/sectionate.odin index 5a44441..c69ea85 100644 --- a/sectionate.odin +++ b/markdown/sectionate.odin @@ -1,4 +1,4 @@ -package main +package markdown import "core:strings" diff --git a/site.odin b/site.odin index 95e36a8..07e6a7a 100644 --- a/site.odin +++ b/site.odin @@ -8,6 +8,8 @@ import "core:mem" import "core:os" import "core:strings" +import md "markdown" + // Site is the primary workhorse. Site :: struct { @@ -26,7 +28,7 @@ Site :: struct { layouts_dir: string, params: json.Object, features: bit_set[Feature], - markdown_extensions: bit_set[Markdown_Extension], + markdown_extensions: bit_set[md.Extension], } Feature :: enum { @@ -35,22 +37,6 @@ Feature :: enum { Watch, } -Markdown_Extension :: enum { - Emoji, - Sidenotes, - Alerts, - Highlight, - Sections, -} - -DEFAULT_MARKDOWN_EXTENSIONS :: bit_set[Markdown_Extension] { - .Emoji, - .Sidenotes, - .Alerts, - // .Highlight, - // .Sections, -} - // Configuration loaded from `thor.json`. Gets folded in to Site before // Flags Config_File :: struct { @@ -89,7 +75,7 @@ init_site :: proc(site: ^Site, args: []string) { // Set defaults site.base_url = "http://localhost:8080" - site.markdown_extensions = DEFAULT_MARKDOWN_EXTENSIONS + site.markdown_extensions = md.DEFAULT_EXTENSIONS _flags: Flags flags.parse_or_exit(&_flags, args, .Odin, alloc) @@ -163,7 +149,7 @@ site_apply_config :: proc(site: ^Site, config: Config_File, config_dir: string) // Apply markdown extensions from config if ext_obj, ok := config.markdown_extensions.(json.Object); ok { - apply_extension_config(&site.markdown_extensions, ext_obj) + md.apply_extension_config(&site.markdown_extensions, ext_obj) } if modules_arr, ok := config.modules.(json.Array); ok { @@ -193,48 +179,8 @@ site_apply_cli_flags :: proc(site: ^Site, flags: Flags) { if flags.watch {site.features += {.Watch}} if flags.minify {site.features += {.Minify}} - site.markdown_extensions += parse_extension_list(flags.md_enable) - site.markdown_extensions -= parse_extension_list(flags.md_disable) -} - -parse_extension_list :: proc(s: string) -> bit_set[Markdown_Extension] { - result: bit_set[Markdown_Extension] - if s == "" do return result - for part in strings.split(s, ",", allocator = context.temp_allocator) { - name := strings.to_lower(strings.trim_space(part), allocator = context.temp_allocator) - switch name { - case "emoji": - result += {.Emoji} - case "sidenotes": - result += {.Sidenotes} - case "alerts": - result += {.Alerts} - case "highlight": - result += {.Highlight} - case "sections": - result += {.Sections} - } - } - return result -} - -apply_extension_config :: proc(ext: ^bit_set[Markdown_Extension], config: json.Object) { - for name, val in config { - enabled, is_bool := val.(json.Boolean) - if !is_bool do continue - switch name { - case "emoji": - if enabled {ext^ += {.Emoji}} else {ext^ -= {.Emoji}} - case "sidenotes": - if enabled {ext^ += {.Sidenotes}} else {ext^ -= {.Sidenotes}} - case "alerts": - if enabled {ext^ += {.Alerts}} else {ext^ -= {.Alerts}} - case "highlight": - if enabled {ext^ += {.Highlight}} else {ext^ -= {.Highlight}} - case "sections": - if enabled {ext^ += {.Sections}} else {ext^ -= {.Sections}} - } - } + site.markdown_extensions += md.parse_extension_list(flags.md_enable) + site.markdown_extensions -= md.parse_extension_list(flags.md_disable) } site_allocator :: proc(site: ^Site) -> mem.Allocator {