Compare commits
33 Commits
v0.47.0
...
test-githu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
689e708baa | ||
|
|
4e38a941de | ||
|
|
471f72e607 | ||
|
|
49f2f818c6 | ||
|
|
f5cd02b54f | ||
|
|
fd01ca3acf | ||
|
|
4845ce1e0f | ||
|
|
0cc6e39f0f | ||
|
|
e90aeb62e5 | ||
|
|
17ab91e668 | ||
|
|
964278255b | ||
|
|
56695078c3 | ||
|
|
96934d5a1d | ||
|
|
f05f81d713 | ||
|
|
9d0740427e | ||
|
|
e62aeb99ed | ||
|
|
fcf48c4f08 | ||
|
|
2ceecad381 | ||
|
|
3b85307f67 | ||
|
|
30e9bf8a75 | ||
|
|
62c6ba7d57 | ||
|
|
72b9e8328d | ||
|
|
38ab7ebefb | ||
|
|
4b30bc6dd3 | ||
|
|
626ca18a23 | ||
|
|
87c3e75811 | ||
|
|
1e05055fff | ||
|
|
0ef3832e59 | ||
|
|
b766ff9c83 | ||
|
|
b2fd6128f6 | ||
|
|
a81f9ea97c | ||
|
|
101bbb0ac5 | ||
|
|
3e11e34181 |
35
.github/workflows/cd.yml
vendored
35
.github/workflows/cd.yml
vendored
@@ -1,35 +0,0 @@
|
||||
name: Continuous Delivery
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
jobs:
|
||||
goreleaser:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
- name: Unshallow repo
|
||||
run: git fetch --prune --unshallow
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.22.x
|
||||
- name: Run goreleaser
|
||||
uses: goreleaser/goreleaser-action@v4
|
||||
with:
|
||||
distribution: goreleaser
|
||||
version: v1.17.2
|
||||
args: release --clean
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_API_TOKEN}}
|
||||
homebrew:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Bump Homebrew formula
|
||||
uses: dawidd6/action-homebrew-bump-formula@v3
|
||||
with:
|
||||
token: ${{secrets.GITHUB_API_TOKEN}}
|
||||
formula: lazygit
|
||||
78
.github/workflows/release.yml
vendored
78
.github/workflows/release.yml
vendored
@@ -1,24 +1,44 @@
|
||||
name: Automated Release
|
||||
name: Release
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Runs at 2:00 AM UTC on the first Saturday of every month
|
||||
- cron: '0 2 * * 6'
|
||||
workflow_dispatch: # Allow manual triggering of the workflow
|
||||
- cron: '0 2 1-7 * 6'
|
||||
# Allow manual triggering of the workflow
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version_bump:
|
||||
description: 'Version bump type'
|
||||
type: choice
|
||||
required: true
|
||||
default: 'patch'
|
||||
options:
|
||||
- minor
|
||||
- patch
|
||||
ignore_blocks:
|
||||
description: 'Ignore blocking PRs/issues'
|
||||
type: boolean
|
||||
required: true
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
check-and-release:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Get Latest Tag
|
||||
run: |
|
||||
latest_tag=$(git describe --tags $(git rev-list --tags --max-count=1) || echo "v0.0.0")
|
||||
|
||||
if ! [[ $latest_tag =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
echo "Error: Tag format is invalid. Expected format: vX.X.X"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Latest tag: $latest_tag"
|
||||
echo "latest_tag=$latest_tag" >> $GITHUB_ENV
|
||||
|
||||
@@ -30,6 +50,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Check for Blocking Issues/PRs
|
||||
if: ${{ !inputs.ignore_blocks }}
|
||||
id: check_blocks
|
||||
run: |
|
||||
gh auth setup-git
|
||||
@@ -61,13 +82,31 @@ jobs:
|
||||
run: |
|
||||
echo "Latest tag: ${{ env.latest_tag }}"
|
||||
IFS='.' read -r major minor patch <<< "${{ env.latest_tag }}"
|
||||
new_minor=$((minor + 1))
|
||||
new_tag="$major.$new_minor.0"
|
||||
|
||||
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
||||
if [[ "${{ inputs.version_bump }}" == "patch" ]]; then
|
||||
patch=$((patch + 1))
|
||||
else
|
||||
minor=$((minor + 1))
|
||||
patch=0
|
||||
fi
|
||||
else
|
||||
# Default behavior for scheduled runs
|
||||
minor=$((minor + 1))
|
||||
patch=0
|
||||
fi
|
||||
|
||||
new_tag="$major.$minor.$patch"
|
||||
|
||||
if ! [[ $new_tag =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
echo "Error: New tag's format is invalid. Expected format: vX.X.X"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "New tag: $new_tag"
|
||||
echo "new_tag=$new_tag" >> $GITHUB_ENV
|
||||
|
||||
# This will trigger a deploy via .github/workflows/cd.yml
|
||||
- name: Push New Tag
|
||||
- name: Create and Push Tag
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
@@ -75,3 +114,24 @@ jobs:
|
||||
git push origin ${{ env.new_tag }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_API_TOKEN }}
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.22.x
|
||||
|
||||
- name: Run goreleaser
|
||||
uses: goreleaser/goreleaser-action@v4
|
||||
with:
|
||||
distribution: goreleaser
|
||||
version: v1.17.2
|
||||
args: release --clean
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_API_TOKEN}}
|
||||
|
||||
- name: Bump Homebrew formula
|
||||
uses: dawidd6/action-homebrew-bump-formula@v3
|
||||
with:
|
||||
token: ${{secrets.GITHUB_API_TOKEN}}
|
||||
formula: lazygit
|
||||
tag: ${{env.new_tag}}
|
||||
|
||||
9
.vscode/launch.json
vendored
9
.vscode/launch.json
vendored
@@ -26,6 +26,15 @@
|
||||
],
|
||||
"console": "integratedTerminal",
|
||||
},
|
||||
{
|
||||
"name": "JSON Schema generator",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "${workspaceFolder}/pkg/jsonschema/generator.go",
|
||||
"cwd": "${workspaceFolder}/pkg/jsonschema",
|
||||
"console": "integratedTerminal",
|
||||
},
|
||||
{
|
||||
"name": "Attach to a running Lazygit",
|
||||
"type": "go",
|
||||
|
||||
@@ -411,6 +411,9 @@ os:
|
||||
# window is closed.
|
||||
editAtLineAndWait: ""
|
||||
|
||||
# Whether lazygit suspends until an edit process returns
|
||||
editInTerminal: false
|
||||
|
||||
# For opening a directory in an editor
|
||||
openDirInEditor: ""
|
||||
|
||||
|
||||
1
go.mod
1
go.mod
@@ -11,7 +11,6 @@ require (
|
||||
github.com/gdamore/tcell/v2 v2.8.1
|
||||
github.com/go-errors/errors v1.5.1
|
||||
github.com/gookit/color v1.4.2
|
||||
github.com/iancoleman/orderedmap v0.3.0
|
||||
github.com/imdario/mergo v0.3.11
|
||||
github.com/integrii/flaggy v1.4.0
|
||||
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68
|
||||
|
||||
2
go.sum
2
go.sum
@@ -171,8 +171,6 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc=
|
||||
github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
|
||||
@@ -149,7 +149,7 @@ func TestRebaseDiscardOldFileChanges(t *testing.T) {
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"rebase", "--interactive", "--autostash", "--keep-empty", "--no-autosquash", "--rebase-merges", "abcdef"}, "", nil).
|
||||
ExpectGitArgs([]string{"cat-file", "-e", "HEAD^:test999.txt"}, "", nil).
|
||||
ExpectGitArgs([]string{"checkout", "HEAD^", "--", "test999.txt"}, "", nil).
|
||||
ExpectGitArgs([]string{"-c", disableHooksFlag, "checkout", "HEAD^", "--", "test999.txt"}, "", nil).
|
||||
ExpectGitArgs([]string{"commit", "--amend", "--no-edit", "--allow-empty"}, "", nil).
|
||||
ExpectGitArgs([]string{"rebase", "--continue"}, "", nil),
|
||||
test: func(err error) {
|
||||
|
||||
@@ -109,6 +109,10 @@ func (self *WorkingTreeCommands) BeforeAndAfterFileForRename(file *models.File)
|
||||
return beforeFile, afterFile, nil
|
||||
}
|
||||
|
||||
func newCheckoutCommand() *GitCommandBuilder {
|
||||
return NewGitCmd("checkout").Config(fmt.Sprintf("core.hooksPath=%s", os.DevNull))
|
||||
}
|
||||
|
||||
// DiscardAllFileChanges directly
|
||||
func (self *WorkingTreeCommands) DiscardAllFileChanges(file *models.File) error {
|
||||
if file.IsRename() {
|
||||
@@ -130,7 +134,7 @@ func (self *WorkingTreeCommands) DiscardAllFileChanges(file *models.File) error
|
||||
|
||||
if file.ShortStatus == "AA" {
|
||||
if err := self.cmd.New(
|
||||
NewGitCmd("checkout").Arg("--ours", "--", file.Name).ToArgv(),
|
||||
newCheckoutCommand().Arg("--ours", "--", file.Name).ToArgv(),
|
||||
).Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -189,7 +193,7 @@ func (self *WorkingTreeCommands) DiscardUnstagedDirChanges(node IFileNode) error
|
||||
return err
|
||||
}
|
||||
|
||||
cmdArgs := NewGitCmd("checkout").Arg("--", node.GetPath()).ToArgv()
|
||||
cmdArgs := newCheckoutCommand().Arg("--", node.GetPath()).ToArgv()
|
||||
if err := self.cmd.New(cmdArgs).Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -222,7 +226,7 @@ func (self *WorkingTreeCommands) RemoveUntrackedDirFiles(node IFileNode) error {
|
||||
}
|
||||
|
||||
func (self *WorkingTreeCommands) DiscardUnstagedFileChanges(file *models.File) error {
|
||||
cmdArgs := NewGitCmd("checkout").Arg("--", file.Name).ToArgv()
|
||||
cmdArgs := newCheckoutCommand().Arg("--", file.Name).ToArgv()
|
||||
return self.cmd.New(cmdArgs).Run()
|
||||
}
|
||||
|
||||
@@ -315,7 +319,7 @@ func (self *WorkingTreeCommands) ShowFileDiffCmdObj(from string, to string, reve
|
||||
|
||||
// CheckoutFile checks out the file for the given commit
|
||||
func (self *WorkingTreeCommands) CheckoutFile(commitHash, fileName string) error {
|
||||
cmdArgs := NewGitCmd("checkout").Arg(commitHash, "--", fileName).
|
||||
cmdArgs := newCheckoutCommand().Arg(commitHash, "--", fileName).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).Run()
|
||||
@@ -323,7 +327,7 @@ func (self *WorkingTreeCommands) CheckoutFile(commitHash, fileName string) error
|
||||
|
||||
// DiscardAnyUnstagedFileChanges discards any unstaged file changes via `git checkout -- .`
|
||||
func (self *WorkingTreeCommands) DiscardAnyUnstagedFileChanges() error {
|
||||
cmdArgs := NewGitCmd("checkout").Arg("--", ".").
|
||||
cmdArgs := newCheckoutCommand().Arg("--", ".").
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).Run()
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package git_commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
@@ -10,6 +12,8 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var disableHooksFlag = fmt.Sprintf("core.hooksPath=%s", os.DevNull)
|
||||
|
||||
func TestWorkingTreeStageFile(t *testing.T) {
|
||||
runner := oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"add", "--", "test.txt"}, "", nil)
|
||||
@@ -113,7 +117,7 @@ func TestWorkingTreeDiscardAllFileChanges(t *testing.T) {
|
||||
},
|
||||
removeFile: func(string) error { return nil },
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"checkout", "--", "test"}, "", errors.New("error")),
|
||||
ExpectGitArgs([]string{"-c", disableHooksFlag, "checkout", "--", "test"}, "", errors.New("error")),
|
||||
expectedError: "error",
|
||||
},
|
||||
{
|
||||
@@ -125,7 +129,7 @@ func TestWorkingTreeDiscardAllFileChanges(t *testing.T) {
|
||||
},
|
||||
removeFile: func(string) error { return nil },
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"checkout", "--", "test"}, "", nil),
|
||||
ExpectGitArgs([]string{"-c", disableHooksFlag, "checkout", "--", "test"}, "", nil),
|
||||
expectedError: "",
|
||||
},
|
||||
{
|
||||
@@ -138,7 +142,7 @@ func TestWorkingTreeDiscardAllFileChanges(t *testing.T) {
|
||||
removeFile: func(string) error { return nil },
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"reset", "--", "test"}, "", nil).
|
||||
ExpectGitArgs([]string{"checkout", "--", "test"}, "", nil),
|
||||
ExpectGitArgs([]string{"-c", disableHooksFlag, "checkout", "--", "test"}, "", nil),
|
||||
expectedError: "",
|
||||
},
|
||||
{
|
||||
@@ -151,7 +155,7 @@ func TestWorkingTreeDiscardAllFileChanges(t *testing.T) {
|
||||
removeFile: func(string) error { return nil },
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"reset", "--", "test"}, "", nil).
|
||||
ExpectGitArgs([]string{"checkout", "--", "test"}, "", nil),
|
||||
ExpectGitArgs([]string{"-c", disableHooksFlag, "checkout", "--", "test"}, "", nil),
|
||||
expectedError: "",
|
||||
},
|
||||
{
|
||||
@@ -428,7 +432,7 @@ func TestWorkingTreeCheckoutFile(t *testing.T) {
|
||||
commitHash: "11af912",
|
||||
fileName: "test999.txt",
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"checkout", "11af912", "--", "test999.txt"}, "", nil),
|
||||
ExpectGitArgs([]string{"-c", disableHooksFlag, "checkout", "11af912", "--", "test999.txt"}, "", nil),
|
||||
test: func(err error) {
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
@@ -438,7 +442,7 @@ func TestWorkingTreeCheckoutFile(t *testing.T) {
|
||||
commitHash: "11af912",
|
||||
fileName: "test999.txt",
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"checkout", "11af912", "--", "test999.txt"}, "", errors.New("error")),
|
||||
ExpectGitArgs([]string{"-c", disableHooksFlag, "checkout", "11af912", "--", "test999.txt"}, "", errors.New("error")),
|
||||
test: func(err error) {
|
||||
assert.Error(t, err)
|
||||
},
|
||||
@@ -468,7 +472,7 @@ func TestWorkingTreeDiscardUnstagedFileChanges(t *testing.T) {
|
||||
testName: "valid case",
|
||||
file: &models.File{Name: "test.txt"},
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"checkout", "--", "test.txt"}, "", nil),
|
||||
ExpectGitArgs([]string{"-c", disableHooksFlag, "checkout", "--", "test.txt"}, "", nil),
|
||||
test: func(err error) {
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
@@ -495,7 +499,7 @@ func TestWorkingTreeDiscardAnyUnstagedFileChanges(t *testing.T) {
|
||||
{
|
||||
testName: "valid case",
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"checkout", "--", "."}, "", nil),
|
||||
ExpectGitArgs([]string{"-c", disableHooksFlag, "checkout", "--", "."}, "", nil),
|
||||
test: func(err error) {
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
|
||||
@@ -306,6 +306,7 @@ func changeElementToSequence(changedContent []byte, path []string) ([]byte, erro
|
||||
|
||||
func changeCommitPrefixesMap(changedContent []byte) ([]byte, error) {
|
||||
return yaml_utils.TransformNode(changedContent, []string{"git", "commitPrefixes"}, func(prefixesNode *yaml.Node) (bool, error) {
|
||||
changedAnyNodes := false
|
||||
if prefixesNode.Kind == yaml.MappingNode {
|
||||
for _, contentNode := range prefixesNode.Content {
|
||||
if contentNode.Kind == yaml.MappingNode {
|
||||
@@ -317,12 +318,11 @@ func changeCommitPrefixesMap(changedContent []byte) ([]byte, error) {
|
||||
Kind: yaml.MappingNode,
|
||||
Content: nodeContentCopy,
|
||||
}}
|
||||
|
||||
changedAnyNodes = true
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
return changedAnyNodes, nil
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func TestCommitPrefixMigrations(t *testing.T) {
|
||||
@@ -14,65 +13,77 @@ func TestCommitPrefixMigrations(t *testing.T) {
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
"Empty String",
|
||||
"",
|
||||
"",
|
||||
name: "Empty String",
|
||||
input: "",
|
||||
expected: "",
|
||||
}, {
|
||||
"Single CommitPrefix Rename",
|
||||
`
|
||||
git:
|
||||
name: "Single CommitPrefix Rename",
|
||||
input: `git:
|
||||
commitPrefix:
|
||||
pattern: "^\\w+-\\w+.*"
|
||||
replace: '[JIRA $0] '`,
|
||||
`
|
||||
git:
|
||||
replace: '[JIRA $0] '
|
||||
`,
|
||||
expected: `git:
|
||||
commitPrefix:
|
||||
- pattern: "^\\w+-\\w+.*"
|
||||
replace: '[JIRA $0] '`,
|
||||
replace: '[JIRA $0] '
|
||||
`,
|
||||
}, {
|
||||
"Complicated CommitPrefixes Rename",
|
||||
`
|
||||
git:
|
||||
name: "Complicated CommitPrefixes Rename",
|
||||
input: `git:
|
||||
commitPrefixes:
|
||||
foo:
|
||||
pattern: "^\\w+-\\w+.*"
|
||||
replace: '[OTHER $0] '
|
||||
CrazyName!@#$^*&)_-)[[}{f{[]:
|
||||
pattern: "^foo.bar*"
|
||||
replace: '[FUN $0] '`,
|
||||
`
|
||||
git:
|
||||
replace: '[FUN $0] '
|
||||
`,
|
||||
expected: `git:
|
||||
commitPrefixes:
|
||||
foo:
|
||||
- pattern: "^\\w+-\\w+.*"
|
||||
replace: '[OTHER $0] '
|
||||
CrazyName!@#$^*&)_-)[[}{f{[]:
|
||||
- pattern: "^foo.bar*"
|
||||
replace: '[FUN $0] '`,
|
||||
foo:
|
||||
- pattern: "^\\w+-\\w+.*"
|
||||
replace: '[OTHER $0] '
|
||||
CrazyName!@#$^*&)_-)[[}{f{[]:
|
||||
- pattern: "^foo.bar*"
|
||||
replace: '[FUN $0] '
|
||||
`,
|
||||
}, {
|
||||
"Incomplete Configuration",
|
||||
"git:",
|
||||
"git:",
|
||||
name: "Incomplete Configuration",
|
||||
input: "git:",
|
||||
expected: "git:",
|
||||
}, {
|
||||
// This test intentionally uses non-standard indentation to test that the migration
|
||||
// does not change the input.
|
||||
name: "No changes made when already migrated",
|
||||
input: `
|
||||
git:
|
||||
commitPrefix:
|
||||
- pattern: "Hello World"
|
||||
replace: "Goodbye"
|
||||
commitPrefixes:
|
||||
foo:
|
||||
- pattern: "^\\w+-\\w+.*"
|
||||
replace: '[JIRA $0] '`,
|
||||
expected: `
|
||||
git:
|
||||
commitPrefix:
|
||||
- pattern: "Hello World"
|
||||
replace: "Goodbye"
|
||||
commitPrefixes:
|
||||
foo:
|
||||
- pattern: "^\\w+-\\w+.*"
|
||||
replace: '[JIRA $0] '`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.name, func(t *testing.T) {
|
||||
expectedConfig := GetDefaultConfig()
|
||||
err := yaml.Unmarshal([]byte(s.expected), expectedConfig)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
actual, err := computeMigratedConfig("path doesn't matter", []byte(s.input))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
actualConfig := GetDefaultConfig()
|
||||
err = yaml.Unmarshal(actual, actualConfig)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
assert.Equal(t, expectedConfig, actualConfig)
|
||||
assert.Equal(t, s.expected, string(actual))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -557,8 +557,8 @@ type OSConfig struct {
|
||||
EditAtLineAndWait string `yaml:"editAtLineAndWait,omitempty"`
|
||||
|
||||
// Whether lazygit suspends until an edit process returns
|
||||
// Pointer to bool so that we can distinguish unset (nil) from false.
|
||||
// We're naming this `editInTerminal` for backwards compatibility
|
||||
// [dev] Pointer to bool so that we can distinguish unset (nil) from false.
|
||||
// [dev] We're naming this `editInTerminal` for backwards compatibility
|
||||
SuspendOnEdit *bool `yaml:"editInTerminal,omitempty"`
|
||||
|
||||
// For opening a directory in an editor
|
||||
|
||||
@@ -36,25 +36,25 @@ func (self *GlobalController) GetKeybindings(opts types.KeybindingsOpts) []*type
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.CreateRebaseOptionsMenu),
|
||||
Handler: self.c.Helpers().MergeAndRebase.CreateRebaseOptionsMenu,
|
||||
Handler: opts.Guards.NoPopupPanel(self.c.Helpers().MergeAndRebase.CreateRebaseOptionsMenu),
|
||||
Description: self.c.Tr.ViewMergeRebaseOptions,
|
||||
Tooltip: self.c.Tr.ViewMergeRebaseOptionsTooltip,
|
||||
OpensMenu: true,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.Refresh),
|
||||
Handler: self.refresh,
|
||||
Handler: opts.Guards.NoPopupPanel(self.refresh),
|
||||
Description: self.c.Tr.Refresh,
|
||||
Tooltip: self.c.Tr.RefreshTooltip,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.NextScreenMode),
|
||||
Handler: self.nextScreenMode,
|
||||
Handler: opts.Guards.NoPopupPanel(self.nextScreenMode),
|
||||
Description: self.c.Tr.NextScreenMode,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.PrevScreenMode),
|
||||
Handler: self.prevScreenMode,
|
||||
Handler: opts.Guards.NoPopupPanel(self.prevScreenMode),
|
||||
Description: self.c.Tr.PrevScreenMode,
|
||||
},
|
||||
{
|
||||
@@ -78,21 +78,21 @@ func (self *GlobalController) GetKeybindings(opts types.KeybindingsOpts) []*type
|
||||
{
|
||||
ViewName: "",
|
||||
Key: opts.GetKey(opts.Config.Universal.FilteringMenu),
|
||||
Handler: self.createFilteringMenu,
|
||||
Handler: opts.Guards.NoPopupPanel(self.createFilteringMenu),
|
||||
Description: self.c.Tr.OpenFilteringMenu,
|
||||
Tooltip: self.c.Tr.OpenFilteringMenuTooltip,
|
||||
OpensMenu: true,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.DiffingMenu),
|
||||
Handler: self.createDiffingMenu,
|
||||
Handler: opts.Guards.NoPopupPanel(self.createDiffingMenu),
|
||||
Description: self.c.Tr.ViewDiffingOptions,
|
||||
Tooltip: self.c.Tr.ViewDiffingOptionsTooltip,
|
||||
OpensMenu: true,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.DiffingMenuAlt),
|
||||
Handler: self.createDiffingMenu,
|
||||
Handler: opts.Guards.NoPopupPanel(self.createDiffingMenu),
|
||||
Description: self.c.Tr.ViewDiffingOptions,
|
||||
Tooltip: self.c.Tr.ViewDiffingOptionsTooltip,
|
||||
OpensMenu: true,
|
||||
|
||||
@@ -490,9 +490,9 @@ func (self *RefreshHelper) refreshBranches(refreshWorktrees bool, keepBranchSele
|
||||
self.refreshView(self.c.Contexts().Worktrees)
|
||||
}
|
||||
|
||||
self.refreshView(self.c.Contexts().Branches)
|
||||
|
||||
if !keepBranchSelectionIndex && prevSelectedBranch != nil {
|
||||
self.searchHelper.ReApplyFilter(self.c.Contexts().Branches)
|
||||
|
||||
_, idx, found := lo.FindIndexOf(self.c.Contexts().Branches.GetItems(),
|
||||
func(b *models.Branch) bool { return b.Name == prevSelectedBranch.Name })
|
||||
if found {
|
||||
@@ -500,6 +500,8 @@ func (self *RefreshHelper) refreshBranches(refreshWorktrees bool, keepBranchSele
|
||||
}
|
||||
}
|
||||
|
||||
self.refreshView(self.c.Contexts().Branches)
|
||||
|
||||
// Need to re-render the commits view because the visualization of local
|
||||
// branch heads might have changed
|
||||
self.c.Mutexes().LocalCommitsMutex.Lock()
|
||||
|
||||
@@ -2,6 +2,7 @@ package helpers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazycore/pkg/boxlayout"
|
||||
@@ -237,14 +238,14 @@ func mainSectionChildren(args WindowArrangementArgs) []*boxlayout.Box {
|
||||
}
|
||||
|
||||
func getMidSectionWeights(args WindowArrangementArgs) (int, int) {
|
||||
// we originally specified this as a ratio i.e. .20 would correspond to a weight of 1 against 4
|
||||
sidePanelWidthRatio := args.UserConfig.Gui.SidePanelWidth
|
||||
// we could make this better by creating ratios like 2:3 rather than always 1:something
|
||||
mainSectionWeight := int(1/sidePanelWidthRatio) - 1
|
||||
sideSectionWeight := 1
|
||||
// Using 120 so that the default of 0.3333 will remain consistent with previous behavior
|
||||
const maxColumnCount = 120
|
||||
mainSectionWeight := int(math.Round(maxColumnCount * (1 - sidePanelWidthRatio)))
|
||||
sideSectionWeight := int(math.Round(maxColumnCount * sidePanelWidthRatio))
|
||||
|
||||
if splitMainPanelSideBySide(args) {
|
||||
mainSectionWeight = 5 // need to shrink side panel to make way for main panels if side-by-side
|
||||
mainSectionWeight = sideSectionWeight * 5 // need to shrink side panel to make way for main panels if side-by-side
|
||||
}
|
||||
|
||||
if args.CurrentWindow == "main" || args.CurrentWindow == "secondary" {
|
||||
@@ -254,9 +255,9 @@ func getMidSectionWeights(args WindowArrangementArgs) (int, int) {
|
||||
} else {
|
||||
if args.ScreenMode == types.SCREEN_HALF {
|
||||
if args.UserConfig.Gui.EnlargedSideViewLocation == "top" {
|
||||
mainSectionWeight = 2
|
||||
mainSectionWeight = sideSectionWeight * 2
|
||||
} else {
|
||||
mainSectionWeight = 1
|
||||
mainSectionWeight = sideSectionWeight
|
||||
}
|
||||
} else if args.ScreenMode == types.SCREEN_FULL {
|
||||
mainSectionWeight = 0
|
||||
|
||||
@@ -202,6 +202,86 @@ func TestGetWindowDimensions(t *testing.T) {
|
||||
B: information
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "0.5 SidePanelWidth",
|
||||
mutateArgs: func(args *WindowArrangementArgs) {
|
||||
args.UserConfig.Gui.SidePanelWidth = 0.5
|
||||
},
|
||||
expected: `
|
||||
╭status──────────────────────────────╮╭main───────────────────────────────╮
|
||||
│ ││ │
|
||||
╰────────────────────────────────────╯│ │
|
||||
╭files───────────────────────────────╮│ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
╰────────────────────────────────────╯│ │
|
||||
╭branches────────────────────────────╮│ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
╰────────────────────────────────────╯│ │
|
||||
╭commits─────────────────────────────╮│ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
╰────────────────────────────────────╯│ │
|
||||
╭stash───────────────────────────────╮│ │
|
||||
│ ││ │
|
||||
╰────────────────────────────────────╯╰───────────────────────────────────╯
|
||||
<options──────────────────────────────────────────────────────>A<B────────>
|
||||
A: statusSpacer1
|
||||
B: information
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "0.8 SidePanelWidth",
|
||||
mutateArgs: func(args *WindowArrangementArgs) {
|
||||
args.UserConfig.Gui.SidePanelWidth = 0.8
|
||||
},
|
||||
expected: `
|
||||
╭status────────────────────────────────────────────────────╮╭main─────────╮
|
||||
│ ││ │
|
||||
╰──────────────────────────────────────────────────────────╯│ │
|
||||
╭files─────────────────────────────────────────────────────╮│ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
╰──────────────────────────────────────────────────────────╯│ │
|
||||
╭branches──────────────────────────────────────────────────╮│ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
╰──────────────────────────────────────────────────────────╯│ │
|
||||
╭commits───────────────────────────────────────────────────╮│ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
╰──────────────────────────────────────────────────────────╯│ │
|
||||
╭stash─────────────────────────────────────────────────────╮│ │
|
||||
│ ││ │
|
||||
╰──────────────────────────────────────────────────────────╯╰─────────────╯
|
||||
<options──────────────────────────────────────────────────────>A<B────────>
|
||||
A: statusSpacer1
|
||||
B: information
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "half screen mode, enlargedSideViewLocation left",
|
||||
mutateArgs: func(args *WindowArrangementArgs) {
|
||||
|
||||
@@ -42,7 +42,7 @@ func (self *JumpToSideWindowController) GetKeybindings(opts types.KeybindingsOpt
|
||||
// by default the keys are 1, 2, 3, etc
|
||||
Key: opts.GetKey(opts.Config.Universal.JumpToBlock[index]),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: self.goToSideWindow(window),
|
||||
Handler: opts.Guards.NoPopupPanel(self.goToSideWindow(window)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1012,9 +1012,16 @@ func (gui *Gui) showIntroPopupMessage() {
|
||||
return err
|
||||
}
|
||||
|
||||
introMessage := utils.ResolvePlaceholderString(
|
||||
gui.c.Tr.IntroPopupMessage,
|
||||
map[string]string{
|
||||
"confirmationKey": gui.c.UserConfig().Keybinding.Universal.Confirm,
|
||||
},
|
||||
)
|
||||
|
||||
gui.c.Confirm(types.ConfirmOpts{
|
||||
Title: "",
|
||||
Prompt: gui.c.Tr.IntroPopupMessage,
|
||||
Prompt: introMessage,
|
||||
HandleConfirm: onConfirm,
|
||||
HandleClose: onConfirm,
|
||||
})
|
||||
|
||||
@@ -83,7 +83,7 @@ func (self *Gui) GetInitialKeybindings() ([]*types.Binding, []*gocui.ViewMouseBi
|
||||
{
|
||||
ViewName: "",
|
||||
Key: opts.GetKey(opts.Config.Universal.OpenRecentRepos),
|
||||
Handler: self.helpers.Repos.CreateRecentReposMenu,
|
||||
Handler: opts.Guards.NoPopupPanel(self.helpers.Repos.CreateRecentReposMenu),
|
||||
Description: self.c.Tr.SwitchRepo,
|
||||
},
|
||||
{
|
||||
@@ -195,7 +195,7 @@ func (self *Gui) GetInitialKeybindings() ([]*types.Binding, []*gocui.ViewMouseBi
|
||||
{
|
||||
ViewName: "",
|
||||
Key: opts.GetKey(opts.Config.Universal.ExtrasMenu),
|
||||
Handler: self.handleCreateExtrasMenuPanel,
|
||||
Handler: opts.Guards.NoPopupPanel(self.handleCreateExtrasMenuPanel),
|
||||
Description: self.c.Tr.OpenCommandLogMenu,
|
||||
Tooltip: self.c.Tr.OpenCommandLogMenuTooltip,
|
||||
OpensMenu: true,
|
||||
@@ -330,14 +330,14 @@ func (self *Gui) GetInitialKeybindings() ([]*types.Binding, []*gocui.ViewMouseBi
|
||||
{
|
||||
ViewName: "",
|
||||
Key: opts.GetKey(opts.Config.Universal.NextTab),
|
||||
Handler: self.handleNextTab,
|
||||
Handler: opts.Guards.NoPopupPanel(self.handleNextTab),
|
||||
Description: self.c.Tr.NextTab,
|
||||
Tag: "navigation",
|
||||
},
|
||||
{
|
||||
ViewName: "",
|
||||
Key: opts.GetKey(opts.Config.Universal.PrevTab),
|
||||
Handler: self.handlePrevTab,
|
||||
Handler: opts.Guards.NoPopupPanel(self.handlePrevTab),
|
||||
Description: self.c.Tr.PrevTab,
|
||||
Tag: "navigation",
|
||||
},
|
||||
|
||||
@@ -1019,6 +1019,8 @@ Thanks for using lazygit! Seriously you rock. Three things to share with you:
|
||||
You can also sponsor me and tell me what to work on by clicking the donate
|
||||
button at the bottom right.
|
||||
Or even just star the repo to share the love!
|
||||
|
||||
Press {{confirmationKey}} to get started.
|
||||
`
|
||||
|
||||
const englishDeprecatedEditConfigWarning = `
|
||||
@@ -1436,7 +1438,7 @@ func EnglishTranslationSet() *TranslationSet {
|
||||
DiscardOldFileChangeTooltip: "Discard this commit's changes to this file. This runs an interactive rebase in the background, so you may get a merge conflict if a later commit also changes this file.",
|
||||
DiscardFileChangesTitle: "Discard file changes",
|
||||
DiscardFileChangesPrompt: "Are you sure you want to remove changes to the selected file(s) from this commit?\n\nThis action will start a rebase, reverting these file changes. Be aware that if subsequent commits depend on these changes, you may need to resolve conflicts.\nNote: This will also reset any active custom patches.",
|
||||
DisabledForGPG: "Feature not available for users using GPG",
|
||||
DisabledForGPG: "Feature not available for users using GPG.\n\nIf you are using a passphrase agent (e.g. gpg-agent) so that you don't have to type your passphrase when signing, you can enable this feature by adding\n\ngit:\n overrideGpg: true\n\nto your lazygit config file.",
|
||||
CreateRepo: "Not in a git repository. Create a new git repository? (y/n): ",
|
||||
BareRepo: "You've attempted to open Lazygit in a bare repo but Lazygit does not yet support bare repos. Open most recent repo? (y/n) ",
|
||||
InitialBranch: "Branch name? (leave empty for git's default): ",
|
||||
|
||||
@@ -371,7 +371,6 @@ var tests = []*components.IntegrationTest{
|
||||
tag.Reset,
|
||||
ui.Accordion,
|
||||
ui.DisableSwitchTabWithPanelJumpKeys,
|
||||
ui.DoublePopup,
|
||||
ui.EmptyMenu,
|
||||
ui.KeybindingSuggestionsWhenSwitchingRepos,
|
||||
ui.ModeSpecificKeybindingSuggestions,
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var DoublePopup = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Open a popup from within another popup and assert you can escape back to the side panels",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.EmptyCommit("one")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Branches().
|
||||
Focus().
|
||||
// arbitrarily bringing up a popup
|
||||
PressPrimaryAction()
|
||||
|
||||
t.ExpectPopup().Alert().
|
||||
Title(Contains("Error")).
|
||||
Content(Contains("You have already checked out this branch"))
|
||||
|
||||
t.GlobalPress(keys.Universal.OpenRecentRepos)
|
||||
|
||||
t.ExpectPopup().Menu().Title(Contains("Recent repositories")).Cancel()
|
||||
|
||||
t.Views().Branches().IsFocused()
|
||||
|
||||
t.Views().Files().Focus()
|
||||
},
|
||||
})
|
||||
@@ -7,41 +7,87 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazycore/pkg/utils"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/karimkhaleel/jsonschema"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
func GetSchemaDir() string {
|
||||
return utils.GetLazyRootDirectory() + "/schema"
|
||||
}
|
||||
|
||||
func GenerateSchema() {
|
||||
func GenerateSchema() *jsonschema.Schema {
|
||||
schema := customReflect(&config.UserConfig{})
|
||||
obj, _ := json.MarshalIndent(schema, "", " ")
|
||||
obj = append(obj, '\n')
|
||||
|
||||
if err := os.WriteFile(GetSchemaDir()+"/config.json", obj, 0o644); err != nil {
|
||||
fmt.Println("Error writing to file:", err)
|
||||
return
|
||||
return nil
|
||||
}
|
||||
return schema
|
||||
}
|
||||
|
||||
func getSubSchema(rootSchema, parentSchema *jsonschema.Schema, key string) *jsonschema.Schema {
|
||||
subSchema, found := parentSchema.Properties.Get(key)
|
||||
if !found {
|
||||
panic(fmt.Sprintf("Failed to find subSchema at %s on parent", key))
|
||||
}
|
||||
|
||||
// This means the schema is defined on the rootSchema's Definitions
|
||||
if subSchema.Ref != "" {
|
||||
key, _ = strings.CutPrefix(subSchema.Ref, "#/$defs/")
|
||||
refSchema, ok := rootSchema.Definitions[key]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("Failed to find #/$defs/%s", key))
|
||||
}
|
||||
refSchema.Description = subSchema.Description
|
||||
return refSchema
|
||||
}
|
||||
|
||||
return subSchema
|
||||
}
|
||||
|
||||
func customReflect(v *config.UserConfig) *jsonschema.Schema {
|
||||
defaultConfig := config.GetDefaultConfig()
|
||||
r := &jsonschema.Reflector{FieldNameTag: "yaml", RequiredFromJSONSchemaTags: true, DoNotReference: true}
|
||||
r := &jsonschema.Reflector{FieldNameTag: "yaml", RequiredFromJSONSchemaTags: true}
|
||||
if err := r.AddGoComments("github.com/jesseduffield/lazygit/pkg/config", "../config"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
filterOutDevComments(r)
|
||||
schema := r.Reflect(v)
|
||||
defaultConfig := config.GetDefaultConfig()
|
||||
userConfigSchema := schema.Definitions["UserConfig"]
|
||||
|
||||
setDefaultVals(defaultConfig, schema)
|
||||
defaultValue := reflect.ValueOf(defaultConfig).Elem()
|
||||
|
||||
yamlToFieldNames := lo.Invert(userConfigSchema.OriginalPropertiesMapping)
|
||||
|
||||
for pair := userConfigSchema.Properties.Oldest(); pair != nil; pair = pair.Next() {
|
||||
yamlName := pair.Key
|
||||
fieldName := yamlToFieldNames[yamlName]
|
||||
|
||||
subSchema := getSubSchema(schema, userConfigSchema, yamlName)
|
||||
|
||||
setDefaultVals(schema, subSchema, defaultValue.FieldByName(fieldName).Interface())
|
||||
}
|
||||
|
||||
return schema
|
||||
}
|
||||
|
||||
func setDefaultVals(defaults any, schema *jsonschema.Schema) {
|
||||
func filterOutDevComments(r *jsonschema.Reflector) {
|
||||
for k, v := range r.CommentMap {
|
||||
commentLines := strings.Split(v, "\n")
|
||||
filteredCommentLines := lo.Filter(commentLines, func(line string, _ int) bool {
|
||||
return !strings.Contains(line, "[dev]")
|
||||
})
|
||||
r.CommentMap[k] = strings.Join(filteredCommentLines, "\n")
|
||||
}
|
||||
}
|
||||
|
||||
func setDefaultVals(rootSchema, schema *jsonschema.Schema, defaults any) {
|
||||
t := reflect.TypeOf(defaults)
|
||||
v := reflect.ValueOf(defaults)
|
||||
|
||||
@@ -50,6 +96,24 @@ func setDefaultVals(defaults any, schema *jsonschema.Schema) {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
k := t.Kind()
|
||||
_ = k
|
||||
|
||||
switch t.Kind() {
|
||||
case reflect.Bool:
|
||||
schema.Default = v.Bool()
|
||||
case reflect.Int:
|
||||
schema.Default = v.Int()
|
||||
case reflect.String:
|
||||
schema.Default = v.String()
|
||||
default:
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
if t.Kind() != reflect.Struct {
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
value := v.Field(i).Interface()
|
||||
parentKey := t.Field(i).Name
|
||||
@@ -59,13 +123,10 @@ func setDefaultVals(defaults any, schema *jsonschema.Schema) {
|
||||
continue
|
||||
}
|
||||
|
||||
subSchema, ok := schema.Properties.Get(key)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
subSchema := getSubSchema(rootSchema, schema, key)
|
||||
|
||||
if isStruct(value) {
|
||||
setDefaultVals(value, subSchema)
|
||||
setDefaultVals(rootSchema, subSchema, value)
|
||||
} else if !isZeroValue(value) {
|
||||
subSchema.Default = value
|
||||
}
|
||||
|
||||
@@ -2,14 +2,13 @@ package jsonschema
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/iancoleman/orderedmap"
|
||||
"github.com/jesseduffield/lazycore/pkg/utils"
|
||||
"github.com/karimkhaleel/jsonschema"
|
||||
"github.com/samber/lo"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
@@ -106,16 +105,7 @@ func (n *Node) MarshalYAML() (interface{}, error) {
|
||||
setComment(&keyNode, n.Description)
|
||||
}
|
||||
|
||||
if n.Default != nil {
|
||||
valueNode := yaml.Node{
|
||||
Kind: yaml.ScalarNode,
|
||||
}
|
||||
err := valueNode.Encode(n.Default)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
node.Content = append(node.Content, &keyNode, &valueNode)
|
||||
} else if len(n.Children) > 0 {
|
||||
if len(n.Children) > 0 {
|
||||
childrenNode := yaml.Node{
|
||||
Kind: yaml.MappingNode,
|
||||
}
|
||||
@@ -136,62 +126,20 @@ func (n *Node) MarshalYAML() (interface{}, error) {
|
||||
childrenNode.Content = append(childrenNode.Content, childYaml.(*yaml.Node).Content...)
|
||||
}
|
||||
node.Content = append(node.Content, &keyNode, &childrenNode)
|
||||
} else {
|
||||
valueNode := yaml.Node{
|
||||
Kind: yaml.ScalarNode,
|
||||
}
|
||||
err := valueNode.Encode(n.Default)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
node.Content = append(node.Content, &keyNode, &valueNode)
|
||||
}
|
||||
|
||||
return &node, nil
|
||||
}
|
||||
|
||||
func getDescription(v *orderedmap.OrderedMap) string {
|
||||
description, ok := v.Get("description")
|
||||
if !ok {
|
||||
description = ""
|
||||
}
|
||||
return description.(string)
|
||||
}
|
||||
|
||||
func getDefault(v *orderedmap.OrderedMap) (error, any) {
|
||||
defaultValue, ok := v.Get("default")
|
||||
if ok {
|
||||
return nil, defaultValue
|
||||
}
|
||||
|
||||
dataType, ok := v.Get("type")
|
||||
if ok {
|
||||
dataTypeString := dataType.(string)
|
||||
if dataTypeString == "string" {
|
||||
return nil, ""
|
||||
}
|
||||
}
|
||||
|
||||
return errors.New("Failed to get default value"), nil
|
||||
}
|
||||
|
||||
func parseNode(parent *Node, name string, value *orderedmap.OrderedMap) {
|
||||
description := getDescription(value)
|
||||
err, defaultValue := getDefault(value)
|
||||
if err == nil {
|
||||
leaf := &Node{Name: name, Description: description, Default: defaultValue}
|
||||
parent.Children = append(parent.Children, leaf)
|
||||
}
|
||||
|
||||
properties, ok := value.Get("properties")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
orderedProperties := properties.(orderedmap.OrderedMap)
|
||||
|
||||
node := &Node{Name: name, Description: description}
|
||||
parent.Children = append(parent.Children, node)
|
||||
|
||||
keys := orderedProperties.Keys()
|
||||
for _, name := range keys {
|
||||
value, _ := orderedProperties.Get(name)
|
||||
typedValue := value.(orderedmap.OrderedMap)
|
||||
parseNode(node, name, &typedValue)
|
||||
}
|
||||
}
|
||||
|
||||
func writeToConfigDocs(config []byte) error {
|
||||
configPath := utils.GetLazyRootDirectory() + "/docs/Config.md"
|
||||
markdown, err := os.ReadFile(configPath)
|
||||
@@ -222,31 +170,12 @@ func writeToConfigDocs(config []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func GenerateConfigDocs() {
|
||||
content, err := os.ReadFile(GetSchemaDir() + "/config.json")
|
||||
if err != nil {
|
||||
panic("Error reading config.json")
|
||||
func GenerateConfigDocs(schema *jsonschema.Schema) {
|
||||
rootNode := &Node{
|
||||
Children: make([]*Node, 0),
|
||||
}
|
||||
|
||||
schema := orderedmap.New()
|
||||
|
||||
err = json.Unmarshal(content, &schema)
|
||||
if err != nil {
|
||||
panic("Failed to unmarshal config.json")
|
||||
}
|
||||
|
||||
root, ok := schema.Get("properties")
|
||||
if !ok {
|
||||
panic("properties key not found in schema")
|
||||
}
|
||||
orderedRoot := root.(orderedmap.OrderedMap)
|
||||
|
||||
rootNode := Node{}
|
||||
for _, name := range orderedRoot.Keys() {
|
||||
value, _ := orderedRoot.Get(name)
|
||||
typedValue := value.(orderedmap.OrderedMap)
|
||||
parseNode(&rootNode, name, &typedValue)
|
||||
}
|
||||
recurseOverSchema(schema, schema.Definitions["UserConfig"], rootNode)
|
||||
|
||||
var buffer bytes.Buffer
|
||||
encoder := yaml.NewEncoder(&buffer)
|
||||
@@ -262,8 +191,51 @@ func GenerateConfigDocs() {
|
||||
|
||||
config := prepareMarshalledConfig(buffer)
|
||||
|
||||
err = writeToConfigDocs(config)
|
||||
err := writeToConfigDocs(config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func recurseOverSchema(rootSchema, schema *jsonschema.Schema, parent *Node) {
|
||||
if schema == nil || schema.Properties == nil || schema.Properties.Len() == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for pair := schema.Properties.Oldest(); pair != nil; pair = pair.Next() {
|
||||
subSchema := getSubSchema(rootSchema, schema, pair.Key)
|
||||
|
||||
// Skip empty objects
|
||||
if subSchema.Type == "object" && subSchema.Properties == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip empty arrays
|
||||
if isZeroValue(subSchema.Default) && subSchema.Type == "array" {
|
||||
continue
|
||||
}
|
||||
|
||||
node := Node{
|
||||
Name: pair.Key,
|
||||
Description: subSchema.Description,
|
||||
Default: getZeroValue(subSchema.Default, subSchema.Type),
|
||||
}
|
||||
parent.Children = append(parent.Children, &node)
|
||||
recurseOverSchema(rootSchema, subSchema, &node)
|
||||
}
|
||||
}
|
||||
|
||||
func getZeroValue(val any, t string) any {
|
||||
if !isZeroValue(val) {
|
||||
return val
|
||||
}
|
||||
|
||||
switch t {
|
||||
case "string":
|
||||
return ""
|
||||
case "boolean":
|
||||
return false
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,6 @@ import (
|
||||
|
||||
func main() {
|
||||
fmt.Printf("Generating jsonschema in %s...\n", jsonschema.GetSchemaDir())
|
||||
jsonschema.GenerateSchema()
|
||||
jsonschema.GenerateConfigDocs()
|
||||
schema := jsonschema.GenerateSchema()
|
||||
jsonschema.GenerateConfigDocs(schema)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package yaml_utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
@@ -34,7 +35,7 @@ func UpdateYamlValue(yamlBytes []byte, path []string, value string) ([]byte, err
|
||||
}
|
||||
|
||||
// Convert the updated YAML node back to YAML bytes.
|
||||
updatedYAMLBytes, err := yaml.Marshal(body)
|
||||
updatedYAMLBytes, err := yamlMarshal(body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert YAML node to bytes: %w", err)
|
||||
}
|
||||
@@ -126,7 +127,7 @@ func TransformNode(yamlBytes []byte, path []string, transform func(node *yaml.No
|
||||
}
|
||||
|
||||
// Convert the updated YAML node back to YAML bytes.
|
||||
updatedYAMLBytes, err := yaml.Marshal(body)
|
||||
updatedYAMLBytes, err := yamlMarshal(body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert YAML node to bytes: %w", err)
|
||||
}
|
||||
@@ -170,7 +171,7 @@ func RenameYamlKey(yamlBytes []byte, path []string, newKey string) ([]byte, erro
|
||||
}
|
||||
|
||||
// Convert the updated YAML node back to YAML bytes.
|
||||
updatedYAMLBytes, err := yaml.Marshal(body)
|
||||
updatedYAMLBytes, err := yamlMarshal(body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert YAML node to bytes: %w", err)
|
||||
}
|
||||
@@ -227,7 +228,7 @@ func Walk(yamlBytes []byte, callback func(node *yaml.Node, path string) bool) ([
|
||||
}
|
||||
|
||||
// Convert the updated YAML node back to YAML bytes.
|
||||
updatedYAMLBytes, err := yaml.Marshal(body)
|
||||
updatedYAMLBytes, err := yamlMarshal(body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert YAML node to bytes: %w", err)
|
||||
}
|
||||
@@ -273,3 +274,12 @@ func walk(node *yaml.Node, path string, callback func(*yaml.Node, string) bool)
|
||||
|
||||
return didChange, nil
|
||||
}
|
||||
|
||||
func yamlMarshal(node *yaml.Node) ([]byte, error) {
|
||||
var buffer bytes.Buffer
|
||||
encoder := yaml.NewEncoder(&buffer)
|
||||
encoder.SetIndent(2)
|
||||
|
||||
err := encoder.Encode(node)
|
||||
return buffer.Bytes(), err
|
||||
}
|
||||
|
||||
@@ -50,12 +50,11 @@ func TestUpdateYamlValue(t *testing.T) {
|
||||
expectedErr: "",
|
||||
},
|
||||
{
|
||||
name: "nested update",
|
||||
in: "foo:\n bar: baz\n",
|
||||
path: []string{"foo", "bar"},
|
||||
value: "qux",
|
||||
// indentation is not preserved. See https://github.com/go-yaml/yaml/issues/899
|
||||
expectedOut: "foo:\n bar: qux\n",
|
||||
name: "nested update",
|
||||
in: "foo:\n bar: baz\n",
|
||||
path: []string{"foo", "bar"},
|
||||
value: "qux",
|
||||
expectedOut: "foo:\n bar: qux\n",
|
||||
expectedErr: "",
|
||||
},
|
||||
{
|
||||
@@ -63,7 +62,7 @@ func TestUpdateYamlValue(t *testing.T) {
|
||||
in: "",
|
||||
path: []string{"foo", "bar", "baz"},
|
||||
value: "qux",
|
||||
expectedOut: "foo:\n bar:\n baz: qux\n",
|
||||
expectedOut: "foo:\n bar:\n baz: qux\n",
|
||||
expectedErr: "",
|
||||
},
|
||||
{
|
||||
@@ -134,21 +133,19 @@ func TestRenameYamlKey(t *testing.T) {
|
||||
expectedErr: "",
|
||||
},
|
||||
{
|
||||
name: "rename key, nested",
|
||||
in: "foo:\n bar: 5\n",
|
||||
path: []string{"foo", "bar"},
|
||||
newKey: "baz",
|
||||
// indentation is not preserved. See https://github.com/go-yaml/yaml/issues/899
|
||||
expectedOut: "foo:\n baz: 5\n",
|
||||
name: "rename key, nested",
|
||||
in: "foo:\n bar: 5\n",
|
||||
path: []string{"foo", "bar"},
|
||||
newKey: "baz",
|
||||
expectedOut: "foo:\n baz: 5\n",
|
||||
expectedErr: "",
|
||||
},
|
||||
{
|
||||
name: "rename non-scalar key",
|
||||
in: "foo:\n bar: 5\n",
|
||||
path: []string{"foo"},
|
||||
newKey: "qux",
|
||||
// indentation is not preserved. See https://github.com/go-yaml/yaml/issues/899
|
||||
expectedOut: "qux:\n bar: 5\n",
|
||||
name: "rename non-scalar key",
|
||||
in: "foo:\n bar: 5\n",
|
||||
path: []string{"foo"},
|
||||
newKey: "qux",
|
||||
expectedOut: "qux:\n bar: 5\n",
|
||||
expectedErr: "",
|
||||
},
|
||||
{
|
||||
@@ -288,8 +285,7 @@ func TestWalk_inPlaceChanges(t *testing.T) {
|
||||
}
|
||||
return false
|
||||
},
|
||||
// indentation is not preserved. See https://github.com/go-yaml/yaml/issues/899
|
||||
expectedOut: "x:\n y: 7\n",
|
||||
expectedOut: "x:\n y: 7\n",
|
||||
},
|
||||
{
|
||||
name: "change array value",
|
||||
@@ -301,8 +297,7 @@ func TestWalk_inPlaceChanges(t *testing.T) {
|
||||
}
|
||||
return false
|
||||
},
|
||||
// indentation is not preserved. See https://github.com/go-yaml/yaml/issues/899
|
||||
expectedOut: "x:\n - y: 7\n",
|
||||
expectedOut: "x:\n - y: 7\n",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -366,8 +361,8 @@ foo:
|
||||
path: []string{"foo", "bar"},
|
||||
transform: transformIntValueToString,
|
||||
expectedOut: `foo:
|
||||
bar: "2"
|
||||
`, // Note the indentiation change and newlines because of how it re-marshalls
|
||||
bar: "2"
|
||||
`, // Note the trailing newline changes because of how it re-marshalls
|
||||
},
|
||||
{
|
||||
name: "Does nothing when already transformed",
|
||||
|
||||
2738
schema/config.json
2738
schema/config.json
File diff suppressed because it is too large
Load Diff
21
vendor/github.com/iancoleman/orderedmap/LICENSE
generated
vendored
21
vendor/github.com/iancoleman/orderedmap/LICENSE
generated
vendored
@@ -1,21 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 Ian Coleman
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, Subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or Substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
266
vendor/github.com/iancoleman/orderedmap/orderedmap.go
generated
vendored
266
vendor/github.com/iancoleman/orderedmap/orderedmap.go
generated
vendored
@@ -1,266 +0,0 @@
|
||||
package orderedmap
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type Pair struct {
|
||||
key string
|
||||
value interface{}
|
||||
}
|
||||
|
||||
func (kv *Pair) Key() string {
|
||||
return kv.key
|
||||
}
|
||||
|
||||
func (kv *Pair) Value() interface{} {
|
||||
return kv.value
|
||||
}
|
||||
|
||||
type ByPair struct {
|
||||
Pairs []*Pair
|
||||
LessFunc func(a *Pair, j *Pair) bool
|
||||
}
|
||||
|
||||
func (a ByPair) Len() int { return len(a.Pairs) }
|
||||
func (a ByPair) Swap(i, j int) { a.Pairs[i], a.Pairs[j] = a.Pairs[j], a.Pairs[i] }
|
||||
func (a ByPair) Less(i, j int) bool { return a.LessFunc(a.Pairs[i], a.Pairs[j]) }
|
||||
|
||||
type OrderedMap struct {
|
||||
keys []string
|
||||
values map[string]interface{}
|
||||
escapeHTML bool
|
||||
}
|
||||
|
||||
func New() *OrderedMap {
|
||||
o := OrderedMap{}
|
||||
o.keys = []string{}
|
||||
o.values = map[string]interface{}{}
|
||||
o.escapeHTML = true
|
||||
return &o
|
||||
}
|
||||
|
||||
func (o *OrderedMap) SetEscapeHTML(on bool) {
|
||||
o.escapeHTML = on
|
||||
}
|
||||
|
||||
func (o *OrderedMap) Get(key string) (interface{}, bool) {
|
||||
val, exists := o.values[key]
|
||||
return val, exists
|
||||
}
|
||||
|
||||
func (o *OrderedMap) Set(key string, value interface{}) {
|
||||
_, exists := o.values[key]
|
||||
if !exists {
|
||||
o.keys = append(o.keys, key)
|
||||
}
|
||||
o.values[key] = value
|
||||
}
|
||||
|
||||
func (o *OrderedMap) Delete(key string) {
|
||||
// check key is in use
|
||||
_, ok := o.values[key]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
// remove from keys
|
||||
for i, k := range o.keys {
|
||||
if k == key {
|
||||
o.keys = append(o.keys[:i], o.keys[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
// remove from values
|
||||
delete(o.values, key)
|
||||
}
|
||||
|
||||
func (o *OrderedMap) Keys() []string {
|
||||
return o.keys
|
||||
}
|
||||
|
||||
func (o *OrderedMap) Values() map[string]interface{} {
|
||||
return o.values
|
||||
}
|
||||
|
||||
// SortKeys Sort the map keys using your sort func
|
||||
func (o *OrderedMap) SortKeys(sortFunc func(keys []string)) {
|
||||
sortFunc(o.keys)
|
||||
}
|
||||
|
||||
// Sort Sort the map using your sort func
|
||||
func (o *OrderedMap) Sort(lessFunc func(a *Pair, b *Pair) bool) {
|
||||
pairs := make([]*Pair, len(o.keys))
|
||||
for i, key := range o.keys {
|
||||
pairs[i] = &Pair{key, o.values[key]}
|
||||
}
|
||||
|
||||
sort.Sort(ByPair{pairs, lessFunc})
|
||||
|
||||
for i, pair := range pairs {
|
||||
o.keys[i] = pair.key
|
||||
}
|
||||
}
|
||||
|
||||
func (o *OrderedMap) UnmarshalJSON(b []byte) error {
|
||||
if o.values == nil {
|
||||
o.values = map[string]interface{}{}
|
||||
}
|
||||
err := json.Unmarshal(b, &o.values)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dec := json.NewDecoder(bytes.NewReader(b))
|
||||
if _, err = dec.Token(); err != nil { // skip '{'
|
||||
return err
|
||||
}
|
||||
o.keys = make([]string, 0, len(o.values))
|
||||
return decodeOrderedMap(dec, o)
|
||||
}
|
||||
|
||||
func decodeOrderedMap(dec *json.Decoder, o *OrderedMap) error {
|
||||
hasKey := make(map[string]bool, len(o.values))
|
||||
for {
|
||||
token, err := dec.Token()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if delim, ok := token.(json.Delim); ok && delim == '}' {
|
||||
return nil
|
||||
}
|
||||
key := token.(string)
|
||||
if hasKey[key] {
|
||||
// duplicate key
|
||||
for j, k := range o.keys {
|
||||
if k == key {
|
||||
copy(o.keys[j:], o.keys[j+1:])
|
||||
break
|
||||
}
|
||||
}
|
||||
o.keys[len(o.keys)-1] = key
|
||||
} else {
|
||||
hasKey[key] = true
|
||||
o.keys = append(o.keys, key)
|
||||
}
|
||||
|
||||
token, err = dec.Token()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if delim, ok := token.(json.Delim); ok {
|
||||
switch delim {
|
||||
case '{':
|
||||
if values, ok := o.values[key].(map[string]interface{}); ok {
|
||||
newMap := OrderedMap{
|
||||
keys: make([]string, 0, len(values)),
|
||||
values: values,
|
||||
escapeHTML: o.escapeHTML,
|
||||
}
|
||||
if err = decodeOrderedMap(dec, &newMap); err != nil {
|
||||
return err
|
||||
}
|
||||
o.values[key] = newMap
|
||||
} else if oldMap, ok := o.values[key].(OrderedMap); ok {
|
||||
newMap := OrderedMap{
|
||||
keys: make([]string, 0, len(oldMap.values)),
|
||||
values: oldMap.values,
|
||||
escapeHTML: o.escapeHTML,
|
||||
}
|
||||
if err = decodeOrderedMap(dec, &newMap); err != nil {
|
||||
return err
|
||||
}
|
||||
o.values[key] = newMap
|
||||
} else if err = decodeOrderedMap(dec, &OrderedMap{}); err != nil {
|
||||
return err
|
||||
}
|
||||
case '[':
|
||||
if values, ok := o.values[key].([]interface{}); ok {
|
||||
if err = decodeSlice(dec, values, o.escapeHTML); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err = decodeSlice(dec, []interface{}{}, o.escapeHTML); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func decodeSlice(dec *json.Decoder, s []interface{}, escapeHTML bool) error {
|
||||
for index := 0; ; index++ {
|
||||
token, err := dec.Token()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if delim, ok := token.(json.Delim); ok {
|
||||
switch delim {
|
||||
case '{':
|
||||
if index < len(s) {
|
||||
if values, ok := s[index].(map[string]interface{}); ok {
|
||||
newMap := OrderedMap{
|
||||
keys: make([]string, 0, len(values)),
|
||||
values: values,
|
||||
escapeHTML: escapeHTML,
|
||||
}
|
||||
if err = decodeOrderedMap(dec, &newMap); err != nil {
|
||||
return err
|
||||
}
|
||||
s[index] = newMap
|
||||
} else if oldMap, ok := s[index].(OrderedMap); ok {
|
||||
newMap := OrderedMap{
|
||||
keys: make([]string, 0, len(oldMap.values)),
|
||||
values: oldMap.values,
|
||||
escapeHTML: escapeHTML,
|
||||
}
|
||||
if err = decodeOrderedMap(dec, &newMap); err != nil {
|
||||
return err
|
||||
}
|
||||
s[index] = newMap
|
||||
} else if err = decodeOrderedMap(dec, &OrderedMap{}); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err = decodeOrderedMap(dec, &OrderedMap{}); err != nil {
|
||||
return err
|
||||
}
|
||||
case '[':
|
||||
if index < len(s) {
|
||||
if values, ok := s[index].([]interface{}); ok {
|
||||
if err = decodeSlice(dec, values, escapeHTML); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err = decodeSlice(dec, []interface{}{}, escapeHTML); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err = decodeSlice(dec, []interface{}{}, escapeHTML); err != nil {
|
||||
return err
|
||||
}
|
||||
case ']':
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (o OrderedMap) MarshalJSON() ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteByte('{')
|
||||
encoder := json.NewEncoder(&buf)
|
||||
encoder.SetEscapeHTML(o.escapeHTML)
|
||||
for i, k := range o.keys {
|
||||
if i > 0 {
|
||||
buf.WriteByte(',')
|
||||
}
|
||||
// add key
|
||||
if err := encoder.Encode(k); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf.WriteByte(':')
|
||||
// add value
|
||||
if err := encoder.Encode(o.values[k]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
buf.WriteByte('}')
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
81
vendor/github.com/iancoleman/orderedmap/readme.md
generated
vendored
81
vendor/github.com/iancoleman/orderedmap/readme.md
generated
vendored
@@ -1,81 +0,0 @@
|
||||
# orderedmap
|
||||
|
||||
[](https://travis-ci.com/iancoleman/orderedmap)
|
||||
|
||||
A golang data type equivalent to python's collections.OrderedDict
|
||||
|
||||
Retains order of keys in maps
|
||||
|
||||
Can be JSON serialized / deserialized
|
||||
|
||||
# Usage
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/iancoleman/orderedmap"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// use New() instead of o := map[string]interface{}{}
|
||||
o := orderedmap.New()
|
||||
|
||||
// use SetEscapeHTML() to whether escape problematic HTML characters or not, defaults is true
|
||||
o.SetEscapeHTML(false)
|
||||
|
||||
// use Set instead of o["a"] = 1
|
||||
o.Set("a", 1)
|
||||
|
||||
// add some value with special characters
|
||||
o.Set("b", "\\.<>[]{}_-")
|
||||
|
||||
// use Get instead of i, ok := o["a"]
|
||||
val, ok := o.Get("a")
|
||||
|
||||
// use Keys instead of for k, v := range o
|
||||
keys := o.Keys()
|
||||
for _, k := range keys {
|
||||
v, _ := o.Get(k)
|
||||
}
|
||||
|
||||
// use o.Delete instead of delete(o, key)
|
||||
o.Delete("a")
|
||||
|
||||
// serialize to a json string using encoding/json
|
||||
bytes, err := json.Marshal(o)
|
||||
prettyBytes, err := json.MarshalIndent(o, "", " ")
|
||||
|
||||
// deserialize a json string using encoding/json
|
||||
// all maps (including nested maps) will be parsed as orderedmaps
|
||||
s := `{"a": 1}`
|
||||
err := json.Unmarshal([]byte(s), &o)
|
||||
|
||||
// sort the keys
|
||||
o.SortKeys(sort.Strings)
|
||||
|
||||
// sort by Pair
|
||||
o.Sort(func(a *orderedmap.Pair, b *orderedmap.Pair) bool {
|
||||
return a.Value().(float64) < b.Value().(float64)
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
# Caveats
|
||||
|
||||
* OrderedMap only takes strings for the key, as per [the JSON spec](http://json.org/).
|
||||
|
||||
# Tests
|
||||
|
||||
```
|
||||
go test
|
||||
```
|
||||
|
||||
# Alternatives
|
||||
|
||||
None of the alternatives offer JSON serialization.
|
||||
|
||||
* [cevaris/ordered_map](https://github.com/cevaris/ordered_map)
|
||||
* [mantyr/iterator](https://github.com/mantyr/iterator)
|
||||
3
vendor/modules.txt
vendored
3
vendor/modules.txt
vendored
@@ -109,9 +109,6 @@ github.com/gobwas/glob/util/strings
|
||||
# github.com/gookit/color v1.4.2
|
||||
## explicit; go 1.12
|
||||
github.com/gookit/color
|
||||
# github.com/iancoleman/orderedmap v0.3.0
|
||||
## explicit; go 1.16
|
||||
github.com/iancoleman/orderedmap
|
||||
# github.com/imdario/mergo v0.3.11
|
||||
## explicit; go 1.13
|
||||
github.com/imdario/mergo
|
||||
|
||||
Reference in New Issue
Block a user