├── .gitignore ├── test ├── test.org ├── test.html └── test.json ├── node.go ├── node_test.go ├── gorg.go ├── gorg_test.go ├── tree.go └── tree_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /test/test.org: -------------------------------------------------------------------------------- 1 | body 0 2 | * headline 1.1 3 | ** headline 1.2 4 | *** headline 1.3 5 | **** headline 1.4 6 | body 1 7 | * headline 2.1 8 | body 2.1.1 9 | body 2.1.2 10 | ** headline 2.2 11 | body 2.2.1 12 | body 2.2.2 13 | *** headline 2.3 14 | body 2 15 | body 2.2 16 | * headline 3.1 17 | body 3.1 18 | body 3.2 19 | body 3.3 -------------------------------------------------------------------------------- /test/test.html: -------------------------------------------------------------------------------- 1 |

body 0

headline 1.1

headline 1.2

headline 1.3

headline 1.4

body 1

headline 2.1

body 2.1.1

body 2.1.2

headline 2.2

body 2.2.1

body 2.2.2

headline 2.3

body 2

body 2.2

headline 3.1

body 3.1

body 3.2

body 3.3

-------------------------------------------------------------------------------- /test/test.json: -------------------------------------------------------------------------------- 1 | {"nodes":[],"subtrees":[{"nodes":[{"headline":"","position":1,"sections":["body 0"]}],"subtrees":null},{"nodes":[{"headline":"headline 1.1","position":1,"sections":null},{"headline":"headline 1.2","position":2,"sections":null},{"headline":"headline 1.3","position":3,"sections":null},{"headline":"headline 1.4","position":4,"sections":["body 1"]}],"subtrees":null},{"nodes":[{"headline":"headline 2.1","position":1,"sections":["body 2.1.1","body 2.1.2"]},{"headline":"headline 2.2","position":2,"sections":["body 2.2.1","body 2.2.2"]},{"headline":"headline 2.3","position":3,"sections":["body 2","body 2.2"]}],"subtrees":null},{"nodes":[{"headline":"headline 3.1","position":1,"sections":[" body 3.1","body 3.2","body 3.3"]}],"subtrees":null}]} -------------------------------------------------------------------------------- /node.go: -------------------------------------------------------------------------------- 1 | /* 2 | * A Node models an org-mode headline with a following section 3 | * a section can be comprised of multiple lines 4 | * position is the headline's asterisk count 5 | */ 6 | 7 | package gorg 8 | 9 | import ( 10 | "encoding/json" 11 | "fmt" 12 | ) 13 | 14 | type Node struct { 15 | Headline string `json:"headline"` 16 | Position int `json:"position"` 17 | Section []string `json:"sections"` 18 | parent *Node 19 | } 20 | 21 | func (self *Node) findParent(nodes []*Node) *Node { 22 | if len(nodes) == 0 { 23 | return nil 24 | } else if nodes[len(nodes)-1].Position < self.Position { 25 | return nodes[len(nodes)-1] 26 | } else { 27 | nodes = nodes[0 : len(nodes)-1] 28 | return self.findParent(nodes) 29 | } 30 | } 31 | 32 | // the headline gets an tag, with ? determined by the position 33 | // each line of text is a paragraph within a level div 34 | func (self Node) toHtml() string { 35 | var header string 36 | 37 | if self.Headline != "" { 38 | header = fmt.Sprintf( 39 | "%s", 40 | self.Position, 41 | self.Headline, 42 | self.Position, 43 | ) 44 | } 45 | 46 | var body string 47 | if len(self.Section) > 0 { 48 | var text string 49 | for _, line := range self.Section { 50 | text = fmt.Sprintf("%s

%s

