Compare commits

...

1 Commits

Author SHA1 Message Date
brent saner 42c0e48081 v1.16.10
ADDED:
* stringsx.RemoveWhitespace, stringsx.StripWhitespace
2026-07-06 16:21:07 -04:00
5 changed files with 174 additions and 12 deletions
+73 -10
View File
@@ -1,13 +1,13 @@
package stringsx
import (
`bytes`
`errors`
`fmt`
`io`
`slices`
`strings`
`unicode`
"bytes"
"errors"
"fmt"
"io"
"slices"
"strings"
"unicode"
)
/*
@@ -17,6 +17,7 @@ It is more strict than [HasBoundary] (which only requires
that s has sym at the beginning OR the end.)
Examples:
HasBookend("|foo|", "|") → true
HasBookend("|foo", "|") → false
HasBookend("foo|", "|") → false
@@ -38,6 +39,7 @@ func HasBookend(s, sym string) (bounded bool) {
HasBoundary returns true if string s starts OR ends with symbol sym.
Examples:
HasBoundary("|foo|", "|") → true
HasBoundary("|foo", "|") → true
HasBoundary("foo|", "|") → true
@@ -51,9 +53,9 @@ If sym is empty, HasBoundary will *always* return true.
If you instead require string s to be *enclosed* by sym, see [HasBookend].
*/
func HasBoundary(s, sym string) (bounded bool) {
bounded = strings.HasPrefix(s, sym) || strings.HasSuffix(s, sym)
return
}
@@ -453,6 +455,37 @@ func Redact(s, maskStr string, leading, trailing uint, newlines bool) (redacted
return
}
/*
RemoveWhitespace removes all leading, trailing, and INNER whitespace
from unicode string `s`.
This is done by allocating a strings.Builder with len(s).
This may consume more memory than needed if s is mostly whitespace;
in that case, it is better to use [StripWhitespace].
*/
func RemoveWhitespace(s string) (removed string) {
// https://stackoverflow.com/questions/32081808/strip-all-whitespace-from-a-string/32081891#32081891
var c rune
var sb strings.Builder
if s == "" {
return
}
sb.Grow(len(s))
for _, c = range s {
if !unicode.IsSpace(c) {
sb.WriteRune(c)
}
}
removed = sb.String()
return
}
// Reverse reverses string s. (It's absolutely insane that this isn't in stdlib.)
func Reverse(s string) (revS string) {
@@ -465,6 +498,35 @@ func Reverse(s string) (revS string) {
return
}
/*
StripWhitespace removes all leading, trailing, and INNER whitespace
from unicode string `s`.
This is done by mapping each rune in `s`.
This may use far more allocations than necessary if `s` is mostly NON-whitespace;
in that case, it is better to use [RemoveWhitespace].
*/
func StripWhitespace(s string) (stripped string) {
// https://stackoverflow.com/questions/32081808/strip-all-whitespace-from-a-string/32081891#32081891
if s == "" {
return
}
stripped = strings.Map(
func(c rune) rune {
if unicode.IsSpace(c) {
return -1
}
return c
},
s,
)
return
}
/*
TrimLines is like [strings.TrimSpace] but operates on *each line* of s.
It is *NIX-newline (`\n`) vs. Windows-newline (`\r\n`) agnostic.
@@ -500,7 +562,8 @@ func TrimLines(s string, left, right bool) (trimmed string) {
} else if right {
sl = TrimSpaceRight(sl)
}
sb.WriteString(sl + nl)
sb.WriteString(sl)
sb.WriteString(nl)
}
trimmed = sb.String()
@@ -0,0 +1,99 @@
package main
import (
"embed"
"io"
"io/fs"
// "fmt"
"log"
// "os"
// "path/filepath"
"text/template"
// "github.com/davecgh/go-spew/spew"
)
var (
//go:embed "tplfiles"
tplfiles embed.FS
tpl *template.Template = template.New("")
tw *TplWalker = new(TplWalker)
)
type (
TplWalker struct {
tpl *template.Template
fs fs.FS
}
)
func (t *TplWalker) walkFunc(fpath string, d fs.DirEntry, inErr error) (err error) {
var b []byte
var f fs.File
if inErr != nil {
err = inErr
return
}
if d.IsDir() {
return
}
if f, err = t.fs.Open(fpath); err != nil {
return
}
defer func() {
var ok bool
var c io.Closer
if f != nil {
if c, ok = f.(io.Closer); ok {
if err = c.Close(); err != nil {
return
}
}
}
}()
if b, err = io.ReadAll(f); err != nil {
return
}
if _, err = t.tpl.New(fpath).Parse(string(b)); err != nil {
return
}
return
}
func (t *TplWalker) Walk() (err error) {
if err = fs.WalkDir(t.fs, ".", t.walkFunc); err != nil {
return
}
return
}
func init() {
var err error
tw.tpl = tpl
tw.fs = tplfiles
if err = tw.Walk(); err != nil {
log.Panicln(err)
}
}
func main() {
var idx int
var sub *template.Template
log.Printf("Root template name is %q", tpl.Name())
for idx, sub = range tpl.Templates() {
log.Printf("Sub template %d: %q", idx, sub.Name())
}
}
+1 -1
View File
@@ -636,7 +636,7 @@ pre.rouge .gs {
<div class="details">
<span id="author" class="author">Brent Saner</span><br>
<span id="email" class="email"><a href="mailto:bts@square-r00t.net">bts@square-r00t.net</a></span><br>
<span id="revdate">Last rendered 2026-06-22 18:51:24 -0400</span>
<span id="revdate">Last rendered 2026-07-06 16:21:08 -0400</span>
</div>
<div id="toc" class="toc2">
<div id="toctitle">Table of Contents</div>
+1 -1
View File
@@ -6,7 +6,7 @@
<span id="author" class="author">Brent Saner</span>
<span id="email" class="email"><bts@square-r00t.net></span>
<span id="revdate">Last rendered 2026-06-22 18:51:29 -0400</span>
<span id="revdate">Last rendered 2026-07-06 16:21:13 -0400</span>
</div>