Skip to content

Commit

Permalink
Merge pull request #20 from bouncepaw/0.8
Browse files Browse the repository at this point in the history
0.8
  • Loading branch information
bouncepaw authored Sep 1, 2020
2 parents 673c2b1 + 0e6c220 commit 79868ce
Show file tree
Hide file tree
Showing 20 changed files with 1,213 additions and 158 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ run: build
./mycorrhiza metarrhiza

build:
go generate
go build .

test:
Expand Down
38 changes: 26 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,35 @@
# mycorrhiza wiki
A wiki engine inspired by fungi. Not production-ready.
# 🍄 MycorrhizaWiki 0.8
A wiki engine.

Current version: 0.7 (or more?)
## Installation
```sh
git clone --recurse-submodules https://github.com/bouncepaw/mycorrhiza
cd mycorrhiza
make
# That make will:
# * run the default wiki. You can edit it right away.
# * create an executable called `mycorrhiza`. Run it with path to your wiki.
```

## Current features
* Edit pages through html forms
* Responsive design
* Works in text browsers
* Pages (called hyphae) can be in gemtext.
* Wiki pages (called hyphae) are in gemtext
* Everything is stored as simple files, no database required
* Page trees
* Changes are saved to git
* List of hyphae page
* History page
* Random page
* Light on resources: I run a home wiki on this engine 24/7 at an [Orange π Lite](http://www.orangepi.org/orangepilite/).

## Future features
* Tags
* Authorization
* History view
* Granular user rights

## Installation
I guess you can just clone this repo and run `make` to play around with the default wiki.
## Contributing
Help is always needed. We have a [tg chat](https://t.me/mycorrhizadev) where some development is coordinated. Feel free to open an issue or contact me.

## Future plans
* Tagging system
* Authorization
* Better history viewing
* Recent changes page
* More markups
18 changes: 9 additions & 9 deletions gemtext/lexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,6 @@ type GemLexerState struct {
buf string
}

// GeminiToHtml converts gemtext `content` of hypha `name` to html string.
func GeminiToHtml(name, content string) string {
return "TODO: do"
}

type Line struct {
id int
// interface{} may be bad. What I need is a sum of string and Transclusion
Expand Down Expand Up @@ -78,24 +73,29 @@ func wikilink(src string, state *GemLexerState) (href, text, class string) {
func lex(name, content string) (ast []Line) {
var state = GemLexerState{name: name}

for _, line := range strings.Split(content, "\n") {
for _, line := range append(strings.Split(content, "\n"), "") {
geminiLineToAST(line, &state, &ast)
}
return ast
}

// Lex `line` in gemtext and save it to `ast` using `state`.
func geminiLineToAST(line string, state *GemLexerState, ast *[]Line) {
addLine := func(text interface{}) {
*ast = append(*ast, Line{id: state.id, contents: text})
}

if "" == strings.TrimSpace(line) {
if state.where == "list" {
state.where = ""
addLine(state.buf + "</ul>")
}
return
}

startsWith := func(token string) bool {
return strings.HasPrefix(line, token)
}
addLine := func(text interface{}) {
*ast = append(*ast, Line{id: state.id, contents: text})
}

// Beware! Usage of goto. Some may say it is considered evil but in this case it helped to make a better-structured code.
switch state.where {
Expand Down
5 changes: 1 addition & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,4 @@ module github.com/bouncepaw/mycorrhiza

go 1.14

require (
golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect
golang.org/x/tools v0.0.0-20200731060945-b5fad4ed8dd6 // indirect
)
require github.com/valyala/quicktemplate v1.6.2
32 changes: 11 additions & 21 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,25 +1,15 @@
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.10.10/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.15.1/go.mod h1:YOKImeEosDdBPnxc0gy7INqi3m1zK6A+xl6TwOBhHCA=
github.com/valyala/quicktemplate v1.6.2 h1:k0vgK7zlmFzqAoIBIOrhrfmZ6JoTGJlLRPLbkPGr2/M=
github.com/valyala/quicktemplate v1.6.2/go.mod h1:mtEJpQtUiBV0SHhMX6RtiJtqxncgrfmjcUy5T68X8TM=
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7 h1:EBZoQjiKKPaLbPrbpssUfuHtwM6KV/vb4U85g/cigFY=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200731060945-b5fad4ed8dd6 h1:qKpj8TpV+LEhel7H/fR788J+KvhWZ3o3V6N2fU/iuLU=
golang.org/x/tools v0.0.0-20200731060945-b5fad4ed8dd6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
77 changes: 77 additions & 0 deletions history/history.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package history

import (
"bytes"
"fmt"
"log"
"os/exec"
"strconv"
"time"

"github.com/bouncepaw/mycorrhiza/util"
)

// Start initializes git credentials.
func Start(wikiDir string) {
_, err := gitsh("config", "user.name", "wikimind")
if err != nil {
log.Fatal(err)
}
_, err = gitsh("config", "user.email", "wikimind@mycorrhiza")
if err != nil {
log.Fatal(err)
}
}

// Revision represents a revision, duh. Hash is usually short. Username is extracted from email.
type Revision struct {
Hash string
Username string
Time time.Time
Message string
}

// Path to git executable. Set at init()
var gitpath string

func init() {
path, err := exec.LookPath("git")
if err != nil {
log.Fatal("Cound not find the git executable. Check your $PATH.")
} else {
log.Println("Git path is", path)
}
gitpath = path

}

// I pronounce it as [gɪt͡ʃ].
func gitsh(args ...string) (out bytes.Buffer, err error) {
fmt.Printf("$ %v\n", args)
cmd := exec.Command(gitpath, args...)

cmd.Dir = util.WikiDir

b, err := cmd.CombinedOutput()
if err != nil {
log.Println("gitsh:", err)
}
return *bytes.NewBuffer(b), err
}

// Convert a UNIX timestamp as string into a time. If nil is returned, it means that the timestamp could not be converted.
func unixTimestampAsTime(ts string) *time.Time {
i, err := strconv.ParseInt(ts, 10, 64)
if err != nil {
return nil
}
tm := time.Unix(i, 0)
return &tm
}

// Rename renames from `from` to `to` using `git mv`.
func Rename(from, to string) error {
log.Println(util.ShorterPath(from), util.ShorterPath(to))
_, err := gitsh("mv", from, to)
return err
}
58 changes: 58 additions & 0 deletions history/information.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// information.go
// Things related to gathering existing information.
package history

import (
"fmt"
"regexp"
"strings"
"time"
)

// Revisions returns slice of revisions for the given hypha name.
func Revisions(hyphaName string) ([]Revision, error) {
var (
out, err = gitsh(
"log", "--oneline", "--no-merges",
// Hash, Commiter email, Commiter time, Commit msg separated by tab
"--pretty=format:\"%h\t%ce\t%ct\t%s\"",
"--", hyphaName+"&.*",
)
revs []Revision
)
if err == nil {
for _, line := range strings.Split(out.String(), "\n") {
revs = append(revs, parseRevisionLine(line))
}
}
return revs, err
}

// This regex is wrapped in "". For some reason, these quotes appear at some time and we have to get rid of them.
var revisionLinePattern = regexp.MustCompile("\"(.*)\t(.*)@.*\t(.*)\t(.*)\"")

func parseRevisionLine(line string) Revision {
results := revisionLinePattern.FindStringSubmatch(line)
return Revision{
Hash: results[1],
Username: results[2],
Time: *unixTimestampAsTime(results[3]),
Message: results[4],
}
}

// Represent revision as a table row.
func (rev *Revision) AsHtmlTableRow(hyphaName string) string {
return fmt.Sprintf(`
<tr>
<td><time>%s</time></td>
<td><a href="/rev/%s/%s">%s</a></td>
<td>%s</td>
</tr>`, rev.Time.Format(time.RFC822), rev.Hash, hyphaName, rev.Hash, rev.Message)
}

// See how the file with `filepath` looked at commit with `hash`.
func FileAtRevision(filepath, hash string) (string, error) {
out, err := gitsh("show", hash+":"+filepath)
return out.String(), err
}
84 changes: 84 additions & 0 deletions history/operations.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// history/operations.go
// Things related to writing history.
package history

import (
"fmt"

"github.com/bouncepaw/mycorrhiza/util"
)

// OpType is the type a history operation has. Callers shall set appropriate optypes when creating history operations.
type OpType int

const (
TypeNone OpType = iota
TypeEditText
TypeEditBinary
)

// HistoryOp is an object representing a history operation.
type HistoryOp struct {
// All errors are appended here.
Errs []error
opType OpType
userMsg string
name string
email string
}

// Operation is a constructor of a history operation.
func Operation(opType OpType) *HistoryOp {
hop := &HistoryOp{
Errs: []error{},
opType: opType,
}
return hop
}

// git operation maker helper
func (hop *HistoryOp) gitop(args ...string) *HistoryOp {
out, err := gitsh(args...)
if err != nil {
fmt.Println("out:", out.String())
hop.Errs = append(hop.Errs, err)
}
return hop
}

// WithFiles stages all passed `paths`. Paths can be rooted or not.
func (hop *HistoryOp) WithFiles(paths ...string) *HistoryOp {
for i, path := range paths {
paths[i] = util.ShorterPath(path)
}
// 1 git operation is more effective than n operations.
return hop.gitop(append([]string{"add"}, paths...)...)
}

// Apply applies history operation by doing the commit.
func (hop *HistoryOp) Apply() *HistoryOp {
hop.gitop(
"commit",
"--author='"+hop.name+" <"+hop.email+">'",
"--message="+hop.userMsg,
)
return hop
}

// WithMsg sets what message will be used for the future commit. If user message exceeds one line, it is stripped down.
func (hop *HistoryOp) WithMsg(userMsg string) *HistoryOp {
for _, ch := range userMsg {
if ch == '\r' || ch == '\n' {
break
}
hop.userMsg += string(ch)
}
return hop
}

// WithSignature sets a signature for the future commit. You need to pass a username only, the rest is upon us (including email and time).
func (hop *HistoryOp) WithSignature(username string) *HistoryOp {
hop.name = username
hop.email = username + "@mycorrhiza" // A fake email, why not
return hop
}
Loading

0 comments on commit 79868ce

Please sign in to comment.