", text, line) 51 | } 52 | 53 | body = fmt.Sprintf( 54 | "
%s
", 55 | self.Position, 56 | text, 57 | ) 58 | } 59 | 60 | return fmt.Sprintf("%s%s", header, body) 61 | } 62 | 63 | func (self Node) toJson() string { 64 | json, err := json.Marshal(self) 65 | check(err) 66 | 67 | return string(json) 68 | } 69 | -------------------------------------------------------------------------------- /node_test.go: -------------------------------------------------------------------------------- 1 | package gorg 2 | 3 | import ( 4 | "fmt" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | func TestFindParent(t *testing.T) { 10 | fmt.Println("==== Node TestFindParent") 11 | 12 | var node Node 13 | var expected *Node 14 | var parent *Node 15 | 16 | nodes := []*Node{ 17 | &Node{Headline: "headline1", Position: 1}, 18 | &Node{Headline: "headline2", Position: 2}, 19 | &Node{Headline: "headline3", Position: 3}, 20 | &Node{Headline: "headline4", Position: 4}, 21 | &Node{Headline: "headline5", Position: 3}, 22 | &Node{Headline: "headline6", Position: 4}, 23 | } 24 | 25 | node = Node{Position: 1} 26 | parent = node.findParent(nodes) 27 | assert.Equal(t, parent, expected) 28 | 29 | node = Node{Position: 2} 30 | parent = node.findParent(nodes) 31 | expected = nodes[0] 32 | assert.Equal(t, parent, expected) 33 | 34 | node = Node{Position: 3} 35 | parent = node.findParent(nodes) 36 | expected = nodes[1] 37 | assert.Equal(t, parent, expected) 38 | 39 | node = Node{Position: 4} 40 | parent = node.findParent(nodes) 41 | expected = nodes[4] 42 | assert.Equal(t, parent, expected) 43 | 44 | node = Node{Position: 5} 45 | parent = node.findParent(nodes) 46 | expected = nodes[5] 47 | assert.Equal(t, parent, expected) 48 | } 49 | 50 | func TestNodeToHtml(t *testing.T) { 51 | fmt.Println("==== Node TestNodeToHtml ") 52 | 53 | var tests = []struct { 54 | in Node 55 | out string 56 | }{ 57 | { 58 | in: Node{ 59 | Headline: "the headline", 60 | Position: 1, 61 | Section: []string{"the text"}, 62 | }, 63 | out: "

the headline

the text

", 64 | }, 65 | { 66 | in: Node{ 67 | Headline: "the headline3", 68 | Position: 2, 69 | }, 70 | out: "

the headline3

", 71 | }, 72 | { 73 | in: Node{ 74 | Headline: "the headline3", 75 | Position: 4, 76 | }, 77 | out: "

the headline3

