bisync: auto-generate rc help docs

This adds a go generate ./cmd/bisync command to autogenerate the bisync rc docs,
including the list of params.
This commit is contained in:
nielash
2026-03-03 16:13:00 -05:00
parent b864c4f9c9
commit 72c561d209
7 changed files with 178 additions and 57 deletions
+1
View File
@@ -146,6 +146,7 @@ MANUAL.txt: MANUAL.md
commanddocs: rclone commanddocs: rclone
go generate ./lib/transform go generate ./lib/transform
go generate ./cmd/bisync
-@rmdir -p '$$HOME/.config/rclone' -@rmdir -p '$$HOME/.config/rclone'
XDG_CACHE_HOME="" XDG_CONFIG_HOME="" HOME="\$$HOME" USER="\$$USER" rclone gendocs --config=/notfound docs/content/ XDG_CACHE_HOME="" XDG_CONFIG_HOME="" HOME="\$$HOME" USER="\$$USER" rclone gendocs --config=/notfound docs/content/
@[ ! -e '$$HOME' ] || (echo 'Error: created unwanted directory named $$HOME' && exit 1) @[ ! -e '$$HOME' ] || (echo 'Error: created unwanted directory named $$HOME' && exit 1)
+4 -3
View File
@@ -127,14 +127,14 @@ func init() {
// and the Command line syntax section of docs/content/bisync.md (it doesn't update automatically) // and the Command line syntax section of docs/content/bisync.md (it doesn't update automatically)
flags.BoolVarP(cmdFlags, &Opt.Resync, "resync", "1", Opt.Resync, "Performs the resync run. Equivalent to --resync-mode path1. Consider using --verbose or --dry-run first.", "") flags.BoolVarP(cmdFlags, &Opt.Resync, "resync", "1", Opt.Resync, "Performs the resync run. Equivalent to --resync-mode path1. Consider using --verbose or --dry-run first.", "")
flags.FVarP(cmdFlags, &Opt.ResyncMode, "resync-mode", "", "During resync, prefer the version that is: path1, path2, newer, older, larger, smaller (default: path1 if --resync, otherwise none for no resync.)", "") flags.FVarP(cmdFlags, &Opt.ResyncMode, "resync-mode", "", "During resync, prefer the version that is: path1, path2, newer, older, larger, smaller (default: path1 if --resync, otherwise none for no resync.)", "")
flags.BoolVarP(cmdFlags, &Opt.CheckAccess, "check-access", "", Opt.CheckAccess, makeHelp("Ensure expected {CHECKFILE} files are found on both Path1 and Path2 filesystems, else abort."), "") flags.BoolVarP(cmdFlags, &Opt.CheckAccess, "check-access", "", Opt.CheckAccess, MakeHelp("Ensure expected {CHECKFILE} files are found on both Path1 and Path2 filesystems, else abort."), "")
flags.StringVarP(cmdFlags, &Opt.CheckFilename, "check-filename", "", Opt.CheckFilename, makeHelp("Filename for --check-access (default: {CHECKFILE})"), "") flags.StringVarP(cmdFlags, &Opt.CheckFilename, "check-filename", "", Opt.CheckFilename, MakeHelp("Filename for --check-access (default: {CHECKFILE})"), "")
flags.BoolVarP(cmdFlags, &Opt.Force, "force", "", Opt.Force, "Bypass --max-delete safety check and run the sync. Consider using with --verbose", "") flags.BoolVarP(cmdFlags, &Opt.Force, "force", "", Opt.Force, "Bypass --max-delete safety check and run the sync. Consider using with --verbose", "")
flags.FVarP(cmdFlags, &Opt.CheckSync, "check-sync", "", "Controls comparison of final listings: true|false|only (default: true)", "") flags.FVarP(cmdFlags, &Opt.CheckSync, "check-sync", "", "Controls comparison of final listings: true|false|only (default: true)", "")
flags.BoolVarP(cmdFlags, &Opt.CreateEmptySrcDirs, "create-empty-src-dirs", "", Opt.CreateEmptySrcDirs, "Sync creation and deletion of empty directories. (Not compatible with --remove-empty-dirs)", "") flags.BoolVarP(cmdFlags, &Opt.CreateEmptySrcDirs, "create-empty-src-dirs", "", Opt.CreateEmptySrcDirs, "Sync creation and deletion of empty directories. (Not compatible with --remove-empty-dirs)", "")
flags.BoolVarP(cmdFlags, &Opt.RemoveEmptyDirs, "remove-empty-dirs", "", Opt.RemoveEmptyDirs, "Remove ALL empty directories at the final cleanup step.", "") flags.BoolVarP(cmdFlags, &Opt.RemoveEmptyDirs, "remove-empty-dirs", "", Opt.RemoveEmptyDirs, "Remove ALL empty directories at the final cleanup step.", "")
flags.StringVarP(cmdFlags, &Opt.FiltersFile, "filters-file", "", Opt.FiltersFile, "Read filtering patterns from a file", "") flags.StringVarP(cmdFlags, &Opt.FiltersFile, "filters-file", "", Opt.FiltersFile, "Read filtering patterns from a file", "")
flags.StringVarP(cmdFlags, &Opt.Workdir, "workdir", "", Opt.Workdir, makeHelp("Use custom working dir - useful for testing. (default: {WORKDIR})"), "") flags.StringVarP(cmdFlags, &Opt.Workdir, "workdir", "", Opt.Workdir, MakeHelp("Use custom working dir - useful for testing. (default: {WORKDIR})"), "")
flags.StringVarP(cmdFlags, &Opt.BackupDir1, "backup-dir1", "", Opt.BackupDir1, "--backup-dir for Path1. Must be a non-overlapping path on the same remote.", "") flags.StringVarP(cmdFlags, &Opt.BackupDir1, "backup-dir1", "", Opt.BackupDir1, "--backup-dir for Path1. Must be a non-overlapping path on the same remote.", "")
flags.StringVarP(cmdFlags, &Opt.BackupDir2, "backup-dir2", "", Opt.BackupDir2, "--backup-dir for Path2. Must be a non-overlapping path on the same remote.", "") flags.StringVarP(cmdFlags, &Opt.BackupDir2, "backup-dir2", "", Opt.BackupDir2, "--backup-dir for Path2. Must be a non-overlapping path on the same remote.", "")
flags.StringVarP(cmdFlags, &Opt.DebugName, "debugname", "", Opt.DebugName, "Debug by tracking one file at various points throughout a bisync run (when -v or -vv)", "") flags.StringVarP(cmdFlags, &Opt.DebugName, "debugname", "", Opt.DebugName, "Debug by tracking one file at various points throughout a bisync run (when -v or -vv)", "")
@@ -153,6 +153,7 @@ func init() {
flags.StringVarP(cmdFlags, &Opt.ConflictSuffixFlag, "conflict-suffix", "", Opt.ConflictSuffixFlag, "Suffix to use when renaming a --conflict-loser. Can be either one string or two comma-separated strings to assign different suffixes to Path1/Path2. (default: 'conflict')", "") flags.StringVarP(cmdFlags, &Opt.ConflictSuffixFlag, "conflict-suffix", "", Opt.ConflictSuffixFlag, "Suffix to use when renaming a --conflict-loser. Can be either one string or two comma-separated strings to assign different suffixes to Path1/Path2. (default: 'conflict')", "")
_ = cmdFlags.MarkHidden("debugname") _ = cmdFlags.MarkHidden("debugname")
_ = cmdFlags.MarkHidden("localtime") _ = cmdFlags.MarkHidden("localtime")
addRC()
} }
// bisync command definition // bisync command definition
+65 -53
View File
@@ -1,65 +1,77 @@
package bisync //go:build none
// Create the help text for the rc
//
// Run with go generate ./cmd/bisync (defined in rc.go)
package main
import ( import (
"strconv" "fmt"
"os"
"strings" "strings"
"github.com/muesli/reflow/wordwrap"
"github.com/rclone/rclone/cmd"
"github.com/rclone/rclone/cmd/bisync"
"github.com/rclone/rclone/fs"
"github.com/spf13/pflag"
"golang.org/x/text/cases"
"golang.org/x/text/language"
) )
func makeHelp(help string) string { // Output the help to stdout
replacer := strings.NewReplacer( func main() {
"|", "`", out := os.Stdout
"{MAXDELETE}", strconv.Itoa(DefaultMaxDelete), if len(os.Args) > 1 {
"{CHECKFILE}", DefaultCheckFilename, var err error
// "{WORKDIR}", DefaultWorkdir, out, err = os.Create(os.Args[1])
) if err != nil {
return replacer.Replace(help) fs.Fatalf(nil, "Open output failed: %v", err)
}
defer out.Close()
}
fmt.Fprintf(out, "<!--- Docs generated by help.go - use go generate to rebuild - DO NOT EDIT --->\n\n")
fmt.Fprint(out, RcHelp())
} }
var shortHelp = `Perform bidirectional synchronization between two paths.` // RcHelp returns the rc help
func RcHelp() string {
var rcHelp = makeHelp(`This takes the following parameters return wordwrap.String(bisync.MakeHelp(`This takes the following parameters:
- path1 - a remote directory string e.g. |drive:path1|
- path2 - a remote directory string e.g. |drive:path2|
- dryRun - dry-run mode
- resync - performs the resync run
- checkAccess - abort if {CHECKFILE} files are not found on both filesystems
- checkFilename - file name for checkAccess (default: {CHECKFILE})
- maxDelete - abort sync if percentage of deleted files is above
this threshold (default: {MAXDELETE})
- force - Bypass maxDelete safety check and run the sync
- checkSync - |true| by default, |false| disables comparison of final listings,
|only| will skip sync, only compare listings from the last run
- createEmptySrcDirs - Sync creation and deletion of empty directories.
(Not compatible with --remove-empty-dirs)
- removeEmptyDirs - remove empty directories at the final cleanup step
- filtersFile - read filtering patterns from a file
- ignoreListingChecksum - Do not use checksums for listings
- resilient - Allow future runs to retry after certain less-serious errors, instead of requiring resync.
- workdir - server directory for history files (default: |~/.cache/rclone/bisync|)
- backupdir1 - --backup-dir for Path1. Must be a non-overlapping path on the same remote.
- backupdir2 - --backup-dir for Path2. Must be a non-overlapping path on the same remote.
- noCleanup - retain working files
- path1 (required) - (string) a remote directory string e.g. ||drive:path1||
- path2 (required) - (string) a remote directory string e.g. ||drive:path2||
- dryRun - (bool) dry-run mode
`+GenerateParams()+`
See [bisync command help](https://rclone.org/commands/rclone_bisync/) See [bisync command help](https://rclone.org/commands/rclone_bisync/)
and [full bisync description](https://rclone.org/bisync/) and [full bisync description](https://rclone.org/bisync/)
for more information.`) for more information.
`), 76)
}
var longHelp = shortHelp + makeHelp(` // example: "create-empty-src-dirs" -> "createEmptySrcDirs"
func toCamel(s string) string {
split := strings.Split(s, "-")
builder := strings.Builder{}
for i, word := range split {
if i == 0 { // first word always all lowercase
builder.WriteString(strings.ToLower(word))
continue
}
builder.WriteString(cases.Title(language.AmericanEnglish).String(word))
}
return builder.String()
}
[Bisync](https://rclone.org/bisync/) provides a // GenerateParams automatically generates the param list from commandDefinition.Flags
bidirectional cloud sync solution in rclone. func GenerateParams() string {
It retains the Path1 and Path2 filesystem listings from the prior run. builder := strings.Builder{}
On each successive run it will: fn := func(flag *pflag.Flag) {
if flag.Hidden {
- list files on Path1 and Path2, and check for changes on each side. return
Changes include |New|, |Newer|, |Older|, and |Deleted| files. }
- Propagate changes on Path1 to Path2, and vice-versa. builder.WriteString(fmt.Sprintf("- %s - (%s) %s \n", toCamel(flag.Name), flag.Value.Type(), flag.Usage))
}
Bisync is considered an **advanced command**, so use with care. commandDefinition, _, _ := cmd.Root.Find([]string{"bisync"})
Make sure you have read and understood the entire [manual](https://rclone.org/bisync) commandDefinition.Flags().VisitAll(fn)
(especially the [Limitations](https://rclone.org/bisync/#limitations) section) return builder.String()
before using, or data loss can result. Questions can be asked in the }
[Rclone Forum](https://forum.rclone.org/).
See [full bisync description](https://rclone.org/bisync/) for details.`)
+41 -1
View File
@@ -1,10 +1,14 @@
//go:generate go run help.go rc.md
package bisync package bisync
import ( import (
"context" "context"
_ "embed"
"errors" "errors"
"log" "log"
"path/filepath" "path/filepath"
"strconv"
"strings"
"github.com/rclone/rclone/cmd/bisync/bilib" "github.com/rclone/rclone/cmd/bisync/bilib"
"github.com/rclone/rclone/fs" "github.com/rclone/rclone/fs"
@@ -12,7 +16,7 @@ import (
"github.com/rclone/rclone/fs/rc" "github.com/rclone/rclone/fs/rc"
) )
func init() { func addRC() {
rc.Add(rc.Call{ rc.Add(rc.Call{
Path: "sync/bisync", Path: "sync/bisync",
AuthRequired: true, AuthRequired: true,
@@ -22,6 +26,42 @@ func init() {
}) })
} }
//go:embed rc.md
var rcHelp string
var shortHelp = `Perform bidirectional synchronization between two paths.`
var longHelp = shortHelp + MakeHelp(`
[Bisync](https://rclone.org/bisync/) provides a
bidirectional cloud sync solution in rclone.
It retains the Path1 and Path2 filesystem listings from the prior run.
On each successive run it will:
- list files on Path1 and Path2, and check for changes on each side.
Changes include ||New||, ||Newer||, ||Older||, and ||Deleted|| files.
- Propagate changes on Path1 to Path2, and vice-versa.
Bisync is considered an **advanced command**, so use with care.
Make sure you have read and understood the entire [manual](https://rclone.org/bisync)
(especially the [Limitations](https://rclone.org/bisync/#limitations) section)
before using, or data loss can result. Questions can be asked in the
[Rclone Forum](https://forum.rclone.org/).
See [full bisync description](https://rclone.org/bisync/) for details.
`)
// MakeHelp replaces some dynamic variables for the help docs
func MakeHelp(help string) string {
replacer := strings.NewReplacer(
"||", "`",
"{MAXDELETE}", strconv.Itoa(DefaultMaxDelete),
"{CHECKFILE}", DefaultCheckFilename,
"{WORKDIR}", DefaultWorkdir,
)
return replacer.Replace(help)
}
func rcBisync(ctx context.Context, in rc.Params) (out rc.Params, err error) { func rcBisync(ctx context.Context, in rc.Params) (out rc.Params, err error) {
opt := &Options{} opt := &Options{}
octx, ci := fs.AddConfig(ctx) octx, ci := fs.AddConfig(ctx)
+61
View File
@@ -0,0 +1,61 @@
<!--- Docs generated by help.go - use go generate to rebuild - DO NOT EDIT --->
This takes the following parameters:
- path1 (required) - (string) a remote directory string e.g. `drive:path1`
- path2 (required) - (string) a remote directory string e.g. `drive:path2`
- dryRun - (bool) dry-run mode
- backupDir1 - (string) --backup-dir for Path1. Must be a non-overlapping path on
the same remote.
- backupDir2 - (string) --backup-dir for Path2. Must be a non-overlapping path on
the same remote.
- checkAccess - (bool) Ensure expected RCLONE_TEST files are found on both
Path1 and Path2 filesystems, else abort.
- checkFilename - (string) Filename for --check-access (default: RCLONE_TEST)
- checkSync - (string) Controls comparison of final listings: true|false|only
(default: true)
- compare - (string) Comma-separated list of bisync-specific compare options ex.
'size,modtime,checksum' (default: 'size,modtime')
- conflictLoser - (ConflictLoserAction) Action to take on the loser of a sync
conflict (when there is a winner) or on both files (when there is no
winner): , num, pathname, delete (default: num)
- conflictResolve - (string) Automatically resolve conflicts by preferring the
version that is: none, path1, path2, newer, older, larger, smaller (default:
none)
- conflictSuffix - (string) Suffix to use when renaming a --conflict-loser. Can
be either one string or two comma-separated strings to assign different
suffixes to Path1/Path2. (default: 'conflict')
- createEmptySrcDirs - (bool) Sync creation and deletion of empty directories.
(Not compatible with --remove-empty-dirs)
- downloadHash - (bool) Compute hash by downloading when otherwise
unavailable. (warning: may be slow and use lots of data!)
- filtersFile - (string) Read filtering patterns from a file
- force - (bool) Bypass --max-delete safety check and run the sync. Consider
using with --verbose
- ignoreListingChecksum - (bool) Do not use checksums for listings (add --ignore-
checksum to additionally skip post-copy checksum checks)
- maxLock - (Duration) Consider lock files older than this to be expired
(default: 0 (never expire)) (minimum: 2m)
- noCleanup - (bool) Retain working files (useful for troubleshooting and
testing).
- noSlowHash - (bool) Ignore listing checksums only on backends where they are
slow
- recover - (bool) Automatically recover from interruptions without requiring --
resync.
- removeEmptyDirs - (bool) Remove ALL empty directories at the final cleanup
step.
- resilient - (bool) Allow future runs to retry after certain less-serious
errors, instead of requiring --resync.
- resync - (bool) Performs the resync run. Equivalent to --resync-mode path1.
Consider using --verbose or --dry-run first.
- resyncMode - (string) During resync, prefer the version that is: path1,
path2, newer, older, larger, smaller (default: path1 if --resync, otherwise
none for no resync.)
- slowHashSyncOnly - (bool) Ignore slow checksums for listings and deltas, but
still consider them during sync calls.
- workdir - (string) Use custom working dir - useful for testing. (default:
~/.cache/rclone/bisync)
See [bisync command help](https://rclone.org/commands/rclone_bisync/)
and [full bisync description](https://rclone.org/bisync/)
for more information.
+1
View File
@@ -60,6 +60,7 @@ require (
github.com/minio/minio-go/v7 v7.0.98 github.com/minio/minio-go/v7 v7.0.98
github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/go-homedir v1.1.0
github.com/moby/sys/mountinfo v0.7.2 github.com/moby/sys/mountinfo v0.7.2
github.com/muesli/reflow v0.3.0
github.com/ncw/swift/v2 v2.0.5 github.com/ncw/swift/v2 v2.0.5
github.com/oracle/oci-go-sdk/v65 v65.108.2 github.com/oracle/oci-go-sdk/v65 v65.108.2
github.com/patrickmn/go-cache v2.1.0+incompatible github.com/patrickmn/go-cache v2.1.0+incompatible
+5
View File
@@ -499,6 +499,7 @@ github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stg
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.20 h1:WcT52H91ZUAwy8+HUkdM3THM6gXqXuLJi9O3rjcQQaQ= github.com/mattn/go-runewidth v0.0.20 h1:WcT52H91ZUAwy8+HUkdM3THM6gXqXuLJi9O3rjcQQaQ=
github.com/mattn/go-runewidth v0.0.20/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= github.com/mattn/go-runewidth v0.0.20/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/mholt/archives v0.1.5 h1:Fh2hl1j7VEhc6DZs2DLMgiBNChUux154a1G+2esNvzQ= github.com/mholt/archives v0.1.5 h1:Fh2hl1j7VEhc6DZs2DLMgiBNChUux154a1G+2esNvzQ=
@@ -523,6 +524,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/ncw/swift/v2 v2.0.5 h1:9o5Gsd7bInAFEqsGPcaUdsboMbqf8lnNtxqWKFT9iz8= github.com/ncw/swift/v2 v2.0.5 h1:9o5Gsd7bInAFEqsGPcaUdsboMbqf8lnNtxqWKFT9iz8=
@@ -598,6 +601,8 @@ github.com/relvacode/iso8601 v1.7.0 h1:BXy+V60stMP6cpswc+a93Mq3e65PfXCgDFfhvNNGr
github.com/relvacode/iso8601 v1.7.0/go.mod h1:FlNp+jz+TXpyRqgmM7tnzHHzBnz776kmAH2h3sZCn0I= github.com/relvacode/iso8601 v1.7.0/go.mod h1:FlNp+jz+TXpyRqgmM7tnzHHzBnz776kmAH2h3sZCn0I=
github.com/rfjakob/eme v1.2.0 h1:8dAHL+WVAw06+7DkRKnRiFp1JL3QjcJEZFqDnndUaSI= github.com/rfjakob/eme v1.2.0 h1:8dAHL+WVAw06+7DkRKnRiFp1JL3QjcJEZFqDnndUaSI=
github.com/rfjakob/eme v1.2.0/go.mod h1:cVvpasglm/G3ngEfcfT/Wt0GwhkuO32pf/poW6Nyk1k= github.com/rfjakob/eme v1.2.0/go.mod h1:cVvpasglm/G3ngEfcfT/Wt0GwhkuO32pf/poW6Nyk1k=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=