diff --git a/README.tlm b/README.tlm index 8c217c9..9df9e51 100644 --- a/README.tlm +++ b/README.tlm @@ -41,5 +41,11 @@ Text under a blockquote [#named] | table | row | with | | some | ***content*** | in | +```odin +main :: proc() { + fmt.println("Hello, World!") +} +``` + [&1] Ordered list item 2 not included [&named] This is the definition for a named footnote diff --git a/src/html.odin b/src/html.odin index 70081bf..a53ddbc 100644 --- a/src/html.odin +++ b/src/html.odin @@ -187,6 +187,29 @@ print_html :: proc(token: ^Token) { fmt.print("\n") } + case TokenType.CodeBlock: + lang := "" + if v, ok := token.value.(string); ok && v != "" { + lang = v + } + if lang != "" { + fmt.printf("
", lang)
+		} else {
+			fmt.print("
")
+		}
+		for i := 0; i < len(token.children); i += 1 {
+			print_html(token.children[i])
+			if i + 1 < len(token.children) {
+				fmt.print("\n")
+			}
+		}
+		fmt.print("\n
\n") + + case TokenType.InlineCode: + if code, ok := token.value.(string); ok { + fmt.printf("%s", code) + } + case TokenType.BreakLine: fmt.print("
\n") diff --git a/src/main.odin b/src/main.odin index 551e5f3..ee6a502 100644 --- a/src/main.odin +++ b/src/main.odin @@ -59,6 +59,10 @@ print_token :: proc(token: ^Token, depth: int = 0) { type_str = "FootnoteDef" case TokenType.FootnoteRef: type_str = "FootnoteRef" + case TokenType.CodeBlock: + type_str = "CodeBlock" + case TokenType.InlineCode: + type_str = "InlineCode" } value_str := "" diff --git a/src/parser.odin b/src/parser.odin index 5f688c5..b567152 100644 --- a/src/parser.odin +++ b/src/parser.odin @@ -26,6 +26,8 @@ TokenType :: enum { Middle, FootnoteDef, FootnoteRef, + CodeBlock, + InlineCode, } TokenValue :: union { @@ -249,12 +251,61 @@ parse_inline_style :: proc(token: ^Token, symbol: byte, toggles: []StyleToggle) } pass_through_children :: proc(token: ^Token) { + if token.type == .CodeBlock || token.type == .InlineCode { + return + } parse_inline_style(token, '*', []StyleToggle{{2, .Bold}, {1, .Italics}}) parse_inline_style(token, '~', []StyleToggle{{2, .Strikethrough}}) parse_inline_style(token, '-', []StyleToggle{{2, .Strikethrough}}) } +parse_inline_code :: proc(token: ^Token) { + if token.type == .CodeBlock || token.type == .InlineCode { + return + } + if token.children != nil { + for i := 0; i < len(token.children); i += 1 { + parse_inline_code(token.children[i]) + } + } + + if token.type == .Text { + str, ok := token.value.(string) + if !ok { + return + } + + start_idx := strings.index_byte(str, '`') + if start_idx == -1 { + return + } + + end_idx := strings.index_byte(str[start_idx + 1:], '`') + if end_idx == -1 { + return + } + end_idx += start_idx + 1 + + code := str[start_idx + 1:end_idx] + + token.value = nil + + if start_idx > 0 { + push_token(token, .Text, str[:start_idx]) + } + push_token(token, .InlineCode, code) + + if end_idx + 1 < len(str) { + rem := push_token(token, .Text, str[end_idx + 1:]) + parse_inline_code(rem) + } + } +} + parse_inline_footnotes :: proc(token: ^Token) { + if token.type == .CodeBlock || token.type == .InlineCode { + return + } if token.children != nil { for i := 0; i < len(token.children); i += 1 { parse_inline_footnotes(token.children[i]) @@ -295,6 +346,9 @@ parse_inline_footnotes :: proc(token: ^Token) { } add_breaklines :: proc(token: ^Token) { + if token.type == .CodeBlock { + return + } if token.children == nil { return } @@ -331,9 +385,28 @@ parse :: proc(lines: []string, allocator := context.allocator) -> Token { // FIRST PASS + in_code_block := false + for untrimmed_line, line_index in lines { line := strings.trim_space(untrimmed_line) + if in_code_block { + if strings.has_prefix(line, "```") { + in_code_block = false + current_token = &root + } else { + push_token(current_token, TokenType.Text, untrimmed_line) + } + continue + } + + if strings.has_prefix(line, "```") { + lang := strings.trim_space(line[3:]) + current_token = push_token(&root, TokenType.CodeBlock, lang) + in_code_block = true + continue + } + if len(line) == 0 { current_token = &root continue @@ -441,6 +514,7 @@ parse :: proc(lines: []string, allocator := context.allocator) -> Token { // THIRD PASS for token in root.children { + parse_inline_code(token) pass_through_children(token) parse_inline_footnotes(token) }