diff --git a/README.tlm b/README.tlm
index 99b75b9..0f4c43d 100644
--- a/README.tlm
+++ b/README.tlm
@@ -12,6 +12,7 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam blandit dolor nibh,
$[https://krzak.org/krzak-icon.png]
$(https://krzak.org/krzak-icon.png)
+$[https://krzak.org/krzak-icon.png|img]
*italic*
**bold**
diff --git a/src/embed.odin b/src/embed.odin
index 0aaf3bb..169652d 100644
--- a/src/embed.odin
+++ b/src/embed.odin
@@ -2,6 +2,13 @@ package main
import "core:strings"
+is_url :: proc(url: string) -> bool {
+ if strings.contains(url, ":") || strings.contains(url, "/") || strings.contains(url, ".") {
+ return true
+ }
+ return false
+}
+
get_ext :: proc(url: string) -> string {
clean := url
if question := strings.index_byte(clean, '?'); question != -1 {
diff --git a/src/html.odin b/src/html.odin
index 8d94979..eb226e0 100644
--- a/src/html.odin
+++ b/src/html.odin
@@ -278,6 +278,39 @@ print_html :: proc(token: ^Token) {
fmt.print("")
}
+ case TokenType.Image:
+ if url, ok := token.value.(string); ok {
+ b := strings.builder_make(context.temp_allocator)
+ for child in token.children {
+ get_text_content(child, &b)
+ }
+ alt_text := strings.to_string(b)
+ fmt.printf("
", url, alt_text)
+ }
+
+ case TokenType.Audio:
+ if url, ok := token.value.(string); ok {
+ fmt.printf("")
+ }
+
+ case TokenType.Video:
+ if url, ok := token.value.(string); ok {
+ fmt.printf("")
+ }
+
+ case TokenType.Iframe:
+ if url, ok := token.value.(string); ok {
+ fmt.printf("", url)
+ }
+
case TokenType.BreakLine:
fmt.print("
\n")
diff --git a/src/main.odin b/src/main.odin
index 2d0dff0..fdc9e14 100644
--- a/src/main.odin
+++ b/src/main.odin
@@ -75,6 +75,14 @@ print_token :: proc(token: ^Token, depth: int = 0) {
type_str = "HeaderLink"
case TokenType.Link:
type_str = "Link"
+ case TokenType.Image:
+ type_str = "Image"
+ case TokenType.Audio:
+ type_str = "Audio"
+ case TokenType.Video:
+ type_str = "Video"
+ case TokenType.Iframe:
+ type_str = "Iframe"
}
value_str := ""
diff --git a/src/parser.odin b/src/parser.odin
index f4abf89..5bc1056 100644
--- a/src/parser.odin
+++ b/src/parser.odin
@@ -34,6 +34,10 @@ TokenType :: enum {
Subscript,
HeaderLink,
Link,
+ Image,
+ Audio,
+ Video,
+ Iframe,
}
TokenValue :: union {
@@ -476,6 +480,150 @@ parse_links :: proc(token: ^Token) {
}
}
+find_embed :: proc(
+ str: string,
+) -> (
+ start_idx, end_idx: int,
+ is_embed: bool,
+ url: string,
+ alt: string,
+ embed_type: string,
+) {
+ for i := 0; i < len(str); i += 1 {
+ if str[i] == '$' && i + 1 < len(str) {
+ if str[i + 1] == '[' {
+ if end, alt_text, url_text, ok := match_pair(str[i + 1:], '[', ']', '(', ')'); ok {
+ clean_url := url_text
+ type_hint := ""
+ if pipe_idx := strings.index_byte(url_text, '|'); pipe_idx != -1 {
+ clean_url = url_text[:pipe_idx]
+ type_hint = url_text[pipe_idx + 1:]
+ }
+
+ if is_url(clean_url) {
+ e_type := ""
+ if type_hint != "" {
+ e_type = parse_type_hint(type_hint)
+ }
+ if e_type == "" {
+ e_type = detect_embed_type(clean_url)
+ }
+ return i, i + 1 + end, true, clean_url, alt_text, e_type
+ }
+ }
+ if end, inner, ok := match_single(str[i + 1:], '[', ']'); ok {
+ url_text := inner
+ type_hint := ""
+ if pipe_idx := strings.index_byte(inner, '|'); pipe_idx != -1 {
+ url_text = inner[:pipe_idx]
+ type_hint = inner[pipe_idx + 1:]
+ }
+
+ if is_url(url_text) {
+ e_type := ""
+ if type_hint != "" {
+ e_type = parse_type_hint(type_hint)
+ }
+ if e_type == "" {
+ e_type = detect_embed_type(url_text)
+ }
+ return i, i + 1 + end, true, url_text, url_text, e_type
+ }
+ }
+ } else if str[i + 1] == '(' {
+ if end, url_text, alt_text, ok := match_pair(str[i + 1:], '(', ')', '[', ']'); ok {
+ clean_url := url_text
+ type_hint := ""
+ if pipe_idx := strings.index_byte(url_text, '|'); pipe_idx != -1 {
+ clean_url = url_text[:pipe_idx]
+ type_hint = url_text[pipe_idx + 1:]
+ }
+
+ if is_url(clean_url) {
+ e_type := ""
+ if type_hint != "" {
+ e_type = parse_type_hint(type_hint)
+ }
+ if e_type == "" {
+ e_type = detect_embed_type(clean_url)
+ }
+ return i, i + 1 + end, true, clean_url, alt_text, e_type
+ }
+ }
+ if end, inner, ok := match_single(str[i + 1:], '(', ')'); ok {
+ url_text := inner
+ type_hint := ""
+ if pipe_idx := strings.index_byte(inner, '|'); pipe_idx != -1 {
+ url_text = inner[:pipe_idx]
+ type_hint = inner[pipe_idx + 1:]
+ }
+
+ if is_url(url_text) {
+ e_type := ""
+ if type_hint != "" {
+ e_type = parse_type_hint(type_hint)
+ }
+ if e_type == "" {
+ e_type = detect_embed_type(url_text)
+ }
+ return i, i + 1 + end, true, url_text, url_text, e_type
+ }
+ }
+ }
+ }
+ }
+
+ return -1, -1, false, "", "", ""
+}
+
+parse_embeds :: 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_embeds(token.children[i])
+ }
+ }
+
+ if token.type == .Text {
+ str, ok := token.value.(string)
+ if !ok {
+ return
+ }
+
+ start_idx, end_idx, is_embed, url, alt, e_type := find_embed(str)
+ if !is_embed {
+ return
+ }
+
+ token.value = nil
+
+ if start_idx > 0 {
+ push_token(token, .Text, str[:start_idx])
+ }
+
+ embed_token_type: TokenType
+ if e_type == "image" {
+ embed_token_type = .Image
+ } else if e_type == "audio" {
+ embed_token_type = .Audio
+ } else if e_type == "video" {
+ embed_token_type = .Video
+ } else {
+ embed_token_type = .Iframe
+ }
+
+ embed_token := push_token(token, embed_token_type, url)
+ push_token(embed_token, .Text, alt)
+
+ if end_idx < len(str) {
+ rem := push_token(token, .Text, str[end_idx:])
+ parse_embeds(rem)
+ }
+ }
+}
+
add_breaklines :: proc(token: ^Token) {
if token.type == .CodeBlock {
return
@@ -667,6 +815,7 @@ parse :: proc(lines: []string, allocator := context.allocator) -> Token {
for token in root.children {
parse_inline_code(token)
+ parse_embeds(token)
parse_links(token)
parse_inline_footnotes(token)
pass_through_children(token)