build(deps): bump github.com/gookit/config/v2 from 2.2.8 to 2.2.9

Bumps [github.com/gookit/config/v2](https://github.com/gookit/config) from 2.2.8 to 2.2.9.
- [Release notes](https://github.com/gookit/config/releases)
- [Commits](https://github.com/gookit/config/compare/v2.2.8...v2.2.9)

---
updated-dependencies:
- dependency-name: github.com/gookit/config/v2
  dependency-version: 2.2.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
dependabot[bot]
2026-07-06 14:44:49 +00:00
committed by Ralf Haferkamp
parent bce52eec38
commit e4dfada264
31 changed files with 273 additions and 115 deletions

View File

@@ -338,7 +338,9 @@ fire the: clean.data
### Watch loaded config files
To listen for changes to loaded config files, and reload the config when it changes, you need to use the https://github.com/fsnotify/fsnotify library.
For usage, please refer to the example [./_example/watch_file.go](_examples/watch_file.go)
For usage, please refer to the example [./_examples/watch_file.go](_examples/watch_file.go)
Note: editors may emit multiple `WRITE` events while saving, and the file content may not be fully written when the event is received. Use a short debounce, as shown in the example, before calling `ReloadFiles()`.
Also, you need to listen to the `reload.data` event:

View File

@@ -331,7 +331,9 @@ fire the: clean.data
### 监听载入的配置文件变动
想要监听载入的配置文件变动,并在变动时重新加载配置,你需要使用 https://github.com/fsnotify/fsnotify 库。
使用方法可以参考示例 [./_example/watch_file.go](_examples/watch_file.go)
使用方法可以参考示例 [./_examples/watch_file.go](_examples/watch_file.go)
注意:编辑器保存文件时可能触发多次 `WRITE` 事件,且事件触发时文件内容可能还未完全写入。建议像示例中一样做一个很短的 debounce再调用 `ReloadFiles()`
同时,你需要监听 `reload.data` 事件:

View File

@@ -153,7 +153,9 @@ func (c *Config) LoadOSEnvs(nameToKeyMap map[string]string) {
}
// LoadOSEnvByFilter load OS ENVs by custom fitler func. eg: use for load ENV by prefix.
func LoadOSEnvByFilter(filterFn func(key string) (loadIt bool, cfgKey string)) { dc.LoadOSEnvByFilter(filterFn) }
func LoadOSEnvByFilter(filterFn func(key string) (loadIt bool, cfgKey string)) {
dc.LoadOSEnvByFilter(filterFn)
}
// LoadOSEnvByFilter load OS ENVs by custom fitler func. eg: use for load ENV by prefix.
//
@@ -463,7 +465,7 @@ func (c *Config) ReloadFiles() (err error) {
return
}
data := c.Data()
var data map[string]any
c.reloading = true
c.ClearCaches()
@@ -483,6 +485,8 @@ func (c *Config) ReloadFiles() (err error) {
// with lock
c.lock.Lock()
data = c.data
c.data = make(map[string]any)
// reload config files
return c.LoadFiles(files...)

View File

@@ -377,14 +377,33 @@ func (c *Config) tryInt64(key string) (value int64, ok bool) {
// Duration get a time.Duration type value. if not found return default value
func Duration(key string, defVal ...time.Duration) time.Duration { return dc.Duration(key, defVal...) }
// Duration get a time.Duration type value. if not found return default value
// Duration get a time.Duration type value. if not found return default value.
//
// The stored value may be a Go duration string (e.g. "300s", "1h30m", "20m").
// A bare integer is treated as nanoseconds for backwards compatibility.
func (c *Config) Duration(key string, defVal ...time.Duration) time.Duration {
value, exist := c.tryInt64(key)
str, exist := c.getString(key)
if !exist {
if len(defVal) > 0 {
return defVal[0]
}
return 0
}
if !exist && len(defVal) > 0 {
// Prefer a Go duration string, e.g. "300s", "1h30m".
if dur, err := time.ParseDuration(str); err == nil {
return dur
}
// Backwards compatible: a bare integer is nanoseconds.
if n, err := strconv.ParseInt(str, 10, 64); err == nil {
return time.Duration(n)
}
if len(defVal) > 0 {
return defVal[0]
}
return time.Duration(value)
return 0
}
// Float get a float64 value, if not found return default value

View File

@@ -32,7 +32,7 @@
- [`dump`](dump): GO value printing tool. print slice, map will auto wrap each element and display the call location
- [`errorx`](errorx) Provide an enhanced error implements for go, allow with stacktrace and wrap another error.
- [`assert`](testutil/assert) Provides commonly asserts functions for help testing
- [`assert`](x/assert) Provides commonly asserts functions for help testing
- [`testutil`](testutil) Test help util functions. eg: http test, mock ENV value
- [`fakeobj`](x/fakeobj) provides a fake object for testing. such as fake fs.File, fs.FileInfo, fs.DirEntry etc.

View File

@@ -33,7 +33,7 @@
- [`dump`](./dump) GO变量打印工具打印 slice, map 会自动换行显示每个元素,同时会显示打印调用位置
- [`errorx`](./errorx) 为 go 提供增强的错误实现,允许带有堆栈跟踪信息和包装另一个错误。
- [`testutil`](testutil) test help 相关操作的函数工具包. eg: http test, mock ENV value
- [`assert`](testutil/assert) 用于帮助测试的断言函数工具包,方便编写单元测试。
- [`assert`](x/assert) 用于帮助测试的断言函数工具包,方便编写单元测试。
- [`fakeobj`](x/fakeobj) 提供一些接口的MOCK的实现用于模拟测试. 例如 fs.File, fs.FileInfo, fs.DirEntry 等等.
### 扩展工具包

View File

@@ -139,3 +139,13 @@ func TrimStrings(ss []string, cutSet ...string) []string {
}
return ns
}
// FirstNonEmptyString get
func FirstNonEmptyString(values ...string) string {
for _, value := range values {
if strings.TrimSpace(value) != "" {
return value
}
}
return ""
}

View File

@@ -24,6 +24,10 @@ type Dotenv struct {
BaseDir string
// UpperKey change key to upper on set ENV. default: true
UpperKey bool
// DotenvFirst overwrite existing os ENV values on load dotenv.
//
// default: false - os ENV first
DotenvFirst bool
// IgnoreNotExist only load exists.
//
// - default: false - will return error if not exists
@@ -39,7 +43,7 @@ type Dotenv struct {
func NewDotenv() *Dotenv {
return &Dotenv{
UpperKey: true,
Files: []string{DefaultEnvFile},
Files: []string{DefaultEnvFile},
// init fields
loadData: make(map[string]string),
}
@@ -51,7 +55,7 @@ func (c *Dotenv) LoadAndInit() error {
}
// LoadFiles append load dotenv files
// - filename support simple glob pattern. eg: ".env.*"
// - filename support simple glob pattern. eg: ".env.*"
func (c *Dotenv) LoadFiles(files ...string) error {
return c.doLoadFiles(files)
}
@@ -129,6 +133,13 @@ func (c *Dotenv) parseAndSetEnv(contents string) error {
// Set to ENV
for key, val := range envMp {
key = strings.ToUpper(key)
if !c.DotenvFirst {
if _, ok := os.LookupEnv(key); ok {
if _, loaded := c.loadData[key]; !loaded {
continue
}
}
}
c.loadData[key] = val
_ = os.Setenv(key, val)
}
@@ -195,4 +206,3 @@ func LoadEnvFiles(baseDir string, files ...string) error {
func LoadedEnvFiles() []string {
return stdEnv.LoadedFiles()
}

View File

@@ -5,8 +5,6 @@ import (
"strings"
)
var commentsPrefixes = []string{"#", ";", "//"}
// ParseEnvLineOption parse env line options
type ParseEnvLineOption struct {
// NotInlineComments dont parse inline comments.
@@ -74,7 +72,7 @@ func SplitLineToKv(line, sep string) (string, string) {
}
// SplitKvBySep parse string line to k-v, support parse comments.
// - rmInlineComments: check and remove inline comments by ' #'
// - rmInlineComments: check and remove inline comments by ' #'
func SplitKvBySep(line, sep string, rmInlineComments bool) (key, val string) {
sepPos := strings.Index(line, sep)
if sepPos < 0 {

View File

@@ -50,11 +50,6 @@ func ExecCmd(binName string, args []string, workDir ...string) (string, error) {
return string(bs), err
}
var (
cmdList = []string{"cmd", "cmd.exe"}
pwshList = []string{"powershell", "powershell.exe", "pwsh", "pwsh.exe"}
)
// ShellExec exec command by shell
// cmdLine e.g. "ls -al"
func ShellExec(cmdLine string, shells ...string) (string, error) {
@@ -119,13 +114,6 @@ func CurrentShell(onlyName bool, fallbackShell ...string) (binPath string) {
return
}
func checkWinCurrentShell() string {
// 在 Windows 上,可以检查 COMSPEC 环境变量
comSpec := os.Getenv("COMSPEC")
// 没法检查 pwsh, 返回的还是 cmd
return comSpec
}
// HasShellEnv has shell env check.
//
// Usage:

View File

@@ -6,7 +6,7 @@ import (
)
// Aliases implemented a simple string alias map.
// - key: alias, value: real name
// - key: alias, value: real name
type Aliases map[string]string
// AddAlias to the Aliases map

View File

@@ -246,6 +246,36 @@ func StrMapToText(m map[string]string) string {
return strings.Join(lines, "\n")
}
// CloneStringMap copy and return a new string map
func CloneStringMap(items map[string]string) map[string]string {
if len(items) == 0 {
return nil
}
cloned := make(map[string]string, len(items))
for key, value := range items {
cloned[key] = value
}
return cloned
}
// CloneAnyMap copy and return a new any map.
func CloneAnyMap(items map[string]any) map[string]any {
return Clone(items)
}
// Clone copy and return a new map.
func Clone[M ~map[K]V, K comparable, V any](items M) M {
if len(items) == 0 {
return nil
}
cloned := make(M, len(items))
for key, value := range items {
cloned[key] = value
}
return cloned
}
/*************************************************************
* Flat convert tree map to flatten key-value map.
*************************************************************/

View File

@@ -1,6 +1,7 @@
package maputil
import (
"math"
"strings"
"github.com/gookit/goutil/arrutil"
@@ -175,7 +176,10 @@ func (d Data) Uint(key string, defVal ...uint) uint {
// Uint16 value get, or default value
func (d Data) Uint16(key string, defVal ...uint16) uint16 {
if val, ok := d.GetByPath(key); ok {
return uint16(mathutil.SafeUint(val))
u64 := mathutil.QuietUint64(val)
if u64 <= math.MaxUint16 {
return uint16(u64)
}
}
if len(defVal) > 0 {

View File

@@ -7,6 +7,7 @@ import (
// SM is alias of map[string]string
type SM = StrMap
// SMap and StrMap is alias of map[string]string
type SMap = StrMap
type StrMap map[string]string

View File

@@ -190,11 +190,10 @@ func ConvToKind(val any, kind reflect.Kind, fallback ...ConvFunc) (rv reflect.Va
default:
// call fallback func
if len(fallback) > 0 && fallback[0] != nil {
rv, err = fallback[0](val, kind)
return fallback[0](val, kind)
} else {
err = comdef.ErrConvType
}
err = comdef.ErrConvType
}
return
}

View File

@@ -7,18 +7,6 @@ import (
"unsafe"
)
// loopIndirect returns the item at the end of indirection, and a bool to indicate
// if it's nil. If the returned bool is true, the returned value's kind will be
// either a pointer or interface.
func loopIndirect(v reflect.Value) (rv reflect.Value, isNil bool) {
for ; v.Kind() == reflect.Pointer || v.Kind() == reflect.Interface; v = v.Elem() {
if v.IsNil() {
return v, true
}
}
return v, false
}
// indirectInterface returns the concrete value in an interface value,
// or else the zero reflect.Value.
// That is, if v represents the interface value x, the result is the same as reflect.ValueOf(x):

View File

@@ -70,16 +70,6 @@ func WithBeforeSetFn(fn BeforeSetFunc) SetOptFunc {
return func(opt *SetOptions) { opt.BeforeSetFn = fn }
}
// TODO refactoring SetValues to the struct
type ValuesSetter struct {
src any // source struct
// raw reflect.Value of source struct
rv reflect.Value
option *SetOptions
initOpt *InitOptions
}
// BindData set values to struct ptr from map data.
func BindData(ptr any, data map[string]any, optFns ...SetOptFunc) error {
return SetValues(ptr, data, optFns...)

View File

@@ -359,6 +359,31 @@ func VersionCompare(v1, v2, op string) bool {
// parseVersion 将版本号字符串解析为整数数组
func parseVersion(version string) []int {
if version[0] == 'v' || version[0] == 'V' {
version = version[1:]
}
// 处理 Git 描述格式: v1.7.1-16-gc43a587
// 格式: <tag>-<commits>-g<hash>,其中 commits 表示距离 tag 的提交数
var extraCommits int
var preRelease []int
if idx := strings.Index(version, "-"); idx > 0 {
remaining := version[idx+1:]
version = version[:idx]
// 尝试提取提交数(格式: 数字-g<hash> 或 数字)
if dashIdx := strings.Index(remaining, "-"); dashIdx > 0 {
if commits, err := strconv.Atoi(remaining[:dashIdx]); err == nil {
extraCommits = commits
} else {
preRelease = parsePreRelease(remaining)
}
} else if commits, err := strconv.Atoi(remaining); err == nil {
extraCommits = commits
} else {
preRelease = parsePreRelease(remaining)
}
}
parts := strings.Split(version, ".")
result := make([]int, len(parts))
@@ -366,9 +391,46 @@ func parseVersion(version string) []int {
num, _ := strconv.Atoi(part)
result[i] = num
}
// 如果有额外的提交数,追加到版本号末尾
// 这样 v1.7.1-16-gc43a587 会变成 [1, 7, 1, 16]
// 而 v1.7.1 是 [1, 7, 1],比较时前者更大
if extraCommits > 0 {
result = append(result, extraCommits)
} else if len(preRelease) > 0 {
result = append(result, preRelease...)
}
return result
}
func parsePreRelease(label string) []int {
label = strings.ToLower(label)
name := strings.Trim(label, ".-")
num := 0
for idx, char := range name {
if char >= '0' && char <= '9' {
if nVal, err := strconv.Atoi(strings.Trim(name[idx:], ".-")); err == nil {
num = nVal
}
name = strings.Trim(name[:idx], ".-")
break
}
}
rank := 0
switch name {
case "a", "alpha":
rank = 1
case "b", "beta":
rank = 2
case "rc":
rank = 3
}
return []int{-1, rank, num}
}
// compareVersions 比较两个版本号数组
// 返回: -1 表示 v1 < v2, 0 表示 v1 = v2, 1 表示 v1 > v2
func compareVersions(v1, v2 []int) int {

View File

@@ -56,10 +56,19 @@ var layoutMap = map[int][]string{
//
// NOTE: always use local timezone.
func ToTime(s string, layouts ...string) (t time.Time, err error) {
return ToTimeIn(s, time.Local, layouts...)
}
// ToTimeIn convert date string to time.Time with location.
func ToTimeIn(s string, loc *time.Location, layouts ...string) (t time.Time, err error) {
if loc == nil {
loc = time.Local
}
// custom layout
if len(layouts) > 0 {
if len(layouts[0]) > 0 {
return time.ParseInLocation(layouts[0], s, time.Local)
return time.ParseInLocation(layouts[0], s, loc)
}
err = ErrDateLayout
@@ -88,16 +97,19 @@ func ToTime(s string, layouts ...string) (t time.Time, err error) {
// date string has "/". eg: "2006/01/02 15:04:05"
if hasSlashR {
layout = strings.Replace(layout, "-", "/", -1)
layout = strings.ReplaceAll(layout, "-", "/")
}
if strLn > 10 && s[10] != ' ' && len(layout) > 10 && layout[10] == ' ' {
layout = layout[:10] + string(s[10]) + layout[11:]
}
t, err = time.ParseInLocation(layout, s, time.Local)
t, err = time.ParseInLocation(layout, s, loc)
if err == nil {
return
}
}
// t, err = time.ParseInLocation(layout, s, time.Local)
// t, err = time.ParseInLocation(layout, s, loc)
return
}

View File

@@ -141,7 +141,7 @@ func SplitNTrimmed(s, sep string, n int) (ss []string) {
}
// 根据空白字符空格TAB换行等分隔字符串
var whitespaceRegexp = regexp.MustCompile("\\s+")
var whitespaceRegexp = regexp.MustCompile(`\s+`)
// SplitByWhitespace Separate strings by whitespace characters (space, TAB, newline, etc.)
func SplitByWhitespace(s string) []string {

View File

@@ -15,16 +15,14 @@ import (
// StrVarRenderer implements like shell vars renderer
// 简单的实现类似 php, kotlin, shell 插值变量渲染,表达式解析处理。
//
// - var format: $var_name, ${some_var}, ${top.sub_var}
// - var format: $var_name, ${some_var}, ${top.sub_var} ${name|default}
// - func call: ${func($var_name, 'const string')}
type StrVarRenderer struct {
// global variables
vars map[string]any
// fallback func for var not exists
getter FallbackFn
// funcMap map[string]any TODO use any, add reflect value to rfs
funcMap map[string]func(...any) any
// var func map. refer the text/template TODO
// var func map. refer the text/template
//
// Func allow return 1 or 2 values, if return 2 values, the second value is error.
rfs map[string]*reflects.FuncX
@@ -34,8 +32,7 @@ type StrVarRenderer struct {
func NewStrVarRenderer() *StrVarRenderer {
return &StrVarRenderer{
vars: make(map[string]any),
// funcMap: make(map[string]any),
funcMap: make(map[string]func(...any) any),
rfs: make(map[string]*reflects.FuncX),
}
}
@@ -53,21 +50,29 @@ func (r *StrVarRenderer) SetVar(name string, value any) *StrVarRenderer {
return r
}
// SetFunc set a function
func (r *StrVarRenderer) SetFunc(name string, fn any) *StrVarRenderer {
r.rfs[name] = reflects.NewFunc(fn).WithEnhanceConv()
return r
}
// SetFuncMap set function map
func (r *StrVarRenderer) SetFuncMap(funcMap map[string]func(...any) any) *StrVarRenderer {
for k, v := range funcMap {
r.funcMap[k] = v
func (r *StrVarRenderer) SetFuncMap(fnMap map[string]func(...any) any) *StrVarRenderer {
for k, v := range fnMap {
r.SetFunc(k, v)
}
return r
}
// SetFunc set a function
func (r *StrVarRenderer) SetFunc(name string, fn func(...any) any) *StrVarRenderer {
r.funcMap[name] = fn
// SetFuncs set typed function map
func (r *StrVarRenderer) SetFuncs(fnMap map[string]any) *StrVarRenderer {
for k, v := range fnMap {
r.SetFunc(k, v)
}
return r
}
// SetGetter set variable getter
// SetGetter set variable fallback getter
func (r *StrVarRenderer) SetGetter(getter FallbackFn) *StrVarRenderer {
r.getter = getter
return r
@@ -78,8 +83,8 @@ var (
// - 允许:$1..$N 这样的变量
// - 也支持 $@, $* 变量
reS = regexp.MustCompile(`\$(\w[a-zA-Z0-9_]*|[@|*])`)
// 处理 ${var_name} ${top.sub} 格式
reQ = regexp.MustCompile(`\$\{([a-zA-Z][a-zA-Z0-9_.]*)\}`)
// 处理 ${var_name} ${top.sub} ${var|def} 格式
reQ = regexp.MustCompile(`\$\{([a-zA-Z][a-zA-Z0-9_.]*)(?:\s*\|\s*([^}]+))?\}`)
// 处理 ${func(...)} 格式
reFn = regexp.MustCompile(`\$\{([a-zA-Z][a-zA-Z0-9_]*)\(([^}]*)\)\}`)
)
@@ -108,10 +113,12 @@ func (r *StrVarRenderer) handleFuncCalls(input string, re *regexp.Regexp, vars m
funcName := submatch[1]
// 解析参数 并 调用函数
if fn, ok := r.funcMap[funcName]; ok {
if fx, ok := r.rfs[funcName]; ok {
args := r.parseArgs(submatch[2], vars)
result := fn(args...)
return fmt.Sprint(result)
result, err := fx.Call2(args...)
if err == nil {
return fmt.Sprint(result)
}
}
return matched
@@ -154,11 +161,15 @@ func (r *StrVarRenderer) parseArgs(argsStr string, data maputil.Map) []any {
func (r *StrVarRenderer) replaceVars(input string, re *regexp.Regexp, data maputil.Map) string {
return re.ReplaceAllStringFunc(input, func(matched string) string {
var varName string
var varName, defVal string
// format: ${var_name} 提取变量名
if strings.HasPrefix(matched, "${") {
varName = matched[2 : len(matched)-1]
submatch := re.FindStringSubmatch(matched)
varName = submatch[1]
if len(submatch) > 2 {
defVal = strings.TrimSpace(submatch[2])
}
} else {
// format: $var_name
varName = matched[1:]
@@ -176,6 +187,9 @@ func (r *StrVarRenderer) replaceVars(input string, re *regexp.Regexp, data maput
}
}
if len(defVal) > 0 {
return defVal
}
return matched
})
}

View File

@@ -62,6 +62,11 @@ func LastErr() error {
//
// Disable color of current terminal.
//
// Usage:
//
// ccolor.Disable()
// defer ccolor.RevertColorSupport()
func Disable() { termenv.DisableColor() }
// Level value of current terminal.

View File

@@ -2,6 +2,7 @@ package ccolor
import (
"fmt"
"io"
"strconv"
)
@@ -209,6 +210,33 @@ func (c Color) Printf(format string, a ...any) {
// Println messages with new line
func (c Color) Println(a ...any) { doPrintln(c.String(), a) }
// Fprint writes to w with color setting.
//
// Usage:
//
// ccolor.Green.Fprint(os.Stdout, "message")
func (c Color) Fprint(w io.Writer, a ...any) {
doPrintTo(w, c.String(), fmt.Sprint(a...))
}
// Fprintf formats and writes to w with color setting.
//
// Usage:
//
// ccolor.Cyan.Fprintf(os.Stdout, "string %s", "arg0")
func (c Color) Fprintf(w io.Writer, format string, a ...any) {
doPrintTo(w, c.String(), fmt.Sprintf(format, a...))
}
// Fprintln writes to w with color setting and adds a new line.
//
// Usage:
//
// ccolor.Green.Fprintln(os.Stdout, "message")
func (c Color) Fprintln(w io.Writer, a ...any) {
doPrintlnTo(w, c.String(), a)
}
// Light current color. eg: 36(FgCyan) -> 96(FgLightCyan).
//
// Usage:

View File

@@ -65,9 +65,10 @@ var colorTags = map[string]string{
"normal": "0;39", // no color
"brown": "0;33", // #A52A2A
"yellow": "0;33",
"ylw": "0;33",
"ylw": "0;33",
"ylw0": "0;33",
"yellowB": "1;33", // with bold
"yellow1": "1;33", // with bold
"yellowB": "1;33",
"ylw1": "1;33",
"ylwB": "1;33",
"magenta": "0;35",

View File

@@ -64,6 +64,16 @@ func (s *Style) Fprint(w io.Writer, v ...any) {
doPrintTo(w, s.String(), fmt.Sprint(v...))
}
// Fprintf like fmt.Fprintf, but with color
func (s *Style) Fprintf(w io.Writer, format string, v ...any) {
doPrintTo(w, s.String(), fmt.Sprintf(format, v...))
}
// Fprintln like fmt.Fprintln, but with color
func (s *Style) Fprintln(w io.Writer, v ...any) {
doPrintlnTo(w, s.String(), v)
}
// String convert style setting to color code string.
func (s *Style) String() string {
var codes []string

View File

@@ -116,7 +116,12 @@ func doPrintTo(w io.Writer, code, str string) {
// new implementation, support render full color code on pwsh.exe, cmd.exe
func doPrintln(code string, args []any) {
_, lastErr = fmt.Fprintln(output, RenderString(code, formatLikePrintln(args)))
doPrintlnTo(output, code, args)
}
// new implementation, support render full color code on pwsh.exe, cmd.exe
func doPrintlnTo(w io.Writer, code string, args []any) {
_, lastErr = fmt.Fprintln(w, RenderString(code, formatLikePrintln(args)))
}
// use Println, will add spaces for each arg

View File

@@ -61,7 +61,7 @@ func SetColorLevel(level ColorLevel) {
// force set color level
colorLevel = level
supportColor = level > TermColorNone
noColor = supportColor == false
noColor = !supportColor
}
// DisableColor in the current terminal

View File

@@ -98,30 +98,6 @@ func tryEnableOnStdout() bool {
return true
}
// related docs
// https://docs.microsoft.com/zh-cn/windows/console/console-virtual-terminal-sequences
// https://docs.microsoft.com/zh-cn/windows/console/console-virtual-terminal-sequences#samples
var (
// isMSys bool
kernel32 *syscall.LazyDLL
procGetConsoleMode *syscall.LazyProc
procSetConsoleMode *syscall.LazyProc
)
func initKernel32Proc() {
if kernel32 != nil {
return
}
// load related Windows dll
// https://docs.microsoft.com/en-us/windows/console/setconsolemode
kernel32 = syscall.NewLazyDLL("kernel32.dll")
procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
procSetConsoleMode = kernel32.NewProc("SetConsoleMode")
}
/*************************************************************
* render full color code on Windows(8,16,24bit color)
*************************************************************/