This repository has been archived on 2020-11-14. You can view files and clone it, but cannot push or open issues or pull requests.
go-gemtext/parser.go

101 lines
3.5 KiB
Go

// Package gemtext provides a variety of tools for processing gemtext files.
package gemtext
import (
"strings"
"fmt"
"path/filepath"
)
type ObjectType int64
const (
TEXT ObjectType = iota
LINK
PREFORMATTED_TOGGLE
PREFORMATTED_TEXT
HEADING
LIST
LISTMARKER // Marks the start/end of a list. Not a real gemtext object but useful for conversion.
QUOTE
)
// type GemtextObject represents a gemtext object.
type GemtextObject struct {
Type ObjectType
Text string // Contains the text of the element. For a link this is the label.
Literal string // Contains the line as it exists in the file
Path string // Populated if object is a link
Level int // Populated if object is a heading
}
// type GemtextPage represents a gemtext page.
type GemtextPage []GemtextObject
// addPrefix adds the prefix "pref" to a path if it is absolute
// this is to allow protocol-independent absolute links
// in input files.
func addPrefix(pref string, link string) string {
if filepath.IsAbs(link) {
return filepath.Join(pref, link)
} else {
return link
}
}
// ParseLink takes a gemini link as a string and returns a GemtextObject and an error.
func ParseLink(l string) (GemtextObject, error) {
if !strings.HasPrefix(l, "=>") {
return GemtextObject{}, fmt.Errorf("Not a gemtext link!")
} else {
f := strings.Fields(l[2:])
link := GemtextObject{Type: LINK, Literal: l}
link.Path = f[0]
link.Text = strings.TrimSpace(l[3+len(f[0]):])
return link, nil
}
}
// ParseHeading parses a gemtext heading, returns a HEADING GemtextObject if it is between levels 1-3 and TEXT otherwise.
func ParseHeading(l string) (GemtextObject, error) {
switch {
case strings.HasPrefix(l, "####"): // Fake headings
return GemtextObject{Type: TEXT, Text:l, Literal: l}, nil
case strings.HasPrefix(l, "###"):
return GemtextObject{Type: HEADING, Level: 3, Text: strings.TrimSpace(l[3:]), Literal: l},nil
case strings.HasPrefix(l, "##"):
return GemtextObject{Type: HEADING, Level: 2, Text: strings.TrimSpace(l[2:]), Literal: l},nil
case strings.HasPrefix(l, "#"):
return GemtextObject{Type: HEADING, Level: 1, Text: strings.TrimSpace(l[1:]), Literal: l},nil
}
return GemtextObject{Type: TEXT, Text:l, Literal: l}, nil
}
// ParseLine parses a gemtext line, and returns a GemtextObject representing it.
// "isPreformatted" takes a true if the line is in a preformatted block, else otherwise.
func ParseLine(line string, isPreformatted bool) (GemtextObject, error) {
if !isPreformatted {
switch {
case strings.HasPrefix(line, "=>"):
return ParseLink(line)
case strings.HasPrefix(line, "```"):
return GemtextObject{Type: PREFORMATTED_TOGGLE, Literal: line, Text: line[3:]}, nil
case strings.HasPrefix(line, "#"):
return ParseHeading(line)
case strings.HasPrefix(line, ">"):
return GemtextObject{Type: QUOTE, Text: strings.TrimSpace(line[1:]), Literal: line}, nil
case strings.HasPrefix(line, "* "): // Bullets require a space, per the spec
return GemtextObject{Type: LIST, Text: line[2:], Literal: line}, nil
}
return GemtextObject{Type: TEXT, Text: line, Literal: line}, nil
} else {
return GemtextObject{Type: PREFORMATTED_TEXT, Text: line, Literal: line}, nil
}
}
// ParsePage takes a string containing the contents of a gemtext page and returns a GemtextPage.
func ParsePage(p string) {}