mirror of
https://github.com/sbrow/thor.git
synced 2026-08-26 11:23:32 -04:00
fix: Fixed leaks.
This commit is contained in:
+20
-13
@@ -14,11 +14,15 @@ Note_Kind :: enum {
|
|||||||
// sidenotes, [*id]: text for marginnotes), removes them, and returns the cleaned
|
// sidenotes, [*id]: text for marginnotes), removes them, and returns the cleaned
|
||||||
// text plus separate maps of id->definition for each kind.
|
// text plus separate maps of id->definition for each kind.
|
||||||
// Handles multi-line definitions with indented continuation lines.
|
// Handles multi-line definitions with indented continuation lines.
|
||||||
strip_definitions :: proc(body: string) -> (clean_body: string, sn_defs, mn_defs: map[string]string) {
|
strip_definitions :: proc(
|
||||||
sn_defs = make(map[string]string)
|
body: string,
|
||||||
mn_defs = make(map[string]string)
|
) -> (
|
||||||
|
clean_body: string,
|
||||||
|
sn_defs, mn_defs: map[string]string,
|
||||||
|
) {
|
||||||
|
// TODO: Should this be temp allocated?
|
||||||
lines := strings.split(body, "\n")
|
lines := strings.split(body, "\n")
|
||||||
|
defer delete(lines)
|
||||||
output_lines: [dynamic]string
|
output_lines: [dynamic]string
|
||||||
defer delete(output_lines)
|
defer delete(output_lines)
|
||||||
|
|
||||||
@@ -119,8 +123,9 @@ inject_notes :: proc(html: string, sn_defs, mn_defs: map[string]string) -> strin
|
|||||||
return html
|
return html
|
||||||
}
|
}
|
||||||
|
|
||||||
parts: [dynamic]string
|
parts: strings.Builder
|
||||||
defer delete(parts)
|
strings.builder_init_len(&parts, 0) // TODO: Set a reasonable default
|
||||||
|
defer strings.builder_destroy(&parts)
|
||||||
|
|
||||||
remaining := html
|
remaining := html
|
||||||
|
|
||||||
@@ -134,20 +139,20 @@ inject_notes :: proc(html: string, sn_defs, mn_defs: map[string]string) -> strin
|
|||||||
pos = mn_pos
|
pos = mn_pos
|
||||||
}
|
}
|
||||||
if pos < 0 {
|
if pos < 0 {
|
||||||
append(&parts, remaining)
|
strings.write_string(&parts, remaining)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append text before the reference
|
// Append text before the reference
|
||||||
append(&parts, remaining[:pos])
|
strings.write_string(&parts, remaining[:pos])
|
||||||
|
|
||||||
close := strings.index(remaining[pos + 2:], "]")
|
close := strings.index(remaining[pos + 2:], "]")
|
||||||
if close < 0 {
|
if close < 0 {
|
||||||
append(&parts, remaining[pos:])
|
strings.write_string(&parts, remaining[pos:])
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
id := remaining[pos + 2 : pos + 2 + close]
|
id := remaining[pos + 2:pos + 2 + close]
|
||||||
ref_end := pos + 2 + close + 1
|
ref_end := pos + 2 + close + 1
|
||||||
|
|
||||||
defs := sn_defs
|
defs := sn_defs
|
||||||
@@ -157,7 +162,7 @@ inject_notes :: proc(html: string, sn_defs, mn_defs: map[string]string) -> strin
|
|||||||
def_text, found := defs[id]
|
def_text, found := defs[id]
|
||||||
if !found {
|
if !found {
|
||||||
// No definition found, leave as literal text
|
// No definition found, leave as literal text
|
||||||
append(&parts, remaining[pos:ref_end])
|
strings.write_string(&parts, remaining[pos:ref_end])
|
||||||
remaining = remaining[ref_end:]
|
remaining = remaining[ref_end:]
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -167,6 +172,7 @@ inject_notes :: proc(html: string, sn_defs, mn_defs: map[string]string) -> strin
|
|||||||
def_html = strip_p_tags(def_html)
|
def_html = strip_p_tags(def_html)
|
||||||
|
|
||||||
note: string
|
note: string
|
||||||
|
defer delete(note)
|
||||||
if is_margin {
|
if is_margin {
|
||||||
note = fmt.aprintf(
|
note = fmt.aprintf(
|
||||||
`<label for="mn-%s" class="margin-toggle"></label><input type="checkbox" id="mn-%s" class="margin-toggle"><span class="marginnote">%s</span>`,
|
`<label for="mn-%s" class="margin-toggle"></label><input type="checkbox" id="mn-%s" class="margin-toggle"><span class="marginnote">%s</span>`,
|
||||||
@@ -182,12 +188,12 @@ inject_notes :: proc(html: string, sn_defs, mn_defs: map[string]string) -> strin
|
|||||||
def_html,
|
def_html,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
append(&parts, note)
|
strings.write_string(&parts, note)
|
||||||
|
|
||||||
remaining = remaining[ref_end:]
|
remaining = remaining[ref_end:]
|
||||||
}
|
}
|
||||||
|
|
||||||
return strings.join(parts[:], "")
|
return strings.to_string(parts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// strip_p_tags removes surrounding <p></p> if the HTML is a single paragraph.
|
// strip_p_tags removes surrounding <p></p> if the HTML is a single paragraph.
|
||||||
@@ -201,3 +207,4 @@ strip_p_tags :: proc(html: string) -> string {
|
|||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+29
-5
@@ -34,6 +34,13 @@ test_parse_def_line :: proc(t: ^testing.T) {
|
|||||||
test_strip_definitions :: proc(t: ^testing.T) {
|
test_strip_definitions :: proc(t: ^testing.T) {
|
||||||
body := "Intro[^a] and [*b].\n\n[^a]: a side\n\n[*b]: a margin\n"
|
body := "Intro[^a] and [*b].\n\n[^a]: a side\n\n[*b]: a margin\n"
|
||||||
clean, sn_defs, mn_defs := strip_definitions(body)
|
clean, sn_defs, mn_defs := strip_definitions(body)
|
||||||
|
defer {
|
||||||
|
delete(clean)
|
||||||
|
delete(sn_defs["a"])
|
||||||
|
delete(sn_defs)
|
||||||
|
delete(mn_defs["b"])
|
||||||
|
delete(mn_defs)
|
||||||
|
}
|
||||||
|
|
||||||
// references stay; definitions are removed
|
// references stay; definitions are removed
|
||||||
testing.expect(t, strings.contains(clean, "Intro[^a]"))
|
testing.expect(t, strings.contains(clean, "Intro[^a]"))
|
||||||
@@ -60,13 +67,22 @@ test_strip_definitions :: proc(t: ^testing.T) {
|
|||||||
@(test)
|
@(test)
|
||||||
test_inject_notes :: proc(t: ^testing.T) {
|
test_inject_notes :: proc(t: ^testing.T) {
|
||||||
html := "Text[^a] more [*b] end."
|
html := "Text[^a] more [*b] end."
|
||||||
sn_defs := map[string]string{"a" = "side note"}
|
sn_defs := map[string]string {
|
||||||
mn_defs := map[string]string{"b" = "margin note"}
|
"a" = "side note",
|
||||||
|
}
|
||||||
|
defer delete(sn_defs)
|
||||||
|
mn_defs := map[string]string {
|
||||||
|
"b" = "margin note",
|
||||||
|
}
|
||||||
|
defer delete(mn_defs)
|
||||||
|
|
||||||
out := inject_notes(html, sn_defs, mn_defs)
|
out := inject_notes(html, sn_defs, mn_defs)
|
||||||
|
|
||||||
// sidenote: numbered, fn- prefix, .sidenote span, rendered text
|
// sidenote: numbered, fn- prefix, .sidenote span, rendered text
|
||||||
testing.expect(t, strings.contains(out, `for="fn-a" class="margin-toggle sidenote-number"></label>`))
|
testing.expect(
|
||||||
|
t,
|
||||||
|
strings.contains(out, `for="fn-a" class="margin-toggle sidenote-number"></label>`),
|
||||||
|
)
|
||||||
testing.expect(t, strings.contains(out, `class="sidenote"`))
|
testing.expect(t, strings.contains(out, `class="sidenote"`))
|
||||||
testing.expect(t, strings.contains(out, "side note"))
|
testing.expect(t, strings.contains(out, "side note"))
|
||||||
|
|
||||||
@@ -87,12 +103,20 @@ test_inject_notes_no_defs :: proc(t: ^testing.T) {
|
|||||||
@(test)
|
@(test)
|
||||||
test_inject_notes_missing_ref :: proc(t: ^testing.T) {
|
test_inject_notes_missing_ref :: proc(t: ^testing.T) {
|
||||||
html := "Ref[^missing] and [*missing] end."
|
html := "Ref[^missing] and [*missing] end."
|
||||||
sn := map[string]string{"other" = "x"}
|
sn := map[string]string {
|
||||||
mn := map[string]string{"other2" = "y"}
|
"other" = "x",
|
||||||
|
}
|
||||||
|
defer delete_map(sn)
|
||||||
|
mn := map[string]string {
|
||||||
|
"other2" = "y",
|
||||||
|
}
|
||||||
|
defer delete_map(mn)
|
||||||
|
|
||||||
out := inject_notes(html, sn, mn)
|
out := inject_notes(html, sn, mn)
|
||||||
|
|
||||||
// references with no matching definition are left as literal text
|
// references with no matching definition are left as literal text
|
||||||
|
testing.expect(t, len(out) > 2)
|
||||||
testing.expect(t, strings.contains(out, "[^missing]"))
|
testing.expect(t, strings.contains(out, "[^missing]"))
|
||||||
testing.expect(t, strings.contains(out, "[*missing]"))
|
testing.expect(t, strings.contains(out, "[*missing]"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-1
@@ -4,6 +4,7 @@ package main
|
|||||||
|
|
||||||
import "core:encoding/json"
|
import "core:encoding/json"
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
|
import "core:log"
|
||||||
import "core:os"
|
import "core:os"
|
||||||
import "core:testing"
|
import "core:testing"
|
||||||
|
|
||||||
@@ -71,7 +72,11 @@ test_load_site_config_invalid_json :: proc(t: ^testing.T) {
|
|||||||
path := write_temp_config("invalid", `{not valid json}`)
|
path := write_temp_config("invalid", `{not valid json}`)
|
||||||
defer os.remove(path)
|
defer os.remove(path)
|
||||||
|
|
||||||
_, ok := load_site_config(path, context.temp_allocator)
|
ok := false
|
||||||
|
{
|
||||||
|
context.logger = log.nil_logger()
|
||||||
|
_, ok = load_site_config(path, context.temp_allocator)
|
||||||
|
}
|
||||||
testing.expect(t, !ok)
|
testing.expect(t, !ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user