π Blocks or Markdown
Key | Value |
---|---|
π¨βπ©βπ§βπ¦ Parents | |
π· Tags | |
Created | 2024-08-05 09:40:14 |
Modified | 2024-08-06 13:54:02 |
Contents
Blocks or Markdown, the eternal question
Aspect | Blocks | Markdown |
---|---|---|
Data structure | recursive nested | string, but we'll need blocks no matter what. |
Pain | implement every node supported by my markdown engine. Suck shit if it isn't supported | someone else's problem this will never go wrong i swearβ’ |
i guess what i’m thinking is that i really don’t want to have to hook into the markdown internals (π Remove parsing of markdown AST, just render blocks as we have them)
Currently we have this garbage:
$ ag '== "table"'
models/note_migrate.go
178: } else if m["type"] == "table" {
369: } else if m["type"] == "table" {
552: } else if m["type"] == "table" {
models/note.go
324: } else if m["type"] == "table" {
where new data structures have to be handled in 4 different places (grows linearly with migrations which is insane.)
$ git show f2391d06dab664432be247535edc8a584cf6d26c
commit f2391d06dab664432be247535edc8a584cf6d26c
Author: Helena Rasche <hexylena@galaxians.org>
Date: Mon Aug 5 12:24:17 2024 +0200
add support for tables
diff --git a/md/block.go b/md/block.go
index 8635502..6b4769b 100644
--- a/md/block.go
+++ b/md/block.go
@@ -3,11 +3,12 @@ package md
import (
"encoding/json"
"fmt"
+ "strconv"
+ "strings"
+
"github.com/gomarkdown/markdown"
"github.com/gomarkdown/markdown/html"
"github.com/gomarkdown/markdown/parser"
- "strconv"
- "strings"
)
/*
@@ -129,6 +130,7 @@ type SyntaxNode interface {
Html() string
Md() string
Type() string
+ String() string
}
type Heading struct {
@@ -160,12 +162,16 @@ func (h *Heading) MarshalJSON() (b []byte, e error) {
})
}
+func (s *Heading) String() string {
+ return fmt.Sprintf("Sn{%s}: %s", s.Type(), s.Md())
+}
+
type Paragraph struct {
Contents string `json:"contents"`
}
func (p *Paragraph) Html() string {
- return fmt.Sprintf("<p>%s</p>", mdToHTML([]byte(p.Contents)))
+ return string(mdToHTML([]byte(p.Contents)))
}
@@ -184,6 +190,10 @@ func (p *Paragraph) Type() string {
return "paragraph"
}
+func (s *Paragraph) String() string {
+ return fmt.Sprintf("Sn{%s}: %s", s.Type(), s.Md())
+}
+
type List struct {
Contents []string `json:"contents"`
Ordered bool `json:"ordered"`
@@ -224,6 +234,10 @@ func (l *List) Type() string {
return "list"
}
+func (s *List) String() string {
+ return fmt.Sprintf("Sn{%s}: %s", s.Type(), s.Md())
+}
+
type Image struct {
AltText string `json:"alt_text"`
Url string `json:"url"`
@@ -248,6 +262,9 @@ func (i *Image) MarshalJSON() ([]byte, error) {
func (i *Image) Type() string {
return "image"
}
+func (s *Image) String() string {
+ return fmt.Sprintf("Sn{%s}: %s", s.Type(), s.Md())
+}
type HorizontalRule struct{}
@@ -268,6 +285,9 @@ func (h *HorizontalRule) MarshalJSON() ([]byte, error) {
func (h *HorizontalRule) Type() string {
return "horizontal_rule"
}
+func (s *HorizontalRule) String() string {
+ return fmt.Sprintf("Sn{%s}: %s", s.Type(), s.Md())
+}
const TABLE_VIEW = "table_view"
@@ -293,6 +313,9 @@ func (t *TableView) MarshalJSON() ([]byte, error) {
func (t *TableView) Type() string {
return TABLE_VIEW
}
+func (s *TableView) String() string {
+ return fmt.Sprintf("Sn{%s}: %s", s.Type(), s.Md())
+}
type Code struct {
Lang string `json:"lang"`
@@ -318,6 +341,9 @@ func (c *Code) MarshalJSON() ([]byte, error) {
func (c *Code) Type() string {
return "code"
}
+func (s *Code) String() string {
+ return fmt.Sprintf("Sn{%s}: %s", s.Type(), s.Md())
+}
type Link struct {
Url string `json:"url"`
@@ -343,6 +369,9 @@ func (l *Link) MarshalJSON() ([]byte, error) {
func (l *Link) Type() string {
return "link"
}
+func (s *Link) String() string {
+ return fmt.Sprintf("Sn{%s}: %s", s.Type(), s.Md())
+}
// type BlockType string
// const (
@@ -363,3 +392,57 @@ func (l *Link) Type() string {
// Contents any `json:"contents"`
// Type BlockType `json:"type"`
// }
+
+type Table struct {
+ Header []string `json:"thead"`
+ Body [][]string `json:"tbody"`
+}
+
+func (t *Table) Html() string {
+ out := "<table>"
+ // Add table header
+ out += "<thead><tr>"
+ for _, h := range t.Header {
+ out += "<th>" + h + "</th>"
+ }
+ out += "</tr></thead>"
+
+ // Add table body
+ out += "<tbody>"
+ for _, row := range t.Body {
+ out += "<tr>"
+ for _, cell := range row {
+ out += "<td>" + cell + "</td>"
+ }
+ out += "</tr>"
+ }
+ out += "</tbody>"
+
+ out += "</table>"
+ return out
+}
+
+func (t *Table) Md() string {
+ out := "| " + strings.Join(t.Header, " | ") + " |\n"
+ out += "| " + strings.Repeat("--- | ", len(t.Header)) + "\n"
+ for _, row := range t.Body {
+ out += "| " + strings.Join(row, " | ") + " |\n"
+ }
+ return out
+}
+
+func (t *Table) Type() string {
+ return "table"
+}
+
+func (t *Table) MarshalJSON() ([]byte, error) {
+ m := make(map[string]interface{})
+ m["thead"] = t.Header
+ m["tbody"] = t.Body
+ m["type"] = t.Type()
+ return json.Marshal(m)
+}
+
+func (s *Table) String() string {
+ return fmt.Sprintf("Sn{%s}: %s", s.Type(), s.Md())
+}
diff --git a/md/parse.go b/md/parse.go
index 26c60d9..a30920d 100644
--- a/md/parse.go
+++ b/md/parse.go
@@ -5,9 +5,10 @@ package md
import (
// "os"
+ "strings"
+
"github.com/gomarkdown/markdown/ast"
"github.com/gomarkdown/markdown/parser"
- "strings"
"fmt"
)
@@ -150,6 +151,33 @@ func parseBlock(node ast.Node) []SyntaxNode {
Ordered: v.ListFlags&ast.ListTypeOrdered != 0,
},
}
+ case *ast.Table:
+ // need header, body
+ // header
+ header := []string{}
+ for _, row := range c.Children[0].AsContainer().Children {
+ for _, cell := range row.AsContainer().Children {
+ header_actual := cell.AsContainer().Children[0]
+ header = append(header, getContentOrig(header_actual))
+ }
+ }
+ // body
+ body := [][]string{}
+ for _, row := range c.Children[1].AsContainer().Children {
+ row_contents := []string{}
+ for _, cell := range row.AsContainer().Children {
+ cell_actual := cell.AsContainer().Children[0]
+ fmt.Println("cell_actual", cell_actual, getContentOrig(cell_actual))
+ row_contents = append(row_contents, getContentOrig(cell_actual))
+ }
+ body = append(body, row_contents)
+ }
+ return []SyntaxNode{
+ &Table{
+ Header: header,
+ Body: body,
+ },
+ }
default:
panic(fmt.Sprintf("Unhandled container type %T", v))
}
@@ -180,7 +208,6 @@ func parseBlock(node ast.Node) []SyntaxNode {
panic(fmt.Sprintf("Unhandled leaf type %T", v))
}
}
- panic("Should not reach here ever")
}
func MdToBlocks(md []byte) []SyntaxNode {
diff --git a/md/parse2.go b/md/parse2.go
index 0b517a4..da47e91 100644
--- a/md/parse2.go
+++ b/md/parse2.go
@@ -5,9 +5,10 @@ package md
import (
// "os"
+ "strings"
+
"github.com/gomarkdown/markdown/ast"
"github.com/gomarkdown/markdown/parser"
- "strings"
"fmt"
)
diff --git a/models/note.go b/models/note.go
index d5c5d2b..30daf69 100644
--- a/models/note.go
+++ b/models/note.go
@@ -178,9 +178,6 @@ func (ce *Note) UnmarshalJSON(b []byte) error {
// First, deserialize everything into a map of map
var objMap map[string]*json.RawMessage
err := json.Unmarshal(b, &objMap)
- if err != nil {
- return err
- }
// Must manually deserialise each item
if objMap["id"] != nil {
@@ -324,8 +321,15 @@ func (ce *Note) UnmarshalJSON(b []byte) error {
return err
}
ce.Blocks[index] = &a
+ } else if m["type"] == "table" {
+ var a pmd.Table
+ err := json.Unmarshal(*rawMessage, &a)
+ if err != nil {
+ return err
+ }
+ ce.Blocks[index] = &a
} else {
- return fmt.Errorf("Unknown type: %s", m["type"])
+ return fmt.Errorf("Unknown block type: %s", m["type"])
}
}
}
diff --git a/models/note_migrate.go b/models/note_migrate.go
index c832617..6f7f251 100644
--- a/models/note_migrate.go
+++ b/models/note_migrate.go
@@ -175,6 +175,13 @@ func (ce *Note0) UnmarshalJSON(b []byte) error {
return err
}
ce.Blocks[index] = &a
+ } else if m["type"] == "table" {
+ var a pmd.Table
+ err := json.Unmarshal(*rawMessage, &a)
+ if err != nil {
+ return err
+ }
+ ce.Blocks[index] = &a
} else {
return fmt.Errorf("Unknown type: %s", m["type"])
}
@@ -359,6 +366,13 @@ func (ce *Note1) UnmarshalJSON(b []byte) error {
return err
}
ce.Blocks[index] = &a
+ } else if m["type"] == "table" {
+ var a pmd.Table
+ err := json.Unmarshal(*rawMessage, &a)
+ if err != nil {
+ return err
+ }
+ ce.Blocks[index] = &a
} else {
return fmt.Errorf("Unknown type: %s", m["type"])
}
@@ -535,6 +549,13 @@ func (ce *Note2) UnmarshalJSON(b []byte) error {
return err
}
ce.Blocks[index] = &a
+ } else if m["type"] == "table" {
+ var a pmd.Table
+ err := json.Unmarshal(*rawMessage, &a)
+ if err != nil {
+ return err
+ }
+ ce.Blocks[index] = &a
} else {
return fmt.Errorf("Unknown type: %s", m["type"])
}
diff --git a/projects/4/3/432137cb-5a3a-4e75-9150-b49e2aabffeb b/projects/4/3/432137cb-5a3a-4e75-9150-b49e2aabffeb
index 901f291..bae9f36 100644
--- a/projects/4/3/432137cb-5a3a-4e75-9150-b49e2aabffeb
+++ b/projects/4/3/432137cb-5a3a-4e75-9150-b49e2aabffeb
@@ -1 +1 @@
-{"id":"432137cb-5a3a-4e75-9150-b49e2aabffeb","title":"Project Management Blog","type":"project","parents":[],"blocking":[],"_blocks":[{"contents":"The current list of blog posts can be found below, they're all \"children\" of this \"project\".","type":"paragraph"},{"query":"select title, created, Author from asdf where parent = '432137cb-5a3a-4e75-9150-b49e2aabffeb'","type":"table_view"}],"_tags":[{"type":"tag","title":"Author","value":"@13bcdf42-28c0-490b-8912-d60c7bf3a9c7","icon":""}],"created":1722431454,"modified":1722437194,"version":2}
\ No newline at end of file
+{"id":"432137cb-5a3a-4e75-9150-b49e2aabffeb","title":"Project Management Blog","type":"project","parents":[],"blocking":[],"_blocks":[{"contents":"The current list of blog posts can be found below, they're all \"children\" of this \"project\".","type":"paragraph"}],"_tags":[{"type":"tag","title":"Author","value":"@13bcdf42-28c0-490b-8912-d60c7bf3a9c7","icon":""},{"type":"icon","title":"","value":"","icon":"π"}],"created":1722431454,"modified":1722851090,"version":2}
\ No newline at end of file
and I don’t want to be duplicating types for every single node so we really want the absolute minimal set of necessary nodes.
- Markdown
- 2 Column
Any nodes that can be just plain old markdown, must be. Is there any reason for them not to be plain ol’ markdown?
Here were the types from the readme i wanted support for.
Group | Type | Fine as Plain Ol' Markdown? |
---|---|---|
markdown | h1/2/3 {4/5/6} | β |
markdown | todo list (automatic subtasks) | ? |
markdown | table | β |
markdown | bullet/numbered | β |
markdown | details/summary | β (html in md yea) |
markdown | blockquote | β |
markdown | code | β |
markdown | divider | β |
markdown | TeX | β going to be rendered by mathjax anyway |
advanced | url (link preview?) | ? |
advanced | image (local) | ? |
advanced | image (external) | β |
advanced | file (embedded) | ? |
db query | table | β |
db query | kanban board | β |
db query | gallery | ? |
db query | list | ? |
db query | calendar | ? |
db query | timeline | ? |
misc | breadcrumbs | ? |
misc | 2/3/4/5 columns | β no, this needs custom representation. don't want to use HTML for this. |
misc | mermaid | β just use code blocks again and mermaid plugin |
misc | link to person/page/date | β currently working π Blocks or Markdown |
misc | @ a day/time, and then have that show up in queries somehow???? | ? |