package gemtext import "bytes" // ParseLine parses a single line (including the trailing \n) into a gemtext.Line. func ParseLine(line []byte) Line { if len(line) == 0 { return nil } switch line[0] { case '=': if len(line) == 1 { break } if line[1] == '>' { return parseLinkLine(line, LineTypeLink) } if line[1] == ':' { return parseLinkLine(line, LineTypePrompt) } case '`': if len(line) < 3 || line[1] != '`' || line[2] != '`' { break } return parsePreformatToggleLine(line) case '#': level := 1 if len(line) > 1 && line[1] == '#' { level += 1 if len(line) > 2 && line[2] == '#' { level += 1 } } return parseHeadingLine(level, line) case '*': if len(line) == 1 || line[1] != ' ' { break } return parseListItemLine(line) case '>': return parseQuoteLine(line) } return TextLine{raw: line} } func parseLinkLine(raw []byte, typ LineType) LinkLine { line := LinkLine{raw: raw, typ: typ} // move past =>[] raw = bytes.TrimLeft(raw[2:], " \t") // find the next space or tab spIdx := bytes.IndexByte(raw, ' ') tbIdx := bytes.IndexByte(raw, '\t') idx := spIdx if idx == -1 { idx = tbIdx } if tbIdx >= 0 && tbIdx < idx { idx = tbIdx } if idx < 0 { line.url = bytes.TrimRight(raw, "\r\n") return line } line.url = raw[:idx] raw = raw[idx+1:] label := bytes.TrimRight(bytes.TrimLeft(raw, " \t"), "\r\n") if len(label) > 0 { line.label = label } return line } func parsePreformatToggleLine(raw []byte) PreformatToggleLine { line := PreformatToggleLine{raw: raw} raw = bytes.TrimRight(raw[3:], "\r\n") if len(raw) > 0 { line.altText = raw } return line } func parseHeadingLine(level int, raw []byte) HeadingLine { return HeadingLine{ raw: raw, lineType: LineTypeHeading1 - 1 + LineType(level), body: bytes.TrimRight(bytes.TrimLeft(raw[level:], " \t"), "\r\n"), } } func parseListItemLine(raw []byte) ListItemLine { return ListItemLine{ raw: raw, body: bytes.TrimRight(raw[2:], "\r\n"), } } func parseQuoteLine(raw []byte) QuoteLine { return QuoteLine{ raw: raw, body: bytes.TrimRight(raw[1:], "\r\n"), } }