diff --git a/internal/printer/account.go b/internal/printer/account.go
index cbb6496..1ffef82 100644
--- a/internal/printer/account.go
+++ b/internal/printer/account.go
@@ -9,7 +9,6 @@ import (
"strings"
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
- "codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
)
func (p Printer) PrintAccount(account model.Account, relationship *model.AccountRelationship, preferences *model.Preferences) {
@@ -28,11 +27,11 @@ func (p Printer) PrintAccount(account model.Account, relationship *model.Account
builder.WriteString("\n" + p.fieldFormat("Statuses:"))
builder.WriteString(" " + strconv.Itoa(account.StatusCount))
builder.WriteString("\n\n" + p.headerFormat("BIOGRAPHY:"))
- builder.WriteString(utilities.WrapLines(utilities.ConvertHTMLToText(account.Note), "\n", p.maxTerminalWidth))
+ builder.WriteString(p.convertHTMLToText(account.Note, true))
builder.WriteString("\n\n" + p.headerFormat("METADATA:"))
for _, field := range account.Fields {
- builder.WriteString("\n" + p.fieldFormat(field.Name) + ": " + utilities.ConvertHTMLToText(field.Value))
+ builder.WriteString("\n" + p.fieldFormat(field.Name) + ": " + p.convertHTMLToText(field.Value, false))
}
builder.WriteString("\n\n" + p.headerFormat("ACCOUNT URL:"))
@@ -80,7 +79,7 @@ func (p Printer) accountRelationship(relationship *model.AccountRelationship) st
if relationship.PrivateNote != "" {
builder.WriteString("\n\n" + p.headerFormat("YOUR PRIVATE NOTE ABOUT THIS ACCOUNT:"))
- builder.WriteString("\n" + utilities.WrapLines(relationship.PrivateNote, "\n", p.maxTerminalWidth))
+ builder.WriteString("\n" + p.wrapLines(relationship.PrivateNote, 0))
}
return builder.String()
diff --git a/internal/utilities/html.go b/internal/printer/html.go
similarity index 87%
rename from internal/utilities/html.go
rename to internal/printer/html.go
index 54fc55c..ff6644d 100644
--- a/internal/utilities/html.go
+++ b/internal/printer/html.go
@@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: GPL-3.0-or-later
-package utilities
+package printer
import (
"io"
@@ -23,7 +23,7 @@ type htmlConvertState struct {
orderedListIndex int
}
-func ConvertHTMLToText(text string) string {
+func (p Printer) convertHTMLToText(text string, wrapLines bool) string {
var builder strings.Builder
state := htmlConvertState{
@@ -37,6 +37,10 @@ func ConvertHTMLToText(text string) string {
tt := token.Next()
switch tt {
case html.ErrorToken:
+ if wrapLines {
+ return p.wrapLines(builder.String(), 0)
+ }
+
return builder.String()
case html.TextToken:
text := token.Token().Data
@@ -66,7 +70,7 @@ func processTagToken(state *htmlConvertState, writer io.StringWriter, tag string
case "
":
switch state.htmlListType {
case htmlUnorderedList:
- _, _ = writer.WriteString("• ")
+ _, _ = writer.WriteString(symbolBullet + " ")
case htmlOrderedList:
_, _ = writer.WriteString(strconv.Itoa(state.orderedListIndex) + ". ")
state.orderedListIndex++
diff --git a/internal/printer/instance.go b/internal/printer/instance.go
index ce1b14e..a19f53b 100644
--- a/internal/printer/instance.go
+++ b/internal/printer/instance.go
@@ -8,7 +8,6 @@ import (
"strings"
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
- "codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
)
func (p Printer) PrintInstance(instance model.InstanceV2) {
@@ -18,13 +17,13 @@ func (p Printer) PrintInstance(instance model.InstanceV2) {
builder.WriteString("\n" + instance.Title)
builder.WriteString("\n\n" + p.headerFormat("INSTANCE DESCRIPTION:"))
- builder.WriteString("\n" + utilities.WrapLines(instance.DescriptionText, "\n", p.maxTerminalWidth))
+ builder.WriteString("\n" + p.wrapLines(instance.DescriptionText, 0))
builder.WriteString("\n\n" + p.headerFormat("DOMAIN:"))
builder.WriteString("\n" + instance.Domain)
builder.WriteString("\n\n" + p.headerFormat("TERMS AND CONDITIONS:"))
- builder.WriteString("\n" + utilities.WrapLines(instance.TermsText, "\n ", p.maxTerminalWidth))
+ builder.WriteString("\n" + p.wrapLines(instance.TermsText, 2))
builder.WriteString("\n\n" + p.headerFormat("VERSION:"))
builder.WriteString("\nRunning GoToSocial " + instance.Version)
diff --git a/internal/printer/poll.go b/internal/printer/poll.go
index cd24da1..8a8a34d 100644
--- a/internal/printer/poll.go
+++ b/internal/printer/poll.go
@@ -71,8 +71,8 @@ func (p Printer) pollOptions(poll model.Poll) string {
}
func (p Printer) pollMeter(votage float64) string {
- numVoteBlocks := int(math.Floor(float64(p.maxTerminalWidth) * votage))
- numBackgroundBlocks := p.maxTerminalWidth - numVoteBlocks
+ numVoteBlocks := int(math.Floor(float64(p.lineWrapCharacterLimit) * votage))
+ numBackgroundBlocks := p.lineWrapCharacterLimit - numVoteBlocks
voteBlockColor := p.theme.boldgreen
backgroundBlockColor := p.theme.grey
diff --git a/internal/printer/printer.go b/internal/printer/printer.go
index a63fc9f..9b1f641 100644
--- a/internal/printer/printer.go
+++ b/internal/printer/printer.go
@@ -43,17 +43,17 @@ type theme struct {
}
type Printer struct {
- theme theme
- noColor bool
- maxTerminalWidth int
- pager string
- statusSeparator string
+ theme theme
+ noColor bool
+ lineWrapCharacterLimit int
+ pager string
+ statusSeparator string
}
func NewPrinter(
noColor bool,
pager string,
- maxTerminalWidth int,
+ lineWrapCharacterLimit int,
) *Printer {
theme := theme{
reset: "\033[0m",
@@ -68,16 +68,16 @@ func NewPrinter(
boldyellow: "\033[33;1m",
}
- if maxTerminalWidth < minTerminalWidth {
- maxTerminalWidth = minTerminalWidth
+ if lineWrapCharacterLimit < minTerminalWidth {
+ lineWrapCharacterLimit = minTerminalWidth
}
return &Printer{
- theme: theme,
- noColor: noColor,
- maxTerminalWidth: maxTerminalWidth,
- pager: pager,
- statusSeparator: strings.Repeat("\u2501", maxTerminalWidth),
+ theme: theme,
+ noColor: noColor,
+ lineWrapCharacterLimit: lineWrapCharacterLimit,
+ pager: pager,
+ statusSeparator: strings.Repeat("\u2501", lineWrapCharacterLimit),
}
}
diff --git a/internal/printer/status.go b/internal/printer/status.go
index 5bc40db..af37ea9 100644
--- a/internal/printer/status.go
+++ b/internal/printer/status.go
@@ -9,7 +9,6 @@ import (
"strings"
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
- "codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
)
func (p Printer) PrintStatus(status model.Status) {
@@ -24,7 +23,7 @@ func (p Printer) PrintStatus(status model.Status) {
// The content of the status.
builder.WriteString("\n\n" + p.headerFormat("CONTENT:"))
- builder.WriteString(utilities.WrapLines(utilities.ConvertHTMLToText(status.Content), "\n", p.maxTerminalWidth))
+ builder.WriteString(p.convertHTMLToText(status.Content, true))
// Details of media attachments (if any).
if len(status.MediaAttachments) > 0 {
@@ -93,7 +92,10 @@ func (p Printer) PrintStatusList(list model.StatusList) {
if status.Reblog != nil {
builder.WriteString(
- "\n" + utilities.WrapLines("reposted this status from "+p.fullDisplayNameFormat(status.Reblog.Account.DisplayName, status.Reblog.Account.Acct), "\n", p.maxTerminalWidth),
+ "\n" + p.wrapLines(
+ "reposted this status from "+p.fullDisplayNameFormat(status.Reblog.Account.DisplayName, status.Reblog.Account.Acct),
+ 0,
+ ),
)
statusID = status.Reblog.ID
@@ -103,22 +105,25 @@ func (p Printer) PrintStatusList(list model.StatusList) {
mediaAttachments = status.Reblog.MediaAttachments
}
- builder.WriteString("\n" + utilities.WrapLines(utilities.ConvertHTMLToText(content), "\n", p.maxTerminalWidth))
+ builder.WriteString("\n" + p.convertHTMLToText(content, true))
if poll != nil {
builder.WriteString(p.pollOptions(*poll))
}
for _, media := range mediaAttachments {
- builder.WriteString("\n\n" + symbolImage + " Media attachment: " + media.ID)
- builder.WriteString("\n Media type: " + media.Type + "\n ")
+ builder.WriteString("\n\n" + symbolImage + " " + p.fieldFormat("Media attachment: ") + media.ID)
+ builder.WriteString("\n " + p.fieldFormat("Media type: ") + media.Type + "\n")
- description := media.Description
- if description == "" {
- description = noMediaDescription
+ description := " " + p.fieldFormat("Description: ")
+
+ if media.Description == "" {
+ description += noMediaDescription
+ } else {
+ description += media.Description
}
- builder.WriteString(utilities.WrapLines(description, "\n ", p.maxTerminalWidth-3))
+ builder.WriteString(p.wrapLines(description, 2))
}
boosted := symbolBoosted
diff --git a/internal/printer/wrap.go b/internal/printer/wrap.go
new file mode 100644
index 0000000..bae7a58
--- /dev/null
+++ b/internal/printer/wrap.go
@@ -0,0 +1,96 @@
+// SPDX-FileCopyrightText: 2024 Dan Anglin
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package printer
+
+import (
+ "regexp"
+ "strings"
+ "unicode"
+)
+
+type extraIndentConditiion struct {
+ pattern *regexp.Regexp
+ indent string
+}
+
+func (p Printer) wrapLines(text string, nIndent int) string {
+ if nIndent >= p.lineWrapCharacterLimit {
+ nIndent = 0
+ }
+
+ separator := "\n" + strings.Repeat(" ", nIndent)
+
+ lines := strings.Split(text, "\n")
+
+ if len(lines) == 1 {
+ return wrapLine(lines[0], separator, p.lineWrapCharacterLimit-nIndent)
+ }
+
+ var builder strings.Builder
+
+ extraIndentConditions := []extraIndentConditiion{
+ {
+ pattern: regexp.MustCompile(`^[-*` + symbolBullet + `]\s.*$`),
+ indent: " ",
+ },
+ {
+ pattern: regexp.MustCompile(`^[0-9]{1}\.\s.*$`),
+ indent: " ",
+ },
+ {
+ pattern: regexp.MustCompile(`^[0-9]{2}\.\s.*$`),
+ indent: " ",
+ },
+ }
+
+ for ind, line := range lines {
+ builder.WriteString(wrapLine(line, separator+extraIndent(line, extraIndentConditions), p.lineWrapCharacterLimit-nIndent))
+
+ if ind < len(lines)-1 {
+ builder.WriteString(separator)
+ }
+ }
+
+ return builder.String()
+}
+
+func wrapLine(line, separator string, charLimit int) string {
+ if len(line) <= charLimit {
+ return line
+ }
+
+ leftcursor, rightcursor := 0, 0
+
+ var builder strings.Builder
+
+ for rightcursor < (len(line) - charLimit) {
+ rightcursor += (charLimit - 1)
+
+ for (rightcursor > leftcursor) && !unicode.IsSpace(rune(line[rightcursor-1])) {
+ rightcursor--
+ }
+
+ if rightcursor == leftcursor {
+ rightcursor = leftcursor + charLimit
+ }
+
+ builder.WriteString(line[leftcursor:rightcursor] + separator)
+ leftcursor = rightcursor
+ }
+
+ builder.WriteString(line[rightcursor:])
+
+ return builder.String()
+}
+
+func extraIndent(line string, conditions []extraIndentConditiion) string {
+ for ind := range conditions {
+ if conditions[ind].pattern.MatchString(line) {
+ return conditions[ind].indent
+ }
+ }
+
+ return ""
+}
diff --git a/internal/utilities/wrap.go b/internal/utilities/wrap.go
deleted file mode 100644
index 55a4c40..0000000
--- a/internal/utilities/wrap.go
+++ /dev/null
@@ -1,59 +0,0 @@
-// SPDX-FileCopyrightText: 2024 Dan Anglin
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-package utilities
-
-import (
- "strings"
- "unicode"
-)
-
-func WrapLines(text, separator string, charLimit int) string {
- lines := strings.Split(text, "\n")
-
- if len(lines) == 1 {
- return wrapLine(lines[0], separator, charLimit)
- }
-
- var builder strings.Builder
-
- for i, line := range lines {
- builder.WriteString(wrapLine(line, separator, charLimit))
-
- if i < len(lines)-1 {
- builder.WriteString(separator)
- }
- }
-
- return builder.String()
-}
-
-func wrapLine(line, separator string, charLimit int) string {
- if len(line) <= charLimit {
- return line
- }
-
- leftcursor, rightcursor := 0, 0
-
- var builder strings.Builder
-
- for rightcursor < (len(line) - charLimit) {
- rightcursor += (charLimit - 1)
-
- for (rightcursor > leftcursor) && !unicode.IsSpace(rune(line[rightcursor-1])) {
- rightcursor--
- }
-
- if rightcursor == leftcursor {
- rightcursor = leftcursor + charLimit
- }
-
- builder.WriteString(line[leftcursor:rightcursor] + separator)
- leftcursor = rightcursor
- }
-
- builder.WriteString(line[rightcursor:])
-
- return builder.String()
-}