My personal website, in gleam+lustre!
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Redo table rendering


Signed-off-by: MLC Bloeiman <mar@strawmelonjuice.com>

+91 -102
+91 -102
src/homepage/djotparse.gleam
··· 82 82 83 83 pub fn preprocess_tables(djot: String) -> String { 84 84 let lines = string.split(djot, on: "\n") 85 - process_table_lines(lines, [], False, False, False) 85 + process_table_lines(lines, [], None) 86 + } 87 + 88 + fn render_table(rows: List(String)) -> String { 89 + let html_rows = case rows { 90 + // Check if header 91 + [header, separator, ..body] -> { 92 + case is_separator(separator) { 93 + True -> { 94 + let head = 95 + "<thead class=\"bg-neutral text-neutral-content\">" 96 + <> row_to_html(header, "th") 97 + <> "</thead>" 98 + let body_html = 99 + "<tbody>" 100 + <> list.map(body, row_to_html(_, "td")) |> string.join("") 101 + <> "</tbody>" 102 + head <> body_html 103 + } 104 + False -> 105 + "<tbody>" 106 + <> list.map(rows, row_to_html(_, "td")) |> string.join("") 107 + <> "</tbody>" 108 + } 109 + } 110 + _ -> 111 + "<tbody>" 112 + <> list.map(rows, row_to_html(_, "td")) |> string.join("") 113 + <> "</tbody>" 114 + } 115 + 116 + "\n``` =html\n<table class=\"table table-zebra w-full my-4 border border-neutral-content\">\n" 117 + <> html_rows 118 + <> "\n</table>\n```\n" 119 + } 120 + 121 + fn is_separator(line: String) -> Bool { 122 + line 123 + |> string.replace("|", "") 124 + |> string.replace("-", "") 125 + |> string.trim 126 + == "" 127 + } 128 + 129 + fn row_to_html(row: String, tag: String) -> String { 130 + let cells = 131 + row 132 + |> string.split("|") 133 + |> list.filter(fn(s) { string.trim(s) != "" }) 134 + 135 + let class = case tag { 136 + "th" -> "class=\"px-4 py-2 text-left font-bold\"" 137 + _ -> "class=\"px-4 py-2 border-t border-neutral-content\"" 138 + } 139 + 140 + "<tr>" 141 + <> { 142 + list.map(cells, fn(cell) { 143 + "<" 144 + <> tag 145 + <> " " 146 + <> class 147 + <> ">" 148 + <> string.trim(cell) |> jot.to_html 149 + <> "</" 150 + <> tag 151 + <> ">" 152 + }) 153 + |> string.join("") 154 + } 155 + <> "</tr>" 86 156 } 87 157 88 158 fn process_table_lines( 89 159 lines: List(String), 90 160 acc: List(String), 91 - in_table: Bool, 92 - next_is_header: Bool, 93 - tbody_opened: Bool, 161 + table_buffer: Option(List(String)), 94 162 ) -> String { 95 163 case lines { 96 - [] -> string.join(list.reverse(acc), "\n") 164 + [] -> { 165 + let final_acc = case table_buffer { 166 + Some(buffer) -> [render_table(list.reverse(buffer)), ..acc] 167 + None -> acc 168 + } 169 + string.join(list.reverse(final_acc), "\n") 170 + } 171 + 97 172 [line, ..rest] -> { 98 - let is_table_line = string.starts_with(string.trim(line), "|") 99 - let is_header = case 100 - line 101 - |> string.trim 102 - |> string.split("|") 103 - |> string.concat 104 - |> string.trim 105 - |> string.replace("-", "") 106 - |> string.trim 107 - { 108 - "" -> { 109 - // Next line has a |----| structure. 110 - True 111 - } 112 - _ -> False 113 - } 114 - let tstart = 115 - "<table class=\"table table-zebra w-full my-4 border border-neutral-content\">" 116 - case is_header, in_table, is_table_line { 117 - // Header is above 118 - True, True, True -> { 119 - process_table_lines(rest, ["<tbody>", ..acc], True, True, True) 120 - } 121 - // Table starts 122 - _, False, True -> { 123 - process_table_lines( 124 - rest, 125 - [ 126 - "\n``` =html\n" 127 - <> tstart 128 - <> "\n" 129 - <> line_to_table_row(line, next_is_header), 130 - ..acc 131 - ], 132 - True, 133 - is_header, 134 - tbody_opened, 135 - ) 136 - } 173 + let trimmed = string.trim(line) 174 + let is_table_row = string.starts_with(trimmed, "|") 175 + 176 + case table_buffer, is_table_row { 177 + None, True -> process_table_lines(rest, acc, Some([trimmed])) 137 178 138 - // Table continues 139 - False, True, True -> 140 - process_table_lines( 141 - rest, 142 - [line_to_table_row(line, next_is_header), ..acc], 143 - True, 144 - False, 145 - tbody_opened, 146 - ) 179 + Some(buffer), True -> 180 + process_table_lines(rest, acc, Some([trimmed, ..buffer])) 147 181 148 - // Table ends 149 - _, True, False -> { 150 - let acc = case tbody_opened { 151 - // No header was found, paste a <tbody> right after the <table> tag. 152 - False -> 153 - list.map(acc, string.replace(_, tstart, tstart <> "<tbody>")) 154 - True -> acc 155 - } 182 + Some(buffer), False -> 156 183 process_table_lines( 157 184 rest, 158 - [line, "</tbody></table>\n```\n", ..acc], 159 - False, 160 - False, 161 - True, 185 + [line, render_table(list.reverse(buffer)), ..acc], 186 + None, 162 187 ) 163 - } 164 188 165 - // Normal text 166 - _, False, False -> 167 - process_table_lines(rest, [line, ..acc], False, False, False) 189 + None, False -> process_table_lines(rest, [line, ..acc], None) 168 190 } 169 191 } 170 - } 171 - } 172 - 173 - fn line_to_table_row(line: String, as_header: Bool) -> String { 174 - let cells = 175 - line 176 - |> string.trim 177 - |> string.split("|") 178 - 179 - case as_header { 180 - True -> 181 - "<thead class=\"bg-neutral text-neutral-content\"><tr>" 182 - <> { 183 - list.map(cells, fn(c) { 184 - " <th class=\"px-4 py-2 text-left font-bold\">" 185 - <> string.trim(c) |> jot.to_html 186 - <> "</th>" 187 - }) 188 - |> string.join("\n") 189 - } 190 - <> "</tr></thead>" 191 - 192 - False -> 193 - "<tr>" 194 - <> { 195 - list.map(cells, fn(c) { 196 - " <td class=\"px-4 py-2 border-t border-neutral-content\">" 197 - <> string.trim(c) |> jot.to_html 198 - <> "</td>" 199 - }) 200 - |> string.join("\n") 201 - } 202 - <> "</tr>" 203 192 } 204 193 } 205 194