", 78 | }, 79 | } 80 | 81 | for _, test := range tests { 82 | actual := test.in.toHtml() 83 | assert.Equal(t, test.out, actual) 84 | } 85 | } 86 | 87 | func TestNodeToJson(t *testing.T) { 88 | fmt.Println("==== Node TestJsonToHtml") 89 | 90 | node := Node{ 91 | Headline: "the headline", 92 | Position: 2, 93 | Section: []string{"the text", "more text", "even more"}, 94 | } 95 | 96 | expected := "{\"headline\":\"the headline\",\"position\":2,\"sections\":[\"the text\",\"more text\",\"even more\"]}" 97 | 98 | assert.Equal(t, expected, node.toJson()) 99 | } 100 | -------------------------------------------------------------------------------- /gorg.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Converts an org-mode file to a JSON object 3 | * The JSON is a recursive structure - it's subtrees all the way down 4 | * Subtrees contain nodes and more subtrees. 5 | * Properties of a node: 6 | * sections: Paragraphs and code snippets under a headline. 7 | * position: The asterisk count on a headline 8 | * headline: The headline 9 | */ 10 | 11 | package gorg 12 | 13 | import ( 14 | "bufio" 15 | "io/ioutil" 16 | "os" 17 | "regexp" 18 | ) 19 | 20 | func check(e error) { 21 | if e != nil { 22 | panic(e) 23 | } 24 | } 25 | 26 | func OrgToHtmlFile(orgPath string, htmlPath string) { 27 | byteHtml := []byte(OrgToHtml(orgPath)) 28 | 29 | err := ioutil.WriteFile(htmlPath, byteHtml, 0644) 30 | check(err) 31 | } 32 | 33 | func TreeFromFile(orgPath string) *Tree { 34 | return NewTree(nodesFromFile(orgPath)) 35 | } 36 | 37 | func OrgToHtml(orgPath string) string { 38 | tree := NewTree(nodesFromFile(orgPath)) 39 | 40 | return tree.toHtml() 41 | } 42 | 43 | func OrgToJsonFile(orgPath string, jsonPath string) { 44 | byteJson := []byte(OrgToJson(orgPath)) 45 | 46 | err := ioutil.WriteFile(jsonPath, byteJson, 0644) 47 | check(err) 48 | } 49 | 50 | func OrgToJson(orgPath string) []byte { 51 | tree := NewTree(nodesFromFile(orgPath)) 52 | 53 | return tree.toJson() 54 | } 55 | 56 | // read nodes from the file 57 | // needs to be simplified 58 | // bug: if a table ends the file, it will not be included 59 | func nodesFromFile(path string) []*Node { 60 | file, err := os.Open(path) 61 | check(err) 62 | 63 | defer func() { 64 | check(file.Close()) 65 | }() 66 | 67 | scanner := bufio.NewScanner(file) 68 | var node *Node 69 | var nodes []*Node 70 | var headline string 71 | var position int 72 | var section string 73 | var isBlock bool 74 | var isCodeBlock bool 75 | var isTable bool 76 | 77 | for scanner.Scan() { 78 | line := scanner.Text() 79 | 80 | r := regexp.MustCompile(`\A(\*+)\ (.*)`) // should use \S 81 | submatch := r.FindStringSubmatch(line) 82 | 83 | if len(submatch) > 1 { 84 | isBlock = false 85 | isCodeBlock = false 86 | isTable = false 87 | 88 | headline = submatch[2] 89 | position = len(submatch[1]) 90 | 91 | node = &Node{Headline: headline, Position: position} 92 | 93 | node.parent = node.findParent(nodes) 94 | nodes = append(nodes, node) 95 | } else { 96 | codeStartReg := regexp.MustCompile(`\A(\#\+BEGIN_SRC)(.*)`) 97 | codeEndReg := regexp.MustCompile(`\A(\#\+END_SRC)`) 98 | 99 | tableReg := regexp.MustCompile(`\A\s*\|.*`) 100 | 101 | if codeStartReg.MatchString(line) { 102 | isCodeBlock = true 103 | } else if codeEndReg.MatchString(line) { 104 | isCodeBlock = false 105 | } 106 | 107 | isTable = tableReg.MatchString(line) && !isCodeBlock 108 | isBlock = isCodeBlock || isTable 109 | 110 | section += line 111 | 112 | if !isBlock { 113 | if len(nodes) == 0 { 114 | nodes = []*Node{&Node{Position: 1, Section: []string{section}}} 115 | } else { 116 | lastNode := nodes[len(nodes)-1] 117 | lastNode.Section = append(lastNode.Section, section) 118 | } 119 | section = "" 120 | } 121 | } 122 | } 123 | 124 | return nodes 125 | } 126 | -------------------------------------------------------------------------------- /gorg_test.go: -------------------------------------------------------------------------------- 1 | package gorg 2 | 3 | import ( 4 | "fmt" 5 | "github.com/stretchr/testify/assert" 6 | "io/ioutil" 7 | "os" 8 | "path/filepath" 9 | "testing" 10 | ) 11 | 12 | func TestNodesFromFile(t *testing.T) { 13 | fmt.Println("==== gorg TestNodesFromFile") 14 | path, _ := filepath.Abs("test/test.org") 15 | 16 | tree := NewTree(nodesFromFile(path)) 17 | 18 | assert.Equal(t, 0, len(tree.Nodes)) 19 | assert.Equal(t, 4, len(tree.Subtrees)) 20 | 21 | expected := "

body 0

headline 1.1

headline 1.2

headline 1.3

headline 1.4

body 1

headline 2.1

body 2.1.1

body 2.1.2

headline 2.2

body 2.2.1

body 2.2.2

headline 2.3

body 2

body 2.2

headline 3.1

body 3.1

body 3.2

body 3.3

" 22 | 23 | assert.Equal(t, tree.toHtml(), expected) 24 | } 25 | 26 | func TestOrgToHtmlFile(t *testing.T) { 27 | fmt.Println("==== gorg testOrgToHtmlFile") 28 | 29 | inPath, _ := filepath.Abs("test/test.org") 30 | outPath, _ := filepath.Abs("test/test.html") 31 | 32 | // remove last test file, if exists 33 | if _, err := os.Stat(outPath); err == nil { 34 | os.Remove(outPath) 35 | } 36 | 37 | OrgToHtmlFile(inPath, outPath) 38 | 39 | htmlFileContents, _ := ioutil.ReadFile(outPath) 40 | contents := htmlFileContents 41 | 42 | expected := "

body 0

headline 1.1

headline 1.2

headline 1.3

headline 1.4

body 1

headline 2.1

body 2.1.1

body 2.1.2

headline 2.2

body 2.2.1

body 2.2.2

headline 2.3

body 2

body 2.2

headline 3.1

body 3.1

body 3.2

body 3.3

