From af842e40d4d11aea8f9534e42d312fff2860c0ba Mon Sep 17 00:00:00 2001 From: Karim Khaleel Date: Sat, 18 May 2024 19:46:06 +0300 Subject: [PATCH 1/4] Remove unnecesary schema validations in user config --- pkg/config/user_config.go | 6 +++--- schema/config.json | 9 ++------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/pkg/config/user_config.go b/pkg/config/user_config.go index c22594461..7a551bca6 100644 --- a/pkg/config/user_config.go +++ b/pkg/config/user_config.go @@ -252,7 +252,7 @@ type PagingConfig struct { // diff-so-fancy // delta --dark --paging=never // ydiff -p cat -s --wrap --width={{columnWidth}} - Pager PagerType `yaml:"pager" jsonschema:"minLength=1"` + Pager PagerType `yaml:"pager"` // If true, Lazygit will use whatever pager is specified in `$GIT_PAGER`, `$PAGER`, or your *git config*. If the pager ends with something like ` | less` we will strip that part out, because less doesn't play nice with our rendering approach. If the custom pager uses less under the hood, that will also break rendering (hence the `--paging=never` flag for the `delta` pager). UseConfig bool `yaml:"useConfig"` // e.g. 'difft --color=always' @@ -294,9 +294,9 @@ type LogConfig struct { type CommitPrefixConfig struct { // pattern to match on. E.g. for 'feature/AB-123' to match on the AB-123 use "^\\w+\\/(\\w+-\\w+).*" - Pattern string `yaml:"pattern" jsonschema:"example=^\\w+\\/(\\w+-\\w+).*,minLength=1"` + Pattern string `yaml:"pattern" jsonschema:"example=^\\w+\\/(\\w+-\\w+).*"` // Replace directive. E.g. for 'feature/AB-123' to start the commit message with 'AB-123 ' use "[$1] " - Replace string `yaml:"replace" jsonschema:"example=[$1] ,minLength=1"` + Replace string `yaml:"replace" jsonschema:"example=[$1]"` } type UpdateConfig struct { diff --git a/schema/config.json b/schema/config.json index 05a2db151..3e8bc7c80 100644 --- a/schema/config.json +++ b/schema/config.json @@ -428,7 +428,6 @@ }, "pager": { "type": "string", - "minLength": 1, "description": "e.g.\ndiff-so-fancy\ndelta --dark --paging=never\nydiff -p cat -s --wrap --width={{columnWidth}}", "default": "", "examples": [ @@ -543,7 +542,6 @@ "properties": { "pattern": { "type": "string", - "minLength": 1, "description": "pattern to match on. E.g. for 'feature/AB-123' to match on the AB-123 use \"^\\\\w+\\\\/(\\\\w+-\\\\w+).*\"", "examples": [ "^\\w+\\/(\\w+-\\w+).*" @@ -551,10 +549,9 @@ }, "replace": { "type": "string", - "minLength": 1, "description": "Replace directive. E.g. for 'feature/AB-123' to start the commit message with 'AB-123 ' use \"[$1] \"", "examples": [ - "[$1] " + "[$1]" ] } }, @@ -567,7 +564,6 @@ "properties": { "pattern": { "type": "string", - "minLength": 1, "description": "pattern to match on. E.g. for 'feature/AB-123' to match on the AB-123 use \"^\\\\w+\\\\/(\\\\w+-\\\\w+).*\"", "examples": [ "^\\w+\\/(\\w+-\\w+).*" @@ -575,10 +571,9 @@ }, "replace": { "type": "string", - "minLength": 1, "description": "Replace directive. E.g. for 'feature/AB-123' to start the commit message with 'AB-123 ' use \"[$1] \"", "examples": [ - "[$1] " + "[$1]" ] } }, From 7d787afb2cd8f711b508fb63a7d097bffe9c9cb3 Mon Sep 17 00:00:00 2001 From: Karim Khaleel Date: Thu, 16 May 2024 12:52:42 +0300 Subject: [PATCH 2/4] Set default value for WindowSize config to pass validation --- pkg/config/user_config.go | 1 + schema/config.json | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/config/user_config.go b/pkg/config/user_config.go index 7a551bca6..4c0d50e52 100644 --- a/pkg/config/user_config.go +++ b/pkg/config/user_config.go @@ -684,6 +684,7 @@ func GetDefaultConfig() *UserConfig { CommandLogSize: 8, SplitDiff: "auto", SkipRewordInEditorWarning: false, + WindowSize: "normal", Border: "rounded", AnimateExplosion: true, PortraitMode: "auto", diff --git a/schema/config.json b/schema/config.json index 3e8bc7c80..556ce4607 100644 --- a/schema/config.json +++ b/schema/config.json @@ -341,7 +341,8 @@ "half", "full" ], - "description": "Default size for focused window. Window size can be changed from within Lazygit with '+' and '_' (but this won't change the default).\nOne of: 'normal' (default) | 'half' | 'full'" + "description": "Default size for focused window. Window size can be changed from within Lazygit with '+' and '_' (but this won't change the default).\nOne of: 'normal' (default) | 'half' | 'full'", + "default": "normal" }, "border": { "type": "string", From b98ae1c773f3bd264a484a007d4b0cbe1f422ddd Mon Sep 17 00:00:00 2001 From: Karim Khaleel Date: Wed, 24 Apr 2024 23:23:40 +0300 Subject: [PATCH 3/4] Add default lazygit config generator for Config.md from JSON schema --- docs/Config.md | 657 ++++++++++++------ go.mod | 1 + go.sum | 2 + pkg/jsonschema/generate_config_docs.go | 269 +++++++ pkg/jsonschema/generator.go | 1 + .../github.com/iancoleman/orderedmap/LICENSE | 21 + .../iancoleman/orderedmap/orderedmap.go | 266 +++++++ .../iancoleman/orderedmap/readme.md | 81 +++ vendor/modules.txt | 3 + 9 files changed, 1090 insertions(+), 211 deletions(-) create mode 100644 pkg/jsonschema/generate_config_docs.go create mode 100644 vendor/github.com/iancoleman/orderedmap/LICENSE create mode 100644 vendor/github.com/iancoleman/orderedmap/orderedmap.go create mode 100644 vendor/github.com/iancoleman/orderedmap/readme.md diff --git a/docs/Config.md b/docs/Config.md index d85637429..f9c6a729b 100644 --- a/docs/Config.md +++ b/docs/Config.md @@ -29,268 +29,503 @@ to the top of your config file or via [Visual Studio Code settings.json config][ ## Default + ```yaml +# Config relating to the Lazygit UI gui: - # stuff relating to the UI - windowSize: 'normal' # one of 'normal' | 'half' | 'full' default is 'normal' - scrollHeight: 2 # how many lines you scroll by - scrollPastBottom: true # enable scrolling past the bottom - scrollOffMargin: 2 # how many lines to keep before/after the cursor when it reaches the top/bottom of the view; see 'Scroll-off Margin' section below - scrollOffBehavior: 'margin' # one of 'margin' | 'jump'; see 'Scroll-off Margin' section below - sidePanelWidth: 0.3333 # number from 0 to 1 - expandFocusedSidePanel: false - mainPanelSplitMode: 'flexible' # one of 'horizontal' | 'flexible' | 'vertical' - enlargedSideViewLocation: 'left' # one of 'left' | 'top' - language: 'auto' # one of 'auto' | 'en' | 'zh-CN' | 'zh-TW' | 'pl' | 'nl' | 'ja' | 'ko' | 'ru' - timeFormat: '02 Jan 06' # https://pkg.go.dev/time#Time.Format - shortTimeFormat: '3:04PM' + # The number of lines you scroll by when scrolling the main window + scrollHeight: 2 + + # If true, allow scrolling past the bottom of the content in the main window + scrollPastBottom: true + + # See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#scroll-off-margin + scrollOffMargin: 2 + + # One of: 'margin' (default) | 'jump' + scrollOffBehavior: margin + + # If true, capture mouse events. + # When mouse events are captured, it's a little harder to select text: e.g. requiring you to hold the option key when on macOS. + mouseEvents: true + + # Fraction of the total screen width to use for the left side section. You may want to pick a small number (e.g. 0.2) if you're using a narrow screen, so that you can see more of the main section. + # Number from 0 to 1.0. + sidePanelWidth: 0.3333 + + # Sometimes the main window is split in two (e.g. when the selected file has both staged and unstaged changes). This setting controls how the two sections are split. + # Options are: + # - 'horizontal': split the window horizontally + # - 'vertical': split the window vertically + # - 'flexible': (default) split the window horizontally if the window is wide enough, otherwise split vertically + mainPanelSplitMode: flexible + + # How the window is split when in half screen mode (i.e. after hitting '+' once). + # Possible values: + # - 'left': split the window horizontally (side panel on the left, main view on the right) + # - 'top': split the window vertically (side panel on top, main view below) + enlargedSideViewLocation: left + + # One of 'auto' (default) | 'en' | 'zh-CN' | 'zh-TW' | 'pl' | 'nl' | 'ja' | 'ko' | 'ru' + language: auto + + # Format used when displaying time e.g. commit time. + # Uses Go's time format syntax: https://pkg.go.dev/time#Time.Format + timeFormat: 02 Jan 06 + + # Format used when displaying time if the time is less than 24 hours ago. + # Uses Go's time format syntax: https://pkg.go.dev/time#Time.Format + shortTimeFormat: 3:04PM + + # Config relating to colors and styles. + # See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#color-attributes theme: + # Border color of focused window activeBorderColor: - green - bold + + # Border color of non-focused windows inactiveBorderColor: - - white + - default + + # Border color of focused window when searching in that window searchingActiveBorderColor: - cyan - bold + + # Color of keybindings help text in the bottom line optionsTextColor: - blue + + # Background color of selected line. + # See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#highlighting-the-selected-line selectedLineBgColor: - - blue # set to `default` to have no background colour - cherryPickedCommitBgColor: - - cyan + - blue + + # Foreground color of copied commit cherryPickedCommitFgColor: - blue + + # Background color of copied commit + cherryPickedCommitBgColor: + - cyan + + # Foreground color of marked base commit (for rebase) + markedBaseCommitFgColor: + - blue + + # Background color of marked base commit (for rebase) + markedBaseCommitBgColor: + - yellow + + # Color for file with unstaged changes unstagedChangesColor: - red + + # Default text color defaultFgColor: - default + + # Config relating to the commit length indicator commitLength: + # If true, show an indicator of commit message length show: true - mouseEvents: true - skipDiscardChangeWarning: false - skipStashWarning: false - showFileTree: true # for rendering changes files in a tree format - showListFooter: true # for seeing the '5 of 20' message in list panels + + # If true, show the '5 of 20' footer at the bottom of list views + showListFooter: true + + # If true, display the files in the file views as a tree. If false, display the files as a flat list. + # This can be toggled from within Lazygit with the '~' key, but that will not change the default. + showFileTree: true + + # If true, show a random tip in the command log when Lazygit starts showRandomTip: true - showBranchCommitHash: false # show commit hashes alongside branch names - showBottomLine: true # for hiding the bottom information line (unless it has important information to tell you) - showPanelJumps: true # for showing the jump-to-panel keybindings as panel subtitles + + # If true, show the command log showCommandLog: true - showIcons: false # deprecated: use nerdFontsVersion instead - nerdFontsVersion: "" # nerd fonts version to use ("2" or "3"); empty means don't show nerd font icons - showFileIcons: true # for hiding file icons in the file views - commitHashLength: 8 # length of commit hash in commits view. 0 shows '*' if NF icons aren't enabled + + # If true, show the bottom line that contains keybinding info and useful buttons. If false, this line will be hidden except to display a loader for an in-progress action. + showBottomLine: true + + # If true, show jump-to-window keybindings in window titles. + showPanelJumps: true + + # Nerd fonts version to use. + # One of: '2' | '3' | empty string (default) + # If empty, do not show icons. + nerdFontsVersion: "" + + # If true (default), file icons are shown in the file views. Only relevant if NerdFontsVersion is not empty. + showFileIcons: true + + # Length of commit hash in commits view. 0 shows '*' if NF icons aren't on. + commitHashLength: 8 + + # Height of the command log view commandLogSize: 8 - splitDiff: 'auto' # one of 'auto' | 'always' - skipRewordInEditorWarning: false # for skipping the confirmation before launching the reword editor - border: 'rounded' # one of 'single' | 'double' | 'rounded' | 'hidden' - animateExplosion: true # shows an explosion animation when nuking the working tree - portraitMode: 'auto' # one of 'auto' | 'never' | 'always' - filterMode: 'substring' # one of 'substring' | 'fuzzy'; see 'Filtering' section below + + # Whether to split the main window when viewing file changes. + # One of: 'auto' | 'always' + # If 'auto', only split the main window when a file has both staged and unstaged changes + splitDiff: auto + + # Default size for focused window. Window size can be changed from within Lazygit with '+' and '_' (but this won't change the default). + # One of: 'normal' (default) | 'half' | 'full' + windowSize: normal + + # Window border style. + # One of 'rounded' (default) | 'single' | 'double' | 'hidden' + border: rounded + + # If true, show a seriously epic explosion animation when nuking the working tree. + animateExplosion: true + + # Whether to stack UI components on top of each other. + # One of 'auto' (default) | 'always' | 'never' + portraitMode: auto + + # How things are filtered when typing '/'. + # One of 'substring' (default) | 'fuzzy' + filterMode: substring + + # Config relating to the spinner. spinner: - frames: ['|', '/', '-', '\\'] - rate: 50 # spinner rate in milliseconds - statusPanelView: 'dashboard' # one of 'dashboard' | 'allBranchesLog' + # The frames of the spinner animation. + frames: + - '|' + - / + - '-' + - \ + + # The "speed" of the spinner in milliseconds. + rate: 50 + + # Status panel view. + # One of 'dashboard' (default) | 'allBranchesLog' + statusPanelView: dashboard + +# Config relating to git git: + # See https://github.com/jesseduffield/lazygit/blob/master/docs/Custom_Pagers.md paging: + # Value of the --color arg in the git diff command. Some pagers want this to be set to 'always' and some want it set to 'never' colorArg: always - useConfig: false + + # e.g. + # diff-so-fancy + # delta --dark --paging=never + # ydiff -p cat -s --wrap --width={{columnWidth}} + pager: "" + + # e.g. 'difft --color=always' + externalDiffCommand: "" + + # Config relating to committing commit: - signOff: false - autoWrapCommitMessage: true # automatic WYSIWYG wrapping of the commit message as you type - autoWrapWidth: 72 # if autoWrapCommitMessage is true, the width to wrap to + # Automatic WYSIWYG wrapping of the commit message as you type + autoWrapCommitMessage: true + + # If autoWrapCommitMessage is true, the width to wrap to + autoWrapWidth: 72 + + # Config relating to merging merging: - # only applicable to unix users - manualCommit: false - # extra args passed to `git merge`, e.g. --no-ff - args: '' + # Extra args passed to `git merge`, e.g. --no-ff + args: "" + + # list of branches that are considered 'main' branches, used when displaying commits + mainBranches: + - master + - main + + # Prefix to use when skipping hooks. E.g. if set to 'WIP', then pre-commit hooks will be skipped when the commit message starts with 'WIP' + skipHookPrefix: WIP + + # If true, periodically fetch from remote + autoFetch: true + + # If true, periodically refresh files and submodules + autoRefresh: true + + # If true, pass the --all arg to git fetch + fetchAll: true + + # Command used when displaying the current branch git log in the main window + branchLogCmd: git log --graph --color=always --abbrev-commit --decorate --date=relative --pretty=medium {{branchName}} -- + + # Command used to display git log of all branches in the main window + allBranchesLogCmd: git log --graph --all --color=always --abbrev-commit --decorate --date=relative --pretty=medium + + # See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#predefined-commit-message-prefix + commitPrefix: + # pattern to match on. E.g. for 'feature/AB-123' to match on the AB-123 use "^\\w+\\/(\\w+-\\w+).*" + pattern: "" + + # Replace directive. E.g. for 'feature/AB-123' to start the commit message with 'AB-123 ' use "[$1] " + replace: "" + + # Config for showing the log in the commits view log: - # one of date-order, author-date-order, topo-order or default. - # topo-order makes it easier to read the git log graph, but commits may not - # appear chronologically. See https://git-scm.com/docs/git-log#_commit_ordering + # One of: 'date-order' | 'author-date-order' | 'topo-order' | 'default' + # 'topo-order' makes it easier to read the git log graph, but commits may not + # appear chronologically. See https://git-scm.com/docs/ # # Deprecated: Configure this with `Log menu -> Commit sort order` ( in the commits window by default). - order: 'topo-order' - # one of always, never, when-maximised - # this determines whether the git graph is rendered in the commits panel + order: topo-order + + # This determines whether the git graph is rendered in the commits panel + # One of 'always' | 'never' | 'when-maximised' # # Deprecated: Configure this with `Log menu -> Show git graph` ( in the commits window by default). - showGraph: 'always' - # displays the whole git graph by default in the commits panel (equivalent to passing the `--all` argument to `git log`) - showWholeGraph: false - skipHookPrefix: WIP - # The main branches. We colour commits green if they belong to one of these branches, - # so that you can easily see which commits are unique to your branch (coloured in yellow) - mainBranches: [master, main] - autoFetch: true - autoRefresh: true - fetchAll: true # Pass --all flag when running git fetch. Set to false to fetch only origin (or the current branch's upstream remote if there is one) - branchLogCmd: 'git log --graph --color=always --abbrev-commit --decorate --date=relative --pretty=medium {{branchName}} --' - allBranchesLogCmd: 'git log --graph --all --color=always --abbrev-commit --decorate --date=relative --pretty=medium' - overrideGpg: false # prevents lazygit from spawning a separate process when using GPG - disableForcePushing: false - parseEmoji: false - truncateCopiedCommitHashesTo: 12 # When copying commit hashes to the clipboard, truncate them to this length. Set to 40 to disable truncation. -os: - copyToClipboardCmd: '' # See 'Custom Command for Copying to Clipboard' section - editPreset: '' # see 'Configuring File Editing' section - edit: '' - editAtLine: '' - editAtLineAndWait: '' - open: '' - openLink: '' -refresher: - refreshInterval: 10 # File/submodule refresh interval in seconds. Auto-refresh can be disabled via option 'git.autoRefresh'. - fetchInterval: 60 # Re-fetch interval in seconds. Auto-fetch can be disabled via option 'git.autoFetch'. + showGraph: always + + # When copying commit hashes to the clipboard, truncate them to this + # length. Set to 40 to disable truncation. + truncateCopiedCommitHashesTo: 12 + +# Periodic update checks update: - method: prompt # can be: prompt | background | never - days: 14 # how often an update is checked for -confirmOnQuit: false -# determines whether hitting 'esc' will quit the application when there is nothing to cancel/close -quitOnTopLevelReturn: false -disableStartupPopups: false -notARepository: 'prompt' # one of: 'prompt' | 'create' | 'skip' | 'quit' -promptToReturnFromSubprocess: true # display confirmation when subprocess terminates + # One of: 'prompt' (default) | 'background' | 'never' + method: prompt + + # Period in days between update checks + days: 14 + +# Background refreshes +refresher: + # File/submodule refresh interval in seconds. + # Auto-refresh can be disabled via option 'git.autoRefresh'. + refreshInterval: 10 + + # Re-fetch interval in seconds. + # Auto-fetch can be disabled via option 'git.autoFetch'. + fetchInterval: 60 + +# Keybindings keybinding: universal: - quit: 'q' - quit-alt1: '' # alternative/alias of quit - return: '' # return to previous menu, will quit if there's nowhere to return - quitWithoutChangingDirectory: 'Q' - togglePanel: '' # goto the next panel - prevItem: '' # go one line up - nextItem: '' # go one line down - prevItem-alt: 'k' # go one line up - nextItem-alt: 'j' # go one line down - prevPage: ',' # go to next page in list - nextPage: '.' # go to previous page in list - gotoTop: '<' # go to top of list - gotoBottom: '>' # go to bottom of list - scrollLeft: 'H' # scroll left within list view - scrollRight: 'L' # scroll right within list view - prevBlock: '' # goto the previous block / panel - nextBlock: '' # goto the next block / panel - prevBlock-alt: 'h' # goto the previous block / panel - nextBlock-alt: 'l' # goto the next block / panel - jumpToBlock: ['1', '2', '3', '4', '5'] # goto the Nth block / panel - nextMatch: 'n' - prevMatch: 'N' - optionMenu: # show help menu - optionMenu-alt1: '?' # show help menu - select: '' - goInto: '' - openRecentRepos: '' - confirm: '' - remove: 'd' - new: 'n' - edit: 'e' - openFile: 'o' - scrollUpMain: '' # main panel scroll up - scrollDownMain: '' # main panel scroll down - scrollUpMain-alt1: 'K' # main panel scroll up - scrollDownMain-alt1: 'J' # main panel scroll down - scrollUpMain-alt2: '' # main panel scroll up - scrollDownMain-alt2: '' # main panel scroll down + quit: q + quit-alt1: + return: + quitWithoutChangingDirectory: Q + togglePanel: + prevItem: + nextItem: + prevItem-alt: k + nextItem-alt: j + prevPage: ',' + nextPage: . + scrollLeft: H + scrollRight: L + gotoTop: < + gotoBottom: '>' + toggleRangeSelect: v + rangeSelectDown: + rangeSelectUp: + prevBlock: + nextBlock: + prevBlock-alt: h + nextBlock-alt: l + nextBlock-alt2: + prevBlock-alt2: + jumpToBlock: + - "1" + - "2" + - "3" + - "4" + - "5" + nextMatch: "n" + prevMatch: "N" + startSearch: / + optionMenu: + optionMenu-alt1: '?' + select: + goInto: + confirm: + confirmInEditor: + remove: d + new: "n" + edit: e + openFile: o + scrollUpMain: + scrollDownMain: + scrollUpMain-alt1: K + scrollDownMain-alt1: J + scrollUpMain-alt2: + scrollDownMain-alt2: executeCustomCommand: ':' - createRebaseOptionsMenu: 'm' - pushFiles: 'P' - pullFiles: 'p' - refresh: 'R' - createPatchOptionsMenu: '' + createRebaseOptionsMenu: m + + # 'Files' appended for legacy reasons + pushFiles: P + + # 'Files' appended for legacy reasons + pullFiles: p + refresh: R + createPatchOptionsMenu: nextTab: ']' prevTab: '[' - nextScreenMode: '+' - prevScreenMode: '_' - undo: 'z' - redo: '' - filteringMenu: '' - diffingMenu: 'W' - diffingMenu-alt: '' # deprecated - copyToClipboard: '' - submitEditorText: '' + nextScreenMode: + + prevScreenMode: _ + undo: z + redo: + filteringMenu: + diffingMenu: W + diffingMenu-alt: + copyToClipboard: + openRecentRepos: + submitEditorText: extrasMenu: '@' - toggleWhitespaceInDiffView: '' + toggleWhitespaceInDiffView: increaseContextInDiffView: '}' decreaseContextInDiffView: '{' - toggleRangeSelect: 'v' - rangeSelectUp: '' - rangeSelectDown: '' + openDiffTool: status: - checkForUpdate: 'u' - recentRepos: '' + checkForUpdate: u + recentRepos: + allBranchesLogGraph: a files: - commitChanges: 'c' - commitChangesWithoutHook: 'w' # commit changes without pre-commit hook - amendLastCommit: 'A' - commitChangesWithEditor: 'C' - findBaseCommitForFixup: '' - confirmDiscard: 'x' - ignoreFile: 'i' - refreshFiles: 'r' - stashAllChanges: 's' - viewStashOptions: 'S' - toggleStagedAll: 'a' # stage/unstage all - viewResetOptions: 'D' - fetch: 'f' + commitChanges: c + commitChangesWithoutHook: w + amendLastCommit: A + commitChangesWithEditor: C + findBaseCommitForFixup: + confirmDiscard: x + ignoreFile: i + refreshFiles: r + stashAllChanges: s + viewStashOptions: S + toggleStagedAll: a + viewResetOptions: D + fetch: f toggleTreeView: '`' - openMergeTool: 'M' - openStatusFilter: '' + openMergeTool: M + openStatusFilter: + copyFileInfoToClipboard: "y" branches: - createPullRequest: 'o' - viewPullRequestOptions: 'O' - checkoutBranchByName: 'c' - forceCheckoutBranch: 'F' - rebaseBranch: 'r' - renameBranch: 'R' - mergeIntoCurrentBranch: 'M' - viewGitFlowOptions: 'i' - fastForward: 'f' # fast-forward this branch from its upstream - createTag: 'T' - pushTag: 'P' - setUpstream: 'u' # set as upstream of checked-out branch - fetchRemote: 'f' + createPullRequest: o + viewPullRequestOptions: O + copyPullRequestURL: + checkoutBranchByName: c + forceCheckoutBranch: F + rebaseBranch: r + renameBranch: R + mergeIntoCurrentBranch: M + viewGitFlowOptions: i + fastForward: f + createTag: T + pushTag: P + setUpstream: u + fetchRemote: f + sortOrder: s + worktrees: + viewWorktreeOptions: w commits: - squashDown: 's' - renameCommit: 'r' - renameCommitWithEditor: 'R' - viewResetOptions: 'g' - markCommitAsFixup: 'f' - createFixupCommit: 'F' # create fixup commit for this commit - squashAboveCommits: 'S' - moveDownCommit: '' # move commit down one - moveUpCommit: '' # move commit up one - amendToCommit: 'A' - amendAttributeMenu: 'a' - pickCommit: 'p' # pick commit (when mid-rebase) - revertCommit: 't' - cherryPickCopy: 'C' - pasteCommits: 'V' - tagCommit: 'T' - checkoutCommit: '' - resetCherryPick: '' - copyCommitMessageToClipboard: '' - openLogMenu: '' - viewBisectOptions: 'b' - stash: - popStash: 'g' - renameStash: 'r' - commitFiles: - checkoutCommitFile: 'c' - main: - toggleSelectHunk: 'a' - pickBothHunks: 'b' - submodules: - init: 'i' - update: 'u' - bulkMenu: 'b' - commitMessage: - commitMenu: '' + squashDown: s + renameCommit: r + renameCommitWithEditor: R + viewResetOptions: g + markCommitAsFixup: f + createFixupCommit: F + squashAboveCommits: S + moveDownCommit: + moveUpCommit: + amendToCommit: A + resetCommitAuthor: a + pickCommit: p + revertCommit: t + cherryPickCopy: C + pasteCommits: V + markCommitAsBaseForRebase: B + tagCommit: T + checkoutCommit: + resetCherryPick: + copyCommitAttributeToClipboard: "y" + openLogMenu: + openInBrowser: o + viewBisectOptions: b + startInteractiveRebase: i amendAttribute: - addCoAuthor: 'c' - resetAuthor: 'a' - setAuthor: 'A' + resetAuthor: a + setAuthor: A + addCoAuthor: c + stash: + popStash: g + renameStash: r + commitFiles: + checkoutCommitFile: c + main: + toggleSelectHunk: a + pickBothHunks: b + editSelectHunk: E + submodules: + init: i + update: u + bulkMenu: b + commitMessage: + commitMenu: + +# Config relating to things outside of Lazygit like how files are opened, copying to clipboard, etc +os: + # Command for editing a file. Should contain "{{filename}}". + edit: "" + + # Command for editing a file at a given line number. Should contain + # "{{filename}}", and may optionally contain "{{line}}". + editAtLine: "" + + # Same as EditAtLine, except that the command needs to wait until the + # window is closed. + editAtLineAndWait: "" + + # For opening a directory in an editor + openDirInEditor: "" + + # A built-in preset that sets all of the above settings. Supported presets + # are defined in the getPreset function in editor_presets.go. + editPreset: "" + + # Command for opening a file, as if the file is double-clicked. Should + # contain "{{filename}}", but doesn't support "{{line}}". + open: "" + + # Command for opening a link. Should contain "{{link}}". + openLink: "" + + # EditCommand is the command for editing a file. + # Deprecated: use Edit instead. Note that semantics are different: + # EditCommand is just the command itself, whereas Edit contains a + # "{{filename}}" variable. + editCommand: "" + + # EditCommandTemplate is the command template for editing a file + # Deprecated: use EditAtLine instead. + editCommandTemplate: "" + + # OpenCommand is the command for opening a file + # Deprecated: use Open instead. + openCommand: "" + + # OpenLinkCommand is the command for opening a link + # Deprecated: use OpenLink instead. + openLinkCommand: "" + + # CopyToClipboardCmd is the command for copying to clipboard. + # See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-clipboard + copyToClipboardCmd: "" + +# What to do when opening Lazygit outside of a git repo. +# - 'prompt': (default) ask whether to initialize a new repo or open in the most recent repo +# - 'create': initialize a new repo +# - 'skip': open most recent repo +# - 'quit': exit Lazygit +notARepository: prompt + +# If true, display a confirmation when subprocess terminates. This allows you to view the output of the subprocess before returning to Lazygit. +promptToReturnFromSubprocess: true ``` + ## Platform Defaults diff --git a/go.mod b/go.mod index ca0adebe2..6e7d87686 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/gdamore/tcell/v2 v2.7.4 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 diff --git a/go.sum b/go.sum index cdd64127c..ae6adb231 100644 --- a/go.sum +++ b/go.sum @@ -171,6 +171,8 @@ 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= diff --git a/pkg/jsonschema/generate_config_docs.go b/pkg/jsonschema/generate_config_docs.go new file mode 100644 index 000000000..441cc55e7 --- /dev/null +++ b/pkg/jsonschema/generate_config_docs.go @@ -0,0 +1,269 @@ +package jsonschema + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "os" + "strings" + + "github.com/iancoleman/orderedmap" + "github.com/jesseduffield/lazycore/pkg/utils" + "github.com/samber/lo" + + "gopkg.in/yaml.v3" +) + +type Node struct { + Name string + Description string + Default any + Children []*Node +} + +const ( + IndentLevel = 2 + DocumentationCommentStart = "\n" + DocumentationCommentEnd = "" + DocumentationCommentStartLen = len(DocumentationCommentStart) +) + +func insertBlankLines(buffer bytes.Buffer) bytes.Buffer { + lines := strings.Split(strings.TrimRight(buffer.String(), "\n"), "\n") + + var newBuffer bytes.Buffer + + previousIndent := -1 + wasComment := false + + for _, line := range lines { + trimmedLine := strings.TrimLeft(line, " ") + indent := len(line) - len(trimmedLine) + isComment := strings.HasPrefix(trimmedLine, "#") + if isComment && !wasComment && indent <= previousIndent { + newBuffer.WriteString("\n") + } + newBuffer.WriteString(line) + newBuffer.WriteString("\n") + previousIndent = indent + wasComment = isComment + } + + return newBuffer +} + +func prepareMarshalledConfig(buffer bytes.Buffer) []byte { + buffer = insertBlankLines(buffer) + + // Remove all `---` lines + lines := strings.Split(strings.TrimRight(buffer.String(), "\n"), "\n") + + var newBuffer bytes.Buffer + + for _, line := range lines { + if strings.TrimSpace(line) != "---" { + newBuffer.WriteString(line) + newBuffer.WriteString("\n") + } + } + + config := newBuffer.Bytes() + + // Add markdown yaml block tag + config = append([]byte("```yaml\n"), config...) + config = append(config, []byte("```\n")...) + + return config +} + +func setComment(yamlNode *yaml.Node, description string) { + // Workaround for the way yaml formats the HeadComment if it contains + // blank lines: it renders these without a leading "#", but we want a + // leading "#" even on blank lines. However, yaml respects it if the + // HeadComment already contains a leading "#", so we prefix all lines + // (including blank ones) with "#". + yamlNode.HeadComment = strings.Join( + lo.Map(strings.Split(description, "\n"), func(s string, _ int) string { + if s == "" { + return "#" // avoid trailing space on blank lines + } + return "# " + s + }), + "\n") +} + +func (n *Node) MarshalYAML() (interface{}, error) { + node := yaml.Node{ + Kind: yaml.MappingNode, + } + + keyNode := yaml.Node{ + Kind: yaml.ScalarNode, + Value: n.Name, + } + if n.Description != "" { + 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 { + childrenNode := yaml.Node{ + Kind: yaml.MappingNode, + } + for _, child := range n.Children { + childYaml, err := child.MarshalYAML() + if err != nil { + return nil, err + } + + childKey := yaml.Node{ + Kind: yaml.ScalarNode, + Value: child.Name, + } + if child.Description != "" { + setComment(&childKey, child.Description) + } + childYaml = childYaml.(*yaml.Node) + childrenNode.Content = append(childrenNode.Content, childYaml.(*yaml.Node).Content...) + } + node.Content = append(node.Content, &keyNode, &childrenNode) + } + + 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) + if err != nil { + return fmt.Errorf("Error reading Config.md file %w", err) + } + + startConfigSectionIndex := bytes.Index(markdown, []byte(DocumentationCommentStart)) + if startConfigSectionIndex == -1 { + return errors.New("Default config starting comment not found") + } + + endConfigSectionIndex := bytes.Index(markdown[startConfigSectionIndex+DocumentationCommentStartLen:], []byte(DocumentationCommentEnd)) + if endConfigSectionIndex == -1 { + return errors.New("Default config closing comment not found") + } + + endConfigSectionIndex = endConfigSectionIndex + startConfigSectionIndex + DocumentationCommentStartLen + + newMarkdown := make([]byte, 0, len(markdown)-endConfigSectionIndex+startConfigSectionIndex+len(config)) + newMarkdown = append(newMarkdown, markdown[:startConfigSectionIndex+DocumentationCommentStartLen]...) + newMarkdown = append(newMarkdown, config...) + newMarkdown = append(newMarkdown, markdown[endConfigSectionIndex:]...) + + if err := os.WriteFile(configPath, newMarkdown, 0o644); err != nil { + return fmt.Errorf("Error writing to file %w", err) + } + return nil +} + +func GenerateConfigDocs() { + content, err := os.ReadFile(GetSchemaDir() + "/config.json") + if err != nil { + panic("Error reading config.json") + } + + 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) + } + + var buffer bytes.Buffer + encoder := yaml.NewEncoder(&buffer) + encoder.SetIndent(IndentLevel) + + for _, child := range rootNode.Children { + err := encoder.Encode(child) + if err != nil { + panic("Failed to Marshal document") + } + } + encoder.Close() + + config := prepareMarshalledConfig(buffer) + + err = writeToConfigDocs(config) + if err != nil { + panic(err) + } +} diff --git a/pkg/jsonschema/generator.go b/pkg/jsonschema/generator.go index 263f4784b..df53dee56 100644 --- a/pkg/jsonschema/generator.go +++ b/pkg/jsonschema/generator.go @@ -11,4 +11,5 @@ import ( func main() { fmt.Printf("Generating jsonschema in %s...\n", jsonschema.GetSchemaDir()) jsonschema.GenerateSchema() + jsonschema.GenerateConfigDocs() } diff --git a/vendor/github.com/iancoleman/orderedmap/LICENSE b/vendor/github.com/iancoleman/orderedmap/LICENSE new file mode 100644 index 000000000..2732e3795 --- /dev/null +++ b/vendor/github.com/iancoleman/orderedmap/LICENSE @@ -0,0 +1,21 @@ +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. diff --git a/vendor/github.com/iancoleman/orderedmap/orderedmap.go b/vendor/github.com/iancoleman/orderedmap/orderedmap.go new file mode 100644 index 000000000..61587d9c7 --- /dev/null +++ b/vendor/github.com/iancoleman/orderedmap/orderedmap.go @@ -0,0 +1,266 @@ +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 +} diff --git a/vendor/github.com/iancoleman/orderedmap/readme.md b/vendor/github.com/iancoleman/orderedmap/readme.md new file mode 100644 index 000000000..cfe09c2fd --- /dev/null +++ b/vendor/github.com/iancoleman/orderedmap/readme.md @@ -0,0 +1,81 @@ +# orderedmap + +[![Build Status](https://travis-ci.com/iancoleman/orderedmap.svg)](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) diff --git a/vendor/modules.txt b/vendor/modules.txt index 425c1705b..881770146 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -110,6 +110,9 @@ 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 From 9b152d7619327da442d733b45075e17b631775e7 Mon Sep 17 00:00:00 2001 From: Karim Khaleel Date: Sun, 19 May 2024 13:47:15 +0300 Subject: [PATCH 4/4] Make Keybindings definition in UserConfig struct last This makes the generated default config in Config.md match the original order. --- docs/Config.md | 118 ++++---- pkg/config/user_config.go | 20 +- schema/config.json | 570 +++++++++++++++++++------------------- 3 files changed, 354 insertions(+), 354 deletions(-) diff --git a/docs/Config.md b/docs/Config.md index f9c6a729b..b9b325e9a 100644 --- a/docs/Config.md +++ b/docs/Config.md @@ -302,6 +302,65 @@ refresher: # Auto-fetch can be disabled via option 'git.autoFetch'. fetchInterval: 60 +# Config relating to things outside of Lazygit like how files are opened, copying to clipboard, etc +os: + # Command for editing a file. Should contain "{{filename}}". + edit: "" + + # Command for editing a file at a given line number. Should contain + # "{{filename}}", and may optionally contain "{{line}}". + editAtLine: "" + + # Same as EditAtLine, except that the command needs to wait until the + # window is closed. + editAtLineAndWait: "" + + # For opening a directory in an editor + openDirInEditor: "" + + # A built-in preset that sets all of the above settings. Supported presets + # are defined in the getPreset function in editor_presets.go. + editPreset: "" + + # Command for opening a file, as if the file is double-clicked. Should + # contain "{{filename}}", but doesn't support "{{line}}". + open: "" + + # Command for opening a link. Should contain "{{link}}". + openLink: "" + + # EditCommand is the command for editing a file. + # Deprecated: use Edit instead. Note that semantics are different: + # EditCommand is just the command itself, whereas Edit contains a + # "{{filename}}" variable. + editCommand: "" + + # EditCommandTemplate is the command template for editing a file + # Deprecated: use EditAtLine instead. + editCommandTemplate: "" + + # OpenCommand is the command for opening a file + # Deprecated: use Open instead. + openCommand: "" + + # OpenLinkCommand is the command for opening a link + # Deprecated: use OpenLink instead. + openLinkCommand: "" + + # CopyToClipboardCmd is the command for copying to clipboard. + # See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-clipboard + copyToClipboardCmd: "" + +# What to do when opening Lazygit outside of a git repo. +# - 'prompt': (default) ask whether to initialize a new repo or open in the most recent repo +# - 'create': initialize a new repo +# - 'skip': open most recent repo +# - 'quit': exit Lazygit +notARepository: prompt + +# If true, display a confirmation when subprocess terminates. This allows you to view the output of the subprocess before returning to Lazygit. +promptToReturnFromSubprocess: true + # Keybindings keybinding: universal: @@ -465,65 +524,6 @@ keybinding: bulkMenu: b commitMessage: commitMenu: - -# Config relating to things outside of Lazygit like how files are opened, copying to clipboard, etc -os: - # Command for editing a file. Should contain "{{filename}}". - edit: "" - - # Command for editing a file at a given line number. Should contain - # "{{filename}}", and may optionally contain "{{line}}". - editAtLine: "" - - # Same as EditAtLine, except that the command needs to wait until the - # window is closed. - editAtLineAndWait: "" - - # For opening a directory in an editor - openDirInEditor: "" - - # A built-in preset that sets all of the above settings. Supported presets - # are defined in the getPreset function in editor_presets.go. - editPreset: "" - - # Command for opening a file, as if the file is double-clicked. Should - # contain "{{filename}}", but doesn't support "{{line}}". - open: "" - - # Command for opening a link. Should contain "{{link}}". - openLink: "" - - # EditCommand is the command for editing a file. - # Deprecated: use Edit instead. Note that semantics are different: - # EditCommand is just the command itself, whereas Edit contains a - # "{{filename}}" variable. - editCommand: "" - - # EditCommandTemplate is the command template for editing a file - # Deprecated: use EditAtLine instead. - editCommandTemplate: "" - - # OpenCommand is the command for opening a file - # Deprecated: use Open instead. - openCommand: "" - - # OpenLinkCommand is the command for opening a link - # Deprecated: use OpenLink instead. - openLinkCommand: "" - - # CopyToClipboardCmd is the command for copying to clipboard. - # See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-clipboard - copyToClipboardCmd: "" - -# What to do when opening Lazygit outside of a git repo. -# - 'prompt': (default) ask whether to initialize a new repo or open in the most recent repo -# - 'create': initialize a new repo -# - 'skip': open most recent repo -# - 'quit': exit Lazygit -notARepository: prompt - -# If true, display a confirmation when subprocess terminates. This allows you to view the output of the subprocess before returning to Lazygit. -promptToReturnFromSubprocess: true ``` diff --git a/pkg/config/user_config.go b/pkg/config/user_config.go index 4c0d50e52..2df6e60b8 100644 --- a/pkg/config/user_config.go +++ b/pkg/config/user_config.go @@ -19,8 +19,6 @@ type UserConfig struct { ConfirmOnQuit bool `yaml:"confirmOnQuit"` // If true, exit Lazygit when the user presses escape in a context where there is nothing to cancel/close QuitOnTopLevelReturn bool `yaml:"quitOnTopLevelReturn"` - // Keybindings - Keybinding KeybindingConfig `yaml:"keybinding"` // Config relating to things outside of Lazygit like how files are opened, copying to clipboard, etc OS OSConfig `yaml:"os,omitempty"` // If true, don't display introductory popups upon opening Lazygit. @@ -38,6 +36,8 @@ type UserConfig struct { NotARepository string `yaml:"notARepository" jsonschema:"enum=prompt,enum=create,enum=skip,enum=quit"` // If true, display a confirmation when subprocess terminates. This allows you to view the output of the subprocess before returning to Lazygit. PromptToReturnFromSubprocess bool `yaml:"promptToReturnFromSubprocess"` + // Keybindings + Keybinding KeybindingConfig `yaml:"keybinding"` } type RefresherConfig struct { @@ -736,8 +736,14 @@ func GetDefaultConfig() *UserConfig { Method: "prompt", Days: 14, }, - ConfirmOnQuit: false, - QuitOnTopLevelReturn: false, + ConfirmOnQuit: false, + QuitOnTopLevelReturn: false, + OS: OSConfig{}, + DisableStartupPopups: false, + CustomCommands: []CustomCommand(nil), + Services: map[string]string(nil), + NotARepository: "prompt", + PromptToReturnFromSubprocess: true, Keybinding: KeybindingConfig{ Universal: KeybindingUniversalConfig{ Quit: "q", @@ -904,11 +910,5 @@ func GetDefaultConfig() *UserConfig { CommitMenu: "", }, }, - OS: OSConfig{}, - DisableStartupPopups: false, - CustomCommands: []CustomCommand(nil), - Services: map[string]string(nil), - NotARepository: "prompt", - PromptToReturnFromSubprocess: true, } } diff --git a/schema/config.json b/schema/config.json index 556ce4607..9764e2286 100644 --- a/schema/config.json +++ b/schema/config.json @@ -680,6 +680,291 @@ "type": "boolean", "description": "If true, exit Lazygit when the user presses escape in a context where there is nothing to cancel/close" }, + "os": { + "properties": { + "edit": { + "type": "string", + "description": "Command for editing a file. Should contain \"{{filename}}\"." + }, + "editAtLine": { + "type": "string", + "description": "Command for editing a file at a given line number. Should contain\n\"{{filename}}\", and may optionally contain \"{{line}}\"." + }, + "editAtLineAndWait": { + "type": "string", + "description": "Same as EditAtLine, except that the command needs to wait until the\nwindow is closed." + }, + "editInTerminal": { + "type": "boolean", + "description": "Whether lazygit suspends until an edit process returns\nPointer to bool so that we can distinguish unset (nil) from false.\nWe're naming this `editInTerminal` for backwards compatibility" + }, + "openDirInEditor": { + "type": "string", + "description": "For opening a directory in an editor" + }, + "editPreset": { + "type": "string", + "description": "A built-in preset that sets all of the above settings. Supported presets\nare defined in the getPreset function in editor_presets.go.", + "examples": [ + "vim", + "nvim", + "emacs", + "nano", + "vscode", + "sublime", + "kakoune", + "helix", + "xcode" + ] + }, + "open": { + "type": "string", + "description": "Command for opening a file, as if the file is double-clicked. Should\ncontain \"{{filename}}\", but doesn't support \"{{line}}\"." + }, + "openLink": { + "type": "string", + "description": "Command for opening a link. Should contain \"{{link}}\"." + }, + "editCommand": { + "type": "string", + "description": "EditCommand is the command for editing a file.\nDeprecated: use Edit instead. Note that semantics are different:\nEditCommand is just the command itself, whereas Edit contains a\n\"{{filename}}\" variable." + }, + "editCommandTemplate": { + "type": "string", + "description": "EditCommandTemplate is the command template for editing a file\nDeprecated: use EditAtLine instead." + }, + "openCommand": { + "type": "string", + "description": "OpenCommand is the command for opening a file\nDeprecated: use Open instead." + }, + "openLinkCommand": { + "type": "string", + "description": "OpenLinkCommand is the command for opening a link\nDeprecated: use OpenLink instead." + }, + "copyToClipboardCmd": { + "type": "string", + "description": "CopyToClipboardCmd is the command for copying to clipboard.\nSee https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-clipboard" + } + }, + "additionalProperties": false, + "type": "object", + "description": "Config relating to things outside of Lazygit like how files are opened, copying to clipboard, etc" + }, + "disableStartupPopups": { + "type": "boolean", + "description": "If true, don't display introductory popups upon opening Lazygit.\nLazygit sets this to true upon first runninng the program so that you don't see introductory popups every time you open the program." + }, + "customCommands": { + "items": { + "properties": { + "key": { + "type": "string", + "description": "The key to trigger the command. Use a single letter or one of the values from https://github.com/jesseduffield/lazygit/blob/master/docs/keybindings/Custom_Keybindings.md" + }, + "context": { + "type": "string", + "enum": [ + "status", + "files", + "worktrees", + "localBranches", + "remotes", + "remoteBranches", + "tags", + "commits", + "reflogCommits", + "subCommits", + "commitFiles", + "stash", + "global" + ], + "description": "The context in which to listen for the key" + }, + "command": { + "type": "string", + "description": "The command to run (using Go template syntax for placeholder values)", + "examples": [ + "git fetch {{.Form.Remote}} {{.Form.Branch}} \u0026\u0026 git checkout FETCH_HEAD" + ] + }, + "subprocess": { + "type": "boolean", + "description": "If true, run the command in a subprocess (e.g. if the command requires user input)" + }, + "prompts": { + "items": { + "properties": { + "type": { + "type": "string", + "description": "One of: 'input' | 'menu' | 'confirm' | 'menuFromCommand'" + }, + "key": { + "type": "string", + "description": "Used to reference the entered value from within the custom command. E.g. a prompt with `key: 'Branch'` can be referred to as `{{.Form.Branch}}` in the command" + }, + "title": { + "type": "string", + "description": "The title to display in the popup panel" + }, + "initialValue": { + "type": "string", + "description": "The initial value to appear in the text box.\nOnly for input prompts." + }, + "suggestions": { + "properties": { + "preset": { + "type": "string", + "enum": [ + "authors", + "branches", + "files", + "refs", + "remotes", + "remoteBranches", + "tags" + ], + "description": "Uses built-in logic to obtain the suggestions. One of 'authors' | 'branches' | 'files' | 'refs' | 'remotes' | 'remoteBranches' | 'tags'" + }, + "command": { + "type": "string", + "description": "Command to run such that each line in the output becomes a suggestion. Mutually exclusive with 'preset' field.", + "examples": [ + "git fetch {{.Form.Remote}} {{.Form.Branch}} \u0026\u0026 git checkout FETCH_HEAD" + ] + } + }, + "additionalProperties": false, + "type": "object", + "description": "Shows suggestions as the input is entered\nOnly for input prompts." + }, + "body": { + "type": "string", + "description": "The message of the confirmation prompt.\nOnly for confirm prompts.", + "examples": [ + "Are you sure you want to push to the remote?" + ] + }, + "options": { + "items": { + "properties": { + "name": { + "type": "string", + "description": "The first part of the label" + }, + "description": { + "type": "string", + "description": "The second part of the label" + }, + "value": { + "type": "string", + "minLength": 1, + "description": "The value that will be used in the command", + "examples": [ + "feature" + ] + } + }, + "additionalProperties": false, + "type": "object" + }, + "type": "array", + "description": "Menu options.\nOnly for menu prompts." + }, + "command": { + "type": "string", + "description": "The command to run to generate menu options\nOnly for menuFromCommand prompts.", + "examples": [ + "git fetch {{.Form.Remote}} {{.Form.Branch}} \u0026\u0026 git checkout FETCH_HEAD" + ] + }, + "filter": { + "type": "string", + "description": "The regexp to run specifying groups which are going to be kept from the command's output.\nOnly for menuFromCommand prompts.", + "examples": [ + ".*{{.SelectedRemote.Name }}/(?P\u003cbranch\u003e.*)" + ] + }, + "valueFormat": { + "type": "string", + "description": "How to format matched groups from the filter to construct a menu item's value.\nOnly for menuFromCommand prompts.", + "examples": [ + "{{ .branch }}" + ] + }, + "labelFormat": { + "type": "string", + "description": "Like valueFormat but for the labels. If `labelFormat` is not specified, `valueFormat` is shown instead.\nOnly for menuFromCommand prompts.", + "examples": [ + "{{ .branch | green }}" + ] + } + }, + "additionalProperties": false, + "type": "object" + }, + "type": "array", + "description": "A list of prompts that will request user input before running the final command" + }, + "loadingText": { + "type": "string", + "description": "Text to display while waiting for command to finish", + "examples": [ + "Loading..." + ] + }, + "description": { + "type": "string", + "description": "Label for the custom command when displayed in the keybindings menu" + }, + "stream": { + "type": "boolean", + "description": "If true, stream the command's output to the Command Log panel" + }, + "showOutput": { + "type": "boolean", + "description": "If true, show the command's output in a popup within Lazygit" + }, + "after": { + "properties": { + "checkForConflicts": { + "type": "boolean" + } + }, + "additionalProperties": false, + "type": "object", + "description": "Actions to take after the command has completed" + } + }, + "additionalProperties": false, + "type": "object" + }, + "type": "array", + "uniqueItems": true, + "description": "User-configured commands that can be invoked from within Lazygit" + }, + "services": { + "additionalProperties": { + "type": "string" + }, + "type": "object", + "description": "See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-pull-request-urls" + }, + "notARepository": { + "type": "string", + "enum": [ + "prompt", + "create", + "skip", + "quit" + ], + "description": "What to do when opening Lazygit outside of a git repo.\n- 'prompt': (default) ask whether to initialize a new repo or open in the most recent repo\n- 'create': initialize a new repo\n- 'skip': open most recent repo\n- 'quit': exit Lazygit", + "default": "prompt" + }, + "promptToReturnFromSubprocess": { + "type": "boolean", + "description": "If true, display a confirmation when subprocess terminates. This allows you to view the output of the subprocess before returning to Lazygit.", + "default": true + }, "keybinding": { "properties": { "universal": { @@ -1329,291 +1614,6 @@ "additionalProperties": false, "type": "object", "description": "Keybindings" - }, - "os": { - "properties": { - "edit": { - "type": "string", - "description": "Command for editing a file. Should contain \"{{filename}}\"." - }, - "editAtLine": { - "type": "string", - "description": "Command for editing a file at a given line number. Should contain\n\"{{filename}}\", and may optionally contain \"{{line}}\"." - }, - "editAtLineAndWait": { - "type": "string", - "description": "Same as EditAtLine, except that the command needs to wait until the\nwindow is closed." - }, - "editInTerminal": { - "type": "boolean", - "description": "Whether lazygit suspends until an edit process returns\nPointer to bool so that we can distinguish unset (nil) from false.\nWe're naming this `editInTerminal` for backwards compatibility" - }, - "openDirInEditor": { - "type": "string", - "description": "For opening a directory in an editor" - }, - "editPreset": { - "type": "string", - "description": "A built-in preset that sets all of the above settings. Supported presets\nare defined in the getPreset function in editor_presets.go.", - "examples": [ - "vim", - "nvim", - "emacs", - "nano", - "vscode", - "sublime", - "kakoune", - "helix", - "xcode" - ] - }, - "open": { - "type": "string", - "description": "Command for opening a file, as if the file is double-clicked. Should\ncontain \"{{filename}}\", but doesn't support \"{{line}}\"." - }, - "openLink": { - "type": "string", - "description": "Command for opening a link. Should contain \"{{link}}\"." - }, - "editCommand": { - "type": "string", - "description": "EditCommand is the command for editing a file.\nDeprecated: use Edit instead. Note that semantics are different:\nEditCommand is just the command itself, whereas Edit contains a\n\"{{filename}}\" variable." - }, - "editCommandTemplate": { - "type": "string", - "description": "EditCommandTemplate is the command template for editing a file\nDeprecated: use EditAtLine instead." - }, - "openCommand": { - "type": "string", - "description": "OpenCommand is the command for opening a file\nDeprecated: use Open instead." - }, - "openLinkCommand": { - "type": "string", - "description": "OpenLinkCommand is the command for opening a link\nDeprecated: use OpenLink instead." - }, - "copyToClipboardCmd": { - "type": "string", - "description": "CopyToClipboardCmd is the command for copying to clipboard.\nSee https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-clipboard" - } - }, - "additionalProperties": false, - "type": "object", - "description": "Config relating to things outside of Lazygit like how files are opened, copying to clipboard, etc" - }, - "disableStartupPopups": { - "type": "boolean", - "description": "If true, don't display introductory popups upon opening Lazygit.\nLazygit sets this to true upon first runninng the program so that you don't see introductory popups every time you open the program." - }, - "customCommands": { - "items": { - "properties": { - "key": { - "type": "string", - "description": "The key to trigger the command. Use a single letter or one of the values from https://github.com/jesseduffield/lazygit/blob/master/docs/keybindings/Custom_Keybindings.md" - }, - "context": { - "type": "string", - "enum": [ - "status", - "files", - "worktrees", - "localBranches", - "remotes", - "remoteBranches", - "tags", - "commits", - "reflogCommits", - "subCommits", - "commitFiles", - "stash", - "global" - ], - "description": "The context in which to listen for the key" - }, - "command": { - "type": "string", - "description": "The command to run (using Go template syntax for placeholder values)", - "examples": [ - "git fetch {{.Form.Remote}} {{.Form.Branch}} \u0026\u0026 git checkout FETCH_HEAD" - ] - }, - "subprocess": { - "type": "boolean", - "description": "If true, run the command in a subprocess (e.g. if the command requires user input)" - }, - "prompts": { - "items": { - "properties": { - "type": { - "type": "string", - "description": "One of: 'input' | 'menu' | 'confirm' | 'menuFromCommand'" - }, - "key": { - "type": "string", - "description": "Used to reference the entered value from within the custom command. E.g. a prompt with `key: 'Branch'` can be referred to as `{{.Form.Branch}}` in the command" - }, - "title": { - "type": "string", - "description": "The title to display in the popup panel" - }, - "initialValue": { - "type": "string", - "description": "The initial value to appear in the text box.\nOnly for input prompts." - }, - "suggestions": { - "properties": { - "preset": { - "type": "string", - "enum": [ - "authors", - "branches", - "files", - "refs", - "remotes", - "remoteBranches", - "tags" - ], - "description": "Uses built-in logic to obtain the suggestions. One of 'authors' | 'branches' | 'files' | 'refs' | 'remotes' | 'remoteBranches' | 'tags'" - }, - "command": { - "type": "string", - "description": "Command to run such that each line in the output becomes a suggestion. Mutually exclusive with 'preset' field.", - "examples": [ - "git fetch {{.Form.Remote}} {{.Form.Branch}} \u0026\u0026 git checkout FETCH_HEAD" - ] - } - }, - "additionalProperties": false, - "type": "object", - "description": "Shows suggestions as the input is entered\nOnly for input prompts." - }, - "body": { - "type": "string", - "description": "The message of the confirmation prompt.\nOnly for confirm prompts.", - "examples": [ - "Are you sure you want to push to the remote?" - ] - }, - "options": { - "items": { - "properties": { - "name": { - "type": "string", - "description": "The first part of the label" - }, - "description": { - "type": "string", - "description": "The second part of the label" - }, - "value": { - "type": "string", - "minLength": 1, - "description": "The value that will be used in the command", - "examples": [ - "feature" - ] - } - }, - "additionalProperties": false, - "type": "object" - }, - "type": "array", - "description": "Menu options.\nOnly for menu prompts." - }, - "command": { - "type": "string", - "description": "The command to run to generate menu options\nOnly for menuFromCommand prompts.", - "examples": [ - "git fetch {{.Form.Remote}} {{.Form.Branch}} \u0026\u0026 git checkout FETCH_HEAD" - ] - }, - "filter": { - "type": "string", - "description": "The regexp to run specifying groups which are going to be kept from the command's output.\nOnly for menuFromCommand prompts.", - "examples": [ - ".*{{.SelectedRemote.Name }}/(?P\u003cbranch\u003e.*)" - ] - }, - "valueFormat": { - "type": "string", - "description": "How to format matched groups from the filter to construct a menu item's value.\nOnly for menuFromCommand prompts.", - "examples": [ - "{{ .branch }}" - ] - }, - "labelFormat": { - "type": "string", - "description": "Like valueFormat but for the labels. If `labelFormat` is not specified, `valueFormat` is shown instead.\nOnly for menuFromCommand prompts.", - "examples": [ - "{{ .branch | green }}" - ] - } - }, - "additionalProperties": false, - "type": "object" - }, - "type": "array", - "description": "A list of prompts that will request user input before running the final command" - }, - "loadingText": { - "type": "string", - "description": "Text to display while waiting for command to finish", - "examples": [ - "Loading..." - ] - }, - "description": { - "type": "string", - "description": "Label for the custom command when displayed in the keybindings menu" - }, - "stream": { - "type": "boolean", - "description": "If true, stream the command's output to the Command Log panel" - }, - "showOutput": { - "type": "boolean", - "description": "If true, show the command's output in a popup within Lazygit" - }, - "after": { - "properties": { - "checkForConflicts": { - "type": "boolean" - } - }, - "additionalProperties": false, - "type": "object", - "description": "Actions to take after the command has completed" - } - }, - "additionalProperties": false, - "type": "object" - }, - "type": "array", - "uniqueItems": true, - "description": "User-configured commands that can be invoked from within Lazygit" - }, - "services": { - "additionalProperties": { - "type": "string" - }, - "type": "object", - "description": "See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-pull-request-urls" - }, - "notARepository": { - "type": "string", - "enum": [ - "prompt", - "create", - "skip", - "quit" - ], - "description": "What to do when opening Lazygit outside of a git repo.\n- 'prompt': (default) ask whether to initialize a new repo or open in the most recent repo\n- 'create': initialize a new repo\n- 'skip': open most recent repo\n- 'quit': exit Lazygit", - "default": "prompt" - }, - "promptToReturnFromSubprocess": { - "type": "boolean", - "description": "If true, display a confirmation when subprocess terminates. This allows you to view the output of the subprocess before returning to Lazygit.", - "default": true } }, "additionalProperties": false,