diff --git a/main.odin b/main.odin index 8f6c934d25..dd3f11be07 100644 --- a/main.odin +++ b/main.odin @@ -1641,18 +1641,31 @@ write_markup_text :: proc(w: io.Writer, s_: string) { } write_docs :: proc(w: io.Writer, docs: string, name: string = "") { + + trim_empty_and_subtitle_lines_and_replace_lt :: proc(lines: []string, subtitle: string) -> []string { + lines := lines + for len(lines) > 0 && (strings.trim_space(lines[0]) == "" || strings.has_prefix(lines[0], subtitle)) { + lines = lines[1:] + } + for len(lines) > 0 && (strings.trim_space(lines[len(lines) - 1]) == "") { + lines = lines[:len(lines) - 1] + } + for &line in lines { + line = escape_html_string(line) + } + return lines + } + if docs == "" { return } - is_fmt := strings.has_prefix(docs, "package fmt") - _ = is_fmt - Block_Kind :: enum { Paragraph, Code, Example, Output, + Possible_Output, } Block :: struct { kind: Block_Kind, @@ -1664,27 +1677,36 @@ write_docs :: proc(w: io.Writer, docs: string, name: string = "") { start := 0 blocks: [dynamic]Block - has_possible: bool - example_block: Block // when set the kind should be Example - output_block: Block // when set the kind should be Output - // rely on zii that the kinds have not been set - assert(example_block.kind != .Example) - assert(output_block.kind != .Output) + has_any_output: bool + has_example: bool - insert_block :: proc(block: Block, blocks: ^[dynamic]Block, example: ^Block, output: ^Block, name: string) { - switch block.kind { - case .Paragraph: fallthrough - case .Code: append(blocks, block) - case .Example: - if example.kind == .Example { - errorf("The documentation for %q has multiple examples which is not allowed\n", name) + // Find the minimum common prefix length of tabs, so an entire doc comment can be indented + // without it rendering in a
tag. + first_line_special := strings.has_prefix(lines_to_process[0], "Example:") || strings.has_prefix(lines_to_process[0], "Output:") || strings.has_prefix(lines_to_process[0], "Possible Output:") + if !first_line_special && len(lines_to_process) > 1 { + min_tabs: Maybe(int) + for line in lines_to_process[1:] { + if len(strings.trim_space(line)) == 0 { + continue } - example^ = block - case .Output: - if example.kind == .Output { - errorf("The documentation for %q has multiple output which is not allowed\n", name) + + tabs: int + for ch in line { + if ch == '\t' { + tabs += 1 + } else { + break + } + } + min_tabs = min(tabs, min_tabs.? or_else max(int)) + } + if min, has_min := min_tabs.?; has_min { + for &line in lines_to_process[1:] { + if len(strings.trim_space(line)) == 0 { + continue + } + line = line[min:] } - output^ = block } } @@ -1698,11 +1720,13 @@ write_docs :: proc(w: io.Writer, docs: string, name: string = "") { switch { case strings.has_prefix(line, "Example:"): next_block_kind = .Example + has_example = true case strings.has_prefix(line, "Output:"): next_block_kind = .Output + has_any_output = true case strings.has_prefix(line, "Possible Output:"): - next_block_kind = .Output - has_possible = true + next_block_kind = .Possible_Output + has_any_output = true case strings.has_prefix(line, "\t"): next_block_kind = .Code case text == "": force_write_block = true } @@ -1710,11 +1734,13 @@ write_docs :: proc(w: io.Writer, docs: string, name: string = "") { switch { case strings.has_prefix(line, "Example:"): next_block_kind = .Example + has_example = true case strings.has_prefix(line, "Output:"): next_block_kind = .Output + has_any_output = true case strings.has_prefix(line, "Possible Output:"): - next_block_kind = .Output - has_possible = true + next_block_kind = .Possible_Output + has_any_output = true case !strings.has_prefix(line, "\t") && text != "": next_block_kind = .Paragraph } @@ -1722,33 +1748,35 @@ write_docs :: proc(w: io.Writer, docs: string, name: string = "") { switch { case strings.has_prefix(line, "Output:"): next_block_kind = .Output + has_any_output = true case strings.has_prefix(line, "Possible Output:"): - next_block_kind = .Output - has_possible = true + next_block_kind = .Possible_Output + has_any_output = true case !strings.has_prefix(line, "\t") && text != "": next_block_kind = .Paragraph } - case .Output: + case .Output, .Possible_Output: switch { case strings.has_prefix(line, "Example:"): next_block_kind = .Example + has_example = true case !strings.has_prefix(line, "\t") && text != "": next_block_kind = .Paragraph } } - if i-start > 0 && (curr_block_kind != next_block_kind || force_write_block) { - insert_block(Block{curr_block_kind, lines_to_process[start:i]}, &blocks, &example_block, &output_block, name) + if curr_block_kind != next_block_kind || force_write_block { + append(&blocks, Block{curr_block_kind, lines_to_process[start:i]}) curr_block_kind = next_block_kind start = i } } if start < len(lines_to_process) { - insert_block(Block{curr_block_kind, lines_to_process[start:]}, &blocks, &example_block, &output_block, name) + append(&blocks, Block{curr_block_kind, lines_to_process[start:]}) } - if output_block.kind == .Output && example_block.kind != .Example { + if has_any_output && !has_example { errorf("The documentation for %q has an output block but no example\n", name) } @@ -1824,45 +1852,25 @@ write_docs :: proc(w: io.Writer, docs: string, name: string = "") { io.write_string(w, "\n") } io.write_string(w, "\n") - case .Example: panic("We should not have example blocks in the block array") - case .Output: panic("We should not have output blocks in the block array") - } - } - - // Write example and output block if required - if example_block.kind == .Example { - trim_empty_and_subtitle_lines_and_replace_lt :: proc(lines: []string, subtitle: string) -> []string { - lines := lines - for len(lines) > 0 && (strings.trim_space(lines[0]) == "" || strings.has_prefix(lines[0], subtitle)) { - lines = lines[1:] - } - for len(lines) > 0 && (strings.trim_space(lines[len(lines) - 1]) == "") { - lines = lines[:len(lines) - 1] - } - for &line in lines { - line = escape_html_string(line) + case .Example: + // Example block starts with `Example:` and a number of white spaces, + example_lines := trim_empty_and_subtitle_lines_and_replace_lt(block.lines, "Example:") + + io.write_string(w, "
`)
+ for line in example_lines {
+ io.write_string(w, strings.trim_prefix(line, "\t"))
+ io.write_string(w, "\n")
}
- return lines
- }
- // Example block starts with `Example:` and a number of white spaces,
- example_lines := trim_empty_and_subtitle_lines_and_replace_lt(example_block.lines, "Example:")
-
- io.write_string(w, "\n")
- defer io.write_string(w, "\n")
- io.write_string(w, "Example: \n")
- io.write_string(w, ``)
- for line in example_lines {
- io.write_string(w, strings.trim_prefix(line, "\t"))
- io.write_string(w, "\n")
- }
- io.write_string(w, "
\n")
+ io.write_string(w, "
\n")
- // Add the output block if it is present
- if output_block.kind == .Output {
+ case .Output, .Possible_Output:
// Output block starts with `Output:` or `Possible Output:` and a number of white spaces,
- output_lines := trim_empty_and_subtitle_lines_and_replace_lt(output_block.lines, has_possible ? "Possible Output:" : "Output:")
+ output_lines := trim_empty_and_subtitle_lines_and_replace_lt(block.lines, block.kind == .Possible_Output ? "Possible Output:" : "Output:")
- io.write_string(w, has_possible ? "Possible Output:" : "Output:\n")
+ io.write_string(w, block.kind == .Possible_Output ? "Possible Output:" : "Output:\n")
io.write_string(w, ``) for line in output_lines { io.write_string(w, strings.trim_prefix(line, "\t"))