" 43 | 44 | assert.Equal(t, string(contents), expected) 45 | } 46 | 47 | func TestOrgToJsonFile(t *testing.T) { 48 | fmt.Println("==== gorg testOrgToJsonFile") 49 | 50 | inPath, _ := filepath.Abs("test/test.org") 51 | outPath, _ := filepath.Abs("test/test.json") 52 | 53 | // remove last test file 54 | if _, err := os.Stat(outPath); err == nil { 55 | os.Remove(outPath) 56 | } 57 | 58 | OrgToJsonFile(inPath, outPath) 59 | 60 | jsonFileContents, _ := ioutil.ReadFile(outPath) 61 | contents := jsonFileContents 62 | 63 | expected := "{\"nodes\":[],\"subtrees\":[{\"nodes\":[{\"headline\":\"\",\"position\":1,\"sections\":[\"body 0\"]}],\"subtrees\":null},{\"nodes\":[{\"headline\":\"headline 1.1\",\"position\":1,\"sections\":null},{\"headline\":\"headline 1.2\",\"position\":2,\"sections\":null},{\"headline\":\"headline 1.3\",\"position\":3,\"sections\":null},{\"headline\":\"headline 1.4\",\"position\":4,\"sections\":[\"body 1\"]}],\"subtrees\":null},{\"nodes\":[{\"headline\":\"headline 2.1\",\"position\":1,\"sections\":[\"body 2.1.1\",\"body 2.1.2\"]},{\"headline\":\"headline 2.2\",\"position\":2,\"sections\":[\"body 2.2.1\",\"body 2.2.2\"]},{\"headline\":\"headline 2.3\",\"position\":3,\"sections\":[\"body 2\",\"body 2.2\"]}],\"subtrees\":null},{\"nodes\":[{\"headline\":\"headline 3.1\",\"position\":1,\"sections\":[\" body 3.1\",\"body 3.2\",\"body 3.3\"]}],\"subtrees\":null}]}" 64 | 65 | assert.Equal(t, string(contents), expected) 66 | } 67 | -------------------------------------------------------------------------------- /tree.go: -------------------------------------------------------------------------------- 1 | package gorg 2 | 3 | import "fmt" 4 | import "encoding/json" 5 | 6 | type Tree struct { 7 | Nodes []*Node `json:"nodes"` 8 | Subtrees []*Tree `json:"subtrees"` 9 | parent *Tree 10 | } 11 | 12 | func NewTree(nodes []*Node) *Tree { 13 | tree := Tree{Nodes: nodes} 14 | tree.unflatten() 15 | 16 | return &tree 17 | } 18 | 19 | func (self *Tree) addNode(node *Node) { 20 | node.parent = node.findParent(self.Nodes) 21 | 22 | if node.Position == 0 { 23 | node.Position = 1 24 | } 25 | 26 | self.Nodes = append(self.Nodes, node) 27 | } 28 | 29 | func (self *Tree) addSubtree(subtree *Tree) { 30 | subtree.parent = self 31 | self.Subtrees = append(self.Subtrees, subtree) 32 | } 33 | 34 | func (self Tree) isEmpty() bool { 35 | return len(self.Nodes) == 0 36 | } 37 | 38 | func (self Tree) lastNode() *Node { 39 | return self.Nodes[len(self.Nodes)-1] 40 | } 41 | 42 | func (self Tree) toHtml() string { 43 | var html string 44 | 45 | // if top level of tree has Nodes, 46 | // top is one, collapsible subtree 47 | if len(self.Nodes) > 1 { 48 | html = "
" 49 | } 50 | html = self.subtreesToHtml(html) 51 | 52 | if len(self.Nodes) > 1 { 53 | html = html + "
" 54 | } 55 | 56 | return html 57 | } 58 | 59 | func (self Tree) subtreesToHtml(html string) string { 60 | 61 | for _, node := range self.Nodes { 62 | html = fmt.Sprintf("%s%s", html, node.toHtml()) 63 | } 64 | 65 | for _, subtree := range self.Subtrees { 66 | html = html + "
" 67 | html = subtree.subtreesToHtml(html) 68 | html = html + "
" 69 | } 70 | 71 | return html 72 | } 73 | 74 | func (self Tree) toJson() []byte { 75 | json, err := json.Marshal(self) 76 | check(err) 77 | 78 | return json 79 | } 80 | 81 | func (self *Tree) indexOfNode(searchNode *Node) int { 82 | for i, node := range self.Nodes { 83 | if node == searchNode { 84 | return i 85 | } 86 | } 87 | 88 | return -1 89 | } 90 | 91 | func (self *Tree) deleteNode(node *Node) { 92 | i := self.indexOfNode(node) 93 | 94 | if i == -1 { 95 | return 96 | } 97 | 98 | if i == 0 { 99 | self.Nodes = self.Nodes[1:] 100 | } else if i == len(self.Nodes)-1 { 101 | self.Nodes = self.Nodes[:len(self.Nodes)-1] 102 | } else { 103 | self.Nodes = append(self.Nodes[:i], self.Nodes[i+1:]...) 104 | } 105 | } 106 | 107 | func (self *Tree) unflatten() { 108 | subtrees := getSubtrees(self.Nodes) 109 | 110 | for _, s := range subtrees { 111 | self.addSubtree(s) 112 | 113 | for _, n := range s.Nodes { 114 | self.deleteNode(n) 115 | } 116 | } 117 | 118 | for _, subtree := range self.Subtrees { 119 | subtree.unflatten() 120 | } 121 | } 122 | 123 | func getSubtrees(ns []*Node) []*Tree { 124 | 125 | if len(ns) == 1 { 126 | return []*Tree{} 127 | } 128 | 129 | root := ns[0] 130 | nodes := ns[1:] 131 | 132 | subtree := &Tree{Nodes: []*Node{root}} 133 | var subtrees []*Tree 134 | 135 | for _, node := range nodes { 136 | if node.Position > root.Position { 137 | subtree.addNode(node) 138 | } else { 139 | subtrees = append(subtrees, subtree) 140 | 141 | root = node 142 | 143 | subtree = &Tree{Nodes: []*Node{root}} 144 | 145 | } 146 | 147 | if node == nodes[len(nodes)-1] { 148 | subtrees = append(subtrees, subtree) 149 | } 150 | } 151 | 152 | if len(subtrees) > 1 { 153 | return subtrees 154 | } else { 155 | return getSubtrees(nodes) 156 | } 157 | } 158 | 159 | func printTree(tree Tree) { 160 | for _, node := range tree.Nodes { 161 | line := "" 162 | for i := 0; i < node.Position; i++ { 163 | line = line + "*" 164 | } 165 | 166 | line = line + " " + node.Headline 167 | fmt.Println(line) 168 | } 169 | 170 | for _, subtree := range tree.Subtrees { 171 | printTree(*subtree) 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /tree_test.go: -------------------------------------------------------------------------------- 1 | package gorg 2 | 3 | import ( 4 | "fmt" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | func TestIsEmpty(t *testing.T) { 10 | fmt.Println("==== Tree TestisEmpty") 11 | 12 | var testTree Tree 13 | 14 | assert.True(t, testTree.isEmpty()) 15 | 16 | testTree = Tree{Nodes: []*Node{&Node{Headline: "test"}}} 17 | 18 | assert.False(t, testTree.isEmpty()) 19 | } 20 | 21 | func TestAddNode(t *testing.T) { 22 | fmt.Println("==== Tree TestAddNode") 23 | 24 | var tree Tree 25 | 26 | assert.True(t, tree.isEmpty()) 27 | 28 | node1 := &Node{Headline: "test node1", Position: 1} 29 | tree.addNode(node1) 30 | 31 | assert.False(t, tree.isEmpty()) 32 | assert.Equal(t, tree.Nodes[0].Headline, "test node1") 33 | 34 | node2 := &Node{Headline: "test node2", Position: 3} 35 | tree.addNode(node2) 36 | assert.Equal(t, tree.Nodes[0].Headline, "test node1") 37 | assert.Equal(t, tree.Nodes[1].Headline, "test node2") 38 | 39 | node3 := &Node{Headline: "test node3", Position: 2} 40 | tree.addNode(node3) 41 | assert.Equal(t, tree.Nodes[0].Headline, "test node1") 42 | assert.Equal(t, tree.Nodes[1].Headline, "test node2") 43 | assert.Equal(t, tree.Nodes[2].Headline, "test node3") 44 | 45 | node4 := &Node{Headline: "test node4", Position: 4} 46 | tree.addNode(node4) 47 | assert.Equal(t, tree.Nodes[0].Headline, "test node1") 48 | assert.Equal(t, tree.Nodes[1].Headline, "test node2") 49 | assert.Equal(t, tree.Nodes[2].Headline, "test node3") 50 | assert.Equal(t, tree.Nodes[3].Headline, "test node4") 51 | 52 | node5 := &Node{Headline: "test node5", Position: 5} 53 | tree.addNode(node5) 54 | assert.Equal(t, tree.Nodes[0].Headline, "test node1") 55 | assert.Equal(t, tree.Nodes[1].Headline, "test node2") 56 | assert.Equal(t, tree.Nodes[2].Headline, "test node3") 57 | assert.Equal(t, tree.Nodes[3].Headline, "test node4") 58 | assert.Equal(t, tree.Nodes[4].Headline, "test node5") 59 | 60 | var parent *Node 61 | assert.Equal(t, tree.Nodes[0].parent, parent) 62 | assert.Equal(t, tree.Nodes[1].parent, node1) 63 | assert.Equal(t, tree.Nodes[2].parent, node1) 64 | assert.Equal(t, tree.Nodes[3].parent, node3) 65 | assert.Equal(t, tree.Nodes[4].parent, node4) 66 | } 67 | 68 | func TestAddSubtree(t *testing.T) { 69 | fmt.Println("==== Tree TestSubtree") 70 | 71 | var tree Tree 72 | 73 | subtree1 := Tree{Nodes: []*Node{&Node{Headline: "test"}}} 74 | tree.addSubtree(&subtree1) 75 | 76 | subtree2 := Tree{Nodes: []*Node{&Node{Headline: "test"}}} 77 | tree.addSubtree(&subtree2) 78 | 79 | subtree3 := Tree{Nodes: []*Node{&Node{Headline: "test"}}} 80 | tree.addSubtree(&subtree3) 81 | 82 | subtree4 := Tree{Nodes: []*Node{&Node{Headline: "test"}}} 83 | tree.addSubtree(&subtree4) 84 | 85 | assert.Equal(t, tree.Subtrees[0], &subtree1) 86 | assert.Equal(t, tree.Subtrees[1], &subtree2) 87 | assert.Equal(t, tree.Subtrees[2], &subtree3) 88 | assert.Equal(t, tree.Subtrees[3], &subtree4) 89 | 90 | assert.Equal(t, tree.Subtrees[0].parent, &tree) 91 | assert.Equal(t, tree.Subtrees[1].parent, &tree) 92 | assert.Equal(t, tree.Subtrees[2].parent, &tree) 93 | assert.Equal(t, tree.Subtrees[3].parent, &tree) 94 | } 95 | 96 | func TestTreeToHtml(t *testing.T) { 97 | fmt.Println("==== Tree TestTreeToHtml") 98 | 99 | node1 := &Node{Headline: "headline1", Position: 1} 100 | node2 := &Node{ 101 | Headline: "headline2", 102 | Position: 2, 103 | Section: []string{"the section for node2"}, 104 | parent: node1, 105 | } 106 | node3 := &Node{Headline: "headline3", Position: 3, parent: node2} 107 | node4 := &Node{ 108 | Headline: "headline4", 109 | Position: 4, 110 | Section: []string{"the section for node4"}, 111 | parent: node3, 112 | } 113 | node5 := &Node{Headline: "headline5", Position: 3, parent: node2} 114 | node6 := &Node{ 115 | Headline: "headline6", 116 | Position: 4, 117 | Section: []string{"the section for node6", "some more text"}, 118 | parent: node5, 119 | } 120 | 121 | nodes := []*Node{node1, node2, node3, node4, node5, node6} 122 | tree := NewTree(nodes) 123 | html := tree.toHtml() 124 | 125 | var tests = []struct { 126 | in string 127 | out string 128 | }{ 129 | { 130 | in: html, 131 | out: "

headline1

headline2

the section for node2

headline3

headline4

the section for node4

headline5

headline6

the section for node6

some more text

", 132 | }, 133 | } 134 | 135 | for _, test := range tests { 136 | assert.Equal(t, test.out, test.in) 137 | } 138 | } 139 | 140 | func TestTreeToJson(t *testing.T) { 141 | fmt.Println("==== Tree TestTreeToJson") 142 | 143 | node1 := &Node{Headline: "headline1", Position: 1} 144 | node2 := &Node{ 145 | Headline: "headline2", 146 | Position: 2, 147 | Section: []string{"the section for node2"}, 148 | parent: node1, 149 | } 150 | node3 := &Node{Headline: "headline3", Position: 3, parent: node2} 151 | node4 := &Node{ 152 | Headline: "headline4", 153 | Position: 4, 154 | Section: []string{"the section for node4"}, 155 | parent: node3, 156 | } 157 | node5 := &Node{Headline: "headline5", Position: 3, parent: node2} 158 | node6 := &Node{ 159 | Headline: "headline6", 160 | Position: 4, 161 | Section: []string{"the section for node6", "some more text"}, 162 | parent: node5, 163 | } 164 | 165 | nodes := []*Node{node1, node2, node3, node4, node5, node6} 166 | tree := NewTree(nodes) 167 | json := string(tree.toJson()) 168 | 169 | var tests = []struct { 170 | in string 171 | out string 172 | }{ 173 | { 174 | in: json, 175 | out: "{\"nodes\":[{\"headline\":\"headline1\",\"position\":1,\"sections\":null},{\"headline\":\"headline2\",\"position\":2,\"sections\":[\"the section for node2\"]}],\"subtrees\":[{\"nodes\":[{\"headline\":\"headline3\",\"position\":3,\"sections\":null},{\"headline\":\"headline4\",\"position\":4,\"sections\":[\"the section for node4\"]}],\"subtrees\":null},{\"nodes\":[{\"headline\":\"headline5\",\"position\":3,\"sections\":null},{\"headline\":\"headline6\",\"position\":4,\"sections\":[\"the section for node6\",\"some more text\"]}],\"subtrees\":null}]}", 176 | }, 177 | } 178 | 179 | for _, test := range tests { 180 | assert.Equal(t, test.out, test.in) 181 | } 182 | } 183 | 184 | func TestIndexOfNode(t *testing.T) { 185 | fmt.Println("==== Tree TestIndexOfNode") 186 | 187 | node1 := &Node{Headline: "headline1", Position: 1} 188 | node2 := &Node{Headline: "headline2", Position: 2} 189 | node3 := &Node{Headline: "headline3", Position: 3} 190 | node4 := &Node{Headline: "headline4", Position: 4} 191 | 192 | tree := Tree{Nodes: []*Node{node1, node2, node3, node4}} 193 | 194 | assert.Equal(t, tree.indexOfNode(node4), 3) 195 | assert.Equal(t, tree.indexOfNode(node1), 0) 196 | assert.Equal(t, tree.indexOfNode(node2), 1) 197 | assert.Equal(t, tree.indexOfNode(node3), 2) 198 | } 199 | 200 | func TestDeleteNode(t *testing.T) { 201 | fmt.Println("==== Tree TestDeleteNode") 202 | 203 | node1 := &Node{Headline: "headline1", Position: 1} 204 | node2 := &Node{Headline: "headline2", Position: 2} 205 | node3 := &Node{Headline: "headline3", Position: 3} 206 | node4 := &Node{Headline: "headline4", Position: 4} 207 | 208 | tree := Tree{Nodes: []*Node{node1, node2, node3, node4}} 209 | 210 | assert.Equal(t, len(tree.Nodes), 4) 211 | 212 | tree.deleteNode(node2) 213 | 214 | assert.Equal(t, len(tree.Nodes), 3) 215 | assert.Equal(t, tree.Nodes[0].Headline, "headline1") 216 | assert.Equal(t, tree.Nodes[1].Headline, "headline3") 217 | assert.Equal(t, tree.Nodes[2].Headline, "headline4") 218 | 219 | tree.deleteNode(node1) 220 | 221 | assert.Equal(t, len(tree.Nodes), 2) 222 | assert.Equal(t, tree.Nodes[0].Headline, "headline3") 223 | assert.Equal(t, tree.Nodes[1].Headline, "headline4") 224 | 225 | tree.deleteNode(node4) 226 | 227 | assert.Equal(t, len(tree.Nodes), 1) 228 | assert.Equal(t, tree.Nodes[0].Headline, "headline3") 229 | 230 | tree.deleteNode(node3) 231 | 232 | assert.Equal(t, len(tree.Nodes), 0) 233 | } 234 | 235 | func TestUnflattenTree(t *testing.T) { 236 | fmt.Println("==== Tree TestUnflattenTree") 237 | 238 | var tree Tree 239 | 240 | tree.addNode(&Node{Headline: "sub0.0.1", Position: 2}) 241 | tree.addNode(&Node{Headline: "sub1.1.1", Position: 1}) 242 | tree.addNode(&Node{Headline: "sub1.1.2", Position: 2}) 243 | tree.addNode(&Node{Headline: "sub1.1.3", Position: 3}) 244 | tree.addNode(&Node{Headline: "sub2.1.1", Position: 1}) 245 | tree.addNode(&Node{Headline: "sub2.1.2", Position: 3}) 246 | tree.addNode(&Node{Headline: "sub2.2.1", Position: 4}) 247 | tree.addNode(&Node{Headline: "sub2.2.2", Position: 5}) 248 | tree.addNode(&Node{Headline: "sub2.3.1", Position: 4}) 249 | tree.addNode(&Node{Headline: "sub2.3.2", Position: 5}) 250 | tree.addNode(&Node{Headline: "sub2.3.3", Position: 6}) 251 | tree.addNode(&Node{Headline: "sub2.4.1", Position: 4}) 252 | tree.addNode(&Node{Headline: "sub2.4.2", Position: 5}) 253 | tree.addNode(&Node{Headline: "sub3.1.1", Position: 1}) 254 | 255 | // ** sub0.1.1 256 | // * sub1.1.1 257 | // ** sub1.1.2 258 | // *** sub1.1.3 259 | // * sub2.1.1 260 | // *** sub2.1.2 261 | // **** sub2.2.1 262 | // ***** sub2.2.2 263 | // **** sub2.3.1 264 | // ***** sub2.3.2 265 | // ****** sub2.3.3 266 | // **** sub2.4.1 267 | // ***** sub2.4.2 268 | // * sub3.1.1 269 | 270 | tree.unflatten() 271 | 272 | assert.Equal(t, len(tree.Nodes), 0) 273 | assert.Equal(t, len(tree.Subtrees), 4) 274 | 275 | sub01 := tree.Subtrees[0] 276 | assert.Equal(t, len(sub01.Nodes), 1) 277 | assert.Equal(t, len(sub01.Subtrees), 0) 278 | 279 | assert.Equal(t, sub01.Nodes[0].Headline, "sub0.0.1") 280 | 281 | sub11 := tree.Subtrees[1] 282 | assert.Equal(t, len(sub11.Nodes), 3) 283 | assert.Equal(t, len(sub11.Subtrees), 0) 284 | 285 | assert.Equal(t, sub11.Nodes[0].Headline, "sub1.1.1") 286 | assert.Equal(t, sub11.Nodes[1].Headline, "sub1.1.2") 287 | assert.Equal(t, sub11.Nodes[2].Headline, "sub1.1.3") 288 | 289 | sub21 := tree.Subtrees[2] 290 | assert.Equal(t, len(sub21.Nodes), 2) 291 | assert.Equal(t, len(sub21.Subtrees), 3) 292 | 293 | assert.Equal(t, sub21.Nodes[0].Headline, "sub2.1.1") 294 | assert.Equal(t, sub21.Nodes[1].Headline, "sub2.1.2") 295 | 296 | sub22 := tree.Subtrees[2].Subtrees[0] 297 | assert.Equal(t, len(sub22.Nodes), 2) 298 | assert.Equal(t, len(sub22.Subtrees), 0) 299 | 300 | assert.Equal(t, sub22.Nodes[0].Headline, "sub2.2.1") 301 | assert.Equal(t, sub22.Nodes[1].Headline, "sub2.2.2") 302 | 303 | sub23 := tree.Subtrees[2].Subtrees[1] 304 | assert.Equal(t, len(sub23.Nodes), 3) 305 | assert.Equal(t, len(sub23.Subtrees), 0) 306 | 307 | assert.Equal(t, sub23.Nodes[0].Headline, "sub2.3.1") 308 | assert.Equal(t, sub23.Nodes[1].Headline, "sub2.3.2") 309 | assert.Equal(t, sub23.Nodes[2].Headline, "sub2.3.3") 310 | 311 | sub24 := tree.Subtrees[2].Subtrees[2] 312 | assert.Equal(t, len(sub24.Nodes), 2) 313 | assert.Equal(t, len(sub24.Subtrees), 0) 314 | 315 | assert.Equal(t, sub24.Nodes[0].Headline, "sub2.4.1") 316 | assert.Equal(t, sub24.Nodes[1].Headline, "sub2.4.2") 317 | 318 | sub31 := tree.Subtrees[3] 319 | assert.Equal(t, len(sub31.Nodes), 1) 320 | assert.Equal(t, len(sub31.Subtrees), 0) 321 | 322 | assert.Equal(t, sub31.Nodes[0].Headline, "sub3.1.1") 323 | } 324 | --------------------------------------------------------------------------------