commit 9c2ec2374267b2315493b4ead6554c59ae7aa951
parent 71a27cf6a0348f2c7b29c4cc9689458d891326c5
Author: Sean Enck <sean@ttypty.com>
Date: Sun, 3 Sep 2023 17:26:16 -0400
help text wrapping improved and cleaned up
Diffstat:
5 files changed, 75 insertions(+), 37 deletions(-)
diff --git a/internal/app/core.go b/internal/app/core.go
@@ -16,7 +16,6 @@ import (
"github.com/enckse/lockbox/internal/backend"
"github.com/enckse/lockbox/internal/config"
"github.com/enckse/lockbox/internal/platform"
- "github.com/muesli/reflow/wordwrap"
)
const (
@@ -268,28 +267,7 @@ func processDoc(header, file string, doc Documentation) (string, error) {
if err := t.Execute(&buf, doc); err != nil {
return "", err
}
- var sections []string
- var cur []string
- for _, line := range strings.Split(strings.TrimSpace(buf.String()), "\n") {
- trimmed := strings.TrimSpace(line)
- if trimmed == "" {
- if len(cur) > 0 {
- sections = append(sections, strings.Join(cur, " "))
- cur = []string{}
- }
- continue
- }
- cur = append(cur, line)
- }
- if len(cur) > 0 {
- sections = append(sections, strings.Join(cur, " "))
- }
- var out bytes.Buffer
- fmt.Fprintf(&out, "%s\n", header)
- for _, s := range sections {
- fmt.Fprintf(&out, "%s\n\n", wordwrap.String(s, 80))
- }
- return out.String(), nil
+ return fmt.Sprintf("%s\n%s", header, config.Wrap(0, buf.String())), nil
}
func setDocFlag(f string) string {
diff --git a/internal/config/core.go b/internal/config/core.go
@@ -2,6 +2,7 @@
package config
import (
+ "bytes"
"errors"
"fmt"
"os"
@@ -11,12 +12,14 @@ import (
"strconv"
"strings"
+ "github.com/muesli/reflow/wordwrap"
"mvdan.cc/sh/v3/shell"
)
const (
colorWindowDelimiter = ","
colorWindowSpan = ":"
+ exampleColorWindow = "start" + colorWindowSpan + "end"
yes = "yes"
no = "no"
detectEnvironment = "detect"
@@ -41,7 +44,10 @@ const (
ReKeyKeyModeFlag = "keymode"
)
-var detectEnvironmentPaths = []string{filepath.Join(".config", envFile), filepath.Join(".config", "lockbox", envFile)}
+var (
+ detectEnvironmentPaths = []string{filepath.Join(".config", envFile), filepath.Join(".config", "lockbox", envFile)}
+ exampleColorWindows = []string{strings.Join([]string{exampleColorWindow, exampleColorWindow, exampleColorWindow + "..."}, colorWindowDelimiter)}
+)
type (
// JSONOutputMode is the output mode definition
@@ -416,3 +422,38 @@ func expandParsed(inputs map[string]string) map[string]string {
}
return result
}
+
+// Wrap performs simple block text word wrapping
+func Wrap(indent uint, in string) string {
+ var sections []string
+ var cur []string
+ for _, line := range strings.Split(strings.TrimSpace(in), "\n") {
+ trimmed := strings.TrimSpace(line)
+ if trimmed == "" {
+ if len(cur) > 0 {
+ sections = append(sections, strings.Join(cur, " "))
+ cur = []string{}
+ }
+ continue
+ }
+ cur = append(cur, line)
+ }
+ if len(cur) > 0 {
+ sections = append(sections, strings.Join(cur, " "))
+ }
+ var out bytes.Buffer
+ indenting := ""
+ var cnt uint
+ for cnt < indent {
+ indenting = fmt.Sprintf("%s ", indenting)
+ cnt++
+ }
+ indenture := int(80 - indent)
+ for _, s := range sections {
+ for _, line := range strings.Split(wordwrap.String(s, indenture), "\n") {
+ fmt.Fprintf(&out, "%s%s\n", indenting, line)
+ }
+ fmt.Fprint(&out, "\n")
+ }
+ return out.String()
+}
diff --git a/internal/config/core_test.go b/internal/config/core_test.go
@@ -227,3 +227,22 @@ func TestExpandParsed(t *testing.T) {
t.Errorf("invalid expand: %v", r)
}
}
+
+func TestWrap(t *testing.T) {
+ w := config.Wrap(0, "")
+ if w != "" {
+ t.Errorf("invalid wrap: %s", w)
+ }
+ w = config.Wrap(0, "abc\n\nabc\nxyz\n")
+ if w != "abc\n\nabc xyz\n\n" {
+ t.Errorf("invalid wrap: %s", w)
+ }
+ w = config.Wrap(0, "abc\n\nabc\nxyz\n\nx")
+ if w != "abc\n\nabc xyz\n\nx\n\n" {
+ t.Errorf("invalid wrap: %s", w)
+ }
+ w = config.Wrap(5, "abc\n\nabc\nxyz\n\nx")
+ if w != " abc\n\n abc xyz\n\n x\n\n" {
+ t.Errorf("invalid wrap: %s", w)
+ }
+}
diff --git a/internal/config/help b/internal/config/help
diff --git a/internal/config/vars.go b/internal/config/vars.go
@@ -42,9 +42,9 @@ var (
// TOTPDefaultBetween is the default color window as a string
TOTPDefaultBetween = toString(TOTPDefaultColorWindow)
// EnvClipMax gets the maximum clipboard time
- EnvClipMax = EnvironmentInt{environmentBase: environmentBase{key: clipBaseEnv + "MAX", desc: "Override the amount of time before totp clears the clipboard (e.g. 10),\nmust be an integer."}, shortDesc: "clipboard max time", allowZero: false, defaultValue: 45}
+ EnvClipMax = EnvironmentInt{environmentBase: environmentBase{key: clipBaseEnv + "MAX", desc: "Override the amount of time before totp clears the clipboard (seconds)."}, shortDesc: "clipboard max time", allowZero: false, defaultValue: 45}
// EnvHashLength handles the hashing output length
- EnvHashLength = EnvironmentInt{environmentBase: environmentBase{key: EnvJSONDataOutput.key + "_HASH_LENGTH", desc: fmt.Sprintf("Maximum hash length the JSON output should contain when '%s' mode is\nset for JSON output.", JSONDataOutputHash)}, shortDesc: "hash length", allowZero: true, defaultValue: 0}
+ EnvHashLength = EnvironmentInt{environmentBase: environmentBase{key: EnvJSONDataOutput.key + "_HASH_LENGTH", desc: fmt.Sprintf("Maximum hash string length the JSON output should contain when '%s' mode is set for JSON output.", JSONDataOutputHash)}, shortDesc: "hash length", allowZero: true, defaultValue: 0}
// EnvClipOSC52 indicates if OSC52 clipboard mode is enabled
EnvClipOSC52 = EnvironmentBool{environmentBase: environmentBase{key: clipBaseEnv + "OSC52", desc: "Enable OSC52 clipboard mode."}, defaultValue: false}
// EnvNoTOTP indicates if TOTP is disabled
@@ -72,22 +72,22 @@ var (
// EnvClipPaste allows overriding the clipboard paste command
EnvClipPaste = EnvironmentCommand{environmentBase: environmentBase{key: clipBaseEnv + "PASTE", desc: "Override the detected platform paste command."}}
// EnvTOTPColorBetween handles terminal coloring for TOTP windows (seconds)
- EnvTOTPColorBetween = EnvironmentString{environmentBase: environmentBase{key: EnvTOTPToken.key + "_BETWEEN", desc: "Override when to set totp generated outputs to different colors, must be a\nlist of one (or more) rules where a semicolon delimits the start and end\nsecond (0-60 for each)."}, canDefault: true, defaultValue: TOTPDefaultBetween, allowed: []string{"start:end,start:end,start:end..."}}
+ EnvTOTPColorBetween = EnvironmentString{environmentBase: environmentBase{key: EnvTOTPToken.key + "_BETWEEN", desc: fmt.Sprintf("Override when to set totp generated outputs to different colors, must be a list of one (or more) rules where a '%s' delimits the start and end second (0-60 for each), and '%s' allows for multiple windows.", colorWindowSpan, colorWindowDelimiter)}, canDefault: true, defaultValue: TOTPDefaultBetween, allowed: exampleColorWindows}
// EnvKeyFile is an keyfile for the database
EnvKeyFile = EnvironmentString{environmentBase: environmentBase{key: prefixKey + "KEYFILE", requirement: requiredKeyOrKeyFile, desc: "A keyfile to access/protect the database."}, allowed: []string{"keyfile"}, canDefault: true, defaultValue: ""}
// EnvModTime is modtime override ability for entries
- EnvModTime = EnvironmentString{environmentBase: environmentBase{key: prefixKey + "SET_MODTIME", desc: fmt.Sprintf("Input modification time to set for the entry\n(expected format: %s).", ModTimeFormat)}, canDefault: true, defaultValue: "", allowed: []string{"modtime"}}
+ EnvModTime = EnvironmentString{environmentBase: environmentBase{key: prefixKey + "SET_MODTIME", desc: fmt.Sprintf("Input modification time to set for the entry\n\nExpected format: %s.", ModTimeFormat)}, canDefault: true, defaultValue: "", allowed: []string{"modtime"}}
// EnvJSONDataOutput controls how JSON is output in the 'data' field
- EnvJSONDataOutput = EnvironmentString{environmentBase: environmentBase{key: prefixKey + "JSON_DATA", desc: fmt.Sprintf("Changes what the data field in JSON outputs will contain use\n'%s' with CAUTION.", JSONDataOutputRaw)}, canDefault: true, defaultValue: string(JSONDataOutputHash), allowed: []string{string(JSONDataOutputRaw), string(JSONDataOutputHash), string(JSONDataOutputBlank)}}
+ EnvJSONDataOutput = EnvironmentString{environmentBase: environmentBase{key: prefixKey + "JSON_DATA", desc: fmt.Sprintf("Changes what the data field in JSON outputs will contain.\n\nUse '%s' with CAUTION.", JSONDataOutputRaw)}, canDefault: true, defaultValue: string(JSONDataOutputHash), allowed: []string{string(JSONDataOutputRaw), string(JSONDataOutputHash), string(JSONDataOutputBlank)}}
// EnvFormatTOTP supports formatting the TOTP tokens for generation of tokens
- EnvFormatTOTP = EnvironmentFormatter{environmentBase: environmentBase{key: EnvTOTPToken.key + "_FORMAT", desc: "Override the otpauth url used to store totp tokens. It must have ONE format\nstring ('%s') to insert the totp base code."}, fxn: formatterTOTP, allowed: "otpauth//url/%s/args..."}
+ EnvFormatTOTP = EnvironmentFormatter{environmentBase: environmentBase{key: EnvTOTPToken.key + "_FORMAT", desc: "Override the otpauth url used to store totp tokens. It must have ONE format string ('%s') to insert the totp base code."}, fxn: formatterTOTP, allowed: "otpauth//url/%s/args..."}
// EnvConfig is the location of the config file to read environment variables from
- EnvConfig = EnvironmentString{environmentBase: environmentBase{key: prefixKey + "ENV", desc: fmt.Sprintf("Allows setting a specific file of environment variables for lockbox\nto read and use as configuration values (an '.env' file). The keyword\n'%s' will disable this functionality the keyword '%s' will search\nfor a file in the following paths in user's home directory matching\nthe first file found.\n\ndefault search paths:\n%v\n\nNote that this setting is not output as part of the environment.", noEnvironment, detectEnvironment, detectEnvironmentPaths)}, canDefault: true, defaultValue: detectEnvironment, allowed: []string{detectEnvironment, fileExample, noEnvironment}}
+ EnvConfig = EnvironmentString{environmentBase: environmentBase{key: prefixKey + "ENV", desc: fmt.Sprintf("Allows setting a specific file of environment variables for lockbox to read and use as configuration values (an '.env' file). The keyword '%s' will disable this functionality and the keyword '%s' will search for a file in the following paths in the user's home directory matching the first file found.\n\npaths: %v\n\nNote that this setting is not output as part of the environment.", noEnvironment, detectEnvironment, detectEnvironmentPaths)}, canDefault: true, defaultValue: detectEnvironment, allowed: []string{detectEnvironment, fileExample, noEnvironment}}
// EnvCompletion is the completion method to use
- EnvCompletion = EnvironmentString{environmentBase: environmentBase{key: EnvironmentCompletionKey, desc: "Use to select the non-default completions,\nplease review the shell completion help for more information.", requirement: "must be exported via a shell variable"}, canDefault: false}
- envKeyMode = EnvironmentString{environmentBase: environmentBase{key: prefixKey + "KEYMODE", requirement: "must be set to a valid mode when using a key", desc: fmt.Sprintf("How to retrieve the database store password.\nSet to '%s' when only using a key file\nSet to '%s' to ignore the set key value", noKeyMode, IgnoreKeyMode), whenUnset: string(DefaultKeyMode)}, allowed: []string{string(askKeyMode), string(commandKeyMode), string(IgnoreKeyMode), string(noKeyMode), string(plainKeyMode)}, canDefault: true, defaultValue: ""}
- envKey = EnvironmentString{environmentBase: environmentBase{requirement: requiredKeyOrKeyFile, key: prefixKey + "KEY", desc: fmt.Sprintf("The database key ('%s' mode) or command to run ('%s' mode)\nto retrieve the database password.", plainKeyMode, commandKeyMode)}, allowed: []string{commandArgsExample, "password"}, canDefault: false}
- envConfigExpands = EnvironmentInt{environmentBase: environmentBase{key: EnvConfig.key + "_EXPANDS", desc: "The maximum number of times to expand the input env to resolve variables,\nset to 0 to disable expansion. This value can NOT be an expansion itself\nif set in the env config file."}, shortDesc: "max expands", allowZero: true, defaultValue: 20}
+ EnvCompletion = EnvironmentString{environmentBase: environmentBase{key: EnvironmentCompletionKey, desc: "Use to select the non-default completions, please review the shell completion help for more information.", requirement: "must be exported via a shell variable"}, canDefault: false}
+ envKeyMode = EnvironmentString{environmentBase: environmentBase{key: prefixKey + "KEYMODE", requirement: "must be set to a valid mode when using a key", desc: fmt.Sprintf("How to retrieve the database store password. Set to '%s' when only using a key file. Set to '%s' to ignore the set key value", noKeyMode, IgnoreKeyMode), whenUnset: string(DefaultKeyMode)}, allowed: []string{string(askKeyMode), string(commandKeyMode), string(IgnoreKeyMode), string(noKeyMode), string(plainKeyMode)}, canDefault: true, defaultValue: ""}
+ envKey = EnvironmentString{environmentBase: environmentBase{requirement: requiredKeyOrKeyFile, key: prefixKey + "KEY", desc: fmt.Sprintf("The database key ('%s' mode) or command to run ('%s' mode) to retrieve the database password.", plainKeyMode, commandKeyMode)}, allowed: []string{commandArgsExample, "password"}, canDefault: false}
+ envConfigExpands = EnvironmentInt{environmentBase: environmentBase{key: EnvConfig.key + "_EXPANDS", desc: "The maximum number of times to expand the input env to resolve variables (set to 0 to disable expansion).\n\nThis value can NOT be an expansion itself."}, shortDesc: "max expands", allowZero: true, defaultValue: 20}
)
// GetReKey will get the rekey environment settings
@@ -136,13 +136,13 @@ func ListEnvironmentVariables() []string {
value = env.whenUnset
}
}
- description := strings.ReplaceAll(env.desc, "\n", "\n ")
+ description := Wrap(2, env.desc)
requirement := "optional/default"
r := strings.TrimSpace(env.requirement)
if r != "" {
requirement = r
}
- text := fmt.Sprintf("\n%s\n %s\n\n requirement: %s\n default: %s\n options: %s\n", env.key, description, requirement, value, strings.Join(allow, "|"))
+ text := fmt.Sprintf("\n%s\n%s requirement: %s\n default: %s\n options: %s\n", env.key, description, requirement, value, strings.Join(allow, "|"))
results = append(results, text)
}
sort.Strings(results)