From db7d9652c9160ef4c2a12a0a71da325cbb994c72 Mon Sep 17 00:00:00 2001 From: Dominic Ricottone Date: Tue, 18 Aug 2020 23:36:16 -0400 Subject: [PATCH] Initial commit --- Makefile | 6 ++++ README.md | 59 ++++++++++++++++++++++++++++++++ common/textwrap.go | 65 +++++++++++++++++++++++++++++++++++ go.mod | 3 ++ main.go | 84 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 217 insertions(+) create mode 100644 Makefile create mode 100644 README.md create mode 100644 common/textwrap.go create mode 100644 go.mod create mode 100644 main.go diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..77b7cf0 --- /dev/null +++ b/Makefile @@ -0,0 +1,6 @@ +clean: + rm -rf textwrap + +build: + go build + diff --git a/README.md b/README.md new file mode 100644 index 0000000..1f686bb --- /dev/null +++ b/README.md @@ -0,0 +1,59 @@ +# textwrap + +I have a variety of projects that: + + * **need** a textwrapping library, even if it is overly simple + * **could benefit from** a thorough textwrapping library + * I want to work on **instead of** a thorough textwrapping library + +This package serves as: + + * a handy utility + * a simple library that works for now + * a distribution mechanism for making incremental updates from a simple + library to a thorough one that all of my projects can benefit from + + +## Development Status + +Very alpha + + +## Usage (utility) + +```sh +$ echo '12345' | textwrap -length 2 +12 +34 +5 +``` + + +## Usage (library) + +```go +import ( + "fmt" + "regexp" + textwrap "git.dominic-ricottone.com/textwrap/common" +) + +func main() { + a := []string{ + "> hello!", + "> this is quoted", + "hi there!", + "this is not", + } + for _, line := range textwrap.WrapArray(a,10) { + fmt.Println(line) + } +} +``` + + +## Licensing + +GPLv3 + + diff --git a/common/textwrap.go b/common/textwrap.go new file mode 100644 index 0000000..56aee78 --- /dev/null +++ b/common/textwrap.go @@ -0,0 +1,65 @@ +package common + +import ( + "strings" + "regexp" +) + +func MakeBreakline(length int) string { + return strings.Repeat("-", length) +} + +func MakeWrappedLine(line string, length int, re_quote *regexp.Regexp) []string { + offset := 0 + prefix := "" + + quote := re_quote.FindString(line) + len_quote := len(quote) + if len_quote != 0 && len_quote < length { + offset = len_quote + prefix = quote + } + + buffer := []string{prefix} + line_number := 0 + for index, rune := range line[offset:] { + buffer[line_number] += string(rune) + if (index + 1) % (length - offset) == 0 { + buffer = append(buffer, prefix) + line_number += 1 + } + } + + return buffer +} + +func WrapArray(lines []string, length int) ([]string, error) { + // Compile regular expressions + re_quote, err := regexp.Compile("^([> ]*)") + if err != nil { + return nil, err + } + re_break, err := regexp.Compile("^(?:-{5,}|={5,})$") + if err != nil { + return nil, err + } + + wrapped := []string{} + + for i := 0; i < len(lines); i++ { + line := strings.TrimSpace(lines[i]) + + if len(line) > length { + if re_break.MatchString(line) { + wrapped = append(wrapped, MakeBreakline(length)) + } else { + wrapped = append(wrapped, MakeWrappedLine(line, length, re_quote)...) + } + } else { + wrapped = append(wrapped, line) + } + } + + return wrapped, nil +} + diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..44cdbdb --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module git.dominic-ricottone.com/textwrap + +go 1.15 diff --git a/main.go b/main.go new file mode 100644 index 0000000..f77a72d --- /dev/null +++ b/main.go @@ -0,0 +1,84 @@ +package main + +import ( + "fmt" + "os" + "strings" + "io" + "bufio" + "regexp" + "flag" + + "git.dominic-ricottone.com/textwrap/common" +) + +const LINE_LENGTH = 80 + +func wrap_stream(reader io.Reader, length int) { + // Create scanner from reader + input := bufio.NewScanner(reader) + + // Compile regular expressions + re_quote, err := regexp.Compile("^([> ]*)") + if err != nil { + fmt.Printf("internal error - %v\n", err) + os.Exit(1) + } + re_break, err := regexp.Compile("^(?:-{5,}|={5,})$") + if err != nil { + fmt.Printf("internal error - %v\n", err) + os.Exit(1) + } + + // Scan line by line + for input.Scan() { + line := input.Text() + line = strings.TrimSpace(line) + + if len(line) > length { + if re_break.MatchString(line) { + fmt.Printf("%s\n", common.MakeBreakline(length)) + } else { + for _, wrapped := range common.MakeWrappedLine(line, length, re_quote) { + fmt.Printf("%s\n", wrapped) + } + } + } else { + fmt.Printf("%s\n", line) + } + } + + // Check for scanner errors + if err = input.Err(); err != nil { + fmt.Printf("internal error - %v\n", err) + os.Exit(1) + } +} + +func wrap_file(filename string) { + // Check file + file, err := os.Open(filename) + if err != nil { + fmt.Printf("cannot read file '%s'\n", filename) + os.Exit(1) + } + defer file.Close() + + wrap_stream(file, LINE_LENGTH) +} + +func main() { + // Check STDIN + _, err := os.Stdin.Stat() + if err != nil { + fmt.Println("cannot read input") + os.Exit(1) + } + + // Look for arguments + var length = flag.Int("length", LINE_LENGTH, "maximum length of lines") + flag.Parse() + + wrap_stream(os.Stdin, *length) +} + -- 2.45.2