diff --git a/docs-master/Config.md b/docs-master/Config.md index 05d3b6d20..882baec63 100644 --- a/docs-master/Config.md +++ b/docs-master/Config.md @@ -591,36 +591,30 @@ notARepository: prompt # view the output of the subprocess before returning to Lazygit. promptToReturnFromSubprocess: true -# Keybindings +# Keybindings. +# Each binding can be a single key or a list of keys; see +# https://github.com/jesseduffield/lazygit/blob/master/docs/keybindings/Custom_Keybindings.md +# for the syntax. keybinding: universal: - quit: q - quit-alt1: + quit: [q, ] suspendApp: return: quitWithoutChangingDirectory: Q togglePanel: - prevItem: - nextItem: - prevItem-alt: k - nextItem-alt: j + prevItem: [, k] + nextItem: [, j] prevPage: ',' nextPage: . scrollLeft: H scrollRight: L - gotoTop: < - gotoBottom: '>' - gotoTop-alt: - gotoBottom-alt: + gotoTop: [<, ] + gotoBottom: ['>', ] toggleRangeSelect: v rangeSelectDown: rangeSelectUp: - prevBlock: - nextBlock: - prevBlock-alt: h - nextBlock-alt: l - nextBlock-alt2: - prevBlock-alt2: + prevBlock: [, h, ] + nextBlock: [, l, ] jumpToBlock: - "1" - "2" @@ -643,8 +637,7 @@ keybinding: # on Mac forwardDeleteWord: - optionMenu: - optionMenu-alt1: '?' + optionMenu: '?' select: goInto: confirm: @@ -652,18 +645,13 @@ keybinding: confirmSuggestion: # on Mac - confirmInEditor: - confirmInEditor-alt: + confirmInEditor: [, ] remove: d new: "n" edit: e openFile: o - scrollUpMain: - scrollDownMain: - scrollUpMain-alt1: K - scrollDownMain-alt1: J - scrollUpMain-alt2: - scrollDownMain-alt2: + scrollUpMain: [, K, ] + scrollDownMain: [, J, ] executeShellCommand: ':' createRebaseOptionsMenu: m @@ -682,8 +670,7 @@ keybinding: undo: z redo: Z filteringMenu: - diffingMenu: W - diffingMenu-alt: + diffingMenu: [W, ] copyToClipboard: openRecentRepos: submitEditorText: @@ -779,6 +766,8 @@ keybinding: commitFiles: checkoutCommitFile: c main: + prevHunk: [, h] + nextHunk: [, l] toggleSelectHunk: a pickBothHunks: b editSelectHunk: E diff --git a/docs-master/Custom_Command_Keybindings.md b/docs-master/Custom_Command_Keybindings.md index c8036ea41..55e14d5f1 100644 --- a/docs-master/Custom_Command_Keybindings.md +++ b/docs-master/Custom_Command_Keybindings.md @@ -50,7 +50,7 @@ Custom command keybindings will appear alongside inbuilt keybindings when you vi For a given custom command, here are the allowed fields: | _field_ | _description_ | required | |-----------------|----------------------|-| -| key | The key to trigger the command. Use a single letter or one of the values from [here](https://github.com/jesseduffield/lazygit/blob/master/docs/keybindings/Custom_Keybindings.md). Custom commands without a key specified can be triggered by selecting them from the keybindings (`?`) menu | no | +| key | The key to trigger the command. Use a single key or list of keys, as described in [Custom_Keybindings.md](https://github.com/jesseduffield/lazygit/blob/master/docs/keybindings/Custom_Keybindings.md). Custom commands without a key specified can be triggered by selecting them from the keybindings (`?`) menu | no | | command | The command to run (using Go template syntax for placeholder values) | yes | | context | The context in which to listen for the key (see [below](#contexts)) | yes | | prompts | A list of prompts that will request user input before running the final command | no | @@ -193,7 +193,7 @@ The permitted option fields are: | name | The first part of the label | no | | description | The second part of the label | no | | value | the value that will be used in the command | yes | -| key | Keybinding to invoke this menu option without needing to navigate to it. Can be a single letter or one of the values from [here](https://github.com/jesseduffield/lazygit/blob/master/docs/keybindings/Custom_Keybindings.md) | no | +| key | Keybinding to invoke this menu option without needing to navigate to it. Use a single key or list of keys, as described in [Custom_Keybindings.md](https://github.com/jesseduffield/lazygit/blob/master/docs/keybindings/Custom_Keybindings.md) | no | If an option has no name the value will be displayed to the user in place of the name, so you're allowed to only include the value like so: diff --git a/docs-master/keybindings/Custom_Keybindings.md b/docs-master/keybindings/Custom_Keybindings.md index ad24070a3..998aae14c 100644 --- a/docs-master/keybindings/Custom_Keybindings.md +++ b/docs-master/keybindings/Custom_Keybindings.md @@ -7,6 +7,8 @@ A keybinding is one of: - A special key name in angle brackets, e.g. ``, ``, ``. - A key with modifiers in angle brackets, e.g. ``, ``. - The literal string `` to disable a binding. +- A list of any of the above, to bind multiple keys to the same action: + `quit: [q, ]`. ### Modifiers diff --git a/docs-master/keybindings/Keybindings_en.md b/docs-master/keybindings/Keybindings_en.md index 74dbcc97d..71ecdae5b 100644 --- a/docs-master/keybindings/Keybindings_en.md +++ b/docs-master/keybindings/Keybindings_en.md @@ -7,8 +7,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | Key | Action | Info | |-----|--------|-------------| | `` `` | Switch to a recent repo | | -| `` (fn+up/shift+k) `` | Scroll up main window | | -| `` (fn+down/shift+j) `` | Scroll down main window | | +| `` , K, (fn+up/shift+k) `` | Scroll up main window | | +| `` , J, (fn+down/shift+j) `` | Scroll down main window | | | `` @ `` | View command log options | View options for the command log e.g. show/hide the command log and focus the command log. | | `` P `` | Push | Push the current branch to its upstream branch. If no upstream is configured, you will be prompted to configure an upstream branch. | | `` p `` | Pull | Pull changes from the remote for the current branch. If no upstream is configured, you will be prompted to configure an upstream branch. | @@ -26,9 +26,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | Cancel | | | `` ? `` | Open keybindings menu | | | `` `` | View filter options | View options for filtering the commit log, so that only commits matching the filter are shown. | -| `` W `` | View diffing options | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. | -| `` `` | View diffing options | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. | -| `` q `` | Quit | | +| `` W, `` | View diffing options | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. | +| `` q, `` | Quit | | | `` `` | Suspend the application | | | `` `` | Toggle whitespace | Toggle whether or not whitespace changes are shown in the diff view.

The default can be changed in the config file with the key 'git.ignoreWhitespaceInDiffView'. | | `` z `` | Undo | The reflog will be used to determine what git command to run to undo the last git command. This does not include changes to the working tree; only commits are taken into consideration. | @@ -40,8 +39,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct |-----|--------|-------------| | `` , `` | Previous page | | | `` . `` | Next page | | -| `` < () `` | Scroll to top | | -| `` > () `` | Scroll to bottom | | +| `` <, `` | Scroll to top | | +| `` >, `` | Scroll to bottom | | | `` v `` | Toggle range select | | | `` `` | Range select down | | | `` `` | Range select up | | @@ -205,10 +204,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct |-----|--------|-------------| | `` `` | Pick hunk | | | `` b `` | Pick all hunks | | -| `` `` | Previous hunk | | -| `` `` | Next hunk | | -| `` `` | Previous conflict | | -| `` `` | Next conflict | | +| `` , k `` | Previous hunk | | +| `` , j `` | Next hunk | | +| `` , h `` | Previous conflict | | +| `` , l `` | Next conflict | | | `` z `` | Undo | Undo last merge conflict resolution. | | `` e `` | Edit file | Open file in external editor. | | `` o `` | Open file | Open file in default application. | @@ -229,8 +228,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | Key | Action | Info | |-----|--------|-------------| -| `` `` | Go to previous hunk | | -| `` `` | Go to next hunk | | +| `` , h `` | Go to previous hunk | | +| `` , l `` | Go to next hunk | | | `` v `` | Toggle range select | | | `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. | | `` `` | Copy selected text to clipboard | | @@ -245,8 +244,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | Key | Action | Info | |-----|--------|-------------| -| `` `` | Go to previous hunk | | -| `` `` | Go to next hunk | | +| `` , h `` | Go to previous hunk | | +| `` , l `` | Go to next hunk | | | `` v `` | Toggle range select | | | `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. | | `` `` | Copy selected text to clipboard | | diff --git a/docs-master/keybindings/Keybindings_ja.md b/docs-master/keybindings/Keybindings_ja.md index f06f1a0bf..5a958f80e 100644 --- a/docs-master/keybindings/Keybindings_ja.md +++ b/docs-master/keybindings/Keybindings_ja.md @@ -7,8 +7,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | Key | Action | Info | |-----|--------|-------------| | `` `` | 最近のリポジトリをチェックアウト | | -| `` (fn+up/shift+k) `` | メインウィンドウを上にスクロール | | -| `` (fn+down/shift+j) `` | メインウィンドウを下にスクロール | | +| `` , K, (fn+up/shift+k) `` | メインウィンドウを上にスクロール | | +| `` , J, (fn+down/shift+j) `` | メインウィンドウを下にスクロール | | | `` @ `` | コマンドログオプションを表示 | コマンドログのオプションを表示します(例:コマンドログの表示/非表示、コマンドログへのフォーカスなど)。 | | `` P `` | プッシュ | 現在のブランチを対応するアップストリームブランチにプッシュします。アップストリームが設定されていない場合、アップストリームブランチの設定を求められます。 | | `` p `` | プル | 現在のブランチのリモートから変更をプルします。アップストリームが設定されていない場合、アップストリームブランチの設定を求められます。 | @@ -26,9 +26,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | キャンセル | | | `` ? `` | キーバインディングメニューを開く | | | `` `` | フィルターオプションを表示 | コミットログのフィルタリングオプションを表示し、フィルタに一致するコミットのみを表示します。 | -| `` W `` | 差分オプションを表示 | 2つのrefの差分に関連するオプションを表示します(例:選択したrefとの差分表示、差分を取るrefの入力、差分方向の反転など)。 | -| `` `` | 差分オプションを表示 | 2つのrefの差分に関連するオプションを表示します(例:選択したrefとの差分表示、差分を取るrefの入力、差分方向の反転など)。 | -| `` q `` | 終了 | | +| `` W, `` | 差分オプションを表示 | 2つのrefの差分に関連するオプションを表示します(例:選択したrefとの差分表示、差分を取るrefの入力、差分方向の反転など)。 | +| `` q, `` | 終了 | | | `` `` | Suspend the application | | | `` `` | 空白表示の切り替え | Toggle whether or not whitespace changes are shown in the diff view.

The default can be changed in the config file with the key 'git.ignoreWhitespaceInDiffView'. | | `` z `` | 元に戻す | 最後のgitコマンドを元に戻すために実行するgitコマンドを決定するためにreflogが使用されます。これにはワーキングツリーへの変更は含まれません。コミットのみが考慮されます。 | @@ -40,8 +39,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct |-----|--------|-------------| | `` , `` | 前のページ | | | `` . `` | 次のページ | | -| `` < () `` | 先頭にスクロール | | -| `` > () `` | 末尾にスクロール | | +| `` <, `` | 先頭にスクロール | | +| `` >, `` | 末尾にスクロール | | | `` v `` | 範囲選択を切り替え | | | `` `` | 範囲選択を下に | | | `` `` | 範囲選択を上に | | @@ -248,8 +247,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | Key | Action | Info | |-----|--------|-------------| -| `` `` | 前のハンクに移動 | | -| `` `` | 次のハンクに移動 | | +| `` , h `` | 前のハンクに移動 | | +| `` , l `` | 次のハンクに移動 | | | `` v `` | 範囲選択を切り替え | | | `` a `` | ハンクの選択を切り替える | Toggle line-by-line vs. hunk selection mode. | | `` `` | 選択したテキストをクリップボードにコピー | | @@ -270,8 +269,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | Key | Action | Info | |-----|--------|-------------| -| `` `` | 前のハンクに移動 | | -| `` `` | 次のハンクに移動 | | +| `` , h `` | 前のハンクに移動 | | +| `` , l `` | 次のハンクに移動 | | | `` v `` | 範囲選択を切り替え | | | `` a `` | ハンクの選択を切り替える | Toggle line-by-line vs. hunk selection mode. | | `` `` | 選択したテキストをクリップボードにコピー | | @@ -288,10 +287,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct |-----|--------|-------------| | `` `` | ハンクを選択 | | | `` b `` | すべてのハンクを選択 | | -| `` `` | 前のハンク | | -| `` `` | 次のハンク | | -| `` `` | 前のコンフリクト | | -| `` `` | 次のコンフリクト | | +| `` , k `` | 前のハンク | | +| `` , j `` | 次のハンク | | +| `` , h `` | 前のコンフリクト | | +| `` , l `` | 次のコンフリクト | | | `` z `` | 元に戻す | 最後のマージコンフリクト解決を元に戻します。 | | `` e `` | ファイルを編集 | 外部エディタでファイルを開きます。 | | `` o `` | ファイルを開く | デフォルトのアプリケーションでファイルを開きます。 | diff --git a/docs-master/keybindings/Keybindings_ko.md b/docs-master/keybindings/Keybindings_ko.md index 2b96d0aa8..d6121452b 100644 --- a/docs-master/keybindings/Keybindings_ko.md +++ b/docs-master/keybindings/Keybindings_ko.md @@ -7,8 +7,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | Key | Action | Info | |-----|--------|-------------| | `` `` | 최근에 사용한 저장소로 전환 | | -| `` (fn+up/shift+k) `` | 메인 패널을 위로 스크롤 | | -| `` (fn+down/shift+j) `` | 메인 패널을 아래로로 스크롤 | | +| `` , K, (fn+up/shift+k) `` | 메인 패널을 위로 스크롤 | | +| `` , J, (fn+down/shift+j) `` | 메인 패널을 아래로로 스크롤 | | | `` @ `` | 명령어 로그 메뉴 열기 | View options for the command log e.g. show/hide the command log and focus the command log. | | `` P `` | 푸시 | Push the current branch to its upstream branch. If no upstream is configured, you will be prompted to configure an upstream branch. | | `` p `` | 업데이트 | Pull changes from the remote for the current branch. If no upstream is configured, you will be prompted to configure an upstream branch. | @@ -26,9 +26,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | 취소 | | | `` ? `` | 매뉴 열기 | | | `` `` | View filter-by-path options | View options for filtering the commit log, so that only commits matching the filter are shown. | -| `` W `` | Diff 메뉴 열기 | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. | -| `` `` | Diff 메뉴 열기 | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. | -| `` q `` | 종료 | | +| `` W, `` | Diff 메뉴 열기 | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. | +| `` q, `` | 종료 | | | `` `` | Suspend the application | | | `` `` | 공백문자를 Diff 뷰에서 표시 여부 전환 | Toggle whether or not whitespace changes are shown in the diff view.

The default can be changed in the config file with the key 'git.ignoreWhitespaceInDiffView'. | | `` z `` | 되돌리기 (reflog) (실험적) | The reflog will be used to determine what git command to run to undo the last git command. This does not include changes to the working tree; only commits are taken into consideration. | @@ -40,8 +39,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct |-----|--------|-------------| | `` , `` | 이전 페이지 | | | `` . `` | 다음 페이지 | | -| `` < () `` | 맨 위로 스크롤 | | -| `` > () `` | 맨 아래로 스크롤 | | +| `` <, `` | 맨 위로 스크롤 | | +| `` >, `` | 맨 아래로 스크롤 | | | `` v `` | 드래그 선택 전환 | | | `` `` | Range select down | | | `` `` | Range select up | | @@ -144,10 +143,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct |-----|--------|-------------| | `` `` | Pick hunk | | | `` b `` | Pick all hunks | | -| `` `` | 이전 hunk를 선택 | | -| `` `` | 다음 hunk를 선택 | | -| `` `` | 이전 충돌을 선택 | | -| `` `` | 다음 충돌을 선택 | | +| `` , k `` | 이전 hunk를 선택 | | +| `` , j `` | 다음 hunk를 선택 | | +| `` , h `` | 이전 충돌을 선택 | | +| `` , l `` | 다음 충돌을 선택 | | | `` z `` | 되돌리기 | Undo last merge conflict resolution. | | `` e `` | 파일 편집 | Open file in external editor. | | `` o `` | 파일 닫기 | Open file in default application. | @@ -168,8 +167,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | Key | Action | Info | |-----|--------|-------------| -| `` `` | 이전 hunk를 선택 | | -| `` `` | 다음 hunk를 선택 | | +| `` , h `` | 이전 hunk를 선택 | | +| `` , l `` | 다음 hunk를 선택 | | | `` v `` | 드래그 선택 전환 | | | `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. | | `` `` | 선택한 텍스트를 클립보드에 복사 | | @@ -184,8 +183,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | Key | Action | Info | |-----|--------|-------------| -| `` `` | 이전 hunk를 선택 | | -| `` `` | 다음 hunk를 선택 | | +| `` , h `` | 이전 hunk를 선택 | | +| `` , l `` | 다음 hunk를 선택 | | | `` v `` | 드래그 선택 전환 | | | `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. | | `` `` | 선택한 텍스트를 클립보드에 복사 | | diff --git a/docs-master/keybindings/Keybindings_nl.md b/docs-master/keybindings/Keybindings_nl.md index f048e2a2c..23a08aae4 100644 --- a/docs-master/keybindings/Keybindings_nl.md +++ b/docs-master/keybindings/Keybindings_nl.md @@ -7,8 +7,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | Key | Action | Info | |-----|--------|-------------| | `` `` | Wissel naar een recente repo | | -| `` (fn+up/shift+k) `` | Scroll naar beneden vanaf hoofdpaneel | | -| `` (fn+down/shift+j) `` | Scroll naar beneden vanaf hoofdpaneel | | +| `` , K, (fn+up/shift+k) `` | Scroll naar beneden vanaf hoofdpaneel | | +| `` , J, (fn+down/shift+j) `` | Scroll naar beneden vanaf hoofdpaneel | | | `` @ `` | View command log options | View options for the command log e.g. show/hide the command log and focus the command log. | | `` P `` | Push | Push the current branch to its upstream branch. If no upstream is configured, you will be prompted to configure an upstream branch. | | `` p `` | Pull | Pull changes from the remote for the current branch. If no upstream is configured, you will be prompted to configure an upstream branch. | @@ -26,9 +26,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | Annuleren | | | `` ? `` | Open menu | | | `` `` | Bekijk scoping opties | View options for filtering the commit log, so that only commits matching the filter are shown. | -| `` W `` | Open diff menu | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. | -| `` `` | Open diff menu | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. | -| `` q `` | Quit | | +| `` W, `` | Open diff menu | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. | +| `` q, `` | Quit | | | `` `` | Suspend the application | | | `` `` | Toggle whitespace | Toggle whether or not whitespace changes are shown in the diff view.

The default can be changed in the config file with the key 'git.ignoreWhitespaceInDiffView'. | | `` z `` | Ongedaan maken (via reflog) (experimenteel) | The reflog will be used to determine what git command to run to undo the last git command. This does not include changes to the working tree; only commits are taken into consideration. | @@ -40,8 +39,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct |-----|--------|-------------| | `` , `` | Vorige pagina | | | `` . `` | Volgende pagina | | -| `` < () `` | Scroll naar boven | | -| `` > () `` | Scroll naar beneden | | +| `` <, `` | Scroll naar boven | | +| `` >, `` | Scroll naar beneden | | | `` v `` | Toggle drag selecteer | | | `` `` | Range select down | | | `` `` | Range select up | | @@ -213,10 +212,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct |-----|--------|-------------| | `` `` | Kies stuk | | | `` b `` | Kies beide stukken | | -| `` `` | Selecteer bovenste hunk | | -| `` `` | Selecteer onderste hunk | | -| `` `` | Selecteer voorgaand conflict | | -| `` `` | Selecteer volgende conflict | | +| `` , k `` | Selecteer bovenste hunk | | +| `` , j `` | Selecteer onderste hunk | | +| `` , h `` | Selecteer voorgaand conflict | | +| `` , l `` | Selecteer volgende conflict | | | `` z `` | Ongedaan maken | Undo last merge conflict resolution. | | `` e `` | Verander bestand | Open file in external editor. | | `` o `` | Open bestand | Open file in default application. | @@ -237,8 +236,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | Key | Action | Info | |-----|--------|-------------| -| `` `` | Selecteer de vorige hunk | | -| `` `` | Selecteer de volgende hunk | | +| `` , h `` | Selecteer de vorige hunk | | +| `` , l `` | Selecteer de volgende hunk | | | `` v `` | Toggle drag selecteer | | | `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. | | `` `` | Copy selected text to clipboard | | @@ -312,8 +311,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | Key | Action | Info | |-----|--------|-------------| -| `` `` | Selecteer de vorige hunk | | -| `` `` | Selecteer de volgende hunk | | +| `` , h `` | Selecteer de vorige hunk | | +| `` , l `` | Selecteer de volgende hunk | | | `` v `` | Toggle drag selecteer | | | `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. | | `` `` | Copy selected text to clipboard | | diff --git a/docs-master/keybindings/Keybindings_pl.md b/docs-master/keybindings/Keybindings_pl.md index fcde7bfb4..b233fd6d4 100644 --- a/docs-master/keybindings/Keybindings_pl.md +++ b/docs-master/keybindings/Keybindings_pl.md @@ -7,8 +7,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | Key | Action | Info | |-----|--------|-------------| | `` `` | Przełącz na ostatnie repozytorium | | -| `` (fn+up/shift+k) `` | Przewiń główne okno w górę | | -| `` (fn+down/shift+j) `` | Przewiń główne okno w dół | | +| `` , K, (fn+up/shift+k) `` | Przewiń główne okno w górę | | +| `` , J, (fn+down/shift+j) `` | Przewiń główne okno w dół | | | `` @ `` | Pokaż opcje dziennika poleceń | Pokaż opcje dla dziennika poleceń, np. pokazywanie/ukrywanie dziennika poleceń i skupienie na dzienniku poleceń. | | `` P `` | Wypchnij | Wypchnij bieżącą gałąź do jej gałęzi nadrzędnej. Jeśli nie skonfigurowano gałęzi nadrzędnej, zostaniesz poproszony o skonfigurowanie gałęzi nadrzędnej. | | `` p `` | Pociągnij | Pociągnij zmiany z zdalnego dla bieżącej gałęzi. Jeśli nie skonfigurowano gałęzi nadrzędnej, zostaniesz poproszony o skonfigurowanie gałęzi nadrzędnej. | @@ -26,9 +26,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | Anuluj | | | `` ? `` | Otwórz menu przypisań klawiszy | | | `` `` | Pokaż opcje filtrowania | Pokaż opcje filtrowania dziennika commitów, tak aby pokazywane były tylko commity pasujące do filtra. | -| `` W `` | Pokaż opcje różnicowania | Pokaż opcje dotyczące różnicowania dwóch refów, np. różnicowanie względem wybranego refa, wprowadzanie refa do różnicowania i odwracanie kierunku różnic. | -| `` `` | Pokaż opcje różnicowania | Pokaż opcje dotyczące różnicowania dwóch refów, np. różnicowanie względem wybranego refa, wprowadzanie refa do różnicowania i odwracanie kierunku różnic. | -| `` q `` | Wyjdź | | +| `` W, `` | Pokaż opcje różnicowania | Pokaż opcje dotyczące różnicowania dwóch refów, np. różnicowanie względem wybranego refa, wprowadzanie refa do różnicowania i odwracanie kierunku różnic. | +| `` q, `` | Wyjdź | | | `` `` | Suspend the application | | | `` `` | Przełącz białe znaki | Toggle whether or not whitespace changes are shown in the diff view.

The default can be changed in the config file with the key 'git.ignoreWhitespaceInDiffView'. | | `` z `` | Cofnij | Dziennik reflog zostanie użyty do określenia, jakie polecenie git należy uruchomić, aby cofnąć ostatnie polecenie git. Nie obejmuje to zmian w drzewie roboczym; brane są pod uwagę tylko commity. | @@ -40,8 +39,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct |-----|--------|-------------| | `` , `` | Poprzednia strona | | | `` . `` | Następna strona | | -| `` < () `` | Przewiń do góry | | -| `` > () `` | Przewiń do dołu | | +| `` <, `` | Przewiń do góry | | +| `` >, `` | Przewiń do dołu | | | `` v `` | Przełącz zaznaczenie zakresu | | | `` `` | Zaznacz zakres w dół | | | `` `` | Zaznacz zakres w górę | | @@ -115,8 +114,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | Key | Action | Info | |-----|--------|-------------| -| `` `` | Idź do poprzedniego fragmentu | | -| `` `` | Idź do następnego fragmentu | | +| `` , h `` | Idź do poprzedniego fragmentu | | +| `` , l `` | Idź do następnego fragmentu | | | `` v `` | Przełącz zaznaczenie zakresu | | | `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. | | `` `` | Kopiuj zaznaczony tekst do schowka | | @@ -189,10 +188,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct |-----|--------|-------------| | `` `` | Wybierz fragment | | | `` b `` | Wybierz wszystkie fragmenty | | -| `` `` | Poprzedni fragment | | -| `` `` | Następny fragment | | -| `` `` | Poprzedni konflikt | | -| `` `` | Następny konflikt | | +| `` , k `` | Poprzedni fragment | | +| `` , j `` | Następny fragment | | +| `` , h `` | Poprzedni konflikt | | +| `` , l `` | Następny konflikt | | | `` z `` | Cofnij | Cofnij ostatnie rozwiązanie konfliktu scalania. | | `` e `` | Edytuj plik | Otwórz plik w zewnętrznym edytorze. | | `` o `` | Otwórz plik | Otwórz plik w domyślnej aplikacji. | @@ -203,8 +202,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | Key | Action | Info | |-----|--------|-------------| -| `` `` | Idź do poprzedniego fragmentu | | -| `` `` | Idź do następnego fragmentu | | +| `` , h `` | Idź do poprzedniego fragmentu | | +| `` , l `` | Idź do następnego fragmentu | | | `` v `` | Przełącz zaznaczenie zakresu | | | `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. | | `` `` | Kopiuj zaznaczony tekst do schowka | | diff --git a/docs-master/keybindings/Keybindings_pt.md b/docs-master/keybindings/Keybindings_pt.md index 1aacc5722..22d5cbb55 100644 --- a/docs-master/keybindings/Keybindings_pt.md +++ b/docs-master/keybindings/Keybindings_pt.md @@ -7,8 +7,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | Key | Action | Info | |-----|--------|-------------| | `` `` | Mudar para um repositório recente | | -| `` (fn+up/shift+k) `` | Rolar janela principal para cima | | -| `` (fn+down/shift+j) `` | Rolar a janela principal para baixo | | +| `` , K, (fn+up/shift+k) `` | Rolar janela principal para cima | | +| `` , J, (fn+down/shift+j) `` | Rolar a janela principal para baixo | | | `` @ `` | View command log options | View options for the command log e.g. show/hide the command log and focus the command log. | | `` P `` | Empurre (Push) | Faça push do branch atual para o seu branch upstream. Se nenhum upstream estiver configurado, você será solicitado a configurar um branch a montante. | | `` p `` | Puxar (Pull) | Puxe alterações do controle remoto para o ramo atual. Se nenhum upstream estiver configurado, será solicitado configurar um ramo a montante. | @@ -26,9 +26,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | Cancelar | | | `` ? `` | Abrir o menu de atalhos do teclado | | | `` `` | Ver opções de filtro | View options for filtering the commit log, so that only commits matching the filter are shown. | -| `` W `` | View diffing options | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. | -| `` `` | View diffing options | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. | -| `` q `` | Sair | | +| `` W, `` | View diffing options | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. | +| `` q, `` | Sair | | | `` `` | Suspender a aplicação | | | `` `` | Toggle whitespace | Toggle whether or not whitespace changes are shown in the diff view.

The default can be changed in the config file with the key 'git.ignoreWhitespaceInDiffView'. | | `` z `` | Desfazer | O reflog será usado para determinar qual comando git para executar para desfazer o último comando git. Isto não inclui mudanças na árvore de trabalho; apenas compromissos são tidos em consideração. | @@ -40,8 +39,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct |-----|--------|-------------| | `` , `` | Aba anterior | | | `` . `` | Próxima aba | | -| `` < () `` | Voltar ao topo | | -| `` > () `` | Ir para o final | | +| `` <, `` | Voltar ao topo | | +| `` >, `` | Ir para o final | | | `` v `` | Toggle range select | | | `` `` | Range select down | | | `` `` | Range select up | | @@ -241,8 +240,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | Key | Action | Info | |-----|--------|-------------| -| `` `` | Ir para o local anterior | | -| `` `` | Ir para o próximo trecho | | +| `` , h `` | Ir para o local anterior | | +| `` , l `` | Ir para o próximo trecho | | | `` v `` | Toggle range select | | | `` a `` | Toggle hunk selection | Ativa/desativa modo linha por linha vs. modo de seleção por partes. | | `` `` | Copiar texto selecionado para área de transferência | | @@ -273,10 +272,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct |-----|--------|-------------| | `` `` | Escolha o local | | | `` b `` | Pegar todos os pedaços | | -| `` `` | Trecho anterior | | -| `` `` | Próximo trecho | | -| `` `` | Conflito anterior | | -| `` `` | Próximo conflito | | +| `` , k `` | Trecho anterior | | +| `` , j `` | Próximo trecho | | +| `` , h `` | Conflito anterior | | +| `` , l `` | Próximo conflito | | | `` z `` | Desfazer | Desfazer resolução de conflitos de última mesclagem. | | `` e `` | Editar arquivo | Abrir arquivo no editor externo. | | `` o `` | Abrir arquivo | Abrir arquivo no aplicativo padrão. | @@ -287,8 +286,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | Key | Action | Info | |-----|--------|-------------| -| `` `` | Ir para o local anterior | | -| `` `` | Ir para o próximo trecho | | +| `` , h `` | Ir para o local anterior | | +| `` , l `` | Ir para o próximo trecho | | | `` v `` | Toggle range select | | | `` a `` | Toggle hunk selection | Ativa/desativa modo linha por linha vs. modo de seleção por partes. | | `` `` | Copiar texto selecionado para área de transferência | | diff --git a/docs-master/keybindings/Keybindings_ru.md b/docs-master/keybindings/Keybindings_ru.md index 0dcb6753e..3b81d2433 100644 --- a/docs-master/keybindings/Keybindings_ru.md +++ b/docs-master/keybindings/Keybindings_ru.md @@ -7,8 +7,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | Key | Action | Info | |-----|--------|-------------| | `` `` | Переключиться на последний репозиторий | | -| `` (fn+up/shift+k) `` | Прокрутить вверх главную панель | | -| `` (fn+down/shift+j) `` | Прокрутить вниз главную панель | | +| `` , K, (fn+up/shift+k) `` | Прокрутить вверх главную панель | | +| `` , J, (fn+down/shift+j) `` | Прокрутить вниз главную панель | | | `` @ `` | Открыть меню журнала команд | View options for the command log e.g. show/hide the command log and focus the command log. | | `` P `` | Отправить изменения | Push the current branch to its upstream branch. If no upstream is configured, you will be prompted to configure an upstream branch. | | `` p `` | Получить и слить изменения | Pull changes from the remote for the current branch. If no upstream is configured, you will be prompted to configure an upstream branch. | @@ -26,9 +26,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | Отменить | | | `` ? `` | Открыть меню | | | `` `` | Просмотреть параметры фильтрации по пути | View options for filtering the commit log, so that only commits matching the filter are shown. | -| `` W `` | Открыть меню сравнении | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. | -| `` `` | Открыть меню сравнении | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. | -| `` q `` | Выйти | | +| `` W, `` | Открыть меню сравнении | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. | +| `` q, `` | Выйти | | | `` `` | Suspend the application | | | `` `` | Переключить отображение изменении пробелов в просмотрщике сравнении | Toggle whether or not whitespace changes are shown in the diff view.

The default can be changed in the config file with the key 'git.ignoreWhitespaceInDiffView'. | | `` z `` | Отменить (через reflog) (экспериментальный) | Журнал ссылок (reflog) будет использоваться для определения того, какую команду git запустить, чтобы отменить последнюю команду git. Сюда не входят изменения в рабочем дереве; учитываются только коммиты. | @@ -40,8 +39,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct |-----|--------|-------------| | `` , `` | Предыдущая страница | | | `` . `` | Следующая страница | | -| `` < () `` | Пролистать наверх | | -| `` > () `` | Прокрутить вниз | | +| `` <, `` | Пролистать наверх | | +| `` >, `` | Прокрутить вниз | | | `` v `` | Переключить выборку перетаскивания | | | `` `` | Range select down | | | `` `` | Range select up | | @@ -80,8 +79,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | Key | Action | Info | |-----|--------|-------------| -| `` `` | Выбрать предыдущую часть | | -| `` `` | Выбрать следующую часть | | +| `` , h `` | Выбрать предыдущую часть | | +| `` , l `` | Выбрать следующую часть | | | `` v `` | Переключить выборку перетаскивания | | | `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. | | `` `` | Скопировать выделенный текст в буфер обмена | | @@ -114,10 +113,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct |-----|--------|-------------| | `` `` | Выбрать эту часть | | | `` b `` | Выбрать все части | | -| `` `` | Выбрать предыдущую часть | | -| `` `` | Выбрать следующую часть | | -| `` `` | Выбрать предыдущий конфликт | | -| `` `` | Выбрать следующий конфликт | | +| `` , k `` | Выбрать предыдущую часть | | +| `` , j `` | Выбрать следующую часть | | +| `` , h `` | Выбрать предыдущий конфликт | | +| `` , l `` | Выбрать следующий конфликт | | | `` z `` | Отменить | Undo last merge conflict resolution. | | `` e `` | Редактировать файл | Open file in external editor. | | `` o `` | Открыть файл | Open file in default application. | @@ -128,8 +127,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | Key | Action | Info | |-----|--------|-------------| -| `` `` | Выбрать предыдущую часть | | -| `` `` | Выбрать следующую часть | | +| `` , h `` | Выбрать предыдущую часть | | +| `` , l `` | Выбрать следующую часть | | | `` v `` | Переключить выборку перетаскивания | | | `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. | | `` `` | Скопировать выделенный текст в буфер обмена | | diff --git a/docs-master/keybindings/Keybindings_zh-CN.md b/docs-master/keybindings/Keybindings_zh-CN.md index d78572f1b..76e378695 100644 --- a/docs-master/keybindings/Keybindings_zh-CN.md +++ b/docs-master/keybindings/Keybindings_zh-CN.md @@ -7,8 +7,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | Key | Action | Info | |-----|--------|-------------| | `` `` | 切换到最近的仓库 | | -| `` (fn+up/shift+k) `` | 向上滚动主面板 | | -| `` (fn+down/shift+j) `` | 向下滚动主面板 | | +| `` , K, (fn+up/shift+k) `` | 向上滚动主面板 | | +| `` , J, (fn+down/shift+j) `` | 向下滚动主面板 | | | `` @ `` | 打开命令日志菜单 | 查看命令日志的选项,例如显示/隐藏命令日志以及聚焦命令日志 | | `` P `` | 推送 | 推送当前分支到它的上游。如果上游未配置,您可以在弹窗中配置上游分支。 | | `` p `` | 拉取 | 从当前分支的远程分支获取改动。如果上游未配置,您可以在弹窗中配置上游分支。 | @@ -26,9 +26,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | 取消 | | | `` ? `` | 打开菜单 | | | `` `` | 查看按路径过滤选项 | 查看用于过滤提交日志的选项,以便仅显示与过滤器匹配的提交。 | -| `` W `` | 打开 diff 菜单 | 查看与比较两个引用相关的选项,例如与选定的 ref 进行比较,输入要比较的 ref,然后反转比较方向。 | -| `` `` | 打开 diff 菜单 | 查看与比较两个引用相关的选项,例如与选定的 ref 进行比较,输入要比较的 ref,然后反转比较方向。 | -| `` q `` | 退出 | | +| `` W, `` | 打开 diff 菜单 | 查看与比较两个引用相关的选项,例如与选定的 ref 进行比较,输入要比较的 ref,然后反转比较方向。 | +| `` q, `` | 退出 | | | `` `` | 挂起应用程序 | | | `` `` | 切换是否在差异视图中显示空白字符差异 | 切换是否在差异视图中显示空白字符更改。

默认值可在配置文件中通过键 'git.ignoreWhitespaceInDiffView' 更改。 | | `` z `` | 撤销 | Reflog将用于确定运行哪个git命令来撤消最后一个git命令。这并不包括对工作树的更改,只考虑提交。 | @@ -40,8 +39,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct |-----|--------|-------------| | `` , `` | 上一页 | | | `` . `` | 下一页 | | -| `` < () `` | 滚动到顶部 | | -| `` > () `` | 滚动到底部 | | +| `` <, `` | 滚动到顶部 | | +| `` >, `` | 滚动到底部 | | | `` v `` | 切换拖动选择 | | | `` `` | 向下扩展选择范围 | | | `` `` | 向上扩展选择范围 | | @@ -252,8 +251,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | Key | Action | Info | |-----|--------|-------------| -| `` `` | 选择上一个区块 | | -| `` `` | 选择下一个区块 | | +| `` , h `` | 选择上一个区块 | | +| `` , l `` | 选择下一个区块 | | | `` v `` | 切换拖动选择 | | | `` a `` | 切换代码块选择 | 切换逐行选择与代码块选择模式。 | | `` `` | 复制选中文本到剪贴板 | | @@ -294,10 +293,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct |-----|--------|-------------| | `` `` | 选中区块 | | | `` b `` | 选中所有区块 | | -| `` `` | 选择顶部块 | | -| `` `` | 选择底部块 | | -| `` `` | 选择上一个冲突 | | -| `` `` | 选择下一个冲突 | | +| `` , k `` | 选择顶部块 | | +| `` , j `` | 选择底部块 | | +| `` , h `` | 选择上一个冲突 | | +| `` , l `` | 选择下一个冲突 | | | `` z `` | 撤销 | 撤消上次合并冲突解决 | | `` e `` | 编辑文件 | 使用外部编辑器打开文件 | | `` o `` | 打开文件 | 使用默认程序打开该文件 | @@ -308,8 +307,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | Key | Action | Info | |-----|--------|-------------| -| `` `` | 选择上一个区块 | | -| `` `` | 选择下一个区块 | | +| `` , h `` | 选择上一个区块 | | +| `` , l `` | 选择下一个区块 | | | `` v `` | 切换拖动选择 | | | `` a `` | 切换代码块选择 | 切换逐行选择与代码块选择模式。 | | `` `` | 复制选中文本到剪贴板 | | diff --git a/docs-master/keybindings/Keybindings_zh-TW.md b/docs-master/keybindings/Keybindings_zh-TW.md index 70294eab9..6ebfa5c68 100644 --- a/docs-master/keybindings/Keybindings_zh-TW.md +++ b/docs-master/keybindings/Keybindings_zh-TW.md @@ -7,8 +7,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | Key | Action | Info | |-----|--------|-------------| | `` `` | 切換到最近使用的版本庫 | | -| `` (fn+up/shift+k) `` | 向上捲動主面板 | | -| `` (fn+down/shift+j) `` | 向下捲動主面板 | | +| `` , K, (fn+up/shift+k) `` | 向上捲動主面板 | | +| `` , J, (fn+down/shift+j) `` | 向下捲動主面板 | | | `` @ `` | 開啟命令記錄選單 | View options for the command log e.g. show/hide the command log and focus the command log. | | `` P `` | 推送 | 推送到遠端。如果沒有設定遠端,會開啟設定視窗。 | | `` p `` | 拉取 | 從遠端同步當前分支。如果沒有設定遠端,會開啟設定視窗。 | @@ -26,9 +26,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | 取消 | | | `` ? `` | 開啟選單 | | | `` `` | 檢視篩選路徑選項 | View options for filtering the commit log, so that only commits matching the filter are shown. | -| `` W `` | 開啟差異比較選單 | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. | -| `` `` | 開啟差異比較選單 | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. | -| `` q `` | 結束 | | +| `` W, `` | 開啟差異比較選單 | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. | +| `` q, `` | 結束 | | | `` `` | Suspend the application | | | `` `` | 切換是否在差異檢視中顯示空格變更 | Toggle whether or not whitespace changes are shown in the diff view.

The default can be changed in the config file with the key 'git.ignoreWhitespaceInDiffView'. | | `` z `` | 復原 | 將使用 reflog 確任 git 指令以復原。這不包括工作區更改;只考慮提交。 | @@ -40,8 +39,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct |-----|--------|-------------| | `` , `` | 上一頁 | | | `` . `` | 下一頁 | | -| `` < () `` | 捲動到頂部 | | -| `` > () `` | 捲動到底部 | | +| `` <, `` | 捲動到頂部 | | +| `` >, `` | 捲動到底部 | | | `` v `` | 切換拖曳選擇 | | | `` `` | Range select down | | | `` `` | Range select up | | @@ -62,8 +61,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | Key | Action | Info | |-----|--------|-------------| -| `` `` | 選擇上一段 | | -| `` `` | 選擇下一段 | | +| `` , h `` | 選擇上一段 | | +| `` , l `` | 選擇下一段 | | | `` v `` | 切換拖曳選擇 | | | `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. | | `` `` | 複製所選文本至剪貼簿 | | @@ -90,10 +89,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct |-----|--------|-------------| | `` `` | 挑選程式碼片段 | | | `` b `` | 挑選所有程式碼片段 | | -| `` `` | 選擇上一段 | | -| `` `` | 選擇下一段 | | -| `` `` | 選擇上一個衝突 | | -| `` `` | 選擇下一個衝突 | | +| `` , k `` | 選擇上一段 | | +| `` , j `` | 選擇下一段 | | +| `` , h `` | 選擇上一個衝突 | | +| `` , l `` | 選擇下一個衝突 | | | `` z `` | 復原 | Undo last merge conflict resolution. | | `` e `` | 編輯檔案 | 使用外部編輯器開啟 | | `` o `` | 開啟檔案 | 使用預設軟體開啟 | @@ -104,8 +103,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | Key | Action | Info | |-----|--------|-------------| -| `` `` | 選擇上一段 | | -| `` `` | 選擇下一段 | | +| `` , h `` | 選擇上一段 | | +| `` , l `` | 選擇下一段 | | | `` v `` | 切換拖曳選擇 | | | `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. | | `` `` | 複製所選文本至剪貼簿 | | diff --git a/pkg/cheatsheet/generate.go b/pkg/cheatsheet/generate.go index 3b5c490b8..a9cee6494 100644 --- a/pkg/cheatsheet/generate.go +++ b/pkg/cheatsheet/generate.go @@ -22,6 +22,7 @@ import ( "github.com/jesseduffield/lazycore/pkg/utils" "github.com/jesseduffield/lazygit/pkg/app" "github.com/jesseduffield/lazygit/pkg/config" + "github.com/jesseduffield/lazygit/pkg/gocui" "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/i18n" "github.com/samber/lo" @@ -145,7 +146,7 @@ func getBindingSections(bindings []*types.Binding, tr *i18n.TranslationSet) []*b return false } - return (binding.Description != "" || binding.Alternative != "") && binding.Key.IsSet() + return (binding.Description != "" || binding.Alternative != "") && len(binding.Keys) > 0 }) bindingsByHeader := lo.GroupBy(bindingsToDisplay, func(binding *types.Binding) header { @@ -156,7 +157,7 @@ func getBindingSections(bindings []*types.Binding, tr *i18n.TranslationSet) []*b bindingsByHeader, func(header header, hBindings []*types.Binding) headerWithBindings { uniqBindings := lo.UniqBy(hBindings, func(binding *types.Binding) string { - return binding.Description + config.LabelForKey(binding.Key) + return binding.Description + keyLabels(binding.Keys) }) return headerWithBindings{ @@ -213,8 +214,14 @@ func formatTitle(title string) string { return fmt.Sprintf("\n## %s\n\n", title) } +func keyLabels(keys []gocui.Key) string { + return strings.Join(lo.Map(keys, func(k gocui.Key, _ int) string { + return config.LabelForKey(k) + }), ", ") +} + func formatBinding(binding *types.Binding) string { - action := config.LabelForKey(binding.Key) + action := keyLabels(binding.Keys) description := binding.Description if binding.Alternative != "" { action += fmt.Sprintf(" (%s)", binding.Alternative) diff --git a/pkg/cheatsheet/generate_test.go b/pkg/cheatsheet/generate_test.go index bae2ec498..373fcaa35 100644 --- a/pkg/cheatsheet/generate_test.go +++ b/pkg/cheatsheet/generate_test.go @@ -28,7 +28,7 @@ func TestGetBindingSections(t *testing.T) { { ViewName: "files", Description: "stage file", - Key: gocui.NewKeyRune('a'), + Keys: []gocui.Key{gocui.NewKeyRune('a')}, }, }, expected: []*bindingSection{ @@ -38,7 +38,7 @@ func TestGetBindingSections(t *testing.T) { { ViewName: "files", Description: "stage file", - Key: gocui.NewKeyRune('a'), + Keys: []gocui.Key{gocui.NewKeyRune('a')}, }, }, }, @@ -50,7 +50,7 @@ func TestGetBindingSections(t *testing.T) { { ViewName: "", Description: "quit", - Key: gocui.NewKeyRune('a'), + Keys: []gocui.Key{gocui.NewKeyRune('a')}, }, }, expected: []*bindingSection{ @@ -60,7 +60,7 @@ func TestGetBindingSections(t *testing.T) { { ViewName: "", Description: "quit", - Key: gocui.NewKeyRune('a'), + Keys: []gocui.Key{gocui.NewKeyRune('a')}, }, }, }, @@ -72,17 +72,17 @@ func TestGetBindingSections(t *testing.T) { { ViewName: "files", Description: "stage file", - Key: gocui.NewKeyRune('a'), + Keys: []gocui.Key{gocui.NewKeyRune('a')}, }, { ViewName: "files", Description: "unstage file", - Key: gocui.NewKeyRune('a'), + Keys: []gocui.Key{gocui.NewKeyRune('a')}, }, { ViewName: "submodules", Description: "drop submodule", - Key: gocui.NewKeyRune('a'), + Keys: []gocui.Key{gocui.NewKeyRune('a')}, }, }, expected: []*bindingSection{ @@ -92,12 +92,12 @@ func TestGetBindingSections(t *testing.T) { { ViewName: "files", Description: "stage file", - Key: gocui.NewKeyRune('a'), + Keys: []gocui.Key{gocui.NewKeyRune('a')}, }, { ViewName: "files", Description: "unstage file", - Key: gocui.NewKeyRune('a'), + Keys: []gocui.Key{gocui.NewKeyRune('a')}, }, }, }, @@ -107,7 +107,7 @@ func TestGetBindingSections(t *testing.T) { { ViewName: "submodules", Description: "drop submodule", - Key: gocui.NewKeyRune('a'), + Keys: []gocui.Key{gocui.NewKeyRune('a')}, }, }, }, @@ -119,23 +119,23 @@ func TestGetBindingSections(t *testing.T) { { ViewName: "files", Description: "stage file", - Key: gocui.NewKeyRune('a'), + Keys: []gocui.Key{gocui.NewKeyRune('a')}, }, { ViewName: "files", Description: "unstage file", - Key: gocui.NewKeyRune('a'), + Keys: []gocui.Key{gocui.NewKeyRune('a')}, }, { ViewName: "files", Description: "scroll", - Key: gocui.NewKeyRune('a'), + Keys: []gocui.Key{gocui.NewKeyRune('a')}, Tag: "navigation", }, { ViewName: "commits", Description: "revert commit", - Key: gocui.NewKeyRune('a'), + Keys: []gocui.Key{gocui.NewKeyRune('a')}, }, }, expected: []*bindingSection{ @@ -145,7 +145,7 @@ func TestGetBindingSections(t *testing.T) { { ViewName: "files", Description: "scroll", - Key: gocui.NewKeyRune('a'), + Keys: []gocui.Key{gocui.NewKeyRune('a')}, Tag: "navigation", }, }, @@ -156,7 +156,7 @@ func TestGetBindingSections(t *testing.T) { { ViewName: "commits", Description: "revert commit", - Key: gocui.NewKeyRune('a'), + Keys: []gocui.Key{gocui.NewKeyRune('a')}, }, }, }, @@ -166,12 +166,12 @@ func TestGetBindingSections(t *testing.T) { { ViewName: "files", Description: "stage file", - Key: gocui.NewKeyRune('a'), + Keys: []gocui.Key{gocui.NewKeyRune('a')}, }, { ViewName: "files", Description: "unstage file", - Key: gocui.NewKeyRune('a'), + Keys: []gocui.Key{gocui.NewKeyRune('a')}, }, }, }, @@ -183,34 +183,34 @@ func TestGetBindingSections(t *testing.T) { { ViewName: "files", Description: "stage file", - Key: gocui.NewKeyRune('a'), + Keys: []gocui.Key{gocui.NewKeyRune('a')}, }, { ViewName: "files", Description: "unstage file", - Key: gocui.NewKeyRune('a'), + Keys: []gocui.Key{gocui.NewKeyRune('a')}, }, { ViewName: "files", Description: "scroll", - Key: gocui.NewKeyRune('a'), + Keys: []gocui.Key{gocui.NewKeyRune('a')}, Tag: "navigation", }, { ViewName: "commits", Description: "revert commit", - Key: gocui.NewKeyRune('a'), + Keys: []gocui.Key{gocui.NewKeyRune('a')}, }, { ViewName: "commits", Description: "scroll", - Key: gocui.NewKeyRune('a'), + Keys: []gocui.Key{gocui.NewKeyRune('a')}, Tag: "navigation", }, { ViewName: "commits", Description: "page up", - Key: gocui.NewKeyRune('a'), + Keys: []gocui.Key{gocui.NewKeyRune('a')}, Tag: "navigation", }, }, @@ -221,13 +221,13 @@ func TestGetBindingSections(t *testing.T) { { ViewName: "files", Description: "scroll", - Key: gocui.NewKeyRune('a'), + Keys: []gocui.Key{gocui.NewKeyRune('a')}, Tag: "navigation", }, { ViewName: "commits", Description: "page up", - Key: gocui.NewKeyRune('a'), + Keys: []gocui.Key{gocui.NewKeyRune('a')}, Tag: "navigation", }, }, @@ -238,7 +238,7 @@ func TestGetBindingSections(t *testing.T) { { ViewName: "commits", Description: "revert commit", - Key: gocui.NewKeyRune('a'), + Keys: []gocui.Key{gocui.NewKeyRune('a')}, }, }, }, @@ -248,12 +248,12 @@ func TestGetBindingSections(t *testing.T) { { ViewName: "files", Description: "stage file", - Key: gocui.NewKeyRune('a'), + Keys: []gocui.Key{gocui.NewKeyRune('a')}, }, { ViewName: "files", Description: "unstage file", - Key: gocui.NewKeyRune('a'), + Keys: []gocui.Key{gocui.NewKeyRune('a')}, }, }, }, diff --git a/pkg/config/app_config.go b/pkg/config/app_config.go index 27ee38c0b..e7267158a 100644 --- a/pkg/config/app_config.go +++ b/pkg/config/app_config.go @@ -203,6 +203,7 @@ func loadUserConfig(configFiles []*ConfigFile, base *UserConfig, isGuiInitialize } } + base.Keybinding.MergeLegacyAltKeybindings() return base, nil } diff --git a/pkg/config/dummies.go b/pkg/config/dummies.go index 06c8755a6..5bc349fa0 100644 --- a/pkg/config/dummies.go +++ b/pkg/config/dummies.go @@ -6,11 +6,13 @@ import ( // NewDummyAppConfig creates a new dummy AppConfig for testing func NewDummyAppConfig() *AppConfig { + userConfig := GetDefaultConfig() + userConfig.Keybinding.MergeLegacyAltKeybindings() appConfig := &AppConfig{ name: "lazygit", version: "unversioned", debug: false, - userConfig: GetDefaultConfig(), + userConfig: userConfig, appState: &AppState{}, } _ = yaml.Unmarshal([]byte{}, appConfig.appState) diff --git a/pkg/config/keybinding.go b/pkg/config/keybinding.go new file mode 100644 index 000000000..905bc2bd7 --- /dev/null +++ b/pkg/config/keybinding.go @@ -0,0 +1,92 @@ +package config + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/karimkhaleel/jsonschema" + "github.com/samber/lo" + "gopkg.in/yaml.v3" +) + +// Keybinding represents the value of a single keybinding entry in the user's +// config. It's a slice of key strings to allow alternates, but for backward +// compatibility (and because most bindings only have one key) it can be +// written in YAML/JSON as either a single scalar string or as a sequence of +// strings. +type Keybinding []string + +func (k *Keybinding) UnmarshalYAML(node *yaml.Node) error { + var ss []string + switch node.Kind { + case yaml.ScalarNode: + var s string + if err := node.Decode(&s); err != nil { + return err + } + ss = []string{s} + case yaml.SequenceNode: + if err := node.Decode(&ss); err != nil { + return err + } + default: + return fmt.Errorf("expected a string or a sequence of strings for keybinding, got %v", node.Tag) + } + // Drop empty and entries so clients never have to special-case + // them: an empty Keybinding means "no key bound", a non-empty one is + // guaranteed to contain only real keys. + *k = lo.Filter(ss, func(s string, _ int) bool { + return s != "" && s != "" + }) + return nil +} + +func (k Keybinding) MarshalYAML() (any, error) { + if len(k) == 1 { + return k[0], nil + } + // Render multi-key bindings in flow style (`[a, b]`) rather than the default + // block style, which is more compact and reads better in the generated docs. + node := &yaml.Node{ + Kind: yaml.SequenceNode, + Style: yaml.FlowStyle, + } + for _, s := range k { + node.Content = append(node.Content, &yaml.Node{ + Kind: yaml.ScalarNode, + Value: s, + }) + } + return node, nil +} + +func (k Keybinding) MarshalJSON() ([]byte, error) { + if len(k) == 1 { + return json.Marshal(k[0]) + } + return json.Marshal([]string(k)) +} + +// String renders the keybinding as a human-readable label, joining +// alternates with " or " for use in help text. +func (k Keybinding) String() string { + return strings.Join(k, " or ") +} + +// JSONSchema lets the schema generator describe this type as a union of a +// string and an array of strings instead of just an array. +func (Keybinding) JSONSchema() *jsonschema.Schema { + return &jsonschema.Schema{ + OneOf: []*jsonschema.Schema{ + {Type: "string"}, + {Type: "array", Items: &jsonschema.Schema{Type: "string"}}, + }, + } +} + +// mergeLegacyAlt folds a deprecated `*Alt*` field into the corresponding +// multi-key main field. +func mergeLegacyAlt(main *Keybinding, alt Keybinding) { + *main = lo.Union(*main, alt) +} diff --git a/pkg/config/keybinding_test.go b/pkg/config/keybinding_test.go new file mode 100644 index 000000000..be9f15acb --- /dev/null +++ b/pkg/config/keybinding_test.go @@ -0,0 +1,250 @@ +package config + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" +) + +func TestKeybindingUnmarshalYAML(t *testing.T) { + scenarios := []struct { + name string + input string + expected Keybinding + wantErr bool + }{ + { + name: "scalar string", + input: `q`, + expected: Keybinding{"q"}, + }, + { + name: "scalar with special characters", + input: ``, + expected: Keybinding{""}, + }, + { + name: "sequence with one element", + input: `[q]`, + expected: Keybinding{"q"}, + }, + { + name: "sequence with multiple elements", + input: `["q", ""]`, + expected: Keybinding{"q", ""}, + }, + { + name: "empty sequence", + input: `[]`, + expected: Keybinding{}, + }, + { + name: "scalar decodes to empty", + input: ``, + expected: Keybinding{}, + }, + { + name: "scalar empty string decodes to empty", + input: `""`, + expected: Keybinding{}, + }, + { + name: " entries are filtered out of a sequence", + input: `["q", "", ""]`, + expected: Keybinding{"q", ""}, + }, + { + name: "mapping is rejected", + input: `{key: q}`, + wantErr: true, + }, + } + + for _, s := range scenarios { + t.Run(s.name, func(t *testing.T) { + var k Keybinding + err := yaml.Unmarshal([]byte(s.input), &k) + if s.wantErr { + assert.Error(t, err) + return + } + assert.NoError(t, err) + assert.Equal(t, s.expected, k) + }) + } +} + +func TestKeybindingMarshalYAML(t *testing.T) { + scenarios := []struct { + name string + input Keybinding + expected string + }{ + { + name: "single key emits a scalar", + input: Keybinding{"q"}, + expected: "q\n", + }, + { + name: "multiple keys emit a flow sequence", + input: Keybinding{"q", ""}, + expected: "[q, ]\n", + }, + { + name: "empty keybinding emits an empty sequence", + input: Keybinding{}, + expected: "[]\n", + }, + } + + for _, s := range scenarios { + t.Run(s.name, func(t *testing.T) { + out, err := yaml.Marshal(s.input) + assert.NoError(t, err) + assert.Equal(t, s.expected, string(out)) + }) + } +} + +func TestKeybindingMarshalJSON(t *testing.T) { + scenarios := []struct { + name string + input Keybinding + expected string + }{ + { + name: "single key emits a string", + input: Keybinding{"q"}, + expected: `"q"`, + }, + { + name: "multiple keys emit an array", + input: Keybinding{"q", "esc"}, + expected: `["q","esc"]`, + }, + } + + for _, s := range scenarios { + t.Run(s.name, func(t *testing.T) { + out, err := json.Marshal(s.input) + assert.NoError(t, err) + assert.Equal(t, s.expected, string(out)) + }) + } +} + +func TestMergeLegacyAltKeybindings(t *testing.T) { + scenarios := []struct { + name string + quit Keybinding + quitAlt1 Keybinding + expected Keybinding + }{ + { + name: "alt is folded into main", + quit: Keybinding{"q"}, + quitAlt1: Keybinding{""}, + expected: Keybinding{"q", ""}, + }, + { + name: "alt is not appended if already present", + quit: Keybinding{"q", ""}, + quitAlt1: Keybinding{""}, + expected: Keybinding{"q", ""}, + }, + { + name: "empty alt is ignored", + quit: Keybinding{"q"}, + quitAlt1: nil, + expected: Keybinding{"q"}, + }, + { + name: "user-supplied multi-key main is preserved", + quit: Keybinding{"q", ""}, + quitAlt1: Keybinding{""}, + expected: Keybinding{"q", "", ""}, + }, + { + name: "multi-key alt is folded element by element", + quit: Keybinding{"q"}, + quitAlt1: Keybinding{"", ""}, + expected: Keybinding{"q", "", ""}, + }, + } + + for _, s := range scenarios { + t.Run(s.name, func(t *testing.T) { + cfg := KeybindingConfig{ + Universal: KeybindingUniversalConfig{ + Quit: s.quit, + QuitAlt1: s.quitAlt1, + }, + } + cfg.MergeLegacyAltKeybindings() + assert.Equal(t, s.expected, cfg.Universal.Quit) + }) + } +} + +func TestKeybindingYAMLRoundTrip(t *testing.T) { + scenarios := []Keybinding{ + {"q"}, + {"q", ""}, + {"", "", ""}, + } + for _, original := range scenarios { + out, err := yaml.Marshal(original) + assert.NoError(t, err) + var decoded Keybinding + assert.NoError(t, yaml.Unmarshal(out, &decoded)) + assert.Equal(t, original, decoded) + } +} + +func TestKeybindingConfigYAMLAcceptsBothForms(t *testing.T) { + scenarios := []struct { + name string + yaml string + expected Keybinding + }{ + { + name: "scalar form", + yaml: "quit: q\n", + expected: Keybinding{"q"}, + }, + { + name: "sequence form", + yaml: "quit: [q, ]\n", + expected: Keybinding{"q", ""}, + }, + { + name: "block sequence form", + yaml: "quit:\n - q\n - \n", + expected: Keybinding{"q", ""}, + }, + } + for _, s := range scenarios { + t.Run(s.name, func(t *testing.T) { + var cfg KeybindingUniversalConfig + assert.NoError(t, yaml.Unmarshal([]byte(s.yaml), &cfg)) + assert.Equal(t, s.expected, cfg.Quit) + }) + } +} + +func TestJumpToBlockYAMLAcceptsMixedForms(t *testing.T) { + yamlInput := ` +jumpToBlock: + - "1" + - ["2", "@"] + - "3" + - "4" + - "5" +` + var cfg KeybindingUniversalConfig + assert.NoError(t, yaml.Unmarshal([]byte(yamlInput), &cfg)) + expected := []Keybinding{{"1"}, {"2", "@"}, {"3"}, {"4"}, {"5"}} + assert.Equal(t, expected, cfg.JumpToBlock) +} diff --git a/pkg/config/keynames.go b/pkg/config/keynames.go index 943a2f37a..da5b8dbae 100644 --- a/pkg/config/keynames.go +++ b/pkg/config/keynames.go @@ -205,3 +205,10 @@ func GetValidatedKeyBindingKey(label string) gocui.Key { return key } + +func GetValidatedKeyBindingKeys(labels Keybinding) []gocui.Key { + return lo.FilterMap(labels, func(label string, _ int) (gocui.Key, bool) { + k := GetValidatedKeyBindingKey(label) + return k, k.IsSet() + }) +} diff --git a/pkg/config/user_config.go b/pkg/config/user_config.go index ddb0c0876..0b2dcac0c 100644 --- a/pkg/config/user_config.go +++ b/pkg/config/user_config.go @@ -36,7 +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 + // Keybindings. + // Each binding can be a single key or a list of keys; see https://github.com/jesseduffield/lazygit/blob/master/docs/keybindings/Custom_Keybindings.md for the syntax. Keybinding KeybindingConfig `yaml:"keybinding"` } @@ -422,203 +423,219 @@ type KeybindingConfig struct { // damn looks like we have some inconsistencies here with -alt and -alt1 type KeybindingUniversalConfig struct { - Quit string `yaml:"quit"` - QuitAlt1 string `yaml:"quit-alt1"` - SuspendApp string `yaml:"suspendApp"` - Return string `yaml:"return"` - QuitWithoutChangingDirectory string `yaml:"quitWithoutChangingDirectory"` - TogglePanel string `yaml:"togglePanel"` - PrevItem string `yaml:"prevItem"` - NextItem string `yaml:"nextItem"` - PrevItemAlt string `yaml:"prevItem-alt"` - NextItemAlt string `yaml:"nextItem-alt"` - PrevPage string `yaml:"prevPage"` - NextPage string `yaml:"nextPage"` - ScrollLeft string `yaml:"scrollLeft"` - ScrollRight string `yaml:"scrollRight"` - GotoTop string `yaml:"gotoTop"` - GotoBottom string `yaml:"gotoBottom"` - GotoTopAlt string `yaml:"gotoTop-alt"` - GotoBottomAlt string `yaml:"gotoBottom-alt"` - ToggleRangeSelect string `yaml:"toggleRangeSelect"` - RangeSelectDown string `yaml:"rangeSelectDown"` - RangeSelectUp string `yaml:"rangeSelectUp"` - PrevBlock string `yaml:"prevBlock"` - NextBlock string `yaml:"nextBlock"` - PrevBlockAlt string `yaml:"prevBlock-alt"` - NextBlockAlt string `yaml:"nextBlock-alt"` - NextBlockAlt2 string `yaml:"nextBlock-alt2"` - PrevBlockAlt2 string `yaml:"prevBlock-alt2"` - JumpToBlock []string `yaml:"jumpToBlock"` - FocusMainView string `yaml:"focusMainView"` - NextMatch string `yaml:"nextMatch"` - PrevMatch string `yaml:"prevMatch"` - StartSearch string `yaml:"startSearch"` - MoveWordLeft string `yaml:"moveWordLeft"` // on Mac - MoveWordRight string `yaml:"moveWordRight"` // on Mac - BackspaceWord string `yaml:"backspaceWord"` // on Mac - ForwardDeleteWord string `yaml:"forwardDeleteWord"` // on Mac - OptionMenu string `yaml:"optionMenu"` - OptionMenuAlt1 string `yaml:"optionMenu-alt1"` - Select string `yaml:"select"` - GoInto string `yaml:"goInto"` - Confirm string `yaml:"confirm"` - ConfirmMenu string `yaml:"confirmMenu"` - ConfirmSuggestion string `yaml:"confirmSuggestion"` - ConfirmInEditor string `yaml:"confirmInEditor"` // on Mac - ConfirmInEditorAlt string `yaml:"confirmInEditor-alt"` - Remove string `yaml:"remove"` - New string `yaml:"new"` - Edit string `yaml:"edit"` - OpenFile string `yaml:"openFile"` - ScrollUpMain string `yaml:"scrollUpMain"` - ScrollDownMain string `yaml:"scrollDownMain"` - ScrollUpMainAlt1 string `yaml:"scrollUpMain-alt1"` - ScrollDownMainAlt1 string `yaml:"scrollDownMain-alt1"` - ScrollUpMainAlt2 string `yaml:"scrollUpMain-alt2"` - ScrollDownMainAlt2 string `yaml:"scrollDownMain-alt2"` - ExecuteShellCommand string `yaml:"executeShellCommand"` - CreateRebaseOptionsMenu string `yaml:"createRebaseOptionsMenu"` - Push string `yaml:"pushFiles"` // 'Files' appended for legacy reasons - Pull string `yaml:"pullFiles"` // 'Files' appended for legacy reasons - Refresh string `yaml:"refresh"` - CreatePatchOptionsMenu string `yaml:"createPatchOptionsMenu"` - NextTab string `yaml:"nextTab"` - PrevTab string `yaml:"prevTab"` - NextScreenMode string `yaml:"nextScreenMode"` - PrevScreenMode string `yaml:"prevScreenMode"` - CyclePagers string `yaml:"cyclePagers"` - Undo string `yaml:"undo"` - Redo string `yaml:"redo"` - FilteringMenu string `yaml:"filteringMenu"` - DiffingMenu string `yaml:"diffingMenu"` - DiffingMenuAlt string `yaml:"diffingMenu-alt"` - CopyToClipboard string `yaml:"copyToClipboard"` - OpenRecentRepos string `yaml:"openRecentRepos"` - SubmitEditorText string `yaml:"submitEditorText"` - ExtrasMenu string `yaml:"extrasMenu"` - ToggleWhitespaceInDiffView string `yaml:"toggleWhitespaceInDiffView"` - IncreaseContextInDiffView string `yaml:"increaseContextInDiffView"` - DecreaseContextInDiffView string `yaml:"decreaseContextInDiffView"` - IncreaseRenameSimilarityThreshold string `yaml:"increaseRenameSimilarityThreshold"` - DecreaseRenameSimilarityThreshold string `yaml:"decreaseRenameSimilarityThreshold"` - OpenDiffTool string `yaml:"openDiffTool"` + Quit Keybinding `yaml:"quit"` + // Deprecated: add the key to `quit` instead. + QuitAlt1 Keybinding `yaml:"quit-alt1"` + SuspendApp Keybinding `yaml:"suspendApp"` + Return Keybinding `yaml:"return"` + QuitWithoutChangingDirectory Keybinding `yaml:"quitWithoutChangingDirectory"` + TogglePanel Keybinding `yaml:"togglePanel"` + PrevItem Keybinding `yaml:"prevItem"` + NextItem Keybinding `yaml:"nextItem"` + // Deprecated: add the key to `prevItem` instead. + PrevItemAlt Keybinding `yaml:"prevItem-alt"` + // Deprecated: add the key to `nextItem` instead. + NextItemAlt Keybinding `yaml:"nextItem-alt"` + PrevPage Keybinding `yaml:"prevPage"` + NextPage Keybinding `yaml:"nextPage"` + ScrollLeft Keybinding `yaml:"scrollLeft"` + ScrollRight Keybinding `yaml:"scrollRight"` + GotoTop Keybinding `yaml:"gotoTop"` + GotoBottom Keybinding `yaml:"gotoBottom"` + // Deprecated: add the key to `gotoTop` instead. + GotoTopAlt Keybinding `yaml:"gotoTop-alt"` + // Deprecated: add the key to `gotoBottom` instead. + GotoBottomAlt Keybinding `yaml:"gotoBottom-alt"` + ToggleRangeSelect Keybinding `yaml:"toggleRangeSelect"` + RangeSelectDown Keybinding `yaml:"rangeSelectDown"` + RangeSelectUp Keybinding `yaml:"rangeSelectUp"` + PrevBlock Keybinding `yaml:"prevBlock"` + NextBlock Keybinding `yaml:"nextBlock"` + // Deprecated: add the key to `prevBlock` instead. + PrevBlockAlt Keybinding `yaml:"prevBlock-alt"` + // Deprecated: add the key to `nextBlock` instead. + NextBlockAlt Keybinding `yaml:"nextBlock-alt"` + // Deprecated: add the key to `nextBlock` instead. + NextBlockAlt2 Keybinding `yaml:"nextBlock-alt2"` + // Deprecated: add the key to `prevBlock` instead. + PrevBlockAlt2 Keybinding `yaml:"prevBlock-alt2"` + JumpToBlock []Keybinding `yaml:"jumpToBlock"` + FocusMainView Keybinding `yaml:"focusMainView"` + NextMatch Keybinding `yaml:"nextMatch"` + PrevMatch Keybinding `yaml:"prevMatch"` + StartSearch Keybinding `yaml:"startSearch"` + MoveWordLeft Keybinding `yaml:"moveWordLeft"` // on Mac + MoveWordRight Keybinding `yaml:"moveWordRight"` // on Mac + BackspaceWord Keybinding `yaml:"backspaceWord"` // on Mac + ForwardDeleteWord Keybinding `yaml:"forwardDeleteWord"` // on Mac + OptionMenu Keybinding `yaml:"optionMenu"` + Select Keybinding `yaml:"select"` + GoInto Keybinding `yaml:"goInto"` + Confirm Keybinding `yaml:"confirm"` + ConfirmMenu Keybinding `yaml:"confirmMenu"` + ConfirmSuggestion Keybinding `yaml:"confirmSuggestion"` + ConfirmInEditor Keybinding `yaml:"confirmInEditor"` // on Mac + // Deprecated: add the key to `confirmInEditor` instead. + ConfirmInEditorAlt Keybinding `yaml:"confirmInEditor-alt"` + Remove Keybinding `yaml:"remove"` + New Keybinding `yaml:"new"` + Edit Keybinding `yaml:"edit"` + OpenFile Keybinding `yaml:"openFile"` + ScrollUpMain Keybinding `yaml:"scrollUpMain"` + ScrollDownMain Keybinding `yaml:"scrollDownMain"` + // Deprecated: add the key to `scrollUpMain` instead. + ScrollUpMainAlt1 Keybinding `yaml:"scrollUpMain-alt1"` + // Deprecated: add the key to `scrollDownMain` instead. + ScrollDownMainAlt1 Keybinding `yaml:"scrollDownMain-alt1"` + // Deprecated: add the key to `scrollUpMain` instead. + ScrollUpMainAlt2 Keybinding `yaml:"scrollUpMain-alt2"` + // Deprecated: add the key to `scrollDownMain` instead. + ScrollDownMainAlt2 Keybinding `yaml:"scrollDownMain-alt2"` + ExecuteShellCommand Keybinding `yaml:"executeShellCommand"` + CreateRebaseOptionsMenu Keybinding `yaml:"createRebaseOptionsMenu"` + Push Keybinding `yaml:"pushFiles"` // 'Files' appended for legacy reasons + Pull Keybinding `yaml:"pullFiles"` // 'Files' appended for legacy reasons + Refresh Keybinding `yaml:"refresh"` + CreatePatchOptionsMenu Keybinding `yaml:"createPatchOptionsMenu"` + NextTab Keybinding `yaml:"nextTab"` + PrevTab Keybinding `yaml:"prevTab"` + NextScreenMode Keybinding `yaml:"nextScreenMode"` + PrevScreenMode Keybinding `yaml:"prevScreenMode"` + CyclePagers Keybinding `yaml:"cyclePagers"` + Undo Keybinding `yaml:"undo"` + Redo Keybinding `yaml:"redo"` + FilteringMenu Keybinding `yaml:"filteringMenu"` + DiffingMenu Keybinding `yaml:"diffingMenu"` + // Deprecated: add the key to `diffingMenu` instead. + DiffingMenuAlt Keybinding `yaml:"diffingMenu-alt"` + CopyToClipboard Keybinding `yaml:"copyToClipboard"` + OpenRecentRepos Keybinding `yaml:"openRecentRepos"` + SubmitEditorText Keybinding `yaml:"submitEditorText"` + ExtrasMenu Keybinding `yaml:"extrasMenu"` + ToggleWhitespaceInDiffView Keybinding `yaml:"toggleWhitespaceInDiffView"` + IncreaseContextInDiffView Keybinding `yaml:"increaseContextInDiffView"` + DecreaseContextInDiffView Keybinding `yaml:"decreaseContextInDiffView"` + IncreaseRenameSimilarityThreshold Keybinding `yaml:"increaseRenameSimilarityThreshold"` + DecreaseRenameSimilarityThreshold Keybinding `yaml:"decreaseRenameSimilarityThreshold"` + OpenDiffTool Keybinding `yaml:"openDiffTool"` } type KeybindingStatusConfig struct { - CheckForUpdate string `yaml:"checkForUpdate"` - RecentRepos string `yaml:"recentRepos"` - AllBranchesLogGraph string `yaml:"allBranchesLogGraph"` - AllBranchesLogGraphReverse string `yaml:"allBranchesLogGraphReverse"` + CheckForUpdate Keybinding `yaml:"checkForUpdate"` + RecentRepos Keybinding `yaml:"recentRepos"` + AllBranchesLogGraph Keybinding `yaml:"allBranchesLogGraph"` + AllBranchesLogGraphReverse Keybinding `yaml:"allBranchesLogGraphReverse"` } type KeybindingFilesConfig struct { - CommitChanges string `yaml:"commitChanges"` - CommitChangesWithoutHook string `yaml:"commitChangesWithoutHook"` - AmendLastCommit string `yaml:"amendLastCommit"` - CommitChangesWithEditor string `yaml:"commitChangesWithEditor"` - FindBaseCommitForFixup string `yaml:"findBaseCommitForFixup"` - ConfirmDiscard string `yaml:"confirmDiscard"` - IgnoreFile string `yaml:"ignoreFile"` - RefreshFiles string `yaml:"refreshFiles"` - StashAllChanges string `yaml:"stashAllChanges"` - ViewStashOptions string `yaml:"viewStashOptions"` - ToggleStagedAll string `yaml:"toggleStagedAll"` - ViewResetOptions string `yaml:"viewResetOptions"` - Fetch string `yaml:"fetch"` - ToggleTreeView string `yaml:"toggleTreeView"` - OpenMergeOptions string `yaml:"openMergeOptions"` - OpenStatusFilter string `yaml:"openStatusFilter"` - CopyFileInfoToClipboard string `yaml:"copyFileInfoToClipboard"` - CollapseAll string `yaml:"collapseAll"` - ExpandAll string `yaml:"expandAll"` + CommitChanges Keybinding `yaml:"commitChanges"` + CommitChangesWithoutHook Keybinding `yaml:"commitChangesWithoutHook"` + AmendLastCommit Keybinding `yaml:"amendLastCommit"` + CommitChangesWithEditor Keybinding `yaml:"commitChangesWithEditor"` + FindBaseCommitForFixup Keybinding `yaml:"findBaseCommitForFixup"` + ConfirmDiscard Keybinding `yaml:"confirmDiscard"` + IgnoreFile Keybinding `yaml:"ignoreFile"` + RefreshFiles Keybinding `yaml:"refreshFiles"` + StashAllChanges Keybinding `yaml:"stashAllChanges"` + ViewStashOptions Keybinding `yaml:"viewStashOptions"` + ToggleStagedAll Keybinding `yaml:"toggleStagedAll"` + ViewResetOptions Keybinding `yaml:"viewResetOptions"` + Fetch Keybinding `yaml:"fetch"` + ToggleTreeView Keybinding `yaml:"toggleTreeView"` + OpenMergeOptions Keybinding `yaml:"openMergeOptions"` + OpenStatusFilter Keybinding `yaml:"openStatusFilter"` + CopyFileInfoToClipboard Keybinding `yaml:"copyFileInfoToClipboard"` + CollapseAll Keybinding `yaml:"collapseAll"` + ExpandAll Keybinding `yaml:"expandAll"` } type KeybindingBranchesConfig struct { - CreatePullRequest string `yaml:"createPullRequest"` - ViewPullRequestOptions string `yaml:"viewPullRequestOptions"` - OpenPullRequestInBrowser string `yaml:"openPullRequestInBrowser"` - CopyPullRequestURL string `yaml:"copyPullRequestURL"` - CheckoutBranchByName string `yaml:"checkoutBranchByName"` - ForceCheckoutBranch string `yaml:"forceCheckoutBranch"` - CheckoutPreviousBranch string `yaml:"checkoutPreviousBranch"` - RebaseBranch string `yaml:"rebaseBranch"` - RenameBranch string `yaml:"renameBranch"` - MergeIntoCurrentBranch string `yaml:"mergeIntoCurrentBranch"` - MoveCommitsToNewBranch string `yaml:"moveCommitsToNewBranch"` - ViewGitFlowOptions string `yaml:"viewGitFlowOptions"` - FastForward string `yaml:"fastForward"` - CreateTag string `yaml:"createTag"` - PushTag string `yaml:"pushTag"` - SetUpstream string `yaml:"setUpstream"` - FetchRemote string `yaml:"fetchRemote"` - AddForkRemote string `yaml:"addForkRemote"` - SortOrder string `yaml:"sortOrder"` + CreatePullRequest Keybinding `yaml:"createPullRequest"` + ViewPullRequestOptions Keybinding `yaml:"viewPullRequestOptions"` + OpenPullRequestInBrowser Keybinding `yaml:"openPullRequestInBrowser"` + CopyPullRequestURL Keybinding `yaml:"copyPullRequestURL"` + CheckoutBranchByName Keybinding `yaml:"checkoutBranchByName"` + ForceCheckoutBranch Keybinding `yaml:"forceCheckoutBranch"` + CheckoutPreviousBranch Keybinding `yaml:"checkoutPreviousBranch"` + RebaseBranch Keybinding `yaml:"rebaseBranch"` + RenameBranch Keybinding `yaml:"renameBranch"` + MergeIntoCurrentBranch Keybinding `yaml:"mergeIntoCurrentBranch"` + MoveCommitsToNewBranch Keybinding `yaml:"moveCommitsToNewBranch"` + ViewGitFlowOptions Keybinding `yaml:"viewGitFlowOptions"` + FastForward Keybinding `yaml:"fastForward"` + CreateTag Keybinding `yaml:"createTag"` + PushTag Keybinding `yaml:"pushTag"` + SetUpstream Keybinding `yaml:"setUpstream"` + FetchRemote Keybinding `yaml:"fetchRemote"` + AddForkRemote Keybinding `yaml:"addForkRemote"` + SortOrder Keybinding `yaml:"sortOrder"` } type KeybindingWorktreesConfig struct { - ViewWorktreeOptions string `yaml:"viewWorktreeOptions"` + ViewWorktreeOptions Keybinding `yaml:"viewWorktreeOptions"` } type KeybindingCommitsConfig struct { - SquashDown string `yaml:"squashDown"` - RenameCommit string `yaml:"renameCommit"` - RenameCommitWithEditor string `yaml:"renameCommitWithEditor"` - ViewResetOptions string `yaml:"viewResetOptions"` - MarkCommitAsFixup string `yaml:"markCommitAsFixup"` - SetFixupMessage string `yaml:"setFixupMessage"` - CreateFixupCommit string `yaml:"createFixupCommit"` - SquashAboveCommits string `yaml:"squashAboveCommits"` - MoveDownCommit string `yaml:"moveDownCommit"` - MoveUpCommit string `yaml:"moveUpCommit"` - AmendToCommit string `yaml:"amendToCommit"` - ResetCommitAuthor string `yaml:"resetCommitAuthor"` - PickCommit string `yaml:"pickCommit"` - RevertCommit string `yaml:"revertCommit"` - CherryPickCopy string `yaml:"cherryPickCopy"` - PasteCommits string `yaml:"pasteCommits"` - MarkCommitAsBaseForRebase string `yaml:"markCommitAsBaseForRebase"` - CreateTag string `yaml:"tagCommit"` - CheckoutCommit string `yaml:"checkoutCommit"` - ResetCherryPick string `yaml:"resetCherryPick"` - CopyCommitAttributeToClipboard string `yaml:"copyCommitAttributeToClipboard"` - OpenLogMenu string `yaml:"openLogMenu"` - OpenInBrowser string `yaml:"openInBrowser"` - OpenPullRequestInBrowser string `yaml:"openPullRequestInBrowser"` - ViewBisectOptions string `yaml:"viewBisectOptions"` - StartInteractiveRebase string `yaml:"startInteractiveRebase"` - SelectCommitsOfCurrentBranch string `yaml:"selectCommitsOfCurrentBranch"` + SquashDown Keybinding `yaml:"squashDown"` + RenameCommit Keybinding `yaml:"renameCommit"` + RenameCommitWithEditor Keybinding `yaml:"renameCommitWithEditor"` + ViewResetOptions Keybinding `yaml:"viewResetOptions"` + MarkCommitAsFixup Keybinding `yaml:"markCommitAsFixup"` + SetFixupMessage Keybinding `yaml:"setFixupMessage"` + CreateFixupCommit Keybinding `yaml:"createFixupCommit"` + SquashAboveCommits Keybinding `yaml:"squashAboveCommits"` + MoveDownCommit Keybinding `yaml:"moveDownCommit"` + MoveUpCommit Keybinding `yaml:"moveUpCommit"` + AmendToCommit Keybinding `yaml:"amendToCommit"` + ResetCommitAuthor Keybinding `yaml:"resetCommitAuthor"` + PickCommit Keybinding `yaml:"pickCommit"` + RevertCommit Keybinding `yaml:"revertCommit"` + CherryPickCopy Keybinding `yaml:"cherryPickCopy"` + PasteCommits Keybinding `yaml:"pasteCommits"` + MarkCommitAsBaseForRebase Keybinding `yaml:"markCommitAsBaseForRebase"` + CreateTag Keybinding `yaml:"tagCommit"` + CheckoutCommit Keybinding `yaml:"checkoutCommit"` + ResetCherryPick Keybinding `yaml:"resetCherryPick"` + CopyCommitAttributeToClipboard Keybinding `yaml:"copyCommitAttributeToClipboard"` + OpenLogMenu Keybinding `yaml:"openLogMenu"` + OpenInBrowser Keybinding `yaml:"openInBrowser"` + OpenPullRequestInBrowser Keybinding `yaml:"openPullRequestInBrowser"` + ViewBisectOptions Keybinding `yaml:"viewBisectOptions"` + StartInteractiveRebase Keybinding `yaml:"startInteractiveRebase"` + SelectCommitsOfCurrentBranch Keybinding `yaml:"selectCommitsOfCurrentBranch"` } type KeybindingAmendAttributeConfig struct { - ResetAuthor string `yaml:"resetAuthor"` - SetAuthor string `yaml:"setAuthor"` - AddCoAuthor string `yaml:"addCoAuthor"` + ResetAuthor Keybinding `yaml:"resetAuthor"` + SetAuthor Keybinding `yaml:"setAuthor"` + AddCoAuthor Keybinding `yaml:"addCoAuthor"` } type KeybindingStashConfig struct { - PopStash string `yaml:"popStash"` - RenameStash string `yaml:"renameStash"` + PopStash Keybinding `yaml:"popStash"` + RenameStash Keybinding `yaml:"renameStash"` } type KeybindingCommitFilesConfig struct { - CheckoutCommitFile string `yaml:"checkoutCommitFile"` + CheckoutCommitFile Keybinding `yaml:"checkoutCommitFile"` } type KeybindingMainConfig struct { - ToggleSelectHunk string `yaml:"toggleSelectHunk"` - PickBothHunks string `yaml:"pickBothHunks"` - EditSelectHunk string `yaml:"editSelectHunk"` + PrevHunk Keybinding `yaml:"prevHunk"` + NextHunk Keybinding `yaml:"nextHunk"` + ToggleSelectHunk Keybinding `yaml:"toggleSelectHunk"` + PickBothHunks Keybinding `yaml:"pickBothHunks"` + EditSelectHunk Keybinding `yaml:"editSelectHunk"` } type KeybindingSubmodulesConfig struct { - Init string `yaml:"init"` - Update string `yaml:"update"` - BulkMenu string `yaml:"bulkMenu"` + Init Keybinding `yaml:"init"` + Update Keybinding `yaml:"update"` + BulkMenu Keybinding `yaml:"bulkMenu"` } type KeybindingCommitMessageConfig struct { - CommitMenu string `yaml:"commitMenu"` + CommitMenu Keybinding `yaml:"commitMenu"` } // OSConfig contains config on the level of the os @@ -667,8 +684,8 @@ type CustomCommandAfterHook struct { } type CustomCommand struct { - // 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 - Key string `yaml:"key"` + // 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. To bind several alternates to the same command, use a sequence (e.g. `[a, b]`). + Key Keybinding `yaml:"key"` // Instead of defining a single custom command, create a menu of custom commands. Useful for grouping related commands together under a single keybinding, and for keeping them out of the global keybindings menu. // When using this, all other fields except Key and Description are ignored and must be empty. CommandMenu []CustomCommand `yaml:"commandMenu"` @@ -753,8 +770,8 @@ type CustomCommandMenuOption struct { Description string `yaml:"description"` // The value that will be used in the command Value string `yaml:"value" jsonschema:"example=feature,minLength=1"` - // Keybinding to invoke this menu option without needing to navigate to it - Key string `yaml:"key"` + // Keybinding to invoke this menu option without needing to navigate to it. Accepts either a single key or a sequence of alternates. + Key Keybinding `yaml:"key"` } type CustomIconsConfig struct { @@ -769,6 +786,28 @@ type IconProperties struct { Color string `yaml:"color"` } +// MergeLegacyAltKeybindings folds deprecated `*Alt*` fields into their +// corresponding multi-key main field. New code should treat the main field +// as the single source of truth; the alt fields will be removed in a future +// release. +func (c *KeybindingConfig) MergeLegacyAltKeybindings() { + mergeLegacyAlt(&c.Universal.Quit, c.Universal.QuitAlt1) + mergeLegacyAlt(&c.Universal.PrevItem, c.Universal.PrevItemAlt) + mergeLegacyAlt(&c.Universal.NextItem, c.Universal.NextItemAlt) + mergeLegacyAlt(&c.Universal.GotoTop, c.Universal.GotoTopAlt) + mergeLegacyAlt(&c.Universal.GotoBottom, c.Universal.GotoBottomAlt) + mergeLegacyAlt(&c.Universal.PrevBlock, c.Universal.PrevBlockAlt) + mergeLegacyAlt(&c.Universal.NextBlock, c.Universal.NextBlockAlt) + mergeLegacyAlt(&c.Universal.PrevBlock, c.Universal.PrevBlockAlt2) + mergeLegacyAlt(&c.Universal.NextBlock, c.Universal.NextBlockAlt2) + mergeLegacyAlt(&c.Universal.ConfirmInEditor, c.Universal.ConfirmInEditorAlt) + mergeLegacyAlt(&c.Universal.ScrollUpMain, c.Universal.ScrollUpMainAlt1) + mergeLegacyAlt(&c.Universal.ScrollUpMain, c.Universal.ScrollUpMainAlt2) + mergeLegacyAlt(&c.Universal.ScrollDownMain, c.Universal.ScrollDownMainAlt1) + mergeLegacyAlt(&c.Universal.ScrollDownMain, c.Universal.ScrollDownMainAlt2) + mergeLegacyAlt(&c.Universal.DiffingMenu, c.Universal.DiffingMenuAlt) +} + func GetDefaultConfig() *UserConfig { // This is only for tests; we don't want to use the test runner's host platform in that case, // but always use the fallback bindings @@ -905,192 +944,193 @@ func GetDefaultConfigForPlatform(platform string) *UserConfig { PromptToReturnFromSubprocess: true, Keybinding: KeybindingConfig{ Universal: KeybindingUniversalConfig{ - Quit: "q", - QuitAlt1: "", - SuspendApp: "", - Return: "", - QuitWithoutChangingDirectory: "Q", - TogglePanel: "", - PrevItem: "", - NextItem: "", - PrevItemAlt: "k", - NextItemAlt: "j", - PrevPage: ",", - NextPage: ".", - ScrollLeft: "H", - ScrollRight: "L", - GotoTop: "<", - GotoBottom: ">", - GotoTopAlt: "", - GotoBottomAlt: "", - ToggleRangeSelect: "v", - RangeSelectDown: "", - RangeSelectUp: "", - PrevBlock: "", - NextBlock: "", - PrevBlockAlt: "h", - NextBlockAlt: "l", - PrevBlockAlt2: "", - NextBlockAlt2: "", - JumpToBlock: []string{"1", "2", "3", "4", "5"}, - FocusMainView: "0", - NextMatch: "n", - PrevMatch: "N", - StartSearch: "/", - MoveWordLeft: platformKeyBinding(platform, map[string]string{"darwin": ""}, ""), - MoveWordRight: platformKeyBinding(platform, map[string]string{"darwin": ""}, ""), - BackspaceWord: platformKeyBinding(platform, map[string]string{"darwin": ""}, ""), - ForwardDeleteWord: platformKeyBinding(platform, map[string]string{"darwin": ""}, ""), - OptionMenu: "", - OptionMenuAlt1: "?", - Select: "", - GoInto: "", - Confirm: "", - ConfirmMenu: "", - ConfirmSuggestion: "", - ConfirmInEditor: platformKeyBinding(platform, map[string]string{"darwin": ""}, ""), - ConfirmInEditorAlt: "", - Remove: "d", - New: "n", - Edit: "e", - OpenFile: "o", - OpenRecentRepos: "", - ScrollUpMain: "", - ScrollDownMain: "", - ScrollUpMainAlt1: "K", - ScrollDownMainAlt1: "J", - ScrollUpMainAlt2: "", - ScrollDownMainAlt2: "", - ExecuteShellCommand: ":", - CreateRebaseOptionsMenu: "m", - Push: "P", - Pull: "p", - Refresh: "R", - CreatePatchOptionsMenu: "", - NextTab: "]", - PrevTab: "[", - NextScreenMode: "+", - PrevScreenMode: "_", - CyclePagers: "|", - Undo: "z", - Redo: "Z", - FilteringMenu: "", - DiffingMenu: "W", - DiffingMenuAlt: "", - CopyToClipboard: "", - SubmitEditorText: "", - ExtrasMenu: "@", - ToggleWhitespaceInDiffView: "", - IncreaseContextInDiffView: "}", - DecreaseContextInDiffView: "{", - IncreaseRenameSimilarityThreshold: ")", - DecreaseRenameSimilarityThreshold: "(", - OpenDiffTool: "", + Quit: Keybinding{"q"}, + QuitAlt1: Keybinding{""}, + SuspendApp: Keybinding{""}, + Return: Keybinding{""}, + QuitWithoutChangingDirectory: Keybinding{"Q"}, + TogglePanel: Keybinding{""}, + PrevItem: Keybinding{""}, + NextItem: Keybinding{""}, + PrevItemAlt: Keybinding{"k"}, + NextItemAlt: Keybinding{"j"}, + PrevPage: Keybinding{","}, + NextPage: Keybinding{"."}, + ScrollLeft: Keybinding{"H"}, + ScrollRight: Keybinding{"L"}, + GotoTop: Keybinding{"<"}, + GotoBottom: Keybinding{">"}, + GotoTopAlt: Keybinding{""}, + GotoBottomAlt: Keybinding{""}, + ToggleRangeSelect: Keybinding{"v"}, + RangeSelectDown: Keybinding{""}, + RangeSelectUp: Keybinding{""}, + PrevBlock: Keybinding{""}, + NextBlock: Keybinding{""}, + PrevBlockAlt: Keybinding{"h"}, + NextBlockAlt: Keybinding{"l"}, + PrevBlockAlt2: Keybinding{""}, + NextBlockAlt2: Keybinding{""}, + JumpToBlock: []Keybinding{{"1"}, {"2"}, {"3"}, {"4"}, {"5"}}, + FocusMainView: Keybinding{"0"}, + NextMatch: Keybinding{"n"}, + PrevMatch: Keybinding{"N"}, + StartSearch: Keybinding{"/"}, + MoveWordLeft: Keybinding{platformKeyBinding(platform, map[string]string{"darwin": ""}, "")}, + MoveWordRight: Keybinding{platformKeyBinding(platform, map[string]string{"darwin": ""}, "")}, + BackspaceWord: Keybinding{platformKeyBinding(platform, map[string]string{"darwin": ""}, "")}, + ForwardDeleteWord: Keybinding{platformKeyBinding(platform, map[string]string{"darwin": ""}, "")}, + OptionMenu: Keybinding{"?"}, + Select: Keybinding{""}, + GoInto: Keybinding{""}, + Confirm: Keybinding{""}, + ConfirmMenu: Keybinding{""}, + ConfirmSuggestion: Keybinding{""}, + ConfirmInEditor: Keybinding{platformKeyBinding(platform, map[string]string{"darwin": ""}, "")}, + ConfirmInEditorAlt: Keybinding{""}, + Remove: Keybinding{"d"}, + New: Keybinding{"n"}, + Edit: Keybinding{"e"}, + OpenFile: Keybinding{"o"}, + OpenRecentRepos: Keybinding{""}, + ScrollUpMain: Keybinding{""}, + ScrollDownMain: Keybinding{""}, + ScrollUpMainAlt1: Keybinding{"K"}, + ScrollDownMainAlt1: Keybinding{"J"}, + ScrollUpMainAlt2: Keybinding{""}, + ScrollDownMainAlt2: Keybinding{""}, + ExecuteShellCommand: Keybinding{":"}, + CreateRebaseOptionsMenu: Keybinding{"m"}, + Push: Keybinding{"P"}, + Pull: Keybinding{"p"}, + Refresh: Keybinding{"R"}, + CreatePatchOptionsMenu: Keybinding{""}, + NextTab: Keybinding{"]"}, + PrevTab: Keybinding{"["}, + NextScreenMode: Keybinding{"+"}, + PrevScreenMode: Keybinding{"_"}, + CyclePagers: Keybinding{"|"}, + Undo: Keybinding{"z"}, + Redo: Keybinding{"Z"}, + FilteringMenu: Keybinding{""}, + DiffingMenu: Keybinding{"W"}, + DiffingMenuAlt: Keybinding{""}, + CopyToClipboard: Keybinding{""}, + SubmitEditorText: Keybinding{""}, + ExtrasMenu: Keybinding{"@"}, + ToggleWhitespaceInDiffView: Keybinding{""}, + IncreaseContextInDiffView: Keybinding{"}"}, + DecreaseContextInDiffView: Keybinding{"{"}, + IncreaseRenameSimilarityThreshold: Keybinding{")"}, + DecreaseRenameSimilarityThreshold: Keybinding{"("}, + OpenDiffTool: Keybinding{""}, }, Status: KeybindingStatusConfig{ - CheckForUpdate: "u", - RecentRepos: "", - AllBranchesLogGraph: "a", - AllBranchesLogGraphReverse: "A", + CheckForUpdate: Keybinding{"u"}, + RecentRepos: Keybinding{""}, + AllBranchesLogGraph: Keybinding{"a"}, + AllBranchesLogGraphReverse: Keybinding{"A"}, }, Files: KeybindingFilesConfig{ - CommitChanges: "c", - CommitChangesWithoutHook: "w", - AmendLastCommit: "A", - CommitChangesWithEditor: "C", - FindBaseCommitForFixup: "", - IgnoreFile: "i", - RefreshFiles: "r", - StashAllChanges: "s", - ViewStashOptions: "S", - ToggleStagedAll: "a", - ViewResetOptions: "D", - Fetch: "f", - ToggleTreeView: "`", - OpenMergeOptions: "M", - OpenStatusFilter: "", - ConfirmDiscard: "x", - CopyFileInfoToClipboard: "y", - CollapseAll: "-", - ExpandAll: "=", + CommitChanges: Keybinding{"c"}, + CommitChangesWithoutHook: Keybinding{"w"}, + AmendLastCommit: Keybinding{"A"}, + CommitChangesWithEditor: Keybinding{"C"}, + FindBaseCommitForFixup: Keybinding{""}, + IgnoreFile: Keybinding{"i"}, + RefreshFiles: Keybinding{"r"}, + StashAllChanges: Keybinding{"s"}, + ViewStashOptions: Keybinding{"S"}, + ToggleStagedAll: Keybinding{"a"}, + ViewResetOptions: Keybinding{"D"}, + Fetch: Keybinding{"f"}, + ToggleTreeView: Keybinding{"`"}, + OpenMergeOptions: Keybinding{"M"}, + OpenStatusFilter: Keybinding{""}, + ConfirmDiscard: Keybinding{"x"}, + CopyFileInfoToClipboard: Keybinding{"y"}, + CollapseAll: Keybinding{"-"}, + ExpandAll: Keybinding{"="}, }, Branches: KeybindingBranchesConfig{ - CopyPullRequestURL: "", - CreatePullRequest: "o", - ViewPullRequestOptions: "O", - OpenPullRequestInBrowser: "G", - CheckoutBranchByName: "c", - ForceCheckoutBranch: "F", - CheckoutPreviousBranch: "-", - RebaseBranch: "r", - RenameBranch: "R", - MergeIntoCurrentBranch: "M", - MoveCommitsToNewBranch: "N", - ViewGitFlowOptions: "i", - FastForward: "f", - CreateTag: "T", - PushTag: "P", - SetUpstream: "u", - FetchRemote: "f", - AddForkRemote: "F", - SortOrder: "s", + CopyPullRequestURL: Keybinding{""}, + CreatePullRequest: Keybinding{"o"}, + ViewPullRequestOptions: Keybinding{"O"}, + OpenPullRequestInBrowser: Keybinding{"G"}, + CheckoutBranchByName: Keybinding{"c"}, + ForceCheckoutBranch: Keybinding{"F"}, + CheckoutPreviousBranch: Keybinding{"-"}, + RebaseBranch: Keybinding{"r"}, + RenameBranch: Keybinding{"R"}, + MergeIntoCurrentBranch: Keybinding{"M"}, + MoveCommitsToNewBranch: Keybinding{"N"}, + ViewGitFlowOptions: Keybinding{"i"}, + FastForward: Keybinding{"f"}, + CreateTag: Keybinding{"T"}, + PushTag: Keybinding{"P"}, + SetUpstream: Keybinding{"u"}, + FetchRemote: Keybinding{"f"}, + AddForkRemote: Keybinding{"F"}, + SortOrder: Keybinding{"s"}, }, Worktrees: KeybindingWorktreesConfig{ - ViewWorktreeOptions: "w", + ViewWorktreeOptions: Keybinding{"w"}, }, Commits: KeybindingCommitsConfig{ - SquashDown: "s", - RenameCommit: "r", - RenameCommitWithEditor: "R", - ViewResetOptions: "g", - MarkCommitAsFixup: "f", - SetFixupMessage: "c", - CreateFixupCommit: "F", - SquashAboveCommits: "S", - MoveDownCommit: "", - MoveUpCommit: "", - AmendToCommit: "A", - ResetCommitAuthor: "a", - PickCommit: "p", - RevertCommit: "t", - CherryPickCopy: "C", - PasteCommits: "V", - MarkCommitAsBaseForRebase: "B", - CreateTag: "T", - CheckoutCommit: "", - ResetCherryPick: "", - CopyCommitAttributeToClipboard: "y", - OpenLogMenu: "", - OpenInBrowser: "o", - OpenPullRequestInBrowser: "G", - ViewBisectOptions: "b", - StartInteractiveRebase: "i", - SelectCommitsOfCurrentBranch: "*", + SquashDown: Keybinding{"s"}, + RenameCommit: Keybinding{"r"}, + RenameCommitWithEditor: Keybinding{"R"}, + ViewResetOptions: Keybinding{"g"}, + MarkCommitAsFixup: Keybinding{"f"}, + SetFixupMessage: Keybinding{"c"}, + CreateFixupCommit: Keybinding{"F"}, + SquashAboveCommits: Keybinding{"S"}, + MoveDownCommit: Keybinding{""}, + MoveUpCommit: Keybinding{""}, + AmendToCommit: Keybinding{"A"}, + ResetCommitAuthor: Keybinding{"a"}, + PickCommit: Keybinding{"p"}, + RevertCommit: Keybinding{"t"}, + CherryPickCopy: Keybinding{"C"}, + PasteCommits: Keybinding{"V"}, + MarkCommitAsBaseForRebase: Keybinding{"B"}, + CreateTag: Keybinding{"T"}, + CheckoutCommit: Keybinding{""}, + ResetCherryPick: Keybinding{""}, + CopyCommitAttributeToClipboard: Keybinding{"y"}, + OpenLogMenu: Keybinding{""}, + OpenInBrowser: Keybinding{"o"}, + OpenPullRequestInBrowser: Keybinding{"G"}, + ViewBisectOptions: Keybinding{"b"}, + StartInteractiveRebase: Keybinding{"i"}, + SelectCommitsOfCurrentBranch: Keybinding{"*"}, }, AmendAttribute: KeybindingAmendAttributeConfig{ - ResetAuthor: "a", - SetAuthor: "A", - AddCoAuthor: "c", + ResetAuthor: Keybinding{"a"}, + SetAuthor: Keybinding{"A"}, + AddCoAuthor: Keybinding{"c"}, }, Stash: KeybindingStashConfig{ - PopStash: "g", - RenameStash: "r", + PopStash: Keybinding{"g"}, + RenameStash: Keybinding{"r"}, }, CommitFiles: KeybindingCommitFilesConfig{ - CheckoutCommitFile: "c", + CheckoutCommitFile: Keybinding{"c"}, }, Main: KeybindingMainConfig{ - ToggleSelectHunk: "a", - PickBothHunks: "b", - EditSelectHunk: "E", + PrevHunk: Keybinding{"", "h"}, + NextHunk: Keybinding{"", "l"}, + ToggleSelectHunk: Keybinding{"a"}, + PickBothHunks: Keybinding{"b"}, + EditSelectHunk: Keybinding{"E"}, }, Submodules: KeybindingSubmodulesConfig{ - Init: "i", - Update: "u", - BulkMenu: "b", + Init: Keybinding{"i"}, + Update: Keybinding{"u"}, + BulkMenu: Keybinding{"b"}, }, CommitMessage: KeybindingCommitMessageConfig{ - CommitMenu: "", + CommitMenu: Keybinding{""}, }, }, } diff --git a/pkg/config/user_config_validation.go b/pkg/config/user_config_validation.go index 7eeab32dd..163fc61c4 100644 --- a/pkg/config/user_config_validation.go +++ b/pkg/config/user_config_validation.go @@ -126,10 +126,12 @@ func validateKeybindings(keybindingConfig KeybindingConfig) error { return nil } -func validateCustomCommandKey(key string) error { - if !isValidKeybindingKey(key) { - return fmt.Errorf("Unrecognized key '%s' for custom command. For permitted values see %s", - key, constants.Links.Docs.CustomKeybindings) +func validateCustomCommandKey(key Keybinding) error { + for _, k := range key { + if !isValidKeybindingKey(k) { + return fmt.Errorf("Unrecognized key '%s' for custom command. For permitted values see %s", + k, constants.Links.Docs.CustomKeybindings) + } } return nil } @@ -150,7 +152,7 @@ func validateCustomCommands(customCommands []CustomCommand) error { customCommand.After != nil { commandRef := "" if len(customCommand.Key) > 0 { - commandRef = fmt.Sprintf(" with key '%s'", customCommand.Key) + commandRef = fmt.Sprintf(" with key '%s'", customCommand.Key.String()) } return fmt.Errorf("Error with custom command%s: it is not allowed to use both commandMenu and any of the other fields except key and description.", commandRef) } @@ -176,9 +178,11 @@ func validateCustomCommands(customCommands []CustomCommand) error { func validateCustomCommandPrompt(prompt CustomCommandPrompt) error { for _, option := range prompt.Options { - if !isValidKeybindingKey(option.Key) { - return fmt.Errorf("Unrecognized key '%s' for custom command prompt option. For permitted values see %s", - option.Key, constants.Links.Docs.CustomKeybindings) + for _, k := range option.Key { + if !isValidKeybindingKey(k) { + return fmt.Errorf("Unrecognized key '%s' for custom command prompt option. For permitted values see %s", + k, constants.Links.Docs.CustomKeybindings) + } } } diff --git a/pkg/config/user_config_validation_test.go b/pkg/config/user_config_validation_test.go index a2841684d..bb2d2580f 100644 --- a/pkg/config/user_config_validation_test.go +++ b/pkg/config/user_config_validation_test.go @@ -4,6 +4,7 @@ import ( "strings" "testing" + "github.com/samber/lo" "github.com/stretchr/testify/assert" ) @@ -114,7 +115,7 @@ func TestUserConfigValidate_enums(t *testing.T) { { name: "Keybindings", setup: func(config *UserConfig, value string) { - config.Keybinding.Universal.Quit = value + config.Keybinding.Universal.Quit = Keybinding{value} }, testCases: []testCase{ {value: "", valid: true}, @@ -127,7 +128,10 @@ func TestUserConfigValidate_enums(t *testing.T) { { name: "JumpToBlock keybinding", setup: func(config *UserConfig, value string) { - config.Keybinding.Universal.JumpToBlock = strings.Split(value, ",") + labels := strings.Split(value, ",") + config.Keybinding.Universal.JumpToBlock = lo.Map(labels, func(label string, _ int) Keybinding { + return Keybinding{label} + }) }, testCases: []testCase{ {value: "", valid: false}, @@ -142,7 +146,7 @@ func TestUserConfigValidate_enums(t *testing.T) { setup: func(config *UserConfig, value string) { config.CustomCommands = []CustomCommand{ { - Key: value, + Key: Keybinding{value}, Command: "echo 'hello'", }, } @@ -160,10 +164,10 @@ func TestUserConfigValidate_enums(t *testing.T) { setup: func(config *UserConfig, value string) { config.CustomCommands = []CustomCommand{ { - Key: "X", + Key: Keybinding{"X"}, Description: "My Custom Commands", CommandMenu: []CustomCommand{ - {Key: value, Command: "echo 'hello'", Context: "global"}, + {Key: Keybinding{value}, Command: "echo 'hello'", Context: "global"}, }, }, } @@ -181,12 +185,12 @@ func TestUserConfigValidate_enums(t *testing.T) { setup: func(config *UserConfig, value string) { config.CustomCommands = []CustomCommand{ { - Key: "X", + Key: Keybinding{"X"}, Description: "My Custom Commands", Prompts: []CustomCommandPrompt{ { Options: []CustomCommandMenuOption{ - {Key: value}, + {Key: Keybinding{value}}, }, }, }, @@ -225,10 +229,10 @@ func TestUserConfigValidate_enums(t *testing.T) { setup: func(config *UserConfig, _ string) { config.CustomCommands = []CustomCommand{ { - Key: "X", + Key: Keybinding{"X"}, Description: "My Custom Commands", CommandMenu: []CustomCommand{ - {Key: "1", Command: "echo 'hello'", Context: "global"}, + {Key: Keybinding{"1"}, Command: "echo 'hello'", Context: "global"}, }, }, } @@ -242,10 +246,10 @@ func TestUserConfigValidate_enums(t *testing.T) { setup: func(config *UserConfig, _ string) { config.CustomCommands = []CustomCommand{ { - Key: "X", + Key: Keybinding{"X"}, Context: "global", // context is not allowed for submenus CommandMenu: []CustomCommand{ - {Key: "1", Command: "echo 'hello'", Context: "global"}, + {Key: Keybinding{"1"}, Command: "echo 'hello'", Context: "global"}, }, }, } @@ -259,10 +263,10 @@ func TestUserConfigValidate_enums(t *testing.T) { setup: func(config *UserConfig, _ string) { config.CustomCommands = []CustomCommand{ { - Key: "X", + Key: Keybinding{"X"}, LoadingText: "loading", // other properties are not allowed for submenus (using loadingText as an example) CommandMenu: []CustomCommand{ - {Key: "1", Command: "echo 'hello'", Context: "global"}, + {Key: Keybinding{"1"}, Command: "echo 'hello'", Context: "global"}, }, }, } diff --git a/pkg/gocui/edit.go b/pkg/gocui/edit.go index 76823364e..649379fb6 100644 --- a/pkg/gocui/edit.go +++ b/pkg/gocui/edit.go @@ -4,6 +4,8 @@ package gocui +import "github.com/samber/lo" + // Editor interface must be satisfied by gocui editors. type Editor interface { Edit(v *View, key Key) bool @@ -23,19 +25,19 @@ func (f EditorFunc) Edit(v *View, key Key) bool { var DefaultEditor Editor = EditorFunc(SimpleEditor) var ( - moveWordLeftKeybinding = NewKey(KeyArrowLeft, "", ModCtrl) - moveWordRightKeybinding = NewKey(KeyArrowRight, "", ModCtrl) - backspaceWordKeybinding = NewKey(KeyBackspace, "", ModCtrl) - forwardDeleteWordKeybinding = NewKey(KeyDelete, "", ModCtrl) + moveWordLeftKeybinding = []Key{NewKey(KeyArrowLeft, "", ModCtrl)} + moveWordRightKeybinding = []Key{NewKey(KeyArrowRight, "", ModCtrl)} + backspaceWordKeybinding = []Key{NewKey(KeyBackspace, "", ModCtrl)} + forwardDeleteWordKeybinding = []Key{NewKey(KeyDelete, "", ModCtrl)} ) // SimpleEditor is used as the default gocui editor. func SimpleEditor(v *View, key Key) bool { switch { - case key.Equals(backspaceWordKeybinding), + case lo.SomeBy(backspaceWordKeybinding, func(k Key) bool { return key.Equals(k) }), key.Equals(NewKeyStrMod("w", ModCtrl)): v.TextArea.BackSpaceWord() - case key.Equals(forwardDeleteWordKeybinding), + case lo.SomeBy(forwardDeleteWordKeybinding, func(k Key) bool { return key.Equals(k) }), key.Equals(NewKeyStrMod("d", ModAlt)): v.TextArea.ForwardDeleteWord() case key.Equals(NewKeyName(KeyBackspace)), @@ -49,13 +51,13 @@ func SimpleEditor(v *View, key Key) bool { case key.Equals(NewKeyName(KeyArrowUp)): v.TextArea.MoveCursorUp() case key.Equals(NewKeyStrMod("b", ModAlt)), - key.Equals(moveWordLeftKeybinding): + lo.SomeBy(moveWordLeftKeybinding, func(k Key) bool { return key.Equals(k) }): v.TextArea.MoveLeftWord() case key.Equals(NewKeyName(KeyArrowLeft)), key.Equals(NewKeyStrMod("b", ModCtrl)): v.TextArea.MoveCursorLeft() case key.Equals(NewKeyStrMod("f", ModAlt)), - key.Equals(moveWordRightKeybinding): + lo.SomeBy(moveWordRightKeybinding, func(k Key) bool { return key.Equals(k) }): v.TextArea.MoveRightWord() case key.Equals(NewKeyName(KeyArrowRight)), key.Equals(NewKeyStrMod("f", ModCtrl)): diff --git a/pkg/gocui/gui.go b/pkg/gocui/gui.go index 645329d6b..7b691f6d7 100644 --- a/pkg/gocui/gui.go +++ b/pkg/gocui/gui.go @@ -178,9 +178,9 @@ type Gui struct { OnSearchEscape func() error - SearchEscapeKey Key - NextSearchMatchKey Key - PrevSearchMatchKey Key + SearchEscapeKeys []Key + NextSearchMatchKeys []Key + PrevSearchMatchKeys []Key ErrorHandler func(error) error @@ -256,9 +256,9 @@ func NewGui(opts NewGuiOpts) (*Gui, error) { g.SupportOverlaps = opts.SupportOverlaps // default keys for when searching strings in a view - g.SearchEscapeKey = NewKeyName(KeyEsc) - g.NextSearchMatchKey = NewKeyRune('n') - g.PrevSearchMatchKey = NewKeyRune('N') + g.SearchEscapeKeys = []Key{NewKeyName(KeyEsc)} + g.NextSearchMatchKeys = []Key{NewKeyRune('n')} + g.PrevSearchMatchKeys = []Key{NewKeyRune('N')} g.playRecording = opts.PlayRecording @@ -546,10 +546,9 @@ func (g *Gui) CurrentView() *View { // SetKeybinding creates a new keybinding. If viewname equals to "" // (empty string) then the keybinding will apply to all views. key must // be a rune or a Key. -func (g *Gui) SetKeybinding(viewname string, key Key, handler func(*Gui, *View) error) error { +func (g *Gui) SetKeybinding(viewname string, key Key, handler func(*Gui, *View) error) { kb := newKeybinding(viewname, key, handler) g.keybindings = append(g.keybindings, kb) - return nil } // DeleteKeybindings deletes all keybindings of view. @@ -1526,11 +1525,11 @@ func (g *Gui) execKeybindings(v *View, ev *GocuiEvent) error { // if we're searching, and we've hit n/N/Esc, we ignore the default keybinding if v != nil && v.IsSearching() { - if ev.Key.Equals(g.NextSearchMatchKey) { + if lo.SomeBy(g.NextSearchMatchKeys, func(k Key) bool { return ev.Key.Equals(k) }) { return v.gotoNextMatch() - } else if ev.Key.Equals(g.PrevSearchMatchKey) { + } else if lo.SomeBy(g.PrevSearchMatchKeys, func(k Key) bool { return ev.Key.Equals(k) }) { return v.gotoPreviousMatch() - } else if ev.Key.Equals(g.SearchEscapeKey) { + } else if lo.SomeBy(g.SearchEscapeKeys, func(k Key) bool { return ev.Key.Equals(k) }) { v.searcher.clearSearch() if g.OnSearchEscape != nil { if err := g.OnSearchEscape(); err != nil { @@ -1670,7 +1669,7 @@ func (g *Gui) Snapshot() string { return builder.String() } -func (g *Gui) SetEditKeybindings(moveWordLeft, moveWordRight, backspaceWord, forwardDeleteWord Key) { +func (g *Gui) SetEditKeybindings(moveWordLeft, moveWordRight, backspaceWord, forwardDeleteWord []Key) { moveWordLeftKeybinding = moveWordLeft moveWordRightKeybinding = moveWordRight backspaceWordKeybinding = backspaceWord diff --git a/pkg/gui/command_log_panel.go b/pkg/gui/command_log_panel.go index 3381f371a..8f2e06b98 100644 --- a/pkg/gui/command_log_panel.go +++ b/pkg/gui/command_log_panel.go @@ -136,9 +136,8 @@ func (gui *Gui) getRandomTip() string { config.Universal.NextPage, ), fmt.Sprintf( - "You can jump to the top/bottom of a panel using '%s (or %s)' and '%s (or %s)'", - config.Universal.GotoTop, config.Universal.GotoTopAlt, - config.Universal.GotoBottom, config.Universal.GotoBottomAlt, + "You can jump to the top/bottom of a panel using '%s' and '%s'", + config.Universal.GotoTop, config.Universal.GotoBottom, ), fmt.Sprintf( "To collapse/expand a directory, press '%s'", diff --git a/pkg/gui/context/commit_message_context.go b/pkg/gui/context/commit_message_context.go index d14dc609b..7db3a085b 100644 --- a/pkg/gui/context/commit_message_context.go +++ b/pkg/gui/context/commit_message_context.go @@ -166,8 +166,8 @@ func (self *CommitMessageContext) SetPanelState( self.c.Views().CommitDescription.Subtitle = utils.ResolvePlaceholderString(self.c.Tr.CommitDescriptionSubTitle, map[string]string{ - "togglePanelKeyBinding": self.c.UserConfig().Keybinding.Universal.TogglePanel, - "commitMenuKeybinding": self.c.UserConfig().Keybinding.CommitMessage.CommitMenu, + "togglePanelKeyBinding": self.c.UserConfig().Keybinding.Universal.TogglePanel.String(), + "commitMenuKeybinding": self.c.UserConfig().Keybinding.CommitMessage.CommitMenu.String(), }) self.c.Views().CommitDescription.Visible = true diff --git a/pkg/gui/context/menu_context.go b/pkg/gui/context/menu_context.go index e9fece612..9feef1e4c 100644 --- a/pkg/gui/context/menu_context.go +++ b/pkg/gui/context/menu_context.go @@ -5,6 +5,7 @@ import ( "strings" "github.com/jesseduffield/lazygit/pkg/config" + "github.com/jesseduffield/lazygit/pkg/gocui" "github.com/jesseduffield/lazygit/pkg/gui/style" "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/i18n" @@ -73,7 +74,11 @@ func NewMenuViewModel(c *ContextCommon) *MenuViewModel { func() []*types.MenuItem { return self.menuItems }, func(item *types.MenuItem) []string { if filterKeybindings { - return []string{config.LabelForKey(item.Key)} + // Allow searching all configured keybindings of each item, even though only the + // first one is shown in the menu. + return lo.Map(item.Keys, func(k gocui.Key, _ int) string { + return config.LabelForKey(k) + }) } return item.LabelColumns @@ -138,8 +143,8 @@ func (self *MenuViewModel) GetDisplayStrings(_ int, _ int) [][]string { } keyLabel := "" - if item.Key.IsSet() { - keyLabel = style.FgCyan.Sprint(config.LabelForKey(item.Key)) + if len(item.Keys) > 0 { + keyLabel = style.FgCyan.Sprint(config.LabelForKey(item.Keys[0])) } checkMark := "" @@ -205,12 +210,12 @@ func (self *MenuViewModel) GetNonModelItems() []*NonModelItem { func (self *MenuContext) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { basicBindings := self.ListContextTrait.GetKeybindings(opts) menuItemsWithKeys := lo.Filter(self.menuItems, func(item *types.MenuItem, _ int) bool { - return item.Key.IsSet() + return len(item.Keys) > 0 }) menuItemBindings := lo.Map(menuItemsWithKeys, func(item *types.MenuItem, _ int) *types.Binding { return &types.Binding{ - Key: item.Key, + Keys: item.Keys, Handler: func() error { return self.OnMenuPress(item) }, } }) diff --git a/pkg/gui/controllers/basic_commits_controller.go b/pkg/gui/controllers/basic_commits_controller.go index f425addb5..2ddb5055e 100644 --- a/pkg/gui/controllers/basic_commits_controller.go +++ b/pkg/gui/controllers/basic_commits_controller.go @@ -6,7 +6,6 @@ import ( "github.com/jesseduffield/lazygit/pkg/commands/git_commands" "github.com/jesseduffield/lazygit/pkg/commands/models" - "github.com/jesseduffield/lazygit/pkg/gocui" "github.com/jesseduffield/lazygit/pkg/gui/context/traits" "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/utils" @@ -52,7 +51,7 @@ func NewBasicCommitsController(c *ControllerCommon, context ContainsCommits) *Ba func (self *BasicCommitsController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { bindings := []*types.Binding{ { - Key: opts.GetKey(opts.Config.Commits.CheckoutCommit), + Keys: opts.GetKeys(opts.Config.Commits.CheckoutCommit), Handler: self.withItem(self.checkout), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.Checkout, @@ -60,7 +59,7 @@ func (self *BasicCommitsController) GetKeybindings(opts types.KeybindingsOpts) [ DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Commits.CopyCommitAttributeToClipboard), + Keys: opts.GetKeys(opts.Config.Commits.CopyCommitAttributeToClipboard), Handler: self.withItem(self.copyCommitAttribute), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.CopyCommitAttributeToClipboard, @@ -68,13 +67,13 @@ func (self *BasicCommitsController) GetKeybindings(opts types.KeybindingsOpts) [ OpensMenu: true, }, { - Key: opts.GetKey(opts.Config.Commits.OpenInBrowser), + Keys: opts.GetKeys(opts.Config.Commits.OpenInBrowser), Handler: self.withItem(self.openInBrowser), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.OpenCommitInBrowser, }, { - Key: opts.GetKey(opts.Config.Universal.New), + Keys: opts.GetKeys(opts.Config.Universal.New), Handler: self.withItem(self.newBranch), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.CreateNewBranchFromCommit, @@ -84,14 +83,14 @@ func (self *BasicCommitsController) GetKeybindings(opts types.KeybindingsOpts) [ // panel. But I find it important that this ends up next to "New Branch", and I couldn't // find another way to achieve this. It's not such a big deal to have it in subcommits and // reflog too, I'd say. - Key: opts.GetKey(opts.Config.Branches.MoveCommitsToNewBranch), + Keys: opts.GetKeys(opts.Config.Branches.MoveCommitsToNewBranch), Handler: self.c.Helpers().Refs.MoveCommitsToNewBranch, GetDisabledReason: self.c.Helpers().Refs.CanMoveCommitsToNewBranch, Description: self.c.Tr.MoveCommitsToNewBranch, Tooltip: self.c.Tr.MoveCommitsToNewBranchTooltip, }, { - Key: opts.GetKey(opts.Config.Commits.ViewResetOptions), + Keys: opts.GetKeys(opts.Config.Commits.ViewResetOptions), Handler: self.withItem(self.createResetMenu), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.ViewResetOptions, @@ -100,31 +99,31 @@ func (self *BasicCommitsController) GetKeybindings(opts types.KeybindingsOpts) [ DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Commits.CherryPickCopy), + Keys: opts.GetKeys(opts.Config.Commits.CherryPickCopy), Handler: self.withItem(self.copyRange), GetDisabledReason: self.require(self.itemRangeSelected(self.canCopyCommits)), Description: self.c.Tr.CherryPickCopy, Tooltip: utils.ResolvePlaceholderString(self.c.Tr.CherryPickCopyTooltip, map[string]string{ - "paste": opts.Config.Commits.PasteCommits, - "escape": opts.Config.Universal.Return, + "paste": opts.Config.Commits.PasteCommits.String(), + "escape": opts.Config.Universal.Return.String(), }, ), DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Commits.ResetCherryPick), + Keys: opts.GetKeys(opts.Config.Commits.ResetCherryPick), Handler: self.c.Helpers().CherryPick.Reset, Description: self.c.Tr.ResetCherryPick, }, { - Key: opts.GetKey(opts.Config.Universal.OpenDiffTool), + Keys: opts.GetKeys(opts.Config.Universal.OpenDiffTool), Handler: self.withItem(self.openDiffTool), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.OpenDiffTool, }, { - Key: opts.GetKey(opts.Config.Commits.SelectCommitsOfCurrentBranch), + Keys: opts.GetKeys(opts.Config.Commits.SelectCommitsOfCurrentBranch), Handler: self.selectCommitsOfCurrentBranch, GetDisabledReason: self.require(self.canSelectCommitsOfCurrentBranch), Description: self.c.Tr.SelectCommitsOfCurrentBranch, @@ -164,14 +163,14 @@ func (self *BasicCommitsController) copyCommitAttribute(commit *models.Commit) e OnPress: func() error { return self.copyCommitSubjectToClipboard(commit) }, - Key: gocui.NewKeyRune('s'), + Keys: menuKey('s'), }, { Label: self.c.Tr.CommitMessage, OnPress: func() error { return self.copyCommitMessageToClipboard(commit) }, - Key: gocui.NewKeyRune('m'), + Keys: menuKey('m'), }, { Label: self.c.Tr.CommitMessageBody, @@ -179,28 +178,28 @@ func (self *BasicCommitsController) copyCommitAttribute(commit *models.Commit) e OnPress: func() error { return self.copyCommitMessageBodyToClipboard(commitMessageBody) }, - Key: gocui.NewKeyRune('b'), + Keys: menuKey('b'), }, { Label: self.c.Tr.CommitURL, OnPress: func() error { return self.copyCommitURLToClipboard(commit) }, - Key: gocui.NewKeyRune('u'), + Keys: menuKey('u'), }, { Label: self.c.Tr.CommitDiff, OnPress: func() error { return self.copyCommitDiffToClipboard(commit) }, - Key: gocui.NewKeyRune('d'), + Keys: menuKey('d'), }, { Label: self.c.Tr.CommitAuthor, OnPress: func() error { return self.copyAuthorToClipboard(commit) }, - Key: gocui.NewKeyRune('a'), + Keys: menuKey('a'), }, } @@ -209,7 +208,7 @@ func (self *BasicCommitsController) copyCommitAttribute(commit *models.Commit) e OnPress: func() error { return self.copyCommitTagsToClipboard(commit) }, - Key: gocui.NewKeyRune('t'), + Keys: menuKey('t'), } if len(commit.Tags) == 0 { diff --git a/pkg/gui/controllers/bisect_controller.go b/pkg/gui/controllers/bisect_controller.go index 15c37b88a..1066237c1 100644 --- a/pkg/gui/controllers/bisect_controller.go +++ b/pkg/gui/controllers/bisect_controller.go @@ -6,7 +6,6 @@ import ( "github.com/jesseduffield/lazygit/pkg/commands/git_commands" "github.com/jesseduffield/lazygit/pkg/commands/models" - "github.com/jesseduffield/lazygit/pkg/gocui" "github.com/jesseduffield/lazygit/pkg/gui/context" "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/utils" @@ -39,7 +38,7 @@ func NewBisectController( func (self *BisectController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { bindings := []*types.Binding{ { - Key: opts.GetKey(opts.Config.Commits.ViewBisectOptions), + Keys: opts.GetKeys(opts.Config.Commits.ViewBisectOptions), Handler: opts.Guards.OutsideFilterMode(self.withItem(self.openMenu)), Description: self.c.Tr.ViewBisectOptions, OpensMenu: true, @@ -102,7 +101,7 @@ func (self *BisectController) openMidBisectMenu(info *git_commands.BisectInfo, c return self.afterMark(selectCurrentAfter, waitToReselect) }, DisabledReason: singleItemIfNotBisecting, - Key: gocui.NewKeyRune('b'), + Keys: menuKey('b'), }, { Label: fmt.Sprintf(self.c.Tr.Bisect.Mark, shortHashToMark, info.OldTerm()), @@ -115,7 +114,7 @@ func (self *BisectController) openMidBisectMenu(info *git_commands.BisectInfo, c return self.afterMark(selectCurrentAfter, waitToReselect) }, DisabledReason: singleItemIfNotBisecting, - Key: gocui.NewKeyRune('g'), + Keys: menuKey('g'), }, { Label: fmt.Sprintf(self.c.Tr.Bisect.SkipCurrent, shortHashToMark), @@ -128,7 +127,7 @@ func (self *BisectController) openMidBisectMenu(info *git_commands.BisectInfo, c return self.afterMark(selectCurrentAfter, waitToReselect) }, DisabledReason: singleItemIfNotBisecting, - Key: gocui.NewKeyRune('s'), + Keys: menuKey('s'), }, } if info.GetCurrentHash() != "" && info.GetCurrentHash() != commit.Hash() { @@ -143,7 +142,7 @@ func (self *BisectController) openMidBisectMenu(info *git_commands.BisectInfo, c return self.afterMark(selectCurrentAfter, waitToReselect) }, DisabledReason: self.require(self.singleItemSelected())(), - Key: gocui.NewKeyRune('S'), + Keys: menuKey('S'), })) } menuItems = append(menuItems, lo.ToPtr(types.MenuItem{ @@ -151,7 +150,7 @@ func (self *BisectController) openMidBisectMenu(info *git_commands.BisectInfo, c OnPress: func() error { return self.c.Helpers().Bisect.Reset() }, - Key: gocui.NewKeyRune('r'), + Keys: menuKey('r'), })) return self.c.Menu(types.CreateMenuOptions{ @@ -180,7 +179,7 @@ func (self *BisectController) openStartBisectMenu(info *git_commands.BisectInfo, return nil }, DisabledReason: self.require(self.singleItemSelected())(), - Key: gocui.NewKeyRune('b'), + Keys: menuKey('b'), }, { Label: fmt.Sprintf(self.c.Tr.Bisect.MarkStart, commit.ShortHash(), info.OldTerm()), @@ -198,7 +197,7 @@ func (self *BisectController) openStartBisectMenu(info *git_commands.BisectInfo, return nil }, DisabledReason: self.require(self.singleItemSelected())(), - Key: gocui.NewKeyRune('g'), + Keys: menuKey('g'), }, { Label: self.c.Tr.Bisect.ChooseTerms, @@ -223,7 +222,7 @@ func (self *BisectController) openStartBisectMenu(info *git_commands.BisectInfo, }) return nil }, - Key: gocui.NewKeyRune('t'), + Keys: menuKey('t'), }, }, }) diff --git a/pkg/gui/controllers/branches_controller.go b/pkg/gui/controllers/branches_controller.go index 268e94134..24ef84d54 100644 --- a/pkg/gui/controllers/branches_controller.go +++ b/pkg/gui/controllers/branches_controller.go @@ -45,7 +45,7 @@ func NewBranchesController( func (self *BranchesController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { return []*types.Binding{ { - Key: opts.GetKey(opts.Config.Universal.Select), + Keys: opts.GetKeys(opts.Config.Universal.Select), Handler: self.withItem(self.press), GetDisabledReason: self.require( self.singleItemSelected(), @@ -56,64 +56,64 @@ func (self *BranchesController) GetKeybindings(opts types.KeybindingsOpts) []*ty DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.New), + Keys: opts.GetKeys(opts.Config.Universal.New), Handler: self.withItem(self.newBranch), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.NewBranch, DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Branches.MoveCommitsToNewBranch), + Keys: opts.GetKeys(opts.Config.Branches.MoveCommitsToNewBranch), Handler: self.c.Helpers().Refs.MoveCommitsToNewBranch, GetDisabledReason: self.c.Helpers().Refs.CanMoveCommitsToNewBranch, Description: self.c.Tr.MoveCommitsToNewBranch, Tooltip: self.c.Tr.MoveCommitsToNewBranchTooltip, }, { - Key: opts.GetKey(opts.Config.Branches.CreatePullRequest), + Keys: opts.GetKeys(opts.Config.Branches.CreatePullRequest), Handler: self.withItem(self.handleCreatePullRequest), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.CreatePullRequest, }, { - Key: opts.GetKey(opts.Config.Branches.ViewPullRequestOptions), + Keys: opts.GetKeys(opts.Config.Branches.ViewPullRequestOptions), Handler: self.withItem(self.handleCreatePullRequestMenu), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.CreatePullRequestOptions, OpensMenu: true, }, { - Key: opts.GetKey(opts.Config.Branches.OpenPullRequestInBrowser), + Keys: opts.GetKeys(opts.Config.Branches.OpenPullRequestInBrowser), Handler: self.withItem(self.openPRInBrowser), GetDisabledReason: self.require(self.singleItemSelected(self.branchHasPR)), Description: self.c.Tr.OpenPullRequestInBrowser, }, { - Key: opts.GetKey(opts.Config.Branches.CopyPullRequestURL), + Keys: opts.GetKeys(opts.Config.Branches.CopyPullRequestURL), Handler: self.copyPullRequestURL, GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.CopyPullRequestURL, }, { - Key: opts.GetKey(opts.Config.Branches.CheckoutBranchByName), + Keys: opts.GetKeys(opts.Config.Branches.CheckoutBranchByName), Handler: self.checkoutByName, Description: self.c.Tr.CheckoutByName, Tooltip: self.c.Tr.CheckoutByNameTooltip, }, { - Key: opts.GetKey(opts.Config.Branches.CheckoutPreviousBranch), + Keys: opts.GetKeys(opts.Config.Branches.CheckoutPreviousBranch), Handler: self.checkoutPreviousBranch, Description: self.c.Tr.CheckoutPreviousBranch, }, { - Key: opts.GetKey(opts.Config.Branches.ForceCheckoutBranch), + Keys: opts.GetKeys(opts.Config.Branches.ForceCheckoutBranch), Handler: self.forceCheckout, GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.ForceCheckout, Tooltip: self.c.Tr.ForceCheckoutTooltip, }, { - Key: opts.GetKey(opts.Config.Universal.Remove), + Keys: opts.GetKeys(opts.Config.Universal.Remove), Handler: self.withItems(self.delete), GetDisabledReason: self.require(self.itemRangeSelected(self.branchesAreReal)), Description: self.c.Tr.Delete, @@ -122,7 +122,7 @@ func (self *BranchesController) GetKeybindings(opts types.KeybindingsOpts) []*ty DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Branches.RebaseBranch), + Keys: opts.GetKeys(opts.Config.Branches.RebaseBranch), Handler: opts.Guards.OutsideFilterMode(self.withItem(self.rebase)), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.RebaseBranch, @@ -131,7 +131,7 @@ func (self *BranchesController) GetKeybindings(opts types.KeybindingsOpts) []*ty DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Branches.MergeIntoCurrentBranch), + Keys: opts.GetKeys(opts.Config.Branches.MergeIntoCurrentBranch), Handler: opts.Guards.OutsideFilterMode(self.merge), GetDisabledReason: self.require(self.singleItemSelected(self.notMergingIntoYourself)), Description: self.c.Tr.Merge, @@ -140,26 +140,26 @@ func (self *BranchesController) GetKeybindings(opts types.KeybindingsOpts) []*ty OpensMenu: true, }, { - Key: opts.GetKey(opts.Config.Branches.FastForward), + Keys: opts.GetKeys(opts.Config.Branches.FastForward), Handler: self.withItem(self.fastForward), GetDisabledReason: self.require(self.singleItemSelected(self.branchIsReal)), Description: self.c.Tr.FastForward, Tooltip: self.c.Tr.FastForwardTooltip, }, { - Key: opts.GetKey(opts.Config.Branches.CreateTag), + Keys: opts.GetKeys(opts.Config.Branches.CreateTag), Handler: self.withItem(self.createTag), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.NewTag, }, { - Key: opts.GetKey(opts.Config.Branches.SortOrder), + Keys: opts.GetKeys(opts.Config.Branches.SortOrder), Handler: self.createSortMenu, Description: self.c.Tr.SortOrder, OpensMenu: true, }, { - Key: opts.GetKey(opts.Config.Commits.ViewResetOptions), + Keys: opts.GetKeys(opts.Config.Commits.ViewResetOptions), Handler: self.withItem(self.createResetMenu), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.ViewResetOptions, @@ -167,13 +167,13 @@ func (self *BranchesController) GetKeybindings(opts types.KeybindingsOpts) []*ty DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Branches.RenameBranch), + Keys: opts.GetKeys(opts.Config.Branches.RenameBranch), Handler: self.withItem(self.rename), GetDisabledReason: self.require(self.singleItemSelected(self.branchIsReal)), Description: self.c.Tr.RenameBranch, }, { - Key: opts.GetKey(opts.Config.Branches.SetUpstream), + Keys: opts.GetKeys(opts.Config.Branches.SetUpstream), Handler: self.withItem(self.viewUpstreamOptions), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.ViewBranchUpstreamOptions, @@ -183,7 +183,7 @@ func (self *BranchesController) GetKeybindings(opts types.KeybindingsOpts) []*ty DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.OpenDiffTool), + Keys: opts.GetKeys(opts.Config.Universal.OpenDiffTool), Handler: self.withItem(func(selectedBranch *models.Branch) error { return self.c.Helpers().Diff.OpenDiffToolForRef(selectedBranch) }), @@ -300,7 +300,7 @@ func (self *BranchesController) viewUpstreamOptions(selectedBranch *models.Branc ) viewDivergenceFromBaseBranchItem := &types.MenuItem{ LabelColumns: []string{label}, - Key: gocui.NewKeyRune('b'), + Keys: menuKey('b'), OnPress: func() error { branch := self.context().GetSelected() if branch == nil { @@ -333,7 +333,7 @@ func (self *BranchesController) viewUpstreamOptions(selectedBranch *models.Branc }) return nil }, - Key: gocui.NewKeyRune('u'), + Keys: menuKey('u'), } setUpstreamItem := &types.MenuItem{ @@ -358,7 +358,7 @@ func (self *BranchesController) viewUpstreamOptions(selectedBranch *models.Branc return nil }) }, - Key: gocui.NewKeyRune('s'), + Keys: menuKey('s'), } upstreamResetOptions := utils.ResolvePlaceholderString( @@ -391,7 +391,7 @@ func (self *BranchesController) viewUpstreamOptions(selectedBranch *models.Branc return nil }, Tooltip: upstreamResetTooltip, - Key: gocui.NewKeyRune('g'), + Keys: menuKey('g'), } upstreamRebaseItem := &types.MenuItem{ @@ -404,7 +404,7 @@ func (self *BranchesController) viewUpstreamOptions(selectedBranch *models.Branc return nil }, Tooltip: upstreamRebaseTooltip, - Key: gocui.NewKeyRune('r'), + Keys: menuKey('r'), } if !selectedBranch.IsTrackingRemote() { @@ -624,7 +624,7 @@ func (self *BranchesController) delete(branches []*models.Branch) error { localDeleteItem := &types.MenuItem{ Label: lo.Ternary(len(branches) > 1, self.c.Tr.DeleteLocalBranches, self.c.Tr.DeleteLocalBranch), - Key: gocui.NewKeyRune('c'), + Keys: menuKey('c'), OnPress: func() error { return self.localDelete(branches) }, @@ -635,7 +635,7 @@ func (self *BranchesController) delete(branches []*models.Branch) error { remoteDeleteItem := &types.MenuItem{ Label: lo.Ternary(len(branches) > 1, self.c.Tr.DeleteRemoteBranches, self.c.Tr.DeleteRemoteBranch), - Key: gocui.NewKeyRune('r'), + Keys: menuKey('r'), OnPress: func() error { return self.remoteDelete(branches) }, @@ -648,7 +648,7 @@ func (self *BranchesController) delete(branches []*models.Branch) error { deleteBothItem := &types.MenuItem{ Label: lo.Ternary(len(branches) > 1, self.c.Tr.DeleteLocalAndRemoteBranches, self.c.Tr.DeleteLocalAndRemoteBranch), - Key: gocui.NewKeyRune('b'), + Keys: menuKey('b'), OnPress: func() error { return self.localAndRemoteDelete(branches) }, diff --git a/pkg/gui/controllers/commit_description_controller.go b/pkg/gui/controllers/commit_description_controller.go index 63f6876e5..41ec54103 100644 --- a/pkg/gui/controllers/commit_description_controller.go +++ b/pkg/gui/controllers/commit_description_controller.go @@ -4,6 +4,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/gocui" "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/utils" + "github.com/samber/lo" ) type CommitDescriptionController struct { @@ -25,23 +26,19 @@ func NewCommitDescriptionController( func (self *CommitDescriptionController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { bindings := []*types.Binding{ { - Key: opts.GetKey(opts.Config.Universal.TogglePanel), + Keys: opts.GetKeys(opts.Config.Universal.TogglePanel), Handler: self.handleTogglePanel, }, { - Key: opts.GetKey(opts.Config.Universal.Return), + Keys: opts.GetKeys(opts.Config.Universal.Return), Handler: self.close, }, { - Key: opts.GetKey(opts.Config.Universal.ConfirmInEditor), + Keys: opts.GetKeys(opts.Config.Universal.ConfirmInEditor), Handler: self.confirm, }, { - Key: opts.GetKey(opts.Config.Universal.ConfirmInEditorAlt), - Handler: self.confirm, - }, - { - Key: opts.GetKey(opts.Config.CommitMessage.CommitMenu), + Keys: opts.GetKeys(opts.Config.CommitMessage.CommitMenu), Handler: self.openCommitMenu, }, } @@ -67,24 +64,12 @@ func (self *CommitDescriptionController) GetMouseKeybindings(opts types.Keybindi func (self *CommitDescriptionController) GetOnFocus() func(types.OnFocusOpts) { return func(types.OnFocusOpts) { footer := "" - if self.c.UserConfig().Keybinding.Universal.ConfirmInEditor != "" || self.c.UserConfig().Keybinding.Universal.ConfirmInEditorAlt != "" { - if self.c.UserConfig().Keybinding.Universal.ConfirmInEditor == "" { - footer = utils.ResolvePlaceholderString(self.c.Tr.CommitDescriptionFooter, - map[string]string{ - "confirmInEditorKeybinding": self.c.UserConfig().Keybinding.Universal.ConfirmInEditorAlt, - }) - } else if self.c.UserConfig().Keybinding.Universal.ConfirmInEditorAlt == "" { - footer = utils.ResolvePlaceholderString(self.c.Tr.CommitDescriptionFooter, - map[string]string{ - "confirmInEditorKeybinding": self.c.UserConfig().Keybinding.Universal.ConfirmInEditor, - }) - } else { - footer = utils.ResolvePlaceholderString(self.c.Tr.CommitDescriptionFooterTwoBindings, - map[string]string{ - "confirmInEditorKeybinding1": self.c.UserConfig().Keybinding.Universal.ConfirmInEditor, - "confirmInEditorKeybinding2": self.c.UserConfig().Keybinding.Universal.ConfirmInEditorAlt, - }) - } + keys := self.c.UserConfig().Keybinding.Universal.ConfirmInEditor + if len(keys) > 0 { + footer = utils.ResolvePlaceholderString(self.c.Tr.CommitDescriptionFooter, + map[string]string{ + "confirmInEditorKeybinding": keys.String(), + }) } self.c.Views().CommitDescription.Footer = footer } @@ -112,7 +97,7 @@ func (self *CommitDescriptionController) handleTogglePanel() error { // ctrl key or fn key, which is unlikely to occur in pasted text. And if // they mapped some *other* command to "", then we're totally out of // luck. - if self.c.GocuiGui().IsPasting && self.c.UserConfig().Keybinding.Universal.TogglePanel == "" { + if self.c.GocuiGui().IsPasting && lo.Contains(self.c.UserConfig().Keybinding.Universal.TogglePanel, "") { // Handling tabs in pasted commit messages is not optimal, but hopefully // good enough for now. We simply insert 4 spaces without worrying about // column alignment. This works well enough for leading indentation, diff --git a/pkg/gui/controllers/commit_message_controller.go b/pkg/gui/controllers/commit_message_controller.go index e1561690c..098f42d30 100644 --- a/pkg/gui/controllers/commit_message_controller.go +++ b/pkg/gui/controllers/commit_message_controller.go @@ -8,6 +8,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/gui/context" "github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers" "github.com/jesseduffield/lazygit/pkg/gui/types" + "github.com/samber/lo" ) type CommitMessageController struct { @@ -29,29 +30,29 @@ func NewCommitMessageController( func (self *CommitMessageController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { bindings := []*types.Binding{ { - Key: opts.GetKey(opts.Config.Universal.SubmitEditorText), + Keys: opts.GetKeys(opts.Config.Universal.SubmitEditorText), Handler: self.confirm, Description: self.c.Tr.Confirm, }, { - Key: opts.GetKey(opts.Config.Universal.Return), + Keys: opts.GetKeys(opts.Config.Universal.Return), Handler: self.close, Description: self.c.Tr.Close, }, { - Key: opts.GetKey(opts.Config.Universal.PrevItem), + Keys: opts.GetKeys(opts.Config.Universal.PrevItem), Handler: self.handlePreviousCommit, }, { - Key: opts.GetKey(opts.Config.Universal.NextItem), + Keys: opts.GetKeys(opts.Config.Universal.NextItem), Handler: self.handleNextCommit, }, { - Key: opts.GetKey(opts.Config.Universal.TogglePanel), + Keys: opts.GetKeys(opts.Config.Universal.TogglePanel), Handler: self.handleTogglePanel, }, { - Key: opts.GetKey(opts.Config.CommitMessage.CommitMenu), + Keys: opts.GetKeys(opts.Config.CommitMessage.CommitMenu), Handler: self.openCommitMenu, }, } @@ -123,7 +124,7 @@ func (self *CommitMessageController) handleTogglePanel() error { // ctrl key or fn key, which is unlikely to occur in pasted text. And if // they mapped some *other* command to "", then we're totally out of // luck. - if self.c.GocuiGui().IsPasting && self.c.UserConfig().Keybinding.Universal.TogglePanel == "" { + if self.c.GocuiGui().IsPasting && lo.Contains(self.c.UserConfig().Keybinding.Universal.TogglePanel, "") { // It is unlikely that a pasted commit message contains a tab in the // subject line, so it shouldn't matter too much how we handle it. // Simply insert 4 spaces instead; all that matters is that we don't @@ -183,7 +184,7 @@ func (self *CommitMessageController) confirm() error { // to some ctrl key or fn key, which is unlikely to occur in pasted text. // And if they mapped some *other* command to "", then we're totally // out of luck. - if self.c.GocuiGui().IsPasting && self.c.UserConfig().Keybinding.Universal.SubmitEditorText == "" { + if self.c.GocuiGui().IsPasting && lo.Contains(self.c.UserConfig().Keybinding.Universal.SubmitEditorText, "") { return self.switchToCommitDescription() } diff --git a/pkg/gui/controllers/commits_files_controller.go b/pkg/gui/controllers/commits_files_controller.go index b12819c39..eed9d02b9 100644 --- a/pkg/gui/controllers/commits_files_controller.go +++ b/pkg/gui/controllers/commits_files_controller.go @@ -45,13 +45,13 @@ func NewCommitFilesController( func (self *CommitFilesController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { bindings := []*types.Binding{ { - Key: opts.GetKey(opts.Config.Files.CopyFileInfoToClipboard), + Keys: opts.GetKeys(opts.Config.Files.CopyFileInfoToClipboard), Handler: self.openCopyMenu, Description: self.c.Tr.CopyToClipboardMenu, OpensMenu: true, }, { - Key: opts.GetKey(opts.Config.CommitFiles.CheckoutCommitFile), + Keys: opts.GetKeys(opts.Config.CommitFiles.CheckoutCommitFile), Handler: self.withItem(self.checkout), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.Checkout, @@ -59,7 +59,7 @@ func (self *CommitFilesController) GetKeybindings(opts types.KeybindingsOpts) [] DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.Remove), + Keys: opts.GetKeys(opts.Config.Universal.Remove), Handler: self.withItems(self.discard), GetDisabledReason: self.require(self.itemsSelected(self.canDiscardFileChanges)), Description: self.c.Tr.Discard, @@ -67,14 +67,14 @@ func (self *CommitFilesController) GetKeybindings(opts types.KeybindingsOpts) [] DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.OpenFile), + Keys: opts.GetKeys(opts.Config.Universal.OpenFile), Handler: self.withItem(self.open), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.OpenFile, Tooltip: self.c.Tr.OpenFileTooltip, }, { - Key: opts.GetKey(opts.Config.Universal.Edit), + Keys: opts.GetKeys(opts.Config.Universal.Edit), Handler: self.withItems(self.edit), GetDisabledReason: self.require(self.itemsSelected(self.canEditFiles)), Description: self.c.Tr.Edit, @@ -82,13 +82,13 @@ func (self *CommitFilesController) GetKeybindings(opts types.KeybindingsOpts) [] DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.OpenDiffTool), + Keys: opts.GetKeys(opts.Config.Universal.OpenDiffTool), Handler: self.withItem(self.openDiffTool), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.OpenDiffTool, }, { - Key: opts.GetKey(opts.Config.Universal.Select), + Keys: opts.GetKeys(opts.Config.Universal.Select), Handler: self.withItems(self.toggleForPatch), GetDisabledReason: self.require(self.itemsSelected()), Description: self.c.Tr.ToggleAddToPatch, @@ -98,7 +98,7 @@ func (self *CommitFilesController) GetKeybindings(opts types.KeybindingsOpts) [] DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Files.ToggleStagedAll), + Keys: opts.GetKeys(opts.Config.Files.ToggleStagedAll), Handler: self.withItem(self.toggleAllForPatch), Description: self.c.Tr.ToggleAllInPatch, Tooltip: utils.ResolvePlaceholderString(self.c.Tr.ToggleAllInPatchTooltip, @@ -106,27 +106,27 @@ func (self *CommitFilesController) GetKeybindings(opts types.KeybindingsOpts) [] ), }, { - Key: opts.GetKey(opts.Config.Universal.GoInto), + Keys: opts.GetKeys(opts.Config.Universal.GoInto), Handler: self.withItem(self.enter), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.EnterCommitFile, Tooltip: self.c.Tr.EnterCommitFileTooltip, }, { - Key: opts.GetKey(opts.Config.Files.ToggleTreeView), + Keys: opts.GetKeys(opts.Config.Files.ToggleTreeView), Handler: self.toggleTreeView, Description: self.c.Tr.ToggleTreeView, Tooltip: self.c.Tr.ToggleTreeViewTooltip, }, { - Key: opts.GetKey(opts.Config.Files.CollapseAll), + Keys: opts.GetKeys(opts.Config.Files.CollapseAll), Handler: self.collapseAll, Description: self.c.Tr.CollapseAll, Tooltip: self.c.Tr.CollapseAllTooltip, GetDisabledReason: self.require(self.isInTreeMode), }, { - Key: opts.GetKey(opts.Config.Files.ExpandAll), + Keys: opts.GetKeys(opts.Config.Files.ExpandAll), Handler: self.expandAll, Description: self.c.Tr.ExpandAll, Tooltip: self.c.Tr.ExpandAllTooltip, @@ -230,7 +230,7 @@ func (self *CommitFilesController) openCopyMenu() error { return nil }, DisabledReason: self.require(self.singleItemSelected())(), - Key: gocui.NewKeyRune('n'), + Keys: menuKey('n'), } copyRelativePathItem := &types.MenuItem{ Label: self.c.Tr.CopyRelativeFilePath, @@ -242,7 +242,7 @@ func (self *CommitFilesController) openCopyMenu() error { return nil }, DisabledReason: self.require(self.singleItemSelected())(), - Key: gocui.NewKeyRune('p'), + Keys: menuKey('p'), } copyAbsolutePathItem := &types.MenuItem{ Label: self.c.Tr.CopyAbsoluteFilePath, @@ -258,7 +258,7 @@ func (self *CommitFilesController) openCopyMenu() error { return nil }, DisabledReason: self.require(self.singleItemSelected())(), - Key: gocui.NewKeyRune('P'), + Keys: menuKey('P'), } copyFileDiffItem := &types.MenuItem{ Label: self.c.Tr.CopySelectedDiff, @@ -266,7 +266,7 @@ func (self *CommitFilesController) openCopyMenu() error { return self.copyDiffToClipboard(node.GetPath(), self.c.Tr.FileDiffCopiedToast) }, DisabledReason: self.require(self.singleItemSelected())(), - Key: gocui.NewKeyRune('s'), + Keys: menuKey('s'), } copyAllDiff := &types.MenuItem{ Label: self.c.Tr.CopyAllFilesDiff, @@ -274,7 +274,7 @@ func (self *CommitFilesController) openCopyMenu() error { return self.copyDiffToClipboard(".", self.c.Tr.AllFilesDiffCopiedToast) }, DisabledReason: self.require(self.itemsSelected())(), - Key: gocui.NewKeyRune('a'), + Keys: menuKey('a'), } copyFileContentItem := &types.MenuItem{ Label: self.c.Tr.CopyFileContent, @@ -295,7 +295,7 @@ func (self *CommitFilesController) openCopyMenu() error { } return nil }))(), - Key: gocui.NewKeyRune('c'), + Keys: menuKey('c'), } return self.c.Menu(types.CreateMenuOptions{ diff --git a/pkg/gui/controllers/confirmation_controller.go b/pkg/gui/controllers/confirmation_controller.go index 206818f40..990b2b09e 100644 --- a/pkg/gui/controllers/confirmation_controller.go +++ b/pkg/gui/controllers/confirmation_controller.go @@ -24,19 +24,19 @@ func NewConfirmationController( func (self *ConfirmationController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { bindings := []*types.Binding{ { - Key: opts.GetKey(opts.Config.Universal.Confirm), + Keys: opts.GetKeys(opts.Config.Universal.Confirm), Handler: func() error { return self.context().State.OnConfirm() }, Description: self.c.Tr.Confirm, DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.Return), + Keys: opts.GetKeys(opts.Config.Universal.Return), Handler: func() error { return self.context().State.OnClose() }, Description: self.c.Tr.CloseCancel, DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.CopyToClipboard), + Keys: opts.GetKeys(opts.Config.Universal.CopyToClipboard), Handler: self.handleCopyToClipboard, Description: self.c.Tr.CopyToClipboardMenu, DisplayOnScreen: true, diff --git a/pkg/gui/controllers/context_lines_controller.go b/pkg/gui/controllers/context_lines_controller.go index a1ce9f518..022364c07 100644 --- a/pkg/gui/controllers/context_lines_controller.go +++ b/pkg/gui/controllers/context_lines_controller.go @@ -30,13 +30,13 @@ func NewContextLinesController( func (self *ContextLinesController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { bindings := []*types.Binding{ { - Key: opts.GetKey(opts.Config.Universal.IncreaseContextInDiffView), + Keys: opts.GetKeys(opts.Config.Universal.IncreaseContextInDiffView), Handler: self.Increase, Description: self.c.Tr.IncreaseContextInDiffView, Tooltip: self.c.Tr.IncreaseContextInDiffViewTooltip, }, { - Key: opts.GetKey(opts.Config.Universal.DecreaseContextInDiffView), + Keys: opts.GetKeys(opts.Config.Universal.DecreaseContextInDiffView), Handler: self.Decrease, Description: self.c.Tr.DecreaseContextInDiffView, Tooltip: self.c.Tr.DecreaseContextInDiffViewTooltip, diff --git a/pkg/gui/controllers/custom_patch_options_menu_action.go b/pkg/gui/controllers/custom_patch_options_menu_action.go index 9ef990f79..cabba4739 100644 --- a/pkg/gui/controllers/custom_patch_options_menu_action.go +++ b/pkg/gui/controllers/custom_patch_options_menu_action.go @@ -31,19 +31,19 @@ func (self *CustomPatchOptionsMenuAction) Call() error { Label: self.c.Tr.ResetPatch, Tooltip: self.c.Tr.ResetPatchTooltip, OnPress: self.c.Helpers().PatchBuilding.Reset, - Key: gocui.NewKeyRune('c'), + Keys: menuKey('c'), }, { Label: self.c.Tr.ApplyPatch, Tooltip: self.c.Tr.ApplyPatchTooltip, OnPress: func() error { return self.handleApplyPatch(false) }, - Key: gocui.NewKeyRune('a'), + Keys: menuKey('a'), }, { Label: self.c.Tr.ApplyPatchInReverse, Tooltip: self.c.Tr.ApplyPatchInReverseTooltip, OnPress: func() error { return self.handleApplyPatch(true) }, - Key: gocui.NewKeyRune('r'), + Keys: menuKey('r'), }, } @@ -53,25 +53,25 @@ func (self *CustomPatchOptionsMenuAction) Call() error { Label: fmt.Sprintf(self.c.Tr.RemovePatchFromOriginalCommit, utils.ShortHash(self.c.Git().Patch.PatchBuilder.To)), Tooltip: self.c.Tr.RemovePatchFromOriginalCommitTooltip, OnPress: self.handleDeletePatchFromCommit, - Key: gocui.NewKeyRune('d'), + Keys: menuKey('d'), }, { Label: self.c.Tr.MovePatchOutIntoIndex, Tooltip: self.c.Tr.MovePatchOutIntoIndexTooltip, OnPress: self.handleMovePatchIntoWorkingTree, - Key: gocui.NewKeyRune('i'), + Keys: menuKey('i'), }, { Label: self.c.Tr.MovePatchIntoNewCommit, Tooltip: self.c.Tr.MovePatchIntoNewCommitTooltip, OnPress: self.handlePullPatchIntoNewCommit, - Key: gocui.NewKeyRune('n'), + Keys: menuKey('n'), }, { Label: self.c.Tr.MovePatchIntoNewCommitBefore, Tooltip: self.c.Tr.MovePatchIntoNewCommitBeforeTooltip, OnPress: self.handlePullPatchIntoNewCommitBefore, - Key: gocui.NewKeyRune('N'), + Keys: menuKey('N'), }, }...) @@ -93,7 +93,7 @@ func (self *CustomPatchOptionsMenuAction) Call() error { Label: fmt.Sprintf(self.c.Tr.MovePatchToSelectedCommit, selectedCommit.Hash()), Tooltip: self.c.Tr.MovePatchToSelectedCommitTooltip, OnPress: self.handleMovePatchToSelectedCommit, - Key: gocui.NewKeyRune('m'), + Keys: menuKey('m'), DisabledReason: disabledReason, }, }, menuItems[1:]..., @@ -107,7 +107,7 @@ func (self *CustomPatchOptionsMenuAction) Call() error { { Label: self.c.Tr.CopyPatchToClipboard, OnPress: func() error { return self.copyPatchToClipboard() }, - Key: gocui.NewKeyRune('y'), + Keys: menuKey('y'), }, }...) diff --git a/pkg/gui/controllers/files_controller.go b/pkg/gui/controllers/files_controller.go index c8d50d54d..8ea4425e6 100644 --- a/pkg/gui/controllers/files_controller.go +++ b/pkg/gui/controllers/files_controller.go @@ -42,7 +42,7 @@ func NewFilesController( func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { return []*types.Binding{ { - Key: opts.GetKey(opts.Config.Universal.Select), + Keys: opts.GetKeys(opts.Config.Universal.Select), Handler: self.withItems(self.press), GetDisabledReason: self.require(self.withFileTreeViewModelMutex(self.itemsSelected())), Description: self.c.Tr.Stage, @@ -50,46 +50,46 @@ func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Files.OpenStatusFilter), + Keys: opts.GetKeys(opts.Config.Files.OpenStatusFilter), Handler: self.handleStatusFilterPressed, Description: self.c.Tr.FileFilter, }, { - Key: opts.GetKey(opts.Config.Files.CopyFileInfoToClipboard), + Keys: opts.GetKeys(opts.Config.Files.CopyFileInfoToClipboard), Handler: self.openCopyMenu, Description: self.c.Tr.CopyToClipboardMenu, OpensMenu: true, }, { - Key: opts.GetKey(opts.Config.Files.CommitChanges), + Keys: opts.GetKeys(opts.Config.Files.CommitChanges), Handler: self.c.Helpers().WorkingTree.HandleCommitPress, Description: self.c.Tr.Commit, Tooltip: self.c.Tr.CommitTooltip, DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Files.CommitChangesWithoutHook), + Keys: opts.GetKeys(opts.Config.Files.CommitChangesWithoutHook), Handler: self.c.Helpers().WorkingTree.HandleWIPCommitPress, Description: self.c.Tr.CommitChangesWithoutHook, }, { - Key: opts.GetKey(opts.Config.Files.AmendLastCommit), + Keys: opts.GetKeys(opts.Config.Files.AmendLastCommit), Handler: self.handleAmendCommitPress, Description: self.c.Tr.AmendLastCommit, }, { - Key: opts.GetKey(opts.Config.Files.CommitChangesWithEditor), + Keys: opts.GetKeys(opts.Config.Files.CommitChangesWithEditor), Handler: self.c.Helpers().WorkingTree.HandleCommitEditorPress, Description: self.c.Tr.CommitChangesWithEditor, }, { - Key: opts.GetKey(opts.Config.Files.FindBaseCommitForFixup), + Keys: opts.GetKeys(opts.Config.Files.FindBaseCommitForFixup), Handler: self.c.Helpers().FixupHelper.HandleFindBaseCommitForFixupPress, Description: self.c.Tr.FindBaseCommitForFixup, Tooltip: self.c.Tr.FindBaseCommitForFixupTooltip, }, { - Key: opts.GetKey(opts.Config.Universal.Edit), + Keys: opts.GetKeys(opts.Config.Universal.Edit), Handler: self.withItems(self.edit), GetDisabledReason: self.require(self.withFileTreeViewModelMutex(self.itemsSelected(self.canEditFiles))), Description: self.c.Tr.Edit, @@ -97,53 +97,53 @@ func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.OpenFile), + Keys: opts.GetKeys(opts.Config.Universal.OpenFile), Handler: self.Open, GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.OpenFile, Tooltip: self.c.Tr.OpenFileTooltip, }, { - Key: opts.GetKey(opts.Config.Files.IgnoreFile), + Keys: opts.GetKeys(opts.Config.Files.IgnoreFile), Handler: self.withItem(self.ignoreOrExcludeMenu), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.Actions.IgnoreExcludeFile, OpensMenu: true, }, { - Key: opts.GetKey(opts.Config.Files.RefreshFiles), + Keys: opts.GetKeys(opts.Config.Files.RefreshFiles), Handler: self.refresh, Description: self.c.Tr.RefreshFiles, }, { - Key: opts.GetKey(opts.Config.Files.StashAllChanges), + Keys: opts.GetKeys(opts.Config.Files.StashAllChanges), Handler: self.stash, Description: self.c.Tr.Stash, Tooltip: self.c.Tr.StashTooltip, DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Files.ViewStashOptions), + Keys: opts.GetKeys(opts.Config.Files.ViewStashOptions), Handler: self.createStashMenu, Description: self.c.Tr.ViewStashOptions, Tooltip: self.c.Tr.ViewStashOptionsTooltip, OpensMenu: true, }, { - Key: opts.GetKey(opts.Config.Files.ToggleStagedAll), + Keys: opts.GetKeys(opts.Config.Files.ToggleStagedAll), Handler: self.toggleStagedAll, Description: self.c.Tr.ToggleStagedAll, Tooltip: self.c.Tr.ToggleStagedAllTooltip, }, { - Key: opts.GetKey(opts.Config.Universal.GoInto), + Keys: opts.GetKeys(opts.Config.Universal.GoInto), Handler: self.enter, GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.FileEnter, Tooltip: self.c.Tr.FileEnterTooltip, }, { - Key: opts.GetKey(opts.Config.Universal.Remove), + Keys: opts.GetKeys(opts.Config.Universal.Remove), Handler: self.withItems(self.remove), GetDisabledReason: self.withFileTreeViewModelMutex(self.require(self.itemsSelected(self.canRemove))), Description: self.c.Tr.Discard, @@ -152,13 +152,13 @@ func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Commits.ViewResetOptions), + Keys: opts.GetKeys(opts.Config.Commits.ViewResetOptions), Handler: self.createResetToUpstreamMenu, Description: self.c.Tr.ViewResetToUpstreamOptions, OpensMenu: true, }, { - Key: opts.GetKey(opts.Config.Files.ViewResetOptions), + Keys: opts.GetKeys(opts.Config.Files.ViewResetOptions), Handler: self.createResetMenu, Description: self.c.Tr.Reset, Tooltip: self.c.Tr.FileResetOptionsTooltip, @@ -166,19 +166,19 @@ func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Files.ToggleTreeView), + Keys: opts.GetKeys(opts.Config.Files.ToggleTreeView), Handler: self.toggleTreeView, Description: self.c.Tr.ToggleTreeView, Tooltip: self.c.Tr.ToggleTreeViewTooltip, }, { - Key: opts.GetKey(opts.Config.Universal.OpenDiffTool), + Keys: opts.GetKeys(opts.Config.Universal.OpenDiffTool), Handler: self.withItem(self.openDiffTool), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.OpenDiffTool, }, { - Key: opts.GetKey(opts.Config.Files.OpenMergeOptions), + Keys: opts.GetKeys(opts.Config.Files.OpenMergeOptions), Handler: self.withItems(self.openMergeConflictMenu), Description: self.c.Tr.ViewMergeConflictOptions, Tooltip: self.c.Tr.ViewMergeConflictOptionsTooltip, @@ -187,20 +187,20 @@ func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Files.Fetch), + Keys: opts.GetKeys(opts.Config.Files.Fetch), Handler: self.fetch, Description: self.c.Tr.Fetch, Tooltip: self.c.Tr.FetchTooltip, }, { - Key: opts.GetKey(opts.Config.Files.CollapseAll), + Keys: opts.GetKeys(opts.Config.Files.CollapseAll), Handler: self.collapseAll, Description: self.c.Tr.CollapseAll, Tooltip: self.c.Tr.CollapseAllTooltip, GetDisabledReason: self.require(self.isInTreeMode), }, { - Key: opts.GetKey(opts.Config.Files.ExpandAll), + Keys: opts.GetKeys(opts.Config.Files.ExpandAll), Handler: self.expandAll, Description: self.c.Tr.ExpandAll, Tooltip: self.c.Tr.ExpandAllTooltip, @@ -671,14 +671,14 @@ func (self *FilesController) handleNonInlineConflict(file *models.File) error { OnPress: func() error { return handle(self.c.Git().WorkingTree.StageFile, self.c.Tr.Actions.ResolveConflictByKeepingFile) }, - Key: gocui.NewKeyRune('k'), + Keys: menuKey('k'), } deleteItem := &types.MenuItem{ Label: self.c.Tr.MergeConflictDeleteFile, OnPress: func() error { return handle(self.c.Git().WorkingTree.RemoveConflictedFile, self.c.Tr.Actions.ResolveConflictByDeletingFile) }, - Key: gocui.NewKeyRune('d'), + Keys: menuKey('d'), } items := []*types.MenuItem{} switch file.ShortStatus { @@ -856,7 +856,7 @@ func (self *FilesController) ignoreOrExcludeMenu(node *filetree.FileNode) error } return nil }, - Key: gocui.NewKeyRune('i'), + Keys: menuKey('i'), }, { LabelColumns: []string{self.c.Tr.ExcludeFile}, @@ -866,7 +866,7 @@ func (self *FilesController) ignoreOrExcludeMenu(node *filetree.FileNode) error } return nil }, - Key: gocui.NewKeyRune('e'), + Keys: menuKey('e'), }, }, }) @@ -950,7 +950,7 @@ func (self *FilesController) handleStatusFilterPressed() error { OnPress: func() error { return self.setStatusFiltering(filetree.DisplayStaged) }, - Key: gocui.NewKeyRune('s'), + Keys: menuKey('s'), Widget: types.MakeMenuRadioButton(currentFilter == filetree.DisplayStaged), }, { @@ -958,7 +958,7 @@ func (self *FilesController) handleStatusFilterPressed() error { OnPress: func() error { return self.setStatusFiltering(filetree.DisplayUnstaged) }, - Key: gocui.NewKeyRune('u'), + Keys: menuKey('u'), Widget: types.MakeMenuRadioButton(currentFilter == filetree.DisplayUnstaged), }, { @@ -966,7 +966,7 @@ func (self *FilesController) handleStatusFilterPressed() error { OnPress: func() error { return self.setStatusFiltering(filetree.DisplayTracked) }, - Key: gocui.NewKeyRune('t'), + Keys: menuKey('t'), Widget: types.MakeMenuRadioButton(currentFilter == filetree.DisplayTracked), }, { @@ -974,7 +974,7 @@ func (self *FilesController) handleStatusFilterPressed() error { OnPress: func() error { return self.setStatusFiltering(filetree.DisplayUntracked) }, - Key: gocui.NewKeyRune('T'), + Keys: menuKey('T'), Widget: types.MakeMenuRadioButton(currentFilter == filetree.DisplayUntracked), }, { @@ -982,7 +982,7 @@ func (self *FilesController) handleStatusFilterPressed() error { OnPress: func() error { return self.setStatusFiltering(filetree.DisplayAll) }, - Key: gocui.NewKeyRune('r'), + Keys: menuKey('r'), Widget: types.MakeMenuRadioButton(currentFilter == filetree.DisplayAll), }, }, @@ -1092,7 +1092,7 @@ func (self *FilesController) createStashMenu() error { } return self.handleStashSave(self.c.Git().Stash.Push, self.c.Tr.Actions.StashAllChanges) }, - Key: gocui.NewKeyRune('a'), + Keys: menuKey('a'), }, { Label: self.c.Tr.StashAllChangesKeepIndex, @@ -1103,14 +1103,14 @@ func (self *FilesController) createStashMenu() error { // if there are no staged files it behaves the same as Stash.Save return self.handleStashSave(self.c.Git().Stash.StashAndKeepIndex, self.c.Tr.Actions.StashAllChangesKeepIndex) }, - Key: gocui.NewKeyRune('i'), + Keys: menuKey('i'), }, { Label: self.c.Tr.StashIncludeUntrackedChanges, OnPress: func() error { return self.handleStashSave(self.c.Git().Stash.StashIncludeUntrackedChanges, self.c.Tr.Actions.StashIncludeUntrackedChanges) }, - Key: gocui.NewKeyRune('U'), + Keys: menuKey('U'), }, { Label: self.c.Tr.StashStagedChanges, @@ -1121,7 +1121,7 @@ func (self *FilesController) createStashMenu() error { } return self.handleStashSave(self.c.Git().Stash.SaveStagedChanges, self.c.Tr.Actions.StashStagedChanges) }, - Key: gocui.NewKeyRune('s'), + Keys: menuKey('s'), }, { Label: self.c.Tr.StashUnstagedChanges, @@ -1135,7 +1135,7 @@ func (self *FilesController) createStashMenu() error { // ordinary stash return self.handleStashSave(self.c.Git().Stash.Push, self.c.Tr.Actions.StashUnstagedChanges) }, - Key: gocui.NewKeyRune('u'), + Keys: menuKey('u'), }, }, }) @@ -1182,7 +1182,7 @@ func (self *FilesController) openCopyMenu() error { return nil }, DisabledReason: self.require(self.singleItemSelected())(), - Key: gocui.NewKeyRune('n'), + Keys: menuKey('n'), } copyRelativePathItem := &types.MenuItem{ Label: self.c.Tr.CopyRelativeFilePath, @@ -1194,7 +1194,7 @@ func (self *FilesController) openCopyMenu() error { return nil }, DisabledReason: self.require(self.singleItemSelected())(), - Key: gocui.NewKeyRune('p'), + Keys: menuKey('p'), } copyAbsolutePathItem := &types.MenuItem{ Label: self.c.Tr.CopyAbsoluteFilePath, @@ -1210,7 +1210,7 @@ func (self *FilesController) openCopyMenu() error { return nil }, DisabledReason: self.require(self.singleItemSelected())(), - Key: gocui.NewKeyRune('P'), + Keys: menuKey('P'), } copyFileDiffItem := &types.MenuItem{ Label: self.c.Tr.CopySelectedDiff, @@ -1236,7 +1236,7 @@ func (self *FilesController) openCopyMenu() error { return nil }, ))(), - Key: gocui.NewKeyRune('s'), + Keys: menuKey('s'), } copyAllDiff := &types.MenuItem{ Label: self.c.Tr.CopyAllFilesDiff, @@ -1261,7 +1261,7 @@ func (self *FilesController) openCopyMenu() error { return nil }, )(), - Key: gocui.NewKeyRune('a'), + Keys: menuKey('a'), } return self.c.Menu(types.CreateMenuOptions{ @@ -1502,7 +1502,7 @@ func (self *FilesController) remove(selectedNodes []*filetree.FileNode) error { self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES, types.WORKTREES}}) return nil }, - Key: self.c.KeybindingsOpts().GetKey(self.c.UserConfig().Keybinding.Files.ConfirmDiscard), + Keys: self.c.KeybindingsOpts().GetKeys(self.c.UserConfig().Keybinding.Files.ConfirmDiscard), Tooltip: utils.ResolvePlaceholderString( self.c.Tr.DiscardAllTooltip, map[string]string{ @@ -1528,7 +1528,7 @@ func (self *FilesController) remove(selectedNodes []*filetree.FileNode) error { self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES, types.WORKTREES}}) return nil }, - Key: gocui.NewKeyRune('u'), + Keys: menuKey('u'), Tooltip: utils.ResolvePlaceholderString( self.c.Tr.DiscardUnstagedTooltip, map[string]string{ diff --git a/pkg/gui/controllers/filter_controller.go b/pkg/gui/controllers/filter_controller.go index 8b049b26c..358fb8ed5 100644 --- a/pkg/gui/controllers/filter_controller.go +++ b/pkg/gui/controllers/filter_controller.go @@ -36,7 +36,7 @@ func (self *FilterController) Context() types.Context { func (self *FilterController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { return []*types.Binding{ { - Key: opts.GetKey(opts.Config.Universal.StartSearch), + Keys: opts.GetKeys(opts.Config.Universal.StartSearch), Handler: self.OpenFilterPrompt, Description: self.c.Tr.StartFilter, }, diff --git a/pkg/gui/controllers/git_flow_controller.go b/pkg/gui/controllers/git_flow_controller.go index 2fcb4e5f5..9e892e97f 100644 --- a/pkg/gui/controllers/git_flow_controller.go +++ b/pkg/gui/controllers/git_flow_controller.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/jesseduffield/lazygit/pkg/commands/models" - "github.com/jesseduffield/lazygit/pkg/gocui" "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/utils" ) @@ -36,7 +35,7 @@ func NewGitFlowController( func (self *GitFlowController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { bindings := []*types.Binding{ { - Key: opts.GetKey(opts.Config.Branches.ViewGitFlowOptions), + Keys: opts.GetKeys(opts.Config.Branches.ViewGitFlowOptions), Handler: self.withItem(self.handleCreateGitFlowMenu), Description: self.c.Tr.GitFlowOptions, OpensMenu: true, @@ -83,22 +82,22 @@ func (self *GitFlowController) handleCreateGitFlowMenu(branch *models.Branch) er { Label: "start feature", OnPress: startHandler("feature"), - Key: gocui.NewKeyRune('f'), + Keys: menuKey('f'), }, { Label: "start hotfix", OnPress: startHandler("hotfix"), - Key: gocui.NewKeyRune('h'), + Keys: menuKey('h'), }, { Label: "start bugfix", OnPress: startHandler("bugfix"), - Key: gocui.NewKeyRune('b'), + Keys: menuKey('b'), }, { Label: "start release", OnPress: startHandler("release"), - Key: gocui.NewKeyRune('r'), + Keys: menuKey('r'), }, }, }) diff --git a/pkg/gui/controllers/global_controller.go b/pkg/gui/controllers/global_controller.go index d7f18fcb4..1043ee88f 100644 --- a/pkg/gui/controllers/global_controller.go +++ b/pkg/gui/controllers/global_controller.go @@ -23,20 +23,20 @@ func NewGlobalController( func (self *GlobalController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { return []*types.Binding{ { - Key: opts.GetKey(opts.Config.Universal.ExecuteShellCommand), + Keys: opts.GetKeys(opts.Config.Universal.ExecuteShellCommand), Handler: self.shellCommand, Description: self.c.Tr.ExecuteShellCommand, Tooltip: self.c.Tr.ExecuteShellCommandTooltip, OpensMenu: true, }, { - Key: opts.GetKey(opts.Config.Universal.CreatePatchOptionsMenu), + Keys: opts.GetKeys(opts.Config.Universal.CreatePatchOptionsMenu), Handler: self.createCustomPatchOptionsMenu, Description: self.c.Tr.ViewPatchOptions, OpensMenu: true, }, { - Key: opts.GetKey(opts.Config.Universal.CreateRebaseOptionsMenu), + Keys: opts.GetKeys(opts.Config.Universal.CreateRebaseOptionsMenu), Handler: opts.Guards.NoPopupPanel(self.c.Helpers().MergeAndRebase.CreateRebaseOptionsMenu), Description: self.c.Tr.ViewMergeRebaseOptions, Tooltip: self.c.Tr.ViewMergeRebaseOptionsTooltip, @@ -44,30 +44,30 @@ func (self *GlobalController) GetKeybindings(opts types.KeybindingsOpts) []*type GetDisabledReason: self.canShowRebaseOptions, }, { - Key: opts.GetKey(opts.Config.Universal.Refresh), + Keys: opts.GetKeys(opts.Config.Universal.Refresh), Handler: opts.Guards.NoPopupPanel(self.refresh), Description: self.c.Tr.Refresh, Tooltip: self.c.Tr.RefreshTooltip, }, { - Key: opts.GetKey(opts.Config.Universal.NextScreenMode), + Keys: opts.GetKeys(opts.Config.Universal.NextScreenMode), Handler: opts.Guards.NoPopupPanel(self.nextScreenMode), Description: self.c.Tr.NextScreenMode, }, { - Key: opts.GetKey(opts.Config.Universal.PrevScreenMode), + Keys: opts.GetKeys(opts.Config.Universal.PrevScreenMode), Handler: opts.Guards.NoPopupPanel(self.prevScreenMode), Description: self.c.Tr.PrevScreenMode, }, { - Key: opts.GetKey(opts.Config.Universal.CyclePagers), + Keys: opts.GetKeys(opts.Config.Universal.CyclePagers), Handler: opts.Guards.NoPopupPanel(self.cyclePagers), GetDisabledReason: self.canCyclePagers, Description: self.c.Tr.CyclePagers, Tooltip: self.c.Tr.CyclePagersTooltip, }, { - Key: opts.GetKey(opts.Config.Universal.Return), + Keys: opts.GetKeys(opts.Config.Universal.Return), Handler: self.escape, Description: self.c.Tr.Cancel, DescriptionFunc: self.escapeDescription, @@ -75,59 +75,41 @@ func (self *GlobalController) GetKeybindings(opts types.KeybindingsOpts) []*type DisplayOnScreen: true, }, { - ViewName: "", - Key: opts.GetKey(opts.Config.Universal.OptionMenu), - Handler: self.createOptionsMenu, - OpensMenu: true, - }, - { - ViewName: "", - Key: opts.GetKey(opts.Config.Universal.OptionMenuAlt1), - // we have the description on the alt key and not the main key for legacy reasons - // (the original main key was 'x' but we've reassigned that to other purposes) + ViewName: "", + Keys: opts.GetKeys(opts.Config.Universal.OptionMenu), Description: self.c.Tr.OpenKeybindingsMenu, - Handler: self.createOptionsMenu, ShortDescription: self.c.Tr.Keybindings, - DisplayOnScreen: true, + Handler: self.createOptionsMenu, GetDisabledReason: self.optionsMenuDisabledReason, + OpensMenu: true, + DisplayOnScreen: true, }, { ViewName: "", - Key: opts.GetKey(opts.Config.Universal.FilteringMenu), + Keys: opts.GetKeys(opts.Config.Universal.FilteringMenu), 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), + Keys: opts.GetKeys(opts.Config.Universal.DiffingMenu), 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: opts.Guards.NoPopupPanel(self.createDiffingMenu), - Description: self.c.Tr.ViewDiffingOptions, - Tooltip: self.c.Tr.ViewDiffingOptionsTooltip, - OpensMenu: true, - }, - { - Key: opts.GetKey(opts.Config.Universal.Quit), + Keys: opts.GetKeys(opts.Config.Universal.Quit), Description: self.c.Tr.Quit, Handler: self.quit, }, { - Key: opts.GetKey(opts.Config.Universal.QuitAlt1), - Handler: self.quit, - }, - { - Key: opts.GetKey(opts.Config.Universal.QuitWithoutChangingDirectory), + Keys: opts.GetKeys(opts.Config.Universal.QuitWithoutChangingDirectory), Handler: self.quitWithoutChangingDirectory, }, { - Key: opts.GetKey(opts.Config.Universal.SuspendApp), + Keys: opts.GetKeys(opts.Config.Universal.SuspendApp), Handler: self.c.Helpers().SuspendResume.SuspendApp, Description: self.c.Tr.SuspendApp, GetDisabledReason: func() *types.DisabledReason { @@ -140,7 +122,7 @@ func (self *GlobalController) GetKeybindings(opts types.KeybindingsOpts) []*type }, }, { - Key: opts.GetKey(opts.Config.Universal.ToggleWhitespaceInDiffView), + Keys: opts.GetKeys(opts.Config.Universal.ToggleWhitespaceInDiffView), Handler: self.toggleWhitespace, Description: self.c.Tr.ToggleWhitespaceInDiffView, Tooltip: self.c.Tr.ToggleWhitespaceInDiffViewTooltip, diff --git a/pkg/gui/controllers/helpers/commits_helper.go b/pkg/gui/controllers/helpers/commits_helper.go index 810861401..5e50ba7b2 100644 --- a/pkg/gui/controllers/helpers/commits_helper.go +++ b/pkg/gui/controllers/helpers/commits_helper.go @@ -228,7 +228,7 @@ func (self *CommitsHelper) OpenCommitMenu(suggestionFunc func(string) []*types.S OnPress: func() error { return self.SwitchToEditor() }, - Key: gocui.NewKeyRune('e'), + Keys: menuKey('e'), DisabledReason: disabledReasonForOpenInEditor, }, { @@ -236,14 +236,14 @@ func (self *CommitsHelper) OpenCommitMenu(suggestionFunc func(string) []*types.S OnPress: func() error { return self.addCoAuthor(suggestionFunc) }, - Key: gocui.NewKeyRune('c'), + Keys: menuKey('c'), }, { Label: self.c.Tr.PasteCommitMessageFromClipboard, OnPress: func() error { return self.pasteCommitMessageFromClipboard() }, - Key: gocui.NewKeyRune('p'), + Keys: menuKey('p'), }, } return self.c.Menu(types.CreateMenuOptions{ diff --git a/pkg/gui/controllers/helpers/menu_key.go b/pkg/gui/controllers/helpers/menu_key.go new file mode 100644 index 000000000..d4271fa5b --- /dev/null +++ b/pkg/gui/controllers/helpers/menu_key.go @@ -0,0 +1,11 @@ +package helpers + +import "github.com/jesseduffield/lazygit/pkg/gocui" + +// menuKey is a shorthand for constructing a key value for a menu item from a single rune literal, +// avoiding the noise of `[]gocui.Key{gocui.NewKeyRune('a')}` at every call site. There is an +// intentionally identical helper in the controllers package so that callers in either package can +// use the unqualified form. +func menuKey(r rune) []gocui.Key { + return []gocui.Key{gocui.NewKeyRune(r)} +} diff --git a/pkg/gui/controllers/helpers/merge_and_rebase_helper.go b/pkg/gui/controllers/helpers/merge_and_rebase_helper.go index 986f61b16..cd141c697 100644 --- a/pkg/gui/controllers/helpers/merge_and_rebase_helper.go +++ b/pkg/gui/controllers/helpers/merge_and_rebase_helper.go @@ -39,17 +39,17 @@ const ( func (self *MergeAndRebaseHelper) CreateRebaseOptionsMenu() error { type optionAndKey struct { option string - key gocui.Key + keys []gocui.Key } options := []optionAndKey{ - {option: REBASE_OPTION_CONTINUE, key: gocui.NewKeyRune('c')}, - {option: REBASE_OPTION_ABORT, key: gocui.NewKeyRune('a')}, + {option: REBASE_OPTION_CONTINUE, keys: menuKey('c')}, + {option: REBASE_OPTION_ABORT, keys: menuKey('a')}, } if self.c.Git().Status.WorkingTreeState().CanSkip() { options = append(options, optionAndKey{ - option: REBASE_OPTION_SKIP, key: gocui.NewKeyRune('s'), + option: REBASE_OPTION_SKIP, keys: menuKey('s'), }) } @@ -59,7 +59,7 @@ func (self *MergeAndRebaseHelper) CreateRebaseOptionsMenu() error { OnPress: func() error { return self.genericMergeCommand(row.option) }, - Key: row.key, + Keys: row.keys, } }) @@ -198,7 +198,7 @@ func (self *MergeAndRebaseHelper) PromptForConflictHandling() error { OnPress: func() error { return self.genericMergeCommand(REBASE_OPTION_ABORT) }, - Key: gocui.NewKeyRune('a'), + Keys: menuKey('a'), }, }, HideCancel: true, @@ -284,7 +284,7 @@ func (self *MergeAndRebaseHelper) RebaseOntoRef(ref string) error { Label: utils.ResolvePlaceholderString(self.c.Tr.SimpleRebase, map[string]string{"ref": ref}, ), - Key: gocui.NewKeyRune('s'), + Keys: menuKey('s'), DisabledReason: disabledReason, OnPress: func() error { self.c.LogAction(self.c.Tr.Actions.RebaseBranch) @@ -308,7 +308,7 @@ func (self *MergeAndRebaseHelper) RebaseOntoRef(ref string) error { Label: utils.ResolvePlaceholderString(self.c.Tr.InteractiveRebase, map[string]string{"ref": ref}, ), - Key: gocui.NewKeyRune('i'), + Keys: menuKey('i'), DisabledReason: disabledReason, Tooltip: self.c.Tr.InteractiveRebaseTooltip, OnPress: func() error { @@ -334,7 +334,7 @@ func (self *MergeAndRebaseHelper) RebaseOntoRef(ref string) error { Label: utils.ResolvePlaceholderString(self.c.Tr.RebaseOntoBaseBranch, map[string]string{"baseBranch": ShortBranchName(baseBranch)}, ), - Key: gocui.NewKeyRune('b'), + Keys: menuKey('b'), DisabledReason: baseBranchDisabledReason, Tooltip: self.c.Tr.RebaseOntoBaseBranchTooltip, OnPress: func() error { @@ -392,7 +392,7 @@ func (self *MergeAndRebaseHelper) MergeRefIntoCheckedOutBranch(refName string) e firstRegularMergeItem = &types.MenuItem{ Label: self.c.Tr.RegularMergeFastForward, OnPress: self.RegularMerge(refName, git_commands.MERGE_VARIANT_REGULAR), - Key: gocui.NewKeyRune('m'), + Keys: menuKey('m'), Tooltip: utils.ResolvePlaceholderString( self.c.Tr.RegularMergeFastForwardTooltip, map[string]string{ @@ -406,7 +406,7 @@ func (self *MergeAndRebaseHelper) MergeRefIntoCheckedOutBranch(refName string) e secondRegularMergeItem = &types.MenuItem{ Label: self.c.Tr.RegularMergeNonFastForward, OnPress: self.RegularMerge(refName, git_commands.MERGE_VARIANT_NON_FAST_FORWARD), - Key: gocui.NewKeyRune('n'), + Keys: menuKey('n'), Tooltip: utils.ResolvePlaceholderString( self.c.Tr.RegularMergeNonFastForwardTooltip, map[string]string{ @@ -419,7 +419,7 @@ func (self *MergeAndRebaseHelper) MergeRefIntoCheckedOutBranch(refName string) e firstRegularMergeItem = &types.MenuItem{ Label: self.c.Tr.RegularMergeNonFastForward, OnPress: self.RegularMerge(refName, git_commands.MERGE_VARIANT_REGULAR), - Key: gocui.NewKeyRune('m'), + Keys: menuKey('m'), Tooltip: utils.ResolvePlaceholderString( self.c.Tr.RegularMergeNonFastForwardTooltip, map[string]string{ @@ -432,7 +432,7 @@ func (self *MergeAndRebaseHelper) MergeRefIntoCheckedOutBranch(refName string) e secondRegularMergeItem = &types.MenuItem{ Label: self.c.Tr.RegularMergeFastForward, OnPress: self.RegularMerge(refName, git_commands.MERGE_VARIANT_FAST_FORWARD), - Key: gocui.NewKeyRune('f'), + Keys: menuKey('f'), Tooltip: utils.ResolvePlaceholderString( self.c.Tr.RegularMergeFastForwardTooltip, map[string]string{ @@ -464,7 +464,7 @@ func (self *MergeAndRebaseHelper) MergeRefIntoCheckedOutBranch(refName string) e { Label: self.c.Tr.SquashMergeUncommitted, OnPress: self.SquashMergeUncommitted(refName), - Key: gocui.NewKeyRune('s'), + Keys: menuKey('s'), Tooltip: utils.ResolvePlaceholderString( self.c.Tr.SquashMergeUncommittedTooltip, map[string]string{ @@ -475,7 +475,7 @@ func (self *MergeAndRebaseHelper) MergeRefIntoCheckedOutBranch(refName string) e { Label: self.c.Tr.SquashMergeCommitted, OnPress: self.SquashMergeCommitted(refName, checkedOutBranchName), - Key: gocui.NewKeyRune('S'), + Keys: menuKey('S'), Tooltip: utils.ResolvePlaceholderString( self.c.Tr.SquashMergeCommittedTooltip, map[string]string{ diff --git a/pkg/gui/controllers/helpers/refs_helper.go b/pkg/gui/controllers/helpers/refs_helper.go index 43c8c93c6..a3db043ef 100644 --- a/pkg/gui/controllers/helpers/refs_helper.go +++ b/pkg/gui/controllers/helpers/refs_helper.go @@ -216,15 +216,15 @@ func (self *RefsHelper) ResetToRef(ref string, strength string, envVars []string func (self *RefsHelper) CreateSortOrderMenu(sortOptionsOrder []string, menuPrompt string, onSelected func(sortOrder string) error, currentValue string) error { type sortMenuOption struct { - key gocui.Key + keys []gocui.Key label string description string sortOrder string } availableSortOptions := map[string]sortMenuOption{ - "recency": {label: self.c.Tr.SortByRecency, description: self.c.Tr.SortBasedOnReflog, key: gocui.NewKeyRune('r')}, - "alphabetical": {label: self.c.Tr.SortAlphabetical, description: "--sort=refname", key: gocui.NewKeyRune('a')}, - "date": {label: self.c.Tr.SortByDate, description: "--sort=-committerdate", key: gocui.NewKeyRune('d')}, + "recency": {label: self.c.Tr.SortByRecency, description: self.c.Tr.SortBasedOnReflog, keys: menuKey('r')}, + "alphabetical": {label: self.c.Tr.SortAlphabetical, description: "--sort=refname", keys: menuKey('a')}, + "date": {label: self.c.Tr.SortByDate, description: "--sort=-committerdate", keys: menuKey('d')}, } sortOptions := make([]sortMenuOption, 0, len(sortOptionsOrder)) for _, key := range sortOptionsOrder { @@ -245,7 +245,7 @@ func (self *RefsHelper) CreateSortOrderMenu(sortOptionsOrder []string, menuPromp OnPress: func() error { return onSelected(opt.sortOrder) }, - Key: opt.key, + Keys: opt.keys, Widget: types.MakeMenuRadioButton(opt.sortOrder == currentValue), } }) @@ -260,14 +260,14 @@ func (self *RefsHelper) CreateGitResetMenu(name string, ref string) error { type strengthWithKey struct { strength string label string - key gocui.Key + keys []gocui.Key tooltip string } strengths := []strengthWithKey{ // not i18'ing because it's git terminology - {strength: "mixed", label: "Mixed reset", key: gocui.NewKeyRune('m'), tooltip: self.c.Tr.ResetMixedTooltip}, - {strength: "soft", label: "Soft reset", key: gocui.NewKeyRune('s'), tooltip: self.c.Tr.ResetSoftTooltip}, - {strength: "hard", label: "Hard reset", key: gocui.NewKeyRune('h'), tooltip: self.c.Tr.ResetHardTooltip}, + {strength: "mixed", label: "Mixed reset", keys: menuKey('m'), tooltip: self.c.Tr.ResetMixedTooltip}, + {strength: "soft", label: "Soft reset", keys: menuKey('s'), tooltip: self.c.Tr.ResetSoftTooltip}, + {strength: "hard", label: "Hard reset", keys: menuKey('h'), tooltip: self.c.Tr.ResetHardTooltip}, } menuItems := lo.Map(strengths, func(row strengthWithKey, _ int) *types.MenuItem { @@ -287,7 +287,7 @@ func (self *RefsHelper) CreateGitResetMenu(name string, ref string) error { }, }) }, - Key: row.key, + Keys: row.keys, Tooltip: row.tooltip, } }) @@ -312,15 +312,15 @@ func (self *RefsHelper) CreateCheckoutMenu(commit *models.Commit) error { self.c.LogAction(self.c.Tr.Actions.CheckoutCommit) return self.CheckoutRef(hash, types.CheckoutRefOptions{}) }, - Key: gocui.NewKeyRune('d'), + Keys: menuKey('d'), }, } if len(branches) > 0 { menuItems = append(menuItems, lo.Map(branches, func(branch *models.Branch, index int) *types.MenuItem { - var key gocui.Key + var keys []gocui.Key if index < 9 { - key = gocui.NewKeyRune(rune(index + 1 + '0')) // Convert 1-based index to key + keys = menuKey(rune(index + 1 + '0')) // Convert 1-based index to key } return &types.MenuItem{ LabelColumns: []string{fmt.Sprintf(self.c.Tr.Actions.CheckoutBranchAtCommit, branch.Name)}, @@ -328,7 +328,7 @@ func (self *RefsHelper) CreateCheckoutMenu(commit *models.Commit) error { self.c.LogAction(self.c.Tr.Actions.CheckoutBranch) return self.CheckoutRef(branch.RefName(), types.CheckoutRefOptions{}) }, - Key: key, + Keys: keys, } })...) } else { @@ -336,7 +336,7 @@ func (self *RefsHelper) CreateCheckoutMenu(commit *models.Commit) error { LabelColumns: []string{self.c.Tr.Actions.CheckoutBranch}, OnPress: func() error { return nil }, DisabledReason: &types.DisabledReason{Text: self.c.Tr.NoBranchesFoundAtCommitTooltip}, - Key: gocui.NewKeyRune('1'), + Keys: menuKey('1'), }) } diff --git a/pkg/gui/controllers/helpers/tags_helper.go b/pkg/gui/controllers/helpers/tags_helper.go index 6a7e47219..650e4162c 100644 --- a/pkg/gui/controllers/helpers/tags_helper.go +++ b/pkg/gui/controllers/helpers/tags_helper.go @@ -28,8 +28,8 @@ func (self *TagsHelper) OpenCreateTagPrompt(ref string, onCreate func()) error { self.c.Tr.ForceTagPrompt, map[string]string{ "tagName": tagName, - "cancelKey": self.c.UserConfig().Keybinding.Universal.Return, - "confirmKey": self.c.UserConfig().Keybinding.Universal.Confirm, + "cancelKey": self.c.UserConfig().Keybinding.Universal.Return.String(), + "confirmKey": self.c.UserConfig().Keybinding.Universal.Confirm.String(), }, ) force := self.c.Git().Tag.HasTag(tagName) diff --git a/pkg/gui/controllers/helpers/working_tree_helper.go b/pkg/gui/controllers/helpers/working_tree_helper.go index 28aeb56f6..3ad2c54cf 100644 --- a/pkg/gui/controllers/helpers/working_tree_helper.go +++ b/pkg/gui/controllers/helpers/working_tree_helper.go @@ -10,7 +10,6 @@ import ( "github.com/jesseduffield/lazygit/pkg/commands/git_commands" "github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/config" - "github.com/jesseduffield/lazygit/pkg/gocui" "github.com/jesseduffield/lazygit/pkg/gui/context" "github.com/jesseduffield/lazygit/pkg/gui/style" "github.com/jesseduffield/lazygit/pkg/gui/types" @@ -383,7 +382,7 @@ func (self *WorkingTreeHelper) CreateMergeConflictMenu(selectedFilepaths []strin OnPress: func() error { return onMergeStrategySelected("--ours") }, - Key: gocui.NewKeyRune('c'), + Keys: menuKey('c'), }, { LabelColumns: []string{ @@ -393,7 +392,7 @@ func (self *WorkingTreeHelper) CreateMergeConflictMenu(selectedFilepaths []strin OnPress: func() error { return onMergeStrategySelected("--theirs") }, - Key: gocui.NewKeyRune('i'), + Keys: menuKey('i'), }, { LabelColumns: []string{ @@ -403,7 +402,7 @@ func (self *WorkingTreeHelper) CreateMergeConflictMenu(selectedFilepaths []strin OnPress: func() error { return onMergeStrategySelected("--union") }, - Key: gocui.NewKeyRune('b'), + Keys: menuKey('b'), }, { LabelColumns: []string{ @@ -411,7 +410,7 @@ func (self *WorkingTreeHelper) CreateMergeConflictMenu(selectedFilepaths []strin cmdColor.Sprint("git mergetool"), }, OnPress: self.OpenMergeTool, - Key: gocui.NewKeyRune('m'), + Keys: menuKey('m'), }, }, }) diff --git a/pkg/gui/controllers/jump_to_side_window_controller.go b/pkg/gui/controllers/jump_to_side_window_controller.go index 6a08e3758..2ea8ac762 100644 --- a/pkg/gui/controllers/jump_to_side_window_controller.go +++ b/pkg/gui/controllers/jump_to_side_window_controller.go @@ -39,7 +39,7 @@ func (self *JumpToSideWindowController) GetKeybindings(opts types.KeybindingsOpt return &types.Binding{ ViewName: "", // by default the keys are 1, 2, 3, etc - Key: opts.GetKey(opts.Config.Universal.JumpToBlock[index]), + Keys: opts.GetKeys(opts.Config.Universal.JumpToBlock[index]), Handler: opts.Guards.NoPopupPanel(self.goToSideWindow(window)), } }) diff --git a/pkg/gui/controllers/list_controller.go b/pkg/gui/controllers/list_controller.go index dba08552c..b2d45679b 100644 --- a/pkg/gui/controllers/list_controller.go +++ b/pkg/gui/controllers/list_controller.go @@ -271,26 +271,22 @@ func (self *ListController) isFocused() bool { func (self *ListController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { bindings := []*types.Binding{ - {Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.PrevItemAlt), Handler: self.HandlePrevLine}, - {Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.PrevItem), Handler: self.HandlePrevLine}, - {Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.NextItemAlt), Handler: self.HandleNextLine}, - {Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.NextItem), Handler: self.HandleNextLine}, - {Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.PrevPage), Handler: self.HandlePrevPage, Description: self.c.Tr.PrevPage}, - {Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.NextPage), Handler: self.HandleNextPage, Description: self.c.Tr.NextPage}, - {Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.GotoTop), Handler: self.HandleGotoTop, Description: self.c.Tr.GotoTop, Alternative: ""}, - {Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.GotoBottom), Handler: self.HandleGotoBottom, Description: self.c.Tr.GotoBottom, Alternative: ""}, - {Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.GotoTopAlt), Handler: self.HandleGotoTop}, - {Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.GotoBottomAlt), Handler: self.HandleGotoBottom}, - {Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.ScrollLeft), Handler: self.HandleScrollLeft}, - {Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.ScrollRight), Handler: self.HandleScrollRight}, + {Tag: "navigation", Keys: opts.GetKeys(opts.Config.Universal.PrevItem), Handler: self.HandlePrevLine}, + {Tag: "navigation", Keys: opts.GetKeys(opts.Config.Universal.NextItem), Handler: self.HandleNextLine}, + {Tag: "navigation", Keys: opts.GetKeys(opts.Config.Universal.PrevPage), Handler: self.HandlePrevPage, Description: self.c.Tr.PrevPage}, + {Tag: "navigation", Keys: opts.GetKeys(opts.Config.Universal.NextPage), Handler: self.HandleNextPage, Description: self.c.Tr.NextPage}, + {Tag: "navigation", Keys: opts.GetKeys(opts.Config.Universal.GotoTop), Handler: self.HandleGotoTop, Description: self.c.Tr.GotoTop}, + {Tag: "navigation", Keys: opts.GetKeys(opts.Config.Universal.GotoBottom), Handler: self.HandleGotoBottom, Description: self.c.Tr.GotoBottom}, + {Tag: "navigation", Keys: opts.GetKeys(opts.Config.Universal.ScrollLeft), Handler: self.HandleScrollLeft}, + {Tag: "navigation", Keys: opts.GetKeys(opts.Config.Universal.ScrollRight), Handler: self.HandleScrollRight}, } if self.context.RangeSelectEnabled() { bindings = append(bindings, []*types.Binding{ - {Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.ToggleRangeSelect), Handler: self.HandleToggleRangeSelect, Description: self.c.Tr.ToggleRangeSelect}, - {Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.RangeSelectDown), Handler: self.HandleRangeSelectDown, Description: self.c.Tr.RangeSelectDown}, - {Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.RangeSelectUp), Handler: self.HandleRangeSelectUp, Description: self.c.Tr.RangeSelectUp}, + {Tag: "navigation", Keys: opts.GetKeys(opts.Config.Universal.ToggleRangeSelect), Handler: self.HandleToggleRangeSelect, Description: self.c.Tr.ToggleRangeSelect}, + {Tag: "navigation", Keys: opts.GetKeys(opts.Config.Universal.RangeSelectDown), Handler: self.HandleRangeSelectDown, Description: self.c.Tr.RangeSelectDown}, + {Tag: "navigation", Keys: opts.GetKeys(opts.Config.Universal.RangeSelectUp), Handler: self.HandleRangeSelectUp, Description: self.c.Tr.RangeSelectUp}, }..., ) } diff --git a/pkg/gui/controllers/local_commits_controller.go b/pkg/gui/controllers/local_commits_controller.go index 31c746750..4ab436bbc 100644 --- a/pkg/gui/controllers/local_commits_controller.go +++ b/pkg/gui/controllers/local_commits_controller.go @@ -56,7 +56,7 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [ bindings := []*types.Binding{ { - Key: opts.GetKey(opts.Config.Commits.SquashDown), + Keys: opts.GetKeys(opts.Config.Commits.SquashDown), Handler: opts.Guards.OutsideFilterMode(self.withItemsRange(self.squashDown)), GetDisabledReason: self.require( self.itemRangeSelected( @@ -69,7 +69,7 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [ DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Commits.MarkCommitAsFixup), + Keys: opts.GetKeys(opts.Config.Commits.MarkCommitAsFixup), Handler: opts.Guards.OutsideFilterMode(self.withItemsRange(self.fixup)), GetDisabledReason: self.require( self.itemRangeSelected( @@ -82,7 +82,7 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [ DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Commits.SetFixupMessage), + Keys: opts.GetKeys(opts.Config.Commits.SetFixupMessage), Handler: self.withItem(self.setFixupMessage), GetDisabledReason: self.require( self.singleItemSelected(self.canSetFixupMessage), @@ -91,7 +91,7 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [ Tooltip: self.c.Tr.SetFixupMessageTooltip, }, { - Key: opts.GetKey(opts.Config.Commits.RenameCommit), + Keys: opts.GetKeys(opts.Config.Commits.RenameCommit), Handler: self.withItem(self.reword), GetDisabledReason: self.require( self.singleItemSelected(self.rewordEnabled), @@ -102,7 +102,7 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [ OpensMenu: true, }, { - Key: opts.GetKey(opts.Config.Commits.RenameCommitWithEditor), + Keys: opts.GetKeys(opts.Config.Commits.RenameCommitWithEditor), Handler: self.withItem(self.rewordEditor), GetDisabledReason: self.require( self.singleItemSelected(self.rewordEnabled), @@ -110,7 +110,7 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [ Description: self.c.Tr.RewordCommitEditor, }, { - Key: opts.GetKey(opts.Config.Universal.Remove), + Keys: opts.GetKeys(opts.Config.Universal.Remove), Handler: self.withItemsRange(self.drop), GetDisabledReason: self.require( self.itemRangeSelected( @@ -122,7 +122,7 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [ DisplayOnScreen: true, }, { - Key: opts.GetKey(editCommitKey), + Keys: opts.GetKeys(editCommitKey), Handler: opts.Guards.OutsideFilterMode(self.withItemsRange(self.edit)), GetDisabledReason: self.require( self.itemRangeSelected(self.midRebaseCommandEnabled), @@ -136,16 +136,16 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [ // The user-facing description here is 'Start interactive rebase' but internally // we're calling it 'quick-start interactive rebase' to differentiate it from // when you manually select the base commit. - Key: opts.GetKey(opts.Config.Commits.StartInteractiveRebase), + Keys: opts.GetKeys(opts.Config.Commits.StartInteractiveRebase), Handler: opts.Guards.OutsideFilterMode(self.quickStartInteractiveRebase), GetDisabledReason: self.require(self.notMidRebase(self.c.Tr.AlreadyRebasing), self.canFindCommitForQuickStart), Description: self.c.Tr.QuickStartInteractiveRebase, Tooltip: utils.ResolvePlaceholderString(self.c.Tr.QuickStartInteractiveRebaseTooltip, map[string]string{ - "editKey": editCommitKey, + "editKey": editCommitKey.String(), }), }, { - Key: opts.GetKey(opts.Config.Commits.PickCommit), + Keys: opts.GetKeys(opts.Config.Commits.PickCommit), Handler: opts.Guards.OutsideFilterMode(self.withItems(self.pick)), GetDisabledReason: self.require( self.itemRangeSelected(self.pickEnabled), @@ -154,19 +154,19 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [ Tooltip: self.c.Tr.PickCommitTooltip, }, { - Key: opts.GetKey(opts.Config.Commits.CreateFixupCommit), + Keys: opts.GetKeys(opts.Config.Commits.CreateFixupCommit), Handler: opts.Guards.OutsideFilterMode(self.withItem(self.createFixupCommit)), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.CreateFixupCommit, Tooltip: utils.ResolvePlaceholderString( self.c.Tr.CreateFixupCommitTooltip, map[string]string{ - "squashAbove": opts.Config.Commits.SquashAboveCommits, + "squashAbove": opts.Config.Commits.SquashAboveCommits.String(), }, ), }, { - Key: opts.GetKey(opts.Config.Commits.SquashAboveCommits), + Keys: opts.GetKeys(opts.Config.Commits.SquashAboveCommits), Handler: opts.Guards.OutsideFilterMode(self.squashFixupCommits), GetDisabledReason: self.require( self.notMidRebase(self.c.Tr.AlreadyRebasing), @@ -176,7 +176,7 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [ OpensMenu: true, }, { - Key: opts.GetKey(opts.Config.Commits.MoveDownCommit), + Keys: opts.GetKeys(opts.Config.Commits.MoveDownCommit), Handler: opts.Guards.OutsideFilterMode(self.withItemsRange(self.moveDown)), GetDisabledReason: self.require(self.itemRangeSelected( self.midRebaseMoveCommandEnabled, @@ -185,7 +185,7 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [ Description: self.c.Tr.MoveDownCommit, }, { - Key: opts.GetKey(opts.Config.Commits.MoveUpCommit), + Keys: opts.GetKeys(opts.Config.Commits.MoveUpCommit), Handler: opts.Guards.OutsideFilterMode(self.withItemsRange(self.moveUp)), GetDisabledReason: self.require(self.itemRangeSelected( self.midRebaseMoveCommandEnabled, @@ -194,14 +194,14 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [ Description: self.c.Tr.MoveUpCommit, }, { - Key: opts.GetKey(opts.Config.Commits.PasteCommits), + Keys: opts.GetKeys(opts.Config.Commits.PasteCommits), Handler: opts.Guards.OutsideFilterMode(self.paste), GetDisabledReason: self.require(self.canPaste), Description: self.c.Tr.PasteCommits, DisplayStyle: &style.FgCyan, }, { - Key: opts.GetKey(opts.Config.Commits.MarkCommitAsBaseForRebase), + Keys: opts.GetKeys(opts.Config.Commits.MarkCommitAsBaseForRebase), Handler: opts.Guards.OutsideFilterMode(self.withItem(self.markAsBaseCommit)), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.MarkAsBaseCommit, @@ -210,13 +210,13 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [ // overriding this navigation keybinding because we might need to load // more commits on demand { - Key: opts.GetKey(opts.Config.Universal.StartSearch), + Keys: opts.GetKeys(opts.Config.Universal.StartSearch), Handler: self.openSearch, Description: self.c.Tr.StartSearch, Tag: "navigation", }, { - Key: opts.GetKey(opts.Config.Commits.AmendToCommit), + Keys: opts.GetKeys(opts.Config.Commits.AmendToCommit), Handler: self.withItem(self.amendTo), GetDisabledReason: self.require(self.singleItemSelected(self.canAmend)), Description: self.c.Tr.Amend, @@ -224,7 +224,7 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [ DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Commits.ResetCommitAuthor), + Keys: opts.GetKeys(opts.Config.Commits.ResetCommitAuthor), Handler: self.withItemsRange(self.amendAttribute), GetDisabledReason: self.require(self.itemRangeSelected(self.canAmendRange)), Description: self.c.Tr.AmendCommitAttribute, @@ -232,28 +232,28 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [ OpensMenu: true, }, { - Key: opts.GetKey(opts.Config.Commits.RevertCommit), + Keys: opts.GetKeys(opts.Config.Commits.RevertCommit), Handler: self.withItemsRange(self.revert), GetDisabledReason: self.require(self.itemRangeSelected()), Description: self.c.Tr.Revert, Tooltip: self.c.Tr.RevertCommitTooltip, }, { - Key: opts.GetKey(opts.Config.Commits.CreateTag), + Keys: opts.GetKeys(opts.Config.Commits.CreateTag), Handler: self.withItem(self.createTag), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.TagCommit, Tooltip: self.c.Tr.TagCommitTooltip, }, { - Key: opts.GetKey(opts.Config.Commits.OpenLogMenu), + Keys: opts.GetKeys(opts.Config.Commits.OpenLogMenu), Handler: self.handleOpenLogMenu, Description: self.c.Tr.OpenLogMenu, Tooltip: self.c.Tr.OpenLogMenuTooltip, OpensMenu: true, }, { - Key: opts.GetKey(opts.Config.Commits.OpenPullRequestInBrowser), + Keys: opts.GetKeys(opts.Config.Commits.OpenPullRequestInBrowser), Handler: self.openPRInBrowser, GetDisabledReason: self.checkedOutBranchHasPR, Description: self.c.Tr.OpenPullRequestInBrowser, @@ -361,7 +361,7 @@ func (self *LocalCommitsController) fixup(selectedCommits []*models.Commit, star Items: []*types.MenuItem{ { Label: self.c.Tr.Fixup, - Key: gocui.NewKeyRune('f'), + Keys: menuKey('f'), OnPress: func() error { return self.c.WithWaitingStatus(self.c.Tr.FixingStatus, func(gocui.Task) error { self.c.LogAction(self.c.Tr.Actions.FixupCommit) @@ -372,7 +372,7 @@ func (self *LocalCommitsController) fixup(selectedCommits []*models.Commit, star }, { Label: self.c.Tr.FixupKeepMessage, - Key: gocui.NewKeyRune('c'), + Keys: menuKey('c'), OnPress: func() error { return self.c.WithWaitingStatus(self.c.Tr.FixingStatus, func(gocui.Task) error { self.c.LogAction(self.c.Tr.Actions.FixupCommitKeepMessage) @@ -403,7 +403,7 @@ func (self *LocalCommitsController) setFixupMessage(commit *models.Commit) error Items: []*types.MenuItem{ { Label: self.c.Tr.FixupDiscardMessage, - Key: gocui.NewKeyRune('f'), + Keys: menuKey('f'), OnPress: func() error { return self.updateTodosWithFlag(todo.Fixup, []*models.Commit{commit}, "") }, @@ -411,7 +411,7 @@ func (self *LocalCommitsController) setFixupMessage(commit *models.Commit) error }, { Label: self.c.Tr.FixupKeepMessage, - Key: gocui.NewKeyRune('c'), + Keys: menuKey('c'), OnPress: func() error { return self.updateTodosWithFlag(todo.Fixup, []*models.Commit{commit}, "-C") }, @@ -679,7 +679,7 @@ func (self *LocalCommitsController) findCommitForQuickStartInteractiveRebase() ( if !ok || index == 0 { errorMsg := utils.ResolvePlaceholderString(self.c.Tr.CannotQuickStartInteractiveRebase, map[string]string{ - "editKey": self.c.UserConfig().Keybinding.Universal.Edit, + "editKey": self.c.UserConfig().Keybinding.Universal.Edit.String(), }) return nil, errors.New(errorMsg) @@ -864,19 +864,19 @@ func (self *LocalCommitsController) amendAttribute(commits []*models.Commit, sta { Label: self.c.Tr.ResetAuthor, OnPress: func() error { return self.resetAuthor(start, end) }, - Key: opts.GetKey(opts.Config.AmendAttribute.ResetAuthor), + Keys: opts.GetKeys(opts.Config.AmendAttribute.ResetAuthor), Tooltip: self.c.Tr.ResetAuthorTooltip, }, { Label: self.c.Tr.SetAuthor, OnPress: func() error { return self.setAuthor(start, end) }, - Key: opts.GetKey(opts.Config.AmendAttribute.SetAuthor), + Keys: opts.GetKeys(opts.Config.AmendAttribute.SetAuthor), Tooltip: self.c.Tr.SetAuthorTooltip, }, { Label: self.c.Tr.AddCoAuthor, OnPress: func() error { return self.addCoAuthor(start, end) }, - Key: opts.GetKey(opts.Config.AmendAttribute.AddCoAuthor), + Keys: opts.GetKeys(opts.Config.AmendAttribute.AddCoAuthor), Tooltip: self.c.Tr.AddCoAuthorTooltip, }, }, @@ -1000,7 +1000,7 @@ func (self *LocalCommitsController) createFixupCommit(commit *models.Commit) err Items: []*types.MenuItem{ { Label: self.c.Tr.FixupMenu_Fixup, - Key: gocui.NewKeyRune('f'), + Keys: menuKey('f'), OnPress: func() error { return self.c.Helpers().WorkingTree.WithEnsureCommittableFiles(func() error { self.c.LogAction(self.c.Tr.Actions.CreateFixupCommit) @@ -1024,7 +1024,7 @@ func (self *LocalCommitsController) createFixupCommit(commit *models.Commit) err }, { Label: self.c.Tr.FixupMenu_AmendWithChanges, - Key: gocui.NewKeyRune('a'), + Keys: menuKey('a'), OnPress: func() error { return self.c.Helpers().WorkingTree.WithEnsureCommittableFiles(func() error { return self.createAmendCommit(commit, true) @@ -1035,7 +1035,7 @@ func (self *LocalCommitsController) createFixupCommit(commit *models.Commit) err }, { Label: self.c.Tr.FixupMenu_AmendWithoutChanges, - Key: gocui.NewKeyRune('r'), + Keys: menuKey('r'), OnPress: func() error { return self.createAmendCommit(commit, false) }, Tooltip: self.c.Tr.FixupMenu_AmendWithoutChangesTooltip, }, @@ -1134,14 +1134,14 @@ func (self *LocalCommitsController) squashFixupCommits() error { Label: self.c.Tr.SquashCommitsInCurrentBranch, OnPress: self.squashAllFixupsInCurrentBranch, DisabledReason: self.canFindCommitForSquashFixupsInCurrentBranch(), - Key: gocui.NewKeyRune('b'), + Keys: menuKey('b'), Tooltip: self.c.Tr.SquashCommitsInCurrentBranchTooltip, }, { Label: self.c.Tr.SquashCommitsAboveSelectedCommit, OnPress: self.withItem(self.squashAllFixupsAboveSelectedCommit), DisabledReason: self.singleItemSelected()(), - Key: gocui.NewKeyRune('a'), + Keys: menuKey('a'), Tooltip: self.c.Tr.SquashCommitsAboveSelectedTooltip, }, }, diff --git a/pkg/gui/controllers/main_view_controller.go b/pkg/gui/controllers/main_view_controller.go index 58d88485a..6eb6c86e3 100644 --- a/pkg/gui/controllers/main_view_controller.go +++ b/pkg/gui/controllers/main_view_controller.go @@ -32,21 +32,21 @@ func NewMainViewController( func (self *MainViewController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { return []*types.Binding{ { - Key: opts.GetKey(opts.Config.Universal.TogglePanel), + Keys: opts.GetKeys(opts.Config.Universal.TogglePanel), Handler: self.togglePanel, Description: self.c.Tr.ToggleStagingView, Tooltip: self.c.Tr.ToggleStagingViewTooltip, DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.Return), + Keys: opts.GetKeys(opts.Config.Universal.Return), Handler: self.escape, Description: self.c.Tr.ExitFocusedMainView, DisplayOnScreen: true, }, { // overriding this because we want to read all of the task's output before we start searching - Key: opts.GetKey(opts.Config.Universal.StartSearch), + Keys: opts.GetKeys(opts.Config.Universal.StartSearch), Handler: self.openSearch, Description: self.c.Tr.StartSearch, Tag: "navigation", diff --git a/pkg/gui/controllers/menu_controller.go b/pkg/gui/controllers/menu_controller.go index a2c77e457..283c2bbdf 100644 --- a/pkg/gui/controllers/menu_controller.go +++ b/pkg/gui/controllers/menu_controller.go @@ -33,19 +33,19 @@ func NewMenuController( func (self *MenuController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { bindings := []*types.Binding{ { - Key: opts.GetKey(opts.Config.Universal.Select), + Keys: opts.GetKeys(opts.Config.Universal.Select), Handler: self.withItem(self.press), GetDisabledReason: self.require(self.singleItemSelected()), }, { - Key: opts.GetKey(opts.Config.Universal.ConfirmMenu), + Keys: opts.GetKeys(opts.Config.Universal.ConfirmMenu), Handler: self.withItem(self.press), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.Execute, DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.Return), + Keys: opts.GetKeys(opts.Config.Universal.Return), Handler: self.close, Description: self.c.Tr.CloseCancel, DisplayOnScreen: true, diff --git a/pkg/gui/controllers/menu_key.go b/pkg/gui/controllers/menu_key.go new file mode 100644 index 000000000..95993801b --- /dev/null +++ b/pkg/gui/controllers/menu_key.go @@ -0,0 +1,11 @@ +package controllers + +import "github.com/jesseduffield/lazygit/pkg/gocui" + +// menuKey is a shorthand for constructing a key value for a menu item from a single rune literal, +// avoiding the noise of `[]gocui.Key{gocui.NewKeyRune('a')}` at every call site. There is an +// intentionally identical helper in the helpers package so that callers in either package can use +// the unqualified form. +func menuKey(r rune) []gocui.Key { + return []gocui.Key{gocui.NewKeyRune(r)} +} diff --git a/pkg/gui/controllers/merge_conflicts_controller.go b/pkg/gui/controllers/merge_conflicts_controller.go index 1de192928..e1e3f8e2c 100644 --- a/pkg/gui/controllers/merge_conflicts_controller.go +++ b/pkg/gui/controllers/merge_conflicts_controller.go @@ -28,91 +28,75 @@ func NewMergeConflictsController( func (self *MergeConflictsController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { bindings := []*types.Binding{ { - Key: opts.GetKey(opts.Config.Universal.Select), + Keys: opts.GetKeys(opts.Config.Universal.Select), Handler: self.withRenderAndFocus(self.HandlePickHunk), Description: self.c.Tr.PickHunk, DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Main.PickBothHunks), + Keys: opts.GetKeys(opts.Config.Main.PickBothHunks), Handler: self.withRenderAndFocus(self.HandlePickAllHunks), Description: self.c.Tr.PickAllHunks, DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.PrevItem), + Keys: opts.GetKeys(opts.Config.Universal.PrevItem), Handler: self.withRenderAndFocus(self.PrevConflictHunk), Description: self.c.Tr.SelectPrevHunk, DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.NextItem), + Keys: opts.GetKeys(opts.Config.Universal.NextItem), Handler: self.withRenderAndFocus(self.NextConflictHunk), Description: self.c.Tr.SelectNextHunk, DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.PrevBlock), + Keys: opts.GetKeys(opts.Config.Main.PrevHunk), Handler: self.withRenderAndFocus(self.PrevConflict), Description: self.c.Tr.PrevConflict, DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.NextBlock), + Keys: opts.GetKeys(opts.Config.Main.NextHunk), Handler: self.withRenderAndFocus(self.NextConflict), Description: self.c.Tr.NextConflict, DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.Undo), + Keys: opts.GetKeys(opts.Config.Universal.Undo), Handler: self.withRenderAndFocus(self.HandleUndo), Description: self.c.Tr.Undo, Tooltip: self.c.Tr.UndoMergeResolveTooltip, DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.Edit), + Keys: opts.GetKeys(opts.Config.Universal.Edit), Handler: self.HandleEditFile, Description: self.c.Tr.EditFile, Tooltip: self.c.Tr.EditFileTooltip, DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.OpenFile), + Keys: opts.GetKeys(opts.Config.Universal.OpenFile), Handler: self.HandleOpenFile, Description: self.c.Tr.OpenFile, Tooltip: self.c.Tr.OpenFileTooltip, }, { - Key: opts.GetKey(opts.Config.Universal.PrevBlockAlt), - Handler: self.withRenderAndFocus(self.PrevConflict), - }, - { - Key: opts.GetKey(opts.Config.Universal.NextBlockAlt), - Handler: self.withRenderAndFocus(self.NextConflict), - }, - { - Key: opts.GetKey(opts.Config.Universal.PrevItemAlt), - Handler: self.withRenderAndFocus(self.PrevConflictHunk), - }, - { - Key: opts.GetKey(opts.Config.Universal.NextItemAlt), - Handler: self.withRenderAndFocus(self.NextConflictHunk), - }, - { - Key: opts.GetKey(opts.Config.Universal.ScrollLeft), + Keys: opts.GetKeys(opts.Config.Universal.ScrollLeft), Handler: self.withRenderAndFocus(self.HandleScrollLeft), Description: self.c.Tr.ScrollLeft, Tag: "navigation", }, { - Key: opts.GetKey(opts.Config.Universal.ScrollRight), + Keys: opts.GetKeys(opts.Config.Universal.ScrollRight), Handler: self.withRenderAndFocus(self.HandleScrollRight), Description: self.c.Tr.ScrollRight, Tag: "navigation", }, { - Key: opts.GetKey(opts.Config.Files.OpenMergeOptions), + Keys: opts.GetKeys(opts.Config.Files.OpenMergeOptions), Handler: self.openMergeConflictMenu, Description: self.c.Tr.ViewMergeConflictOptions, Tooltip: self.c.Tr.ViewMergeConflictOptionsTooltip, @@ -120,7 +104,7 @@ func (self *MergeConflictsController) GetKeybindings(opts types.KeybindingsOpts) DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.Return), + Keys: opts.GetKeys(opts.Config.Universal.Return), Handler: self.Escape, Description: self.c.Tr.ReturnToFilesPanel, }, diff --git a/pkg/gui/controllers/options_menu_action.go b/pkg/gui/controllers/options_menu_action.go index e711f9df6..c92fdd589 100644 --- a/pkg/gui/controllers/options_menu_action.go +++ b/pkg/gui/controllers/options_menu_action.go @@ -1,6 +1,10 @@ package controllers import ( + "strings" + + "github.com/jesseduffield/lazygit/pkg/config" + "github.com/jesseduffield/lazygit/pkg/gocui" "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/utils" "github.com/samber/lo" @@ -23,6 +27,14 @@ func (self *OptionsMenuAction) Call() error { if binding.GetDisabledReason != nil { disabledReason = binding.GetDisabledReason() } + tooltip := binding.Tooltip + if len(binding.Keys) > 1 { + if tooltip != "" { + tooltip += "\n\n" + } + keyLabels := lo.Map(binding.Keys, func(k gocui.Key, _ int) string { return config.LabelForKey(k) }) + tooltip += self.c.Tr.KeybindingsTooltip + strings.Join(keyLabels, ", ") + } return &types.MenuItem{ OpensMenu: binding.OpensMenu, Label: binding.GetDescription(), @@ -33,8 +45,8 @@ func (self *OptionsMenuAction) Call() error { return self.c.IGuiCommon.CallKeybindingHandler(binding) }, - Key: binding.Key, - Tooltip: binding.Tooltip, + Keys: binding.Keys, + Tooltip: tooltip, DisabledReason: disabledReason, Section: section, } diff --git a/pkg/gui/controllers/patch_building_controller.go b/pkg/gui/controllers/patch_building_controller.go index 3557a3879..dd8c89fff 100644 --- a/pkg/gui/controllers/patch_building_controller.go +++ b/pkg/gui/controllers/patch_building_controller.go @@ -27,25 +27,25 @@ func NewPatchBuildingController( func (self *PatchBuildingController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { return []*types.Binding{ { - Key: opts.GetKey(opts.Config.Universal.OpenFile), + Keys: opts.GetKeys(opts.Config.Universal.OpenFile), Handler: self.OpenFile, Description: self.c.Tr.OpenFile, Tooltip: self.c.Tr.OpenFileTooltip, }, { - Key: opts.GetKey(opts.Config.Universal.Edit), + Keys: opts.GetKeys(opts.Config.Universal.Edit), Handler: self.EditFile, Description: self.c.Tr.EditFile, Tooltip: self.c.Tr.EditFileTooltip, }, { - Key: opts.GetKey(opts.Config.Universal.Select), + Keys: opts.GetKeys(opts.Config.Universal.Select), Handler: self.ToggleSelectionAndRefresh, Description: self.c.Tr.ToggleSelectionForPatch, DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.Remove), + Keys: opts.GetKeys(opts.Config.Universal.Remove), Handler: self.discardSelection, GetDisabledReason: self.getDisabledReasonForDiscard, Description: self.c.Tr.RemoveSelectionFromPatch, @@ -53,7 +53,7 @@ func (self *PatchBuildingController) GetKeybindings(opts types.KeybindingsOpts) DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.Return), + Keys: opts.GetKeys(opts.Config.Universal.Return), Handler: self.Escape, Description: self.c.Tr.ExitCustomPatchBuilder, DescriptionFunc: self.EscapeDescription, diff --git a/pkg/gui/controllers/patch_explorer_controller.go b/pkg/gui/controllers/patch_explorer_controller.go index 9c0ef078f..aa5fd54bb 100644 --- a/pkg/gui/controllers/patch_explorer_controller.go +++ b/pkg/gui/controllers/patch_explorer_controller.go @@ -41,61 +41,43 @@ func (self *PatchExplorerController) GetKeybindings(opts types.KeybindingsOpts) return []*types.Binding{ { Tag: "navigation", - Key: opts.GetKey(opts.Config.Universal.PrevItemAlt), + Keys: opts.GetKeys(opts.Config.Universal.PrevItem), Handler: self.withRenderAndFocus(self.HandlePrevLine), }, { Tag: "navigation", - Key: opts.GetKey(opts.Config.Universal.PrevItem), - Handler: self.withRenderAndFocus(self.HandlePrevLine), - }, - { - Tag: "navigation", - Key: opts.GetKey(opts.Config.Universal.NextItemAlt), - Handler: self.withRenderAndFocus(self.HandleNextLine), - }, - { - Tag: "navigation", - Key: opts.GetKey(opts.Config.Universal.NextItem), + Keys: opts.GetKeys(opts.Config.Universal.NextItem), Handler: self.withRenderAndFocus(self.HandleNextLine), }, { Tag: "navigation", - Key: opts.GetKey(opts.Config.Universal.RangeSelectUp), + Keys: opts.GetKeys(opts.Config.Universal.RangeSelectUp), Handler: self.withRenderAndFocus(self.HandlePrevLineRange), Description: self.c.Tr.RangeSelectUp, }, { Tag: "navigation", - Key: opts.GetKey(opts.Config.Universal.RangeSelectDown), + Keys: opts.GetKeys(opts.Config.Universal.RangeSelectDown), Handler: self.withRenderAndFocus(self.HandleNextLineRange), Description: self.c.Tr.RangeSelectDown, }, { - Key: opts.GetKey(opts.Config.Universal.PrevBlock), + Keys: opts.GetKeys(opts.Config.Main.PrevHunk), Handler: self.withRenderAndFocus(self.HandlePrevHunk), Description: self.c.Tr.PrevHunk, }, { - Key: opts.GetKey(opts.Config.Universal.PrevBlockAlt), - Handler: self.withRenderAndFocus(self.HandlePrevHunk), - }, - { - Key: opts.GetKey(opts.Config.Universal.NextBlock), + Keys: opts.GetKeys(opts.Config.Main.NextHunk), Handler: self.withRenderAndFocus(self.HandleNextHunk), Description: self.c.Tr.NextHunk, }, { - Key: opts.GetKey(opts.Config.Universal.NextBlockAlt), - Handler: self.withRenderAndFocus(self.HandleNextHunk), - }, - { - Key: opts.GetKey(opts.Config.Universal.ToggleRangeSelect), + Keys: opts.GetKeys(opts.Config.Universal.ToggleRangeSelect), Handler: self.withRenderAndFocus(self.HandleToggleSelectRange), Description: self.c.Tr.ToggleRangeSelect, }, { - Key: opts.GetKey(opts.Config.Main.ToggleSelectHunk), + Keys: opts.GetKeys(opts.Config.Main.ToggleSelectHunk), Handler: self.withRenderAndFocus(self.HandleToggleSelectHunk), Description: self.c.Tr.ToggleSelectHunk, DescriptionFunc: func() string { @@ -109,50 +91,40 @@ func (self *PatchExplorerController) GetKeybindings(opts types.KeybindingsOpts) }, { Tag: "navigation", - Key: opts.GetKey(opts.Config.Universal.PrevPage), + Keys: opts.GetKeys(opts.Config.Universal.PrevPage), Handler: self.withRenderAndFocus(self.HandlePrevPage), Description: self.c.Tr.PrevPage, }, { Tag: "navigation", - Key: opts.GetKey(opts.Config.Universal.NextPage), + Keys: opts.GetKeys(opts.Config.Universal.NextPage), Handler: self.withRenderAndFocus(self.HandleNextPage), Description: self.c.Tr.NextPage, }, { Tag: "navigation", - Key: opts.GetKey(opts.Config.Universal.GotoTop), + Keys: opts.GetKeys(opts.Config.Universal.GotoTop), Handler: self.withRenderAndFocus(self.HandleGotoTop), Description: self.c.Tr.GotoTop, }, { Tag: "navigation", - Key: opts.GetKey(opts.Config.Universal.GotoBottom), + Keys: opts.GetKeys(opts.Config.Universal.GotoBottom), Description: self.c.Tr.GotoBottom, Handler: self.withRenderAndFocus(self.HandleGotoBottom), }, { Tag: "navigation", - Key: opts.GetKey(opts.Config.Universal.GotoTopAlt), - Handler: self.withRenderAndFocus(self.HandleGotoTop), - }, - { - Tag: "navigation", - Key: opts.GetKey(opts.Config.Universal.GotoBottomAlt), - Handler: self.withRenderAndFocus(self.HandleGotoBottom), - }, - { - Tag: "navigation", - Key: opts.GetKey(opts.Config.Universal.ScrollLeft), + Keys: opts.GetKeys(opts.Config.Universal.ScrollLeft), Handler: self.withRenderAndFocus(self.HandleScrollLeft), }, { Tag: "navigation", - Key: opts.GetKey(opts.Config.Universal.ScrollRight), + Keys: opts.GetKeys(opts.Config.Universal.ScrollRight), Handler: self.withRenderAndFocus(self.HandleScrollRight), }, { - Key: opts.GetKey(opts.Config.Universal.CopyToClipboard), + Keys: opts.GetKeys(opts.Config.Universal.CopyToClipboard), Handler: self.withLock(self.CopySelectedToClipboard), Description: self.c.Tr.CopySelectedTextToClipboard, }, diff --git a/pkg/gui/controllers/prompt_controller.go b/pkg/gui/controllers/prompt_controller.go index 1ff40a0ef..abcf73ef5 100644 --- a/pkg/gui/controllers/prompt_controller.go +++ b/pkg/gui/controllers/prompt_controller.go @@ -27,19 +27,19 @@ func NewPromptController( func (self *PromptController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { bindings := []*types.Binding{ { - Key: gocui.NewKeyName(gocui.KeyEnter), + Keys: []gocui.Key{gocui.NewKeyName(gocui.KeyEnter)}, Handler: func() error { return self.context().State.OnConfirm() }, Description: self.c.Tr.Confirm, DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.Return), + Keys: opts.GetKeys(opts.Config.Universal.Return), Handler: func() error { return self.context().State.OnClose() }, Description: self.c.Tr.CloseCancel, DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.TogglePanel), + Keys: opts.GetKeys(opts.Config.Universal.TogglePanel), Handler: func() error { if len(self.c.Contexts().Suggestions.State.Suggestions) > 0 { self.switchToSuggestions() diff --git a/pkg/gui/controllers/remote_branches_controller.go b/pkg/gui/controllers/remote_branches_controller.go index 3a0350477..3a49c0114 100644 --- a/pkg/gui/controllers/remote_branches_controller.go +++ b/pkg/gui/controllers/remote_branches_controller.go @@ -35,7 +35,7 @@ func NewRemoteBranchesController( func (self *RemoteBranchesController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { return []*types.Binding{ { - Key: opts.GetKey(opts.Config.Universal.Select), + Keys: opts.GetKeys(opts.Config.Universal.Select), Handler: self.withItem(self.checkoutBranch), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.Checkout, @@ -43,13 +43,13 @@ func (self *RemoteBranchesController) GetKeybindings(opts types.KeybindingsOpts) DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.New), + Keys: opts.GetKeys(opts.Config.Universal.New), Handler: self.withItem(self.newLocalBranch), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.NewBranch, }, { - Key: opts.GetKey(opts.Config.Branches.MergeIntoCurrentBranch), + Keys: opts.GetKeys(opts.Config.Branches.MergeIntoCurrentBranch), Handler: opts.Guards.OutsideFilterMode(self.withItem(self.merge)), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.Merge, @@ -57,7 +57,7 @@ func (self *RemoteBranchesController) GetKeybindings(opts types.KeybindingsOpts) DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Branches.RebaseBranch), + Keys: opts.GetKeys(opts.Config.Branches.RebaseBranch), Handler: opts.Guards.OutsideFilterMode(self.withItem(self.rebase)), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.RebaseBranch, @@ -65,7 +65,7 @@ func (self *RemoteBranchesController) GetKeybindings(opts types.KeybindingsOpts) DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.Remove), + Keys: opts.GetKeys(opts.Config.Universal.Remove), Handler: self.withItems(self.delete), GetDisabledReason: self.require(self.itemRangeSelected()), Description: self.c.Tr.Delete, @@ -73,7 +73,7 @@ func (self *RemoteBranchesController) GetKeybindings(opts types.KeybindingsOpts) DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Branches.SetUpstream), + Keys: opts.GetKeys(opts.Config.Branches.SetUpstream), Handler: self.withItem(self.setAsUpstream), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.SetAsUpstream, @@ -81,13 +81,13 @@ func (self *RemoteBranchesController) GetKeybindings(opts types.KeybindingsOpts) DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Branches.SortOrder), + Keys: opts.GetKeys(opts.Config.Branches.SortOrder), Handler: self.createSortMenu, Description: self.c.Tr.SortOrder, OpensMenu: true, }, { - Key: opts.GetKey(opts.Config.Commits.ViewResetOptions), + Keys: opts.GetKeys(opts.Config.Commits.ViewResetOptions), Handler: self.withItem(self.createResetMenu), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.ViewResetOptions, @@ -95,7 +95,7 @@ func (self *RemoteBranchesController) GetKeybindings(opts types.KeybindingsOpts) OpensMenu: true, }, { - Key: opts.GetKey(opts.Config.Universal.OpenDiffTool), + Keys: opts.GetKeys(opts.Config.Universal.OpenDiffTool), Handler: self.withItem(func(selectedBranch *models.RemoteBranch) error { return self.c.Helpers().Diff.OpenDiffToolForRef(selectedBranch) }), diff --git a/pkg/gui/controllers/remotes_controller.go b/pkg/gui/controllers/remotes_controller.go index c07626649..b2e30f231 100644 --- a/pkg/gui/controllers/remotes_controller.go +++ b/pkg/gui/controllers/remotes_controller.go @@ -45,20 +45,20 @@ func NewRemotesController( func (self *RemotesController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { bindings := []*types.Binding{ { - Key: opts.GetKey(opts.Config.Universal.GoInto), + Keys: opts.GetKeys(opts.Config.Universal.GoInto), Handler: self.withItem(self.enter), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.ViewBranches, DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.New), + Keys: opts.GetKeys(opts.Config.Universal.New), Handler: self.add, Description: self.c.Tr.NewRemote, DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.Remove), + Keys: opts.GetKeys(opts.Config.Universal.Remove), Handler: self.withItem(self.remove), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.Remove, @@ -66,7 +66,7 @@ func (self *RemotesController) GetKeybindings(opts types.KeybindingsOpts) []*typ DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.Edit), + Keys: opts.GetKeys(opts.Config.Universal.Edit), Handler: self.withItem(self.edit), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.Edit, @@ -74,7 +74,7 @@ func (self *RemotesController) GetKeybindings(opts types.KeybindingsOpts) []*typ DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Branches.FetchRemote), + Keys: opts.GetKeys(opts.Config.Branches.FetchRemote), Handler: self.withItem(self.fetch), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.Fetch, @@ -82,7 +82,7 @@ func (self *RemotesController) GetKeybindings(opts types.KeybindingsOpts) []*typ DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Branches.AddForkRemote), + Keys: opts.GetKeys(opts.Config.Branches.AddForkRemote), Handler: self.addFork, GetDisabledReason: self.hasOriginRemote(), Description: self.c.Tr.AddForkRemote, diff --git a/pkg/gui/controllers/rename_similarity_threshold_controller.go b/pkg/gui/controllers/rename_similarity_threshold_controller.go index 2d5f52bc0..78b8bb7f4 100644 --- a/pkg/gui/controllers/rename_similarity_threshold_controller.go +++ b/pkg/gui/controllers/rename_similarity_threshold_controller.go @@ -28,13 +28,13 @@ func NewRenameSimilarityThresholdController( func (self *RenameSimilarityThresholdController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { bindings := []*types.Binding{ { - Key: opts.GetKey(opts.Config.Universal.IncreaseRenameSimilarityThreshold), + Keys: opts.GetKeys(opts.Config.Universal.IncreaseRenameSimilarityThreshold), Handler: self.Increase, Description: self.c.Tr.IncreaseRenameSimilarityThreshold, Tooltip: self.c.Tr.IncreaseRenameSimilarityThresholdTooltip, }, { - Key: opts.GetKey(opts.Config.Universal.DecreaseRenameSimilarityThreshold), + Keys: opts.GetKeys(opts.Config.Universal.DecreaseRenameSimilarityThreshold), Handler: self.Decrease, Description: self.c.Tr.DecreaseRenameSimilarityThreshold, Tooltip: self.c.Tr.DecreaseRenameSimilarityThresholdTooltip, diff --git a/pkg/gui/controllers/search_controller.go b/pkg/gui/controllers/search_controller.go index 395784d10..f1d5efe2a 100644 --- a/pkg/gui/controllers/search_controller.go +++ b/pkg/gui/controllers/search_controller.go @@ -36,7 +36,7 @@ func (self *SearchController) Context() types.Context { func (self *SearchController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { return []*types.Binding{ { - Key: opts.GetKey(opts.Config.Universal.StartSearch), + Keys: opts.GetKeys(opts.Config.Universal.StartSearch), Handler: self.OpenSearchPrompt, Description: self.c.Tr.StartSearch, }, diff --git a/pkg/gui/controllers/search_prompt_controller.go b/pkg/gui/controllers/search_prompt_controller.go index d5c2f5c3c..1ce02abf0 100644 --- a/pkg/gui/controllers/search_prompt_controller.go +++ b/pkg/gui/controllers/search_prompt_controller.go @@ -24,19 +24,19 @@ func NewSearchPromptController( func (self *SearchPromptController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { return []*types.Binding{ { - Key: gocui.NewKeyName(gocui.KeyEnter), + Keys: []gocui.Key{gocui.NewKeyName(gocui.KeyEnter)}, Handler: self.confirm, }, { - Key: opts.GetKey(opts.Config.Universal.Return), + Keys: opts.GetKeys(opts.Config.Universal.Return), Handler: self.cancel, }, { - Key: opts.GetKey(opts.Config.Universal.PrevItem), + Keys: opts.GetKeys(opts.Config.Universal.PrevItem), Handler: self.prevHistory, }, { - Key: opts.GetKey(opts.Config.Universal.NextItem), + Keys: opts.GetKeys(opts.Config.Universal.NextItem), Handler: self.nextHistory, }, } diff --git a/pkg/gui/controllers/side_window_controller.go b/pkg/gui/controllers/side_window_controller.go index 94706eb8f..67fc9f946 100644 --- a/pkg/gui/controllers/side_window_controller.go +++ b/pkg/gui/controllers/side_window_controller.go @@ -35,12 +35,8 @@ func NewSideWindowController( func (self *SideWindowController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { return []*types.Binding{ - {Key: opts.GetKey(opts.Config.Universal.PrevBlock), Handler: self.previousSideWindow}, - {Key: opts.GetKey(opts.Config.Universal.NextBlock), Handler: self.nextSideWindow}, - {Key: opts.GetKey(opts.Config.Universal.PrevBlockAlt), Handler: self.previousSideWindow}, - {Key: opts.GetKey(opts.Config.Universal.NextBlockAlt), Handler: self.nextSideWindow}, - {Key: opts.GetKey(opts.Config.Universal.PrevBlockAlt2), Handler: self.previousSideWindow}, - {Key: opts.GetKey(opts.Config.Universal.NextBlockAlt2), Handler: self.nextSideWindow}, + {Keys: opts.GetKeys(opts.Config.Universal.PrevBlock), Handler: self.previousSideWindow}, + {Keys: opts.GetKeys(opts.Config.Universal.NextBlock), Handler: self.nextSideWindow}, } } diff --git a/pkg/gui/controllers/snake_controller.go b/pkg/gui/controllers/snake_controller.go index a2a2030b7..b059ce787 100644 --- a/pkg/gui/controllers/snake_controller.go +++ b/pkg/gui/controllers/snake_controller.go @@ -24,23 +24,23 @@ func NewSnakeController( func (self *SnakeController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { bindings := []*types.Binding{ { - Key: opts.GetKey(opts.Config.Universal.NextItem), + Keys: opts.GetKeys(opts.Config.Universal.NextItem), Handler: self.SetDirection(snake.Down), }, { - Key: opts.GetKey(opts.Config.Universal.PrevItem), + Keys: opts.GetKeys(opts.Config.Universal.PrevItem), Handler: self.SetDirection(snake.Up), }, { - Key: opts.GetKey(opts.Config.Universal.PrevBlock), + Keys: opts.GetKeys(opts.Config.Universal.PrevBlock), Handler: self.SetDirection(snake.Left), }, { - Key: opts.GetKey(opts.Config.Universal.NextBlock), + Keys: opts.GetKeys(opts.Config.Universal.NextBlock), Handler: self.SetDirection(snake.Right), }, { - Key: opts.GetKey(opts.Config.Universal.Return), + Keys: opts.GetKeys(opts.Config.Universal.Return), Handler: self.Escape, }, } diff --git a/pkg/gui/controllers/staging_controller.go b/pkg/gui/controllers/staging_controller.go index 75042f46a..8d876acda 100644 --- a/pkg/gui/controllers/staging_controller.go +++ b/pkg/gui/controllers/staging_controller.go @@ -41,69 +41,69 @@ func NewStagingController( func (self *StagingController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { return []*types.Binding{ { - Key: opts.GetKey(opts.Config.Universal.Select), + Keys: opts.GetKeys(opts.Config.Universal.Select), Handler: self.ToggleStaged, Description: self.c.Tr.Stage, Tooltip: self.c.Tr.StageSelectionTooltip, DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.Remove), + Keys: opts.GetKeys(opts.Config.Universal.Remove), Handler: self.DiscardSelection, Description: self.c.Tr.DiscardSelection, Tooltip: self.c.Tr.DiscardSelectionTooltip, DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.OpenFile), + Keys: opts.GetKeys(opts.Config.Universal.OpenFile), Handler: self.OpenFile, Description: self.c.Tr.OpenFile, Tooltip: self.c.Tr.OpenFileTooltip, }, { - Key: opts.GetKey(opts.Config.Universal.Edit), + Keys: opts.GetKeys(opts.Config.Universal.Edit), Handler: self.EditFile, Description: self.c.Tr.EditFile, Tooltip: self.c.Tr.EditFileTooltip, }, { - Key: opts.GetKey(opts.Config.Universal.Return), + Keys: opts.GetKeys(opts.Config.Universal.Return), Handler: self.Escape, Description: self.c.Tr.ReturnToFilesPanel, DescriptionFunc: self.EscapeDescription, DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.TogglePanel), + Keys: opts.GetKeys(opts.Config.Universal.TogglePanel), Handler: self.TogglePanel, Description: self.c.Tr.ToggleStagingView, Tooltip: self.c.Tr.ToggleStagingViewTooltip, DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Main.EditSelectHunk), + Keys: opts.GetKeys(opts.Config.Main.EditSelectHunk), Handler: self.EditHunkAndRefresh, Description: self.c.Tr.EditHunk, Tooltip: self.c.Tr.EditHunkTooltip, }, { - Key: opts.GetKey(opts.Config.Files.CommitChanges), + Keys: opts.GetKeys(opts.Config.Files.CommitChanges), Handler: self.c.Helpers().WorkingTree.HandleCommitPress, Description: self.c.Tr.Commit, Tooltip: self.c.Tr.CommitTooltip, }, { - Key: opts.GetKey(opts.Config.Files.CommitChangesWithoutHook), + Keys: opts.GetKeys(opts.Config.Files.CommitChangesWithoutHook), Handler: self.c.Helpers().WorkingTree.HandleWIPCommitPress, Description: self.c.Tr.CommitChangesWithoutHook, }, { - Key: opts.GetKey(opts.Config.Files.CommitChangesWithEditor), + Keys: opts.GetKeys(opts.Config.Files.CommitChangesWithEditor), Handler: self.c.Helpers().WorkingTree.HandleCommitEditorPress, Description: self.c.Tr.CommitChangesWithEditor, }, { - Key: opts.GetKey(opts.Config.Files.FindBaseCommitForFixup), + Keys: opts.GetKeys(opts.Config.Files.FindBaseCommitForFixup), Handler: self.c.Helpers().FixupHelper.HandleFindBaseCommitForFixupPress, Description: self.c.Tr.FindBaseCommitForFixup, Tooltip: self.c.Tr.FindBaseCommitForFixupTooltip, diff --git a/pkg/gui/controllers/stash_controller.go b/pkg/gui/controllers/stash_controller.go index 20dc2826e..49abedd9f 100644 --- a/pkg/gui/controllers/stash_controller.go +++ b/pkg/gui/controllers/stash_controller.go @@ -36,7 +36,7 @@ func NewStashController( func (self *StashController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { bindings := []*types.Binding{ { - Key: opts.GetKey(opts.Config.Universal.Select), + Keys: opts.GetKeys(opts.Config.Universal.Select), Handler: self.withItem(self.handleStashApply), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.Apply, @@ -44,7 +44,7 @@ func (self *StashController) GetKeybindings(opts types.KeybindingsOpts) []*types DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Stash.PopStash), + Keys: opts.GetKeys(opts.Config.Stash.PopStash), Handler: self.withItem(self.handleStashPop), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.Pop, @@ -52,7 +52,7 @@ func (self *StashController) GetKeybindings(opts types.KeybindingsOpts) []*types DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.Remove), + Keys: opts.GetKeys(opts.Config.Universal.Remove), Handler: self.withItems(self.handleStashDrop), GetDisabledReason: self.require(self.itemRangeSelected()), Description: self.c.Tr.Drop, @@ -60,14 +60,14 @@ func (self *StashController) GetKeybindings(opts types.KeybindingsOpts) []*types DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.New), + Keys: opts.GetKeys(opts.Config.Universal.New), Handler: self.withItem(self.handleNewBranchOffStashEntry), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.NewBranch, Tooltip: self.c.Tr.NewBranchFromStashTooltip, }, { - Key: opts.GetKey(opts.Config.Stash.RenameStash), + Keys: opts.GetKeys(opts.Config.Stash.RenameStash), Handler: self.withItem(self.handleRenameStashEntry), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.RenameStash, diff --git a/pkg/gui/controllers/status_controller.go b/pkg/gui/controllers/status_controller.go index a3e08e384..f29ee97f0 100644 --- a/pkg/gui/controllers/status_controller.go +++ b/pkg/gui/controllers/status_controller.go @@ -34,37 +34,37 @@ func NewStatusController( func (self *StatusController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { bindings := []*types.Binding{ { - Key: opts.GetKey(opts.Config.Universal.OpenFile), + Keys: opts.GetKeys(opts.Config.Universal.OpenFile), Handler: self.openConfig, Description: self.c.Tr.OpenConfig, Tooltip: self.c.Tr.OpenFileTooltip, }, { - Key: opts.GetKey(opts.Config.Universal.Edit), + Keys: opts.GetKeys(opts.Config.Universal.Edit), Handler: self.editConfig, Description: self.c.Tr.EditConfig, Tooltip: self.c.Tr.EditFileTooltip, DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Status.CheckForUpdate), + Keys: opts.GetKeys(opts.Config.Status.CheckForUpdate), Handler: self.handleCheckForUpdate, Description: self.c.Tr.CheckForUpdate, DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Status.RecentRepos), + Keys: opts.GetKeys(opts.Config.Status.RecentRepos), Handler: self.c.Helpers().Repos.CreateRecentReposMenu, Description: self.c.Tr.SwitchRepo, DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Status.AllBranchesLogGraph), + Keys: opts.GetKeys(opts.Config.Status.AllBranchesLogGraph), Handler: func() error { self.switchToOrRotateAllBranchesLogs(); return nil }, Description: self.c.Tr.AllBranchesLogGraph, }, { - Key: opts.GetKey(opts.Config.Status.AllBranchesLogGraphReverse), + Keys: opts.GetKeys(opts.Config.Status.AllBranchesLogGraphReverse), Handler: func() error { self.switchToOrRotateAllBranchesLogsBackward(); return nil }, Description: self.c.Tr.AllBranchesLogGraphReverse, }, diff --git a/pkg/gui/controllers/submodules_controller.go b/pkg/gui/controllers/submodules_controller.go index f6e12cfe4..97b7ff3dd 100644 --- a/pkg/gui/controllers/submodules_controller.go +++ b/pkg/gui/controllers/submodules_controller.go @@ -39,21 +39,21 @@ func NewSubmodulesController( func (self *SubmodulesController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { return []*types.Binding{ { - Key: opts.GetKey(opts.Config.Universal.GoInto), + Keys: opts.GetKeys(opts.Config.Universal.GoInto), Handler: self.withItem(self.enter), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.Enter, Tooltip: utils.ResolvePlaceholderString(self.c.Tr.EnterSubmoduleTooltip, - map[string]string{"escape": opts.Config.Universal.Return}), + map[string]string{"escape": opts.Config.Universal.Return.String()}), DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.Select), + Keys: opts.GetKeys(opts.Config.Universal.Select), Handler: self.withItem(self.enter), GetDisabledReason: self.require(self.singleItemSelected()), }, { - Key: opts.GetKey(opts.Config.Universal.Remove), + Keys: opts.GetKeys(opts.Config.Universal.Remove), Handler: self.withItem(self.remove), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.Remove, @@ -61,7 +61,7 @@ func (self *SubmodulesController) GetKeybindings(opts types.KeybindingsOpts) []* DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Submodules.Update), + Keys: opts.GetKeys(opts.Config.Submodules.Update), Handler: self.withItem(self.update), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.Update, @@ -69,26 +69,26 @@ func (self *SubmodulesController) GetKeybindings(opts types.KeybindingsOpts) []* DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.New), + Keys: opts.GetKeys(opts.Config.Universal.New), Handler: self.add, Description: self.c.Tr.NewSubmodule, DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.Edit), + Keys: opts.GetKeys(opts.Config.Universal.Edit), Handler: self.withItem(self.editURL), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.EditSubmoduleUrl, }, { - Key: opts.GetKey(opts.Config.Submodules.Init), + Keys: opts.GetKeys(opts.Config.Submodules.Init), Handler: self.withItem(self.init), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.Initialize, Tooltip: self.c.Tr.InitSubmoduleTooltip, }, { - Key: opts.GetKey(opts.Config.Submodules.BulkMenu), + Keys: opts.GetKeys(opts.Config.Submodules.BulkMenu), Handler: self.openBulkActionsMenu, Description: self.c.Tr.ViewBulkSubmoduleOptions, OpensMenu: true, @@ -233,7 +233,7 @@ func (self *SubmodulesController) openBulkActionsMenu() error { return nil }) }, - Key: gocui.NewKeyRune('i'), + Keys: menuKey('i'), }, { LabelColumns: []string{self.c.Tr.BulkUpdateSubmodules, style.FgYellow.Sprint(self.c.Git().Submodule.BulkUpdateCmdObj().ToString())}, @@ -248,7 +248,7 @@ func (self *SubmodulesController) openBulkActionsMenu() error { return nil }) }, - Key: gocui.NewKeyRune('u'), + Keys: menuKey('u'), }, { LabelColumns: []string{self.c.Tr.BulkUpdateRecursiveSubmodules, style.FgYellow.Sprint(self.c.Git().Submodule.BulkUpdateRecursivelyCmdObj().ToString())}, @@ -263,7 +263,7 @@ func (self *SubmodulesController) openBulkActionsMenu() error { return nil }) }, - Key: gocui.NewKeyRune('r'), + Keys: menuKey('r'), }, { LabelColumns: []string{self.c.Tr.BulkDeinitSubmodules, style.FgRed.Sprint(self.c.Git().Submodule.BulkDeinitCmdObj().ToString())}, @@ -278,7 +278,7 @@ func (self *SubmodulesController) openBulkActionsMenu() error { return nil }) }, - Key: gocui.NewKeyRune('d'), + Keys: menuKey('d'), }, }, }) diff --git a/pkg/gui/controllers/suggestions_controller.go b/pkg/gui/controllers/suggestions_controller.go index e5de7619b..0553050e5 100644 --- a/pkg/gui/controllers/suggestions_controller.go +++ b/pkg/gui/controllers/suggestions_controller.go @@ -32,26 +32,26 @@ func NewSuggestionsController( func (self *SuggestionsController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { bindings := []*types.Binding{ { - Key: opts.GetKey(opts.Config.Universal.ConfirmSuggestion), + Keys: opts.GetKeys(opts.Config.Universal.ConfirmSuggestion), Handler: func() error { return self.context().State.OnConfirm() }, GetDisabledReason: self.require(self.singleItemSelected()), }, { - Key: opts.GetKey(opts.Config.Universal.Return), + Keys: opts.GetKeys(opts.Config.Universal.Return), Handler: func() error { return self.context().State.OnClose() }, }, { - Key: opts.GetKey(opts.Config.Universal.TogglePanel), + Keys: opts.GetKeys(opts.Config.Universal.TogglePanel), Handler: self.switchToPrompt, }, { - Key: opts.GetKey(opts.Config.Universal.Remove), + Keys: opts.GetKeys(opts.Config.Universal.Remove), Handler: func() error { return self.context().State.OnDeleteSuggestion() }, }, { - Key: opts.GetKey(opts.Config.Universal.Edit), + Keys: opts.GetKeys(opts.Config.Universal.Edit), Handler: func() error { if self.context().State.AllowEditSuggestion { if selectedItem := self.c.Contexts().Suggestions.GetSelected(); selectedItem != nil { diff --git a/pkg/gui/controllers/switch_to_diff_files_controller.go b/pkg/gui/controllers/switch_to_diff_files_controller.go index 94c3c5712..c2ff4d674 100644 --- a/pkg/gui/controllers/switch_to_diff_files_controller.go +++ b/pkg/gui/controllers/switch_to_diff_files_controller.go @@ -40,7 +40,7 @@ func NewSwitchToDiffFilesController( func (self *SwitchToDiffFilesController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { bindings := []*types.Binding{ { - Key: opts.GetKey(opts.Config.Universal.GoInto), + Keys: opts.GetKeys(opts.Config.Universal.GoInto), Handler: self.enter, GetDisabledReason: self.canEnter, Description: self.c.Tr.ViewItemFiles, diff --git a/pkg/gui/controllers/switch_to_focused_main_view_controller.go b/pkg/gui/controllers/switch_to_focused_main_view_controller.go index 5161a5c83..5606a0bab 100644 --- a/pkg/gui/controllers/switch_to_focused_main_view_controller.go +++ b/pkg/gui/controllers/switch_to_focused_main_view_controller.go @@ -29,7 +29,7 @@ func NewSwitchToFocusedMainViewController( func (self *SwitchToFocusedMainViewController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { bindings := []*types.Binding{ { - Key: opts.GetKey(opts.Config.Universal.FocusMainView), + Keys: opts.GetKeys(opts.Config.Universal.FocusMainView), Handler: self.handleFocusMainView, Description: self.c.Tr.FocusMainView, Tag: "global", diff --git a/pkg/gui/controllers/switch_to_sub_commits_controller.go b/pkg/gui/controllers/switch_to_sub_commits_controller.go index 8bee8d7e6..4d723d061 100644 --- a/pkg/gui/controllers/switch_to_sub_commits_controller.go +++ b/pkg/gui/controllers/switch_to_sub_commits_controller.go @@ -47,7 +47,7 @@ func (self *SwitchToSubCommitsController) GetKeybindings(opts types.KeybindingsO { Handler: self.viewCommits, GetDisabledReason: self.require(self.singleItemSelected()), - Key: opts.GetKey(opts.Config.Universal.GoInto), + Keys: opts.GetKeys(opts.Config.Universal.GoInto), Description: self.c.Tr.ViewCommits, }, } diff --git a/pkg/gui/controllers/sync_controller.go b/pkg/gui/controllers/sync_controller.go index 00178b1bb..649b53338 100644 --- a/pkg/gui/controllers/sync_controller.go +++ b/pkg/gui/controllers/sync_controller.go @@ -32,14 +32,14 @@ func NewSyncController( func (self *SyncController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { bindings := []*types.Binding{ { - Key: opts.GetKey(opts.Config.Universal.Push), + Keys: opts.GetKeys(opts.Config.Universal.Push), Handler: opts.Guards.NoPopupPanel(self.HandlePush), GetDisabledReason: self.getDisabledReasonForPushOrPull, Description: self.c.Tr.Push, Tooltip: self.c.Tr.PushTooltip, }, { - Key: opts.GetKey(opts.Config.Universal.Pull), + Keys: opts.GetKeys(opts.Config.Universal.Pull), Handler: opts.Guards.NoPopupPanel(self.HandlePull), GetDisabledReason: self.getDisabledReasonForPushOrPull, Description: self.c.Tr.Pull, @@ -256,8 +256,8 @@ func (self *SyncController) forcePushPrompt() string { return utils.ResolvePlaceholderString( self.c.Tr.ForcePushPrompt, map[string]string{ - "cancelKey": self.c.UserConfig().Keybinding.Universal.Return, - "confirmKey": self.c.UserConfig().Keybinding.Universal.Confirm, + "cancelKey": self.c.UserConfig().Keybinding.Universal.Return.String(), + "confirmKey": self.c.UserConfig().Keybinding.Universal.Confirm.String(), }, ) } diff --git a/pkg/gui/controllers/tags_controller.go b/pkg/gui/controllers/tags_controller.go index 1e274c077..a10f9a374 100644 --- a/pkg/gui/controllers/tags_controller.go +++ b/pkg/gui/controllers/tags_controller.go @@ -39,7 +39,7 @@ func NewTagsController( func (self *TagsController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { bindings := []*types.Binding{ { - Key: opts.GetKey(opts.Config.Universal.Select), + Keys: opts.GetKeys(opts.Config.Universal.Select), Handler: self.withItem(self.checkout), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.Checkout, @@ -47,14 +47,14 @@ func (self *TagsController) GetKeybindings(opts types.KeybindingsOpts) []*types. DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.New), + Keys: opts.GetKeys(opts.Config.Universal.New), Handler: self.create, Description: self.c.Tr.NewTag, Tooltip: self.c.Tr.NewTagTooltip, DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.Remove), + Keys: opts.GetKeys(opts.Config.Universal.Remove), Handler: self.withItem(self.delete), Description: self.c.Tr.Delete, GetDisabledReason: self.require(self.singleItemSelected()), @@ -63,7 +63,7 @@ func (self *TagsController) GetKeybindings(opts types.KeybindingsOpts) []*types. DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Branches.PushTag), + Keys: opts.GetKeys(opts.Config.Branches.PushTag), Handler: self.withItem(self.push), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.PushTag, @@ -71,7 +71,7 @@ func (self *TagsController) GetKeybindings(opts types.KeybindingsOpts) []*types. DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Commits.ViewResetOptions), + Keys: opts.GetKeys(opts.Config.Commits.ViewResetOptions), Handler: self.withItem(self.createResetMenu), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.Reset, @@ -80,7 +80,7 @@ func (self *TagsController) GetKeybindings(opts types.KeybindingsOpts) []*types. OpensMenu: true, }, { - Key: opts.GetKey(opts.Config.Universal.OpenDiffTool), + Keys: opts.GetKeys(opts.Config.Universal.OpenDiffTool), Handler: self.withItem(func(selectedTag *models.Tag) error { return self.c.Helpers().Diff.OpenDiffToolForRef(selectedTag) }), @@ -282,14 +282,14 @@ func (self *TagsController) delete(tag *models.Tag) error { menuItems := []*types.MenuItem{ { Label: self.c.Tr.DeleteLocalTag, - Key: gocui.NewKeyRune('c'), + Keys: menuKey('c'), OnPress: func() error { return self.localDelete(tag) }, }, { Label: self.c.Tr.DeleteRemoteTag, - Key: gocui.NewKeyRune('r'), + Keys: menuKey('r'), OpensMenu: true, OnPress: func() error { return self.remoteDelete(tag) @@ -297,7 +297,7 @@ func (self *TagsController) delete(tag *models.Tag) error { }, { Label: self.c.Tr.DeleteLocalAndRemoteTag, - Key: gocui.NewKeyRune('b'), + Keys: menuKey('b'), OpensMenu: true, OnPress: func() error { return self.localAndRemoteDelete(tag) diff --git a/pkg/gui/controllers/undo_controller.go b/pkg/gui/controllers/undo_controller.go index 02e27ddce..775e871a4 100644 --- a/pkg/gui/controllers/undo_controller.go +++ b/pkg/gui/controllers/undo_controller.go @@ -53,13 +53,13 @@ type reflogAction struct { func (self *UndoController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { bindings := []*types.Binding{ { - Key: opts.GetKey(opts.Config.Universal.Undo), + Keys: opts.GetKeys(opts.Config.Universal.Undo), Handler: self.reflogUndo, Description: self.c.Tr.UndoReflog, Tooltip: self.c.Tr.UndoTooltip, }, { - Key: opts.GetKey(opts.Config.Universal.Redo), + Keys: opts.GetKeys(opts.Config.Universal.Redo), Handler: self.reflogRedo, Description: self.c.Tr.RedoReflog, Tooltip: self.c.Tr.RedoTooltip, diff --git a/pkg/gui/controllers/view_selection_controller.go b/pkg/gui/controllers/view_selection_controller.go index 0d69c5109..31cbd3695 100644 --- a/pkg/gui/controllers/view_selection_controller.go +++ b/pkg/gui/controllers/view_selection_controller.go @@ -36,16 +36,12 @@ func (self *ViewSelectionController) Context() types.Context { func (self *ViewSelectionController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { return []*types.Binding{ - {Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.PrevItem), Handler: self.handlePrevLine}, - {Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.PrevItemAlt), Handler: self.handlePrevLine}, - {Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.NextItem), Handler: self.handleNextLine}, - {Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.NextItemAlt), Handler: self.handleNextLine}, - {Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.PrevPage), Handler: self.handlePrevPage, Description: self.c.Tr.PrevPage}, - {Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.NextPage), Handler: self.handleNextPage, Description: self.c.Tr.NextPage}, - {Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.GotoTop), Handler: self.handleGotoTop, Description: self.c.Tr.GotoTop, Alternative: ""}, - {Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.GotoBottom), Handler: self.handleGotoBottom, Description: self.c.Tr.GotoBottom, Alternative: ""}, - {Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.GotoTopAlt), Handler: self.handleGotoTop}, - {Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.GotoBottomAlt), Handler: self.handleGotoBottom}, + {Tag: "navigation", Keys: opts.GetKeys(opts.Config.Universal.PrevItem), Handler: self.handlePrevLine}, + {Tag: "navigation", Keys: opts.GetKeys(opts.Config.Universal.NextItem), Handler: self.handleNextLine}, + {Tag: "navigation", Keys: opts.GetKeys(opts.Config.Universal.PrevPage), Handler: self.handlePrevPage, Description: self.c.Tr.PrevPage}, + {Tag: "navigation", Keys: opts.GetKeys(opts.Config.Universal.NextPage), Handler: self.handleNextPage, Description: self.c.Tr.NextPage}, + {Tag: "navigation", Keys: opts.GetKeys(opts.Config.Universal.GotoTop), Handler: self.handleGotoTop, Description: self.c.Tr.GotoTop}, + {Tag: "navigation", Keys: opts.GetKeys(opts.Config.Universal.GotoBottom), Handler: self.handleGotoBottom, Description: self.c.Tr.GotoBottom}, } } diff --git a/pkg/gui/controllers/workspace_reset_controller.go b/pkg/gui/controllers/workspace_reset_controller.go index 48ccfd298..9a9005254 100644 --- a/pkg/gui/controllers/workspace_reset_controller.go +++ b/pkg/gui/controllers/workspace_reset_controller.go @@ -53,7 +53,7 @@ func (self *FilesController) createResetMenu() error { }) return nil }, - Key: gocui.NewKeyRune('x'), + Keys: menuKey('x'), Tooltip: self.c.Tr.NukeDescription, }, { @@ -72,7 +72,7 @@ func (self *FilesController) createResetMenu() error { ) return nil }, - Key: gocui.NewKeyRune('u'), + Keys: menuKey('u'), }, { LabelColumns: []string{ @@ -90,7 +90,7 @@ func (self *FilesController) createResetMenu() error { ) return nil }, - Key: gocui.NewKeyRune('c'), + Keys: menuKey('c'), }, { LabelColumns: []string{ @@ -115,7 +115,7 @@ func (self *FilesController) createResetMenu() error { ) return nil }, - Key: gocui.NewKeyRune('S'), + Keys: menuKey('S'), }, { LabelColumns: []string{ @@ -133,7 +133,7 @@ func (self *FilesController) createResetMenu() error { ) return nil }, - Key: gocui.NewKeyRune('s'), + Keys: menuKey('s'), }, { LabelColumns: []string{ @@ -151,7 +151,7 @@ func (self *FilesController) createResetMenu() error { ) return nil }, - Key: gocui.NewKeyRune('m'), + Keys: menuKey('m'), }, { LabelColumns: []string{ @@ -176,7 +176,7 @@ func (self *FilesController) createResetMenu() error { }, }) }, - Key: gocui.NewKeyRune('h'), + Keys: menuKey('h'), }, } diff --git a/pkg/gui/controllers/worktree_options_controller.go b/pkg/gui/controllers/worktree_options_controller.go index 0cdf4d008..b1123e2a8 100644 --- a/pkg/gui/controllers/worktree_options_controller.go +++ b/pkg/gui/controllers/worktree_options_controller.go @@ -36,7 +36,7 @@ func NewWorktreeOptionsController(c *ControllerCommon, context CanViewWorktreeOp func (self *WorktreeOptionsController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { bindings := []*types.Binding{ { - Key: opts.GetKey(opts.Config.Worktrees.ViewWorktreeOptions), + Keys: opts.GetKeys(opts.Config.Worktrees.ViewWorktreeOptions), Handler: self.withItem(self.viewWorktreeOptions), Description: self.c.Tr.ViewWorktreeOptions, OpensMenu: true, diff --git a/pkg/gui/controllers/worktrees_controller.go b/pkg/gui/controllers/worktrees_controller.go index 0e34ec59f..5128ad716 100644 --- a/pkg/gui/controllers/worktrees_controller.go +++ b/pkg/gui/controllers/worktrees_controller.go @@ -39,13 +39,13 @@ func NewWorktreesController( func (self *WorktreesController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { bindings := []*types.Binding{ { - Key: opts.GetKey(opts.Config.Universal.New), + Keys: opts.GetKeys(opts.Config.Universal.New), Handler: self.add, Description: self.c.Tr.NewWorktree, DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.Select), + Keys: opts.GetKeys(opts.Config.Universal.Select), Handler: self.withItem(self.enter), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.Switch, @@ -53,18 +53,18 @@ func (self *WorktreesController) GetKeybindings(opts types.KeybindingsOpts) []*t DisplayOnScreen: true, }, { - Key: opts.GetKey(opts.Config.Universal.GoInto), + Keys: opts.GetKeys(opts.Config.Universal.GoInto), Handler: self.withItem(self.enter), GetDisabledReason: self.require(self.singleItemSelected()), }, { - Key: opts.GetKey(opts.Config.Universal.OpenFile), + Keys: opts.GetKeys(opts.Config.Universal.OpenFile), Handler: self.withItem(self.open), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.OpenInEditor, }, { - Key: opts.GetKey(opts.Config.Universal.Remove), + Keys: opts.GetKeys(opts.Config.Universal.Remove), Handler: self.withItem(self.remove), GetDisabledReason: self.require(self.singleItemSelected()), Description: self.c.Tr.Remove, diff --git a/pkg/gui/extras_panel.go b/pkg/gui/extras_panel.go index 03e82345a..980c31d45 100644 --- a/pkg/gui/extras_panel.go +++ b/pkg/gui/extras_panel.go @@ -15,7 +15,7 @@ func (gui *Gui) handleCreateExtrasMenuPanel() error { Items: []*types.MenuItem{ { Label: gui.c.Tr.ToggleShowCommandLog, - Key: gocui.NewKeyRune('t'), + Keys: []gocui.Key{gocui.NewKeyRune('t')}, OnPress: func() error { currentContext := gui.c.Context().CurrentStatic() if gui.c.State().GetShowExtrasWindow() && currentContext.GetKey() == context.COMMAND_LOG_CONTEXT_KEY { @@ -30,7 +30,7 @@ func (gui *Gui) handleCreateExtrasMenuPanel() error { }, { Label: gui.c.Tr.FocusCommandLog, - Key: gocui.NewKeyRune('f'), + Keys: []gocui.Key{gocui.NewKeyRune('f')}, OnPress: gui.handleFocusCommandLog, }, }, diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 458e33804..e2881cca1 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -472,15 +472,15 @@ func (gui *Gui) onUserConfigLoaded() error { gui.setColorScheme() gui.configureViewProperties() - gui.g.SearchEscapeKey = config.GetValidatedKeyBindingKey(userConfig.Keybinding.Universal.Return) - gui.g.NextSearchMatchKey = config.GetValidatedKeyBindingKey(userConfig.Keybinding.Universal.NextMatch) - gui.g.PrevSearchMatchKey = config.GetValidatedKeyBindingKey(userConfig.Keybinding.Universal.PrevMatch) + gui.g.SearchEscapeKeys = config.GetValidatedKeyBindingKeys(userConfig.Keybinding.Universal.Return) + gui.g.NextSearchMatchKeys = config.GetValidatedKeyBindingKeys(userConfig.Keybinding.Universal.NextMatch) + gui.g.PrevSearchMatchKeys = config.GetValidatedKeyBindingKeys(userConfig.Keybinding.Universal.PrevMatch) gui.g.SetEditKeybindings( - config.GetValidatedKeyBindingKey(userConfig.Keybinding.Universal.MoveWordLeft), - config.GetValidatedKeyBindingKey(userConfig.Keybinding.Universal.MoveWordRight), - config.GetValidatedKeyBindingKey(userConfig.Keybinding.Universal.BackspaceWord), - config.GetValidatedKeyBindingKey(userConfig.Keybinding.Universal.ForwardDeleteWord), + config.GetValidatedKeyBindingKeys(userConfig.Keybinding.Universal.MoveWordLeft), + config.GetValidatedKeyBindingKeys(userConfig.Keybinding.Universal.MoveWordRight), + config.GetValidatedKeyBindingKeys(userConfig.Keybinding.Universal.BackspaceWord), + config.GetValidatedKeyBindingKeys(userConfig.Keybinding.Universal.ForwardDeleteWord), ) gui.g.ShowListFooter = userConfig.Gui.ShowListFooter @@ -1089,7 +1089,7 @@ func (gui *Gui) showIntroPopupMessage() { introMessage := utils.ResolvePlaceholderString( gui.c.Tr.IntroPopupMessage, map[string]string{ - "confirmationKey": gui.c.UserConfig().Keybinding.Universal.Confirm, + "confirmationKey": gui.c.UserConfig().Keybinding.Universal.Confirm.String(), }, ) diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go index 082d99ecc..22d76f01b 100644 --- a/pkg/gui/keybindings.go +++ b/pkg/gui/keybindings.go @@ -69,9 +69,9 @@ func (gui *Gui) keybindingOpts() types.KeybindingsOpts { } return types.KeybindingsOpts{ - GetKey: config.GetValidatedKeyBindingKey, - Config: keybindingConfig, - Guards: guards, + GetKeys: config.GetValidatedKeyBindingKeys, + Config: keybindingConfig, + Guards: guards, } } @@ -81,114 +81,94 @@ func (gui *Gui) GetInitialKeybindings() ([]*types.Binding, []*gocui.ViewMouseBin bindings := []*types.Binding{ { ViewName: "", - Key: opts.GetKey(opts.Config.Universal.OpenRecentRepos), + Keys: opts.GetKeys(opts.Config.Universal.OpenRecentRepos), Handler: opts.Guards.NoPopupPanel(gui.helpers.Repos.CreateRecentReposMenu), Description: gui.c.Tr.SwitchRepo, }, { ViewName: "", - Key: opts.GetKey(opts.Config.Universal.ScrollUpMain), + Keys: opts.GetKeys(opts.Config.Universal.ScrollUpMain), Handler: gui.scrollUpMain, Alternative: "fn+up/shift+k", Description: gui.c.Tr.ScrollUpMainWindow, }, { ViewName: "", - Key: opts.GetKey(opts.Config.Universal.ScrollDownMain), + Keys: opts.GetKeys(opts.Config.Universal.ScrollDownMain), Handler: gui.scrollDownMain, Alternative: "fn+down/shift+j", Description: gui.c.Tr.ScrollDownMainWindow, }, - { - ViewName: "", - Key: opts.GetKey(opts.Config.Universal.ScrollUpMainAlt1), - Handler: gui.scrollUpMain, - }, - { - ViewName: "", - Key: opts.GetKey(opts.Config.Universal.ScrollDownMainAlt1), - Handler: gui.scrollDownMain, - }, - { - ViewName: "", - Key: opts.GetKey(opts.Config.Universal.ScrollUpMainAlt2), - Handler: gui.scrollUpMain, - }, - { - ViewName: "", - Key: opts.GetKey(opts.Config.Universal.ScrollDownMainAlt2), - Handler: gui.scrollDownMain, - }, { ViewName: "files", - Key: opts.GetKey(opts.Config.Universal.CopyToClipboard), + Keys: opts.GetKeys(opts.Config.Universal.CopyToClipboard), Handler: gui.handleCopySelectedSideContextItemToClipboard, GetDisabledReason: gui.getCopySelectedSideContextItemToClipboardDisabledReason, Description: gui.c.Tr.CopyPathToClipboard, }, { ViewName: "localBranches", - Key: opts.GetKey(opts.Config.Universal.CopyToClipboard), + Keys: opts.GetKeys(opts.Config.Universal.CopyToClipboard), Handler: gui.handleCopySelectedSideContextItemToClipboard, GetDisabledReason: gui.getCopySelectedSideContextItemToClipboardDisabledReason, Description: gui.c.Tr.CopyBranchNameToClipboard, }, { ViewName: "remoteBranches", - Key: opts.GetKey(opts.Config.Universal.CopyToClipboard), + Keys: opts.GetKeys(opts.Config.Universal.CopyToClipboard), Handler: gui.handleCopySelectedSideContextItemToClipboard, GetDisabledReason: gui.getCopySelectedSideContextItemToClipboardDisabledReason, Description: gui.c.Tr.CopyBranchNameToClipboard, }, { ViewName: "tags", - Key: opts.GetKey(opts.Config.Universal.CopyToClipboard), + Keys: opts.GetKeys(opts.Config.Universal.CopyToClipboard), Handler: gui.handleCopySelectedSideContextItemToClipboard, GetDisabledReason: gui.getCopySelectedSideContextItemToClipboardDisabledReason, Description: gui.c.Tr.CopyTagToClipboard, }, { ViewName: "commits", - Key: opts.GetKey(opts.Config.Universal.CopyToClipboard), + Keys: opts.GetKeys(opts.Config.Universal.CopyToClipboard), Handler: gui.handleCopySelectedSideContextItemCommitHashToClipboard, GetDisabledReason: gui.getCopySelectedSideContextItemToClipboardDisabledReason, Description: gui.c.Tr.CopyCommitHashToClipboard, }, { ViewName: "commits", - Key: opts.GetKey(opts.Config.Commits.ResetCherryPick), + Keys: opts.GetKeys(opts.Config.Commits.ResetCherryPick), Handler: gui.helpers.CherryPick.Reset, Description: gui.c.Tr.ResetCherryPick, }, { ViewName: "reflogCommits", - Key: opts.GetKey(opts.Config.Universal.CopyToClipboard), + Keys: opts.GetKeys(opts.Config.Universal.CopyToClipboard), Handler: gui.handleCopySelectedSideContextItemToClipboard, GetDisabledReason: gui.getCopySelectedSideContextItemToClipboardDisabledReason, Description: gui.c.Tr.CopyCommitHashToClipboard, }, { ViewName: "subCommits", - Key: opts.GetKey(opts.Config.Universal.CopyToClipboard), + Keys: opts.GetKeys(opts.Config.Universal.CopyToClipboard), Handler: gui.handleCopySelectedSideContextItemCommitHashToClipboard, GetDisabledReason: gui.getCopySelectedSideContextItemToClipboardDisabledReason, Description: gui.c.Tr.CopyCommitHashToClipboard, }, { ViewName: "information", - Key: gocui.NewKeyName(gocui.MouseLeft), + Keys: []gocui.Key{gocui.NewKeyName(gocui.MouseLeft)}, Handler: gui.handleInfoClick, }, { ViewName: "commitFiles", - Key: opts.GetKey(opts.Config.Universal.CopyToClipboard), + Keys: opts.GetKeys(opts.Config.Universal.CopyToClipboard), Handler: gui.handleCopySelectedSideContextItemToClipboard, GetDisabledReason: gui.getCopySelectedSideContextItemToClipboardDisabledReason, Description: gui.c.Tr.CopyPathToClipboard, }, { ViewName: "", - Key: opts.GetKey(opts.Config.Universal.ExtrasMenu), + Keys: opts.GetKeys(opts.Config.Universal.ExtrasMenu), Handler: opts.Guards.NoPopupPanel(gui.handleCreateExtrasMenuPanel), Description: gui.c.Tr.OpenCommandLogMenu, Tooltip: gui.c.Tr.OpenCommandLogMenuTooltip, @@ -196,163 +176,121 @@ func (gui *Gui) GetInitialKeybindings() ([]*types.Binding, []*gocui.ViewMouseBin }, { ViewName: "main", - Key: gocui.NewKeyName(gocui.MouseWheelDown), + Keys: []gocui.Key{gocui.NewKeyName(gocui.MouseWheelDown)}, Handler: gui.scrollDownMain, Description: gui.c.Tr.ScrollDown, Alternative: "fn+up", }, { ViewName: "main", - Key: gocui.NewKeyName(gocui.MouseWheelUp), + Keys: []gocui.Key{gocui.NewKeyName(gocui.MouseWheelUp)}, Handler: gui.scrollUpMain, Description: gui.c.Tr.ScrollUp, Alternative: "fn+down", }, { ViewName: "secondary", - Key: gocui.NewKeyName(gocui.MouseWheelDown), + Keys: []gocui.Key{gocui.NewKeyName(gocui.MouseWheelDown)}, Handler: gui.scrollDownSecondary, }, { ViewName: "secondary", - Key: gocui.NewKeyName(gocui.MouseWheelUp), + Keys: []gocui.Key{gocui.NewKeyName(gocui.MouseWheelUp)}, Handler: gui.scrollUpSecondary, }, { ViewName: "confirmation", - Key: opts.GetKey(opts.Config.Universal.PrevItem), + Keys: opts.GetKeys(opts.Config.Universal.PrevItem), Handler: gui.scrollUpConfirmationPanel, }, { ViewName: "confirmation", - Key: opts.GetKey(opts.Config.Universal.NextItem), + Keys: opts.GetKeys(opts.Config.Universal.NextItem), Handler: gui.scrollDownConfirmationPanel, }, { ViewName: "confirmation", - Key: opts.GetKey(opts.Config.Universal.PrevItemAlt), + Keys: []gocui.Key{gocui.NewKeyName(gocui.MouseWheelUp)}, Handler: gui.scrollUpConfirmationPanel, }, { ViewName: "confirmation", - Key: opts.GetKey(opts.Config.Universal.NextItemAlt), + Keys: []gocui.Key{gocui.NewKeyName(gocui.MouseWheelDown)}, Handler: gui.scrollDownConfirmationPanel, }, { ViewName: "confirmation", - Key: gocui.NewKeyName(gocui.MouseWheelUp), - Handler: gui.scrollUpConfirmationPanel, - }, - { - ViewName: "confirmation", - Key: gocui.NewKeyName(gocui.MouseWheelDown), - Handler: gui.scrollDownConfirmationPanel, - }, - { - ViewName: "confirmation", - Key: opts.GetKey(opts.Config.Universal.NextPage), + Keys: opts.GetKeys(opts.Config.Universal.NextPage), Handler: gui.pageDownConfirmationPanel, }, { ViewName: "confirmation", - Key: opts.GetKey(opts.Config.Universal.PrevPage), + Keys: opts.GetKeys(opts.Config.Universal.PrevPage), Handler: gui.pageUpConfirmationPanel, }, { ViewName: "confirmation", - Key: opts.GetKey(opts.Config.Universal.GotoTop), + Keys: opts.GetKeys(opts.Config.Universal.GotoTop), Handler: gui.goToConfirmationPanelTop, }, { ViewName: "confirmation", - Key: opts.GetKey(opts.Config.Universal.GotoTopAlt), - Handler: gui.goToConfirmationPanelTop, - }, - { - ViewName: "confirmation", - Key: opts.GetKey(opts.Config.Universal.GotoBottom), - Handler: gui.goToConfirmationPanelBottom, - }, - { - ViewName: "confirmation", - Key: opts.GetKey(opts.Config.Universal.GotoBottomAlt), + Keys: opts.GetKeys(opts.Config.Universal.GotoBottom), Handler: gui.goToConfirmationPanelBottom, }, { ViewName: "submodules", - Key: opts.GetKey(opts.Config.Universal.CopyToClipboard), + Keys: opts.GetKeys(opts.Config.Universal.CopyToClipboard), Handler: gui.handleCopySelectedSideContextItemToClipboard, GetDisabledReason: gui.getCopySelectedSideContextItemToClipboardDisabledReason, Description: gui.c.Tr.CopySubmoduleNameToClipboard, }, { ViewName: "extras", - Key: gocui.NewKeyName(gocui.MouseWheelUp), + Keys: []gocui.Key{gocui.NewKeyName(gocui.MouseWheelUp)}, Handler: gui.scrollUpExtra, }, { ViewName: "extras", - Key: gocui.NewKeyName(gocui.MouseWheelDown), + Keys: []gocui.Key{gocui.NewKeyName(gocui.MouseWheelDown)}, Handler: gui.scrollDownExtra, }, { ViewName: "extras", Tag: "navigation", - Key: opts.GetKey(opts.Config.Universal.PrevItemAlt), + Keys: opts.GetKeys(opts.Config.Universal.PrevItem), Handler: gui.scrollUpExtra, }, { ViewName: "extras", Tag: "navigation", - Key: opts.GetKey(opts.Config.Universal.PrevItem), - Handler: gui.scrollUpExtra, - }, - { - ViewName: "extras", - Tag: "navigation", - Key: opts.GetKey(opts.Config.Universal.NextItem), + Keys: opts.GetKeys(opts.Config.Universal.NextItem), Handler: gui.scrollDownExtra, }, { ViewName: "extras", - Tag: "navigation", - Key: opts.GetKey(opts.Config.Universal.NextItemAlt), - Handler: gui.scrollDownExtra, - }, - { - ViewName: "extras", - Key: opts.GetKey(opts.Config.Universal.NextPage), + Keys: opts.GetKeys(opts.Config.Universal.NextPage), Handler: gui.pageDownExtrasPanel, }, { ViewName: "extras", - Key: opts.GetKey(opts.Config.Universal.PrevPage), + Keys: opts.GetKeys(opts.Config.Universal.PrevPage), Handler: gui.pageUpExtrasPanel, }, { ViewName: "extras", - Key: opts.GetKey(opts.Config.Universal.GotoTop), + Keys: opts.GetKeys(opts.Config.Universal.GotoTop), Handler: gui.goToExtrasPanelTop, }, { ViewName: "extras", - Key: opts.GetKey(opts.Config.Universal.GotoTopAlt), - Handler: gui.goToExtrasPanelTop, - }, - { - ViewName: "extras", - Key: opts.GetKey(opts.Config.Universal.GotoBottom), - Handler: gui.goToExtrasPanelBottom, - }, - { - ViewName: "extras", - Key: opts.GetKey(opts.Config.Universal.GotoBottomAlt), + Keys: opts.GetKeys(opts.Config.Universal.GotoBottom), Handler: gui.goToExtrasPanelBottom, }, { ViewName: "extras", Tag: "navigation", - Key: gocui.NewKeyName(gocui.MouseLeft), + Keys: []gocui.Key{gocui.NewKeyName(gocui.MouseLeft)}, Handler: gui.handleFocusCommandLog, }, } @@ -372,14 +310,14 @@ func (gui *Gui) GetInitialKeybindings() ([]*types.Binding, []*gocui.ViewMouseBin bindings = append(bindings, []*types.Binding{ { ViewName: "", - Key: opts.GetKey(opts.Config.Universal.NextTab), + Keys: opts.GetKeys(opts.Config.Universal.NextTab), Handler: opts.Guards.NoPopupPanel(gui.handleNextTab), Description: gui.c.Tr.NextTab, Tag: "navigation", }, { ViewName: "", - Key: opts.GetKey(opts.Config.Universal.PrevTab), + Keys: opts.GetKeys(opts.Config.Universal.PrevTab), Handler: opts.Guards.NoPopupPanel(gui.handlePrevTab), Description: gui.c.Tr.PrevTab, Tag: "navigation", @@ -418,9 +356,7 @@ func (gui *Gui) resetKeybindings() error { bindings, mouseBindings := gui.GetInitialKeybindingsWithCustomCommands() for _, binding := range bindings { - if err := gui.SetKeybinding(binding); err != nil { - return err - } + gui.SetKeybinding(binding) } for _, binding := range mouseBindings { @@ -445,12 +381,14 @@ func (gui *Gui) resetKeybindings() error { return nil } -func (gui *Gui) SetKeybinding(binding *types.Binding) error { +func (gui *Gui) SetKeybinding(binding *types.Binding) { handler := func(g *gocui.Gui, v *gocui.View) error { return gui.callKeybindingHandler(binding) } - return gui.g.SetKeybinding(binding.ViewName, binding.Key, handler) + for _, key := range binding.Keys { + gui.g.SetKeybinding(binding.ViewName, key, handler) + } } func (gui *Gui) SetMouseKeybinding(binding *gocui.ViewMouseBinding) error { diff --git a/pkg/gui/menu_panel.go b/pkg/gui/menu_panel.go index 248da40fb..23016b9a5 100644 --- a/pkg/gui/menu_panel.go +++ b/pkg/gui/menu_panel.go @@ -27,11 +27,15 @@ func (gui *Gui) createMenu(opts types.CreateMenuOptions) error { maxColumnSize := 1 + // Only the primary key of each navigation binding is reserved as + // essential; alternates (e.g. the historical j/k that lived under + // `*Alt` fields) stay available to be reused by menu items, which + // take precedence over the inherited list bindings. essentialKeys := []gocui.Key{ - config.GetValidatedKeyBindingKey(gui.c.UserConfig().Keybinding.Universal.ConfirmMenu), - config.GetValidatedKeyBindingKey(gui.c.UserConfig().Keybinding.Universal.Return), - config.GetValidatedKeyBindingKey(gui.c.UserConfig().Keybinding.Universal.PrevItem), - config.GetValidatedKeyBindingKey(gui.c.UserConfig().Keybinding.Universal.NextItem), + config.GetValidatedKeyBindingKeys(gui.c.UserConfig().Keybinding.Universal.ConfirmMenu)[0], + config.GetValidatedKeyBindingKeys(gui.c.UserConfig().Keybinding.Universal.Return)[0], + config.GetValidatedKeyBindingKeys(gui.c.UserConfig().Keybinding.Universal.PrevItem)[0], + config.GetValidatedKeyBindingKeys(gui.c.UserConfig().Keybinding.Universal.NextItem)[0], } for _, item := range opts.Items { @@ -46,8 +50,10 @@ func (gui *Gui) createMenu(opts types.CreateMenuOptions) error { maxColumnSize = max(maxColumnSize, len(item.LabelColumns)) // Remove all item keybindings that are the same as one of the essential bindings - if !opts.KeepConflictingKeybindings && lo.Contains(essentialKeys, item.Key) { - item.Key = gocui.Key{} + if !opts.KeepConflictingKeybindings { + item.Keys = lo.Filter(item.Keys, func(k gocui.Key, _ int) bool { + return !lo.Contains(essentialKeys, k) + }) } } diff --git a/pkg/gui/options_map.go b/pkg/gui/options_map.go index c6360e27c..890d2f2de 100644 --- a/pkg/gui/options_map.go +++ b/pkg/gui/options_map.go @@ -41,12 +41,12 @@ func (self *OptionsMapMgr) renderContextOptionsMap() { globalBindings := self.c.Contexts().Global.GetKeybindings(self.c.KeybindingsOpts()) currentContextKeys := set.NewFromSlice( - lo.Map(currentContextBindings, func(binding *types.Binding, _ int) gocui.Key { - return binding.Key + lo.FlatMap(currentContextBindings, func(binding *types.Binding, _ int) []gocui.Key { + return binding.Keys })) allBindings := append(currentContextBindings, lo.Filter(globalBindings, func(b *types.Binding, _ int) bool { - return !currentContextKeys.Includes(b.Key) + return len(b.Keys) == 0 || !currentContextKeys.Includes(b.Keys[0]) })...) bindingsToDisplay := lo.Filter(allBindings, func(binding *types.Binding, _ int) bool { @@ -60,7 +60,7 @@ func (self *OptionsMapMgr) renderContextOptionsMap() { } return bindingInfo{ - key: config.LabelForKey(binding.Key), + key: config.LabelForKey(binding.Keys[0]), description: binding.GetShortDescription(), style: displayStyle, } @@ -70,7 +70,7 @@ func (self *OptionsMapMgr) renderContextOptionsMap() { if currentContext.GetKey() == context.LOCAL_COMMITS_CONTEXT_KEY { if self.c.Modes().CherryPicking.Active() { optionsMap = utils.Prepend(optionsMap, bindingInfo{ - key: self.c.KeybindingsOpts().Config.Commits.PasteCommits, + key: self.c.KeybindingsOpts().Config.Commits.PasteCommits.String(), description: self.c.Tr.PasteCommits, style: style.FgCyan, }) @@ -78,7 +78,7 @@ func (self *OptionsMapMgr) renderContextOptionsMap() { if self.c.Model().BisectInfo.Started() { optionsMap = utils.Prepend(optionsMap, bindingInfo{ - key: self.c.KeybindingsOpts().Config.Commits.ViewBisectOptions, + key: self.c.KeybindingsOpts().Config.Commits.ViewBisectOptions.String(), description: self.c.Tr.ViewBisectOptions, style: style.FgGreen, }) @@ -88,7 +88,7 @@ func (self *OptionsMapMgr) renderContextOptionsMap() { // Mode-specific global keybindings if state := self.c.Model().WorkingTreeStateAtLastCommitRefresh; state.Any() { optionsMap = utils.Prepend(optionsMap, bindingInfo{ - key: self.c.KeybindingsOpts().Config.Universal.CreateRebaseOptionsMenu, + key: self.c.KeybindingsOpts().Config.Universal.CreateRebaseOptionsMenu.String(), description: state.OptionsMapTitle(self.c.Tr), style: style.FgYellow, }) @@ -96,7 +96,7 @@ func (self *OptionsMapMgr) renderContextOptionsMap() { if self.c.Git().Patch.PatchBuilder.Active() { optionsMap = utils.Prepend(optionsMap, bindingInfo{ - key: self.c.KeybindingsOpts().Config.Universal.CreatePatchOptionsMenu, + key: self.c.KeybindingsOpts().Config.Universal.CreatePatchOptionsMenu.String(), description: self.c.Tr.ViewPatchOptions, style: style.FgYellow, }) diff --git a/pkg/gui/services/custom_commands/client.go b/pkg/gui/services/custom_commands/client.go index 4b927cf68..3f16b8ba8 100644 --- a/pkg/gui/services/custom_commands/client.go +++ b/pkg/gui/services/custom_commands/client.go @@ -45,7 +45,7 @@ func (self *Client) GetCustomCommandKeybindings() ([]*types.Binding, error) { } bindings = append(bindings, &types.Binding{ ViewName: "", // custom commands menus are global; we filter the commands inside by context - Key: config.GetValidatedKeyBindingKey(customCommand.Key), + Keys: config.GetValidatedKeyBindingKeys(customCommand.Key), Handler: handler, Description: getCustomCommandsMenuDescription(customCommand, self.c.Tr), OpensMenu: true, @@ -72,7 +72,7 @@ func (self *Client) showCustomCommandsMenu(customCommand config.CustomCommand) e } menuItems = append(menuItems, &types.MenuItem{ Label: subCommand.GetDescription(), - Key: config.GetValidatedKeyBindingKey(subCommand.Key), + Keys: config.GetValidatedKeyBindingKeys(subCommand.Key), OnPress: handler, OpensMenu: true, }) @@ -92,7 +92,7 @@ func (self *Client) showCustomCommandsMenu(customCommand config.CustomCommand) e menuItems = append(menuItems, &types.MenuItem{ Label: subCommand.GetDescription(), - Key: config.GetValidatedKeyBindingKey(subCommand.Key), + Keys: config.GetValidatedKeyBindingKeys(subCommand.Key), OnPress: self.handlerCreator.call(subCommand), }) } diff --git a/pkg/gui/services/custom_commands/handler_creator.go b/pkg/gui/services/custom_commands/handler_creator.go index 412fc7de6..19694c481 100644 --- a/pkg/gui/services/custom_commands/handler_creator.go +++ b/pkg/gui/services/custom_commands/handler_creator.go @@ -232,7 +232,7 @@ func (self *HandlerCreator) menuPrompt(prompt *config.CustomCommandPrompt, wrapp OnPress: func() error { return wrappedF(option.Value) }, - Key: config.GetValidatedKeyBindingKey(option.Key), + Keys: config.GetValidatedKeyBindingKeys(option.Key), } }) diff --git a/pkg/gui/services/custom_commands/keybinding_creator.go b/pkg/gui/services/custom_commands/keybinding_creator.go index 644e6af45..f35fc8fd4 100644 --- a/pkg/gui/services/custom_commands/keybinding_creator.go +++ b/pkg/gui/services/custom_commands/keybinding_creator.go @@ -35,7 +35,7 @@ func (self *KeybindingCreator) call(customCommand config.CustomCommand, handler return lo.Map(viewNames, func(viewName string, _ int) *types.Binding { return &types.Binding{ ViewName: viewName, - Key: config.GetValidatedKeyBindingKey(customCommand.Key), + Keys: config.GetValidatedKeyBindingKeys(customCommand.Key), Handler: handler, Description: customCommand.GetDescription(), } @@ -80,9 +80,9 @@ func formatUnknownContextError(customCommand config.CustomCommand) error { return string(key) }) - return fmt.Errorf("Error when setting custom command keybindings: unknown context: %s. Key: %s, Command: %s.\nPermitted contexts: %s", customCommand.Context, customCommand.Key, customCommand.Command, strings.Join(allContextKeyStrings, ", ")) + return fmt.Errorf("Error when setting custom command keybindings: unknown context: %s. Key: %s, Command: %s.\nPermitted contexts: %s", customCommand.Context, customCommand.Key.String(), customCommand.Command, strings.Join(allContextKeyStrings, ", ")) } func formatContextNotProvidedError(customCommand config.CustomCommand) error { - return fmt.Errorf("Error parsing custom command keybindings: context not provided (use context: 'global' for the global context). Key: %s, Command: %s", customCommand.Key, customCommand.Command) + return fmt.Errorf("Error parsing custom command keybindings: context not provided (use context: 'global' for the global context). Key: %s, Command: %s", customCommand.Key.String(), customCommand.Command) } diff --git a/pkg/gui/types/common.go b/pkg/gui/types/common.go index 5856bb5e0..92f004634 100644 --- a/pkg/gui/types/common.go +++ b/pkg/gui/types/common.go @@ -259,9 +259,10 @@ type MenuItem struct { // Only applies when Label is used OpensMenu bool - // If Key is defined it allows the user to press the key to invoke the menu - // item, as opposed to having to navigate to it - Key gocui.Key + // If Keys is non-empty, the user can press any of these keys to invoke the + // menu item, as opposed to having to navigate to it. Only the first key is + // shown in the menu; the alternates are matched silently. + Keys []gocui.Key // A widget to show in front of the menu item. Supported widget types are // checkboxes and radio buttons, diff --git a/pkg/gui/types/context.go b/pkg/gui/types/context.go index 35201c6cf..416b39b95 100644 --- a/pkg/gui/types/context.go +++ b/pkg/gui/types/context.go @@ -239,9 +239,9 @@ type OnFocusLostOpts struct { type ContextKey string type KeybindingsOpts struct { - GetKey func(key string) gocui.Key - Config config.KeybindingConfig - Guards KeybindingGuards + GetKeys func(keys config.Keybinding) []gocui.Key + Config config.KeybindingConfig + Guards KeybindingGuards } type ( diff --git a/pkg/gui/types/keybindings.go b/pkg/gui/types/keybindings.go index 63079ccc7..337162d76 100644 --- a/pkg/gui/types/keybindings.go +++ b/pkg/gui/types/keybindings.go @@ -11,7 +11,7 @@ import ( type Binding struct { ViewName string Handler func() error - Key gocui.Key + Keys []gocui.Key Description string // DescriptionFunc is used instead of Description if non-nil, and is useful for dynamic // descriptions that change depending on context. Important: this must not be an expensive call. diff --git a/pkg/gui/views.go b/pkg/gui/views.go index 093257998..ecfc0ddcd 100644 --- a/pkg/gui/views.go +++ b/pkg/gui/views.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" + "github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/gocui" "github.com/jesseduffield/lazygit/pkg/gui/context" "github.com/jesseduffield/lazygit/pkg/theme" @@ -210,14 +211,14 @@ func (gui *Gui) configureViewProperties() { gui.Views.CommitDescription.TextArea.AutoWrapWidth = gui.c.UserConfig().Git.Commit.AutoWrapWidth if gui.c.UserConfig().Gui.ShowPanelJumps { - keyToTitlePrefix := func(key string) string { - if key == "" { + keyToTitlePrefix := func(binding config.Keybinding) string { + if len(binding) == 0 { return "" } - return fmt.Sprintf("[%s]", key) + return fmt.Sprintf("[%s]", binding[0]) } jumpBindings := gui.c.UserConfig().Keybinding.Universal.JumpToBlock - jumpLabels := lo.Map(jumpBindings, func(binding string, _ int) string { + jumpLabels := lo.Map(jumpBindings, func(binding config.Keybinding, _ int) string { return keyToTitlePrefix(binding) }) diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 31b18dca0..ee5f4ceec 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -337,7 +337,6 @@ type TranslationSet struct { CommitDescriptionTitle string CommitDescriptionSubTitle string CommitDescriptionFooter string - CommitDescriptionFooterTwoBindings string CommitHooksDisabledSubTitle string LocalBranchesTitle string SearchTitle string @@ -614,10 +613,10 @@ type TranslationSet struct { SelectRemoteRepository string FetchingPullRequests string Keybindings string - KeybindingsLegend string KeybindingsMenuSectionLocal string KeybindingsMenuSectionGlobal string KeybindingsMenuSectionNavigation string + KeybindingsTooltip string RenameBranch string Upstream string BranchUpstreamOptionsTitle string @@ -1457,7 +1456,6 @@ func EnglishTranslationSet() *TranslationSet { CommitDescriptionTitle: "Commit description", CommitDescriptionSubTitle: "Press {{.togglePanelKeyBinding}} to toggle focus, {{.commitMenuKeybinding}} to open menu", CommitDescriptionFooter: "Press {{.confirmInEditorKeybinding}} to submit", - CommitDescriptionFooterTwoBindings: "Press {{.confirmInEditorKeybinding1}} or {{.confirmInEditorKeybinding2}} to submit", CommitHooksDisabledSubTitle: "(hooks disabled)", LocalBranchesTitle: "Local branches", SearchTitle: "Search", @@ -1478,6 +1476,7 @@ func EnglishTranslationSet() *TranslationSet { KeybindingsMenuSectionLocal: "Local", KeybindingsMenuSectionGlobal: "Global", KeybindingsMenuSectionNavigation: "Navigation", + KeybindingsTooltip: "Keybindings: ", RebasingTitle: "Rebase '{{.checkedOutBranch}}'", RebasingFromBaseCommitTitle: "Rebase '{{.checkedOutBranch}}' from marked base", SimpleRebase: "Simple rebase onto '{{.ref}}'", diff --git a/pkg/integration/clients/tui.go b/pkg/integration/clients/tui.go index c4f8f94ab..0f07b5b19 100644 --- a/pkg/integration/clients/tui.go +++ b/pkg/integration/clients/tui.go @@ -43,7 +43,7 @@ func RunTUI(raceDetector bool) { g.SetManagerFunc(app.layout) - if err := g.SetKeybinding("list", gocui.NewKeyName(gocui.KeyArrowUp), func(*gocui.Gui, *gocui.View) error { + g.SetKeybinding("list", gocui.NewKeyName(gocui.KeyArrowUp), func(*gocui.Gui, *gocui.View) error { if app.itemIdx > 0 { app.itemIdx-- } @@ -53,11 +53,9 @@ func RunTUI(raceDetector bool) { } listView.FocusPoint(0, app.itemIdx, true) return nil - }); err != nil { - log.Panicln(err) - } + }) - if err := g.SetKeybinding("list", gocui.NewKeyName(gocui.KeyArrowDown), func(*gocui.Gui, *gocui.View) error { + g.SetKeybinding("list", gocui.NewKeyName(gocui.KeyArrowDown), func(*gocui.Gui, *gocui.View) error { if app.itemIdx < len(app.filteredTests)-1 { app.itemIdx++ } @@ -68,19 +66,13 @@ func RunTUI(raceDetector bool) { } listView.FocusPoint(0, app.itemIdx, true) return nil - }); err != nil { - log.Panicln(err) - } + }) - if err := g.SetKeybinding("list", gocui.NewKeyStrMod("c", gocui.ModCtrl), quit); err != nil { - log.Panicln(err) - } + g.SetKeybinding("list", gocui.NewKeyStrMod("c", gocui.ModCtrl), quit) - if err := g.SetKeybinding("list", gocui.NewKeyRune('q'), quit); err != nil { - log.Panicln(err) - } + g.SetKeybinding("list", gocui.NewKeyRune('q'), quit) - if err := g.SetKeybinding("list", gocui.NewKeyRune('s'), func(*gocui.Gui, *gocui.View) error { + g.SetKeybinding("list", gocui.NewKeyRune('s'), func(*gocui.Gui, *gocui.View) error { currentTest := app.getCurrentTest() if currentTest == nil { return nil @@ -89,11 +81,9 @@ func RunTUI(raceDetector bool) { suspendAndRunTest(currentTest, true, false, raceDetector, 0) return nil - }); err != nil { - log.Panicln(err) - } + }) - if err := g.SetKeybinding("list", gocui.NewKeyName(gocui.KeyEnter), func(*gocui.Gui, *gocui.View) error { + g.SetKeybinding("list", gocui.NewKeyName(gocui.KeyEnter), func(*gocui.Gui, *gocui.View) error { currentTest := app.getCurrentTest() if currentTest == nil { return nil @@ -102,11 +92,9 @@ func RunTUI(raceDetector bool) { suspendAndRunTest(currentTest, false, false, raceDetector, 0) return nil - }); err != nil { - log.Panicln(err) - } + }) - if err := g.SetKeybinding("list", gocui.NewKeyRune('t'), func(*gocui.Gui, *gocui.View) error { + g.SetKeybinding("list", gocui.NewKeyRune('t'), func(*gocui.Gui, *gocui.View) error { currentTest := app.getCurrentTest() if currentTest == nil { return nil @@ -115,11 +103,9 @@ func RunTUI(raceDetector bool) { suspendAndRunTest(currentTest, false, false, raceDetector, SLOW_INPUT_DELAY) return nil - }); err != nil { - log.Panicln(err) - } + }) - if err := g.SetKeybinding("list", gocui.NewKeyRune('d'), func(*gocui.Gui, *gocui.View) error { + g.SetKeybinding("list", gocui.NewKeyRune('d'), func(*gocui.Gui, *gocui.View) error { currentTest := app.getCurrentTest() if currentTest == nil { return nil @@ -128,11 +114,9 @@ func RunTUI(raceDetector bool) { suspendAndRunTest(currentTest, false, true, raceDetector, 0) return nil - }); err != nil { - log.Panicln(err) - } + }) - if err := g.SetKeybinding("list", gocui.NewKeyRune('o'), func(*gocui.Gui, *gocui.View) error { + g.SetKeybinding("list", gocui.NewKeyRune('o'), func(*gocui.Gui, *gocui.View) error { currentTest := app.getCurrentTest() if currentTest == nil { return nil @@ -144,11 +128,9 @@ func RunTUI(raceDetector bool) { } return nil - }); err != nil { - log.Panicln(err) - } + }) - if err := g.SetKeybinding("list", gocui.NewKeyRune('O'), func(*gocui.Gui, *gocui.View) error { + g.SetKeybinding("list", gocui.NewKeyRune('O'), func(*gocui.Gui, *gocui.View) error { currentTest := app.getCurrentTest() if currentTest == nil { return nil @@ -160,11 +142,9 @@ func RunTUI(raceDetector bool) { } return nil - }); err != nil { - log.Panicln(err) - } + }) - if err := g.SetKeybinding("list", gocui.NewKeyRune('/'), func(*gocui.Gui, *gocui.View) error { + g.SetKeybinding("list", gocui.NewKeyRune('/'), func(*gocui.Gui, *gocui.View) error { app.filtering = true if _, err := g.SetCurrentView("editor"); err != nil { return err @@ -176,12 +156,10 @@ func RunTUI(raceDetector bool) { editorView.Clear() return nil - }); err != nil { - log.Panicln(err) - } + }) // not using the editor yet, but will use it to help filter the list - if err := g.SetKeybinding("editor", gocui.NewKeyName(gocui.KeyEsc), func(*gocui.Gui, *gocui.View) error { + g.SetKeybinding("editor", gocui.NewKeyName(gocui.KeyEsc), func(*gocui.Gui, *gocui.View) error { app.filtering = false if _, err := g.SetCurrentView("list"); err != nil { return err @@ -194,11 +172,9 @@ func RunTUI(raceDetector bool) { app.editorView.Reset() return nil - }); err != nil { - log.Panicln(err) - } + }) - if err := g.SetKeybinding("editor", gocui.NewKeyName(gocui.KeyEnter), func(*gocui.Gui, *gocui.View) error { + g.SetKeybinding("editor", gocui.NewKeyName(gocui.KeyEnter), func(*gocui.Gui, *gocui.View) error { app.filtering = false if _, err := g.SetCurrentView("list"); err != nil { @@ -208,9 +184,7 @@ func RunTUI(raceDetector bool) { app.renderTests() return nil - }); err != nil { - log.Panicln(err) - } + }) err = g.MainLoop() g.Close() diff --git a/pkg/integration/components/commit_description_panel_driver.go b/pkg/integration/components/commit_description_panel_driver.go index caf6d1b8a..6281e756b 100644 --- a/pkg/integration/components/commit_description_panel_driver.go +++ b/pkg/integration/components/commit_description_panel_driver.go @@ -42,7 +42,7 @@ func (self *CommitDescriptionPanelDriver) GoToBeginning() *CommitDescriptionPane } func (self *CommitDescriptionPanelDriver) AddCoAuthor(author string) *CommitDescriptionPanelDriver { - self.t.press(self.t.keys.CommitMessage.CommitMenu) + self.t.press(self.t.keys.CommitMessage.CommitMenu[0]) self.t.ExpectPopup().Menu().Title(Equals("Commit Menu")). Select(Contains("Add co-author")). Confirm() diff --git a/pkg/integration/components/commit_message_panel_driver.go b/pkg/integration/components/commit_message_panel_driver.go index 047cc59b1..f24950f13 100644 --- a/pkg/integration/components/commit_message_panel_driver.go +++ b/pkg/integration/components/commit_message_panel_driver.go @@ -73,6 +73,6 @@ func (self *CommitMessagePanelDriver) SelectNextMessage() *CommitMessagePanelDri } func (self *CommitMessagePanelDriver) OpenCommitMenu() *CommitMessagePanelDriver { - self.t.press(self.t.keys.CommitMessage.CommitMenu) + self.t.press(self.t.keys.CommitMessage.CommitMenu[0]) return self } diff --git a/pkg/integration/components/prompt_driver.go b/pkg/integration/components/prompt_driver.go index 2c29dd7c4..1000af007 100644 --- a/pkg/integration/components/prompt_driver.go +++ b/pkg/integration/components/prompt_driver.go @@ -68,7 +68,7 @@ func (self *PromptDriver) SuggestionTopLines(matchers ...*TextMatcher) *PromptDr } func (self *PromptDriver) ConfirmFirstSuggestion() { - self.t.press(self.t.keys.Universal.TogglePanel) + self.t.press(self.t.keys.Universal.TogglePanel[0]) self.t.Views().Suggestions(). IsFocused(). SelectedLineIdx(0). @@ -76,7 +76,7 @@ func (self *PromptDriver) ConfirmFirstSuggestion() { } func (self *PromptDriver) ConfirmSuggestion(matcher *TextMatcher) { - self.t.press(self.t.keys.Universal.TogglePanel) + self.t.press(self.t.keys.Universal.TogglePanel[0]) self.t.Views().Suggestions(). IsFocused(). NavigateToLine(matcher). @@ -84,19 +84,19 @@ func (self *PromptDriver) ConfirmSuggestion(matcher *TextMatcher) { } func (self *PromptDriver) DeleteSuggestion(matcher *TextMatcher) *PromptDriver { - self.t.press(self.t.keys.Universal.TogglePanel) + self.t.press(self.t.keys.Universal.TogglePanel[0]) self.t.Views().Suggestions(). IsFocused(). NavigateToLine(matcher) - self.t.press(self.t.keys.Universal.Remove) + self.t.press(self.t.keys.Universal.Remove[0]) return self } func (self *PromptDriver) EditSuggestion(matcher *TextMatcher) *PromptDriver { - self.t.press(self.t.keys.Universal.TogglePanel) + self.t.press(self.t.keys.Universal.TogglePanel[0]) self.t.Views().Suggestions(). IsFocused(). NavigateToLine(matcher) - self.t.press(self.t.keys.Universal.Edit) + self.t.press(self.t.keys.Universal.Edit[0]) return self } diff --git a/pkg/integration/components/shell.go b/pkg/integration/components/shell.go index 2e5fa01ce..70b12146a 100644 --- a/pkg/integration/components/shell.go +++ b/pkg/integration/components/shell.go @@ -77,7 +77,7 @@ func (self *Shell) RunShellCommand(cmdStr string) *Shell { } cmd := exec.Command(shell, shellArg, cmdStr) - cmd.Env = os.Environ() + cmd.Env = self.env cmd.Dir = self.dir output, err := cmd.CombinedOutput() diff --git a/pkg/integration/components/test_driver.go b/pkg/integration/components/test_driver.go index a1775239c..8294f3b46 100644 --- a/pkg/integration/components/test_driver.go +++ b/pkg/integration/components/test_driver.go @@ -52,8 +52,8 @@ func (self *TestDriver) click(x, y int) { // Should only be used in specific cases where you're doing something weird! // E.g. invoking a global keybinding from within a popup. // You probably shouldn't use this function, and should instead go through a view like t.Views().Commit().Focus().Press(...) -func (self *TestDriver) GlobalPress(keyStr string) { - self.press(keyStr) +func (self *TestDriver) GlobalPress(key config.Keybinding) { + self.press(key[0]) } func (self *TestDriver) typeContent(content string) { diff --git a/pkg/integration/components/view_driver.go b/pkg/integration/components/view_driver.go index ee7c5b68a..e9e5fbbc7 100644 --- a/pkg/integration/components/view_driver.go +++ b/pkg/integration/components/view_driver.go @@ -4,6 +4,7 @@ import ( "fmt" "strings" + "github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/gocui" "github.com/samber/lo" ) @@ -362,7 +363,7 @@ func (self *ViewDriver) Focus() *ViewDriver { if lo.Contains(window.viewNames, viewName) { tabIndex := lo.IndexOf(window.viewNames, viewName) // jump to the desired window - self.t.press(self.t.keys.Universal.JumpToBlock[windowIndex]) + self.t.press(self.t.keys.Universal.JumpToBlock[windowIndex][0]) // assert we're in the window before continuing self.t.assertWithRetries(func() (bool, string) { @@ -376,11 +377,11 @@ func (self *ViewDriver) Focus() *ViewDriver { currentViewTabIndex := lo.IndexOf(window.viewNames, currentViewName) if tabIndex > currentViewTabIndex { for range tabIndex - currentViewTabIndex { - self.t.press(self.t.keys.Universal.NextTab) + self.t.press(self.t.keys.Universal.NextTab[0]) } } else if tabIndex < currentViewTabIndex { for range currentViewTabIndex - tabIndex { - self.t.press(self.t.keys.Universal.PrevTab) + self.t.press(self.t.keys.Universal.PrevTab[0]) } } @@ -407,10 +408,10 @@ func (self *ViewDriver) IsFocused() *ViewDriver { return self } -func (self *ViewDriver) Press(keyStr string) *ViewDriver { +func (self *ViewDriver) Press(key config.Keybinding) *ViewDriver { self.IsFocused() - self.t.press(keyStr) + self.t.press(key[0]) return self } @@ -423,10 +424,10 @@ func (self *ViewDriver) Delay() *ViewDriver { // for use when typing or navigating, because in demos we want that to happen // faster -func (self *ViewDriver) PressFast(keyStr string) *ViewDriver { +func (self *ViewDriver) PressFast(key config.Keybinding) *ViewDriver { self.IsFocused() - self.t.pressFast(keyStr) + self.t.pressFast(key[0]) return self } diff --git a/pkg/integration/tests/commit/search.go b/pkg/integration/tests/commit/search.go index 5439a1b32..25f6bb233 100644 --- a/pkg/integration/tests/commit/search.go +++ b/pkg/integration/tests/commit/search.go @@ -58,7 +58,7 @@ var Search = NewIntegrationTest(NewIntegrationTestArgs{ Contains("two").IsSelected(), Contains("one"), ). - Press("n"). + Press(config.Keybinding{"n"}). Tap(func() { t.Views().Search().IsVisible().Content(Contains("matches for 'o' (3 of 3)")) }). @@ -68,7 +68,7 @@ var Search = NewIntegrationTest(NewIntegrationTestArgs{ Contains("two"), Contains("one").IsSelected(), ). - Press("n"). + Press(config.Keybinding{"n"}). Tap(func() { t.Views().Search().IsVisible().Content(Contains("matches for 'o' (1 of 3)")) }). @@ -78,7 +78,7 @@ var Search = NewIntegrationTest(NewIntegrationTestArgs{ Contains("two"), Contains("one"), ). - Press("n"). + Press(config.Keybinding{"n"}). Tap(func() { t.Views().Search().IsVisible().Content(Contains("matches for 'o' (2 of 3)")) }). @@ -88,7 +88,7 @@ var Search = NewIntegrationTest(NewIntegrationTestArgs{ Contains("two").IsSelected(), Contains("one"), ). - Press("N"). + Press(config.Keybinding{"N"}). Tap(func() { t.Views().Search().IsVisible().Content(Contains("matches for 'o' (1 of 3)")) }). @@ -98,7 +98,7 @@ var Search = NewIntegrationTest(NewIntegrationTestArgs{ Contains("two"), Contains("one"), ). - Press("N"). + Press(config.Keybinding{"N"}). Tap(func() { t.Views().Search().IsVisible().Content(Contains("matches for 'o' (3 of 3)")) }). @@ -112,7 +112,7 @@ var Search = NewIntegrationTest(NewIntegrationTestArgs{ Tap(func() { t.Views().Search().IsVisible().Content(Contains("matches for 'o' (1 of 3)")) }). - Press("N"). + Press(config.Keybinding{"N"}). Tap(func() { t.Views().Search().IsVisible().Content(Contains("matches for 'o' (1 of 3)")) }). @@ -146,7 +146,7 @@ var Search = NewIntegrationTest(NewIntegrationTestArgs{ Contains("two"), Contains("one"), ). - Press("n"). + Press(config.Keybinding{"n"}). Tap(func() { t.Views().Search().IsVisible().Content(Contains("matches for 't' (1 of 2)")) }). diff --git a/pkg/integration/tests/config/custom_commands_in_per_repo_config.go b/pkg/integration/tests/config/custom_commands_in_per_repo_config.go index 81f8724aa..929c7eae9 100644 --- a/pkg/integration/tests/config/custom_commands_in_per_repo_config.go +++ b/pkg/integration/tests/config/custom_commands_in_per_repo_config.go @@ -17,12 +17,12 @@ var CustomCommandsInPerRepoConfig = NewIntegrationTest(NewIntegrationTestArgs{ cfg.GetUserConfig().CustomCommands = []config.CustomCommand{ { - Key: "X", + Key: config.Keybinding{"X"}, Context: "global", Command: "printf 'global X' > file.txt", }, { - Key: "Y", + Key: config.Keybinding{"Y"}, Context: "global", Command: "printf 'global Y' > file.txt", }, @@ -48,13 +48,13 @@ customCommands: ).Confirm() t.Views().Status().Content(Contains("other → master")) - t.GlobalPress("X") + t.GlobalPress(config.Keybinding{"X"}) t.FileSystem().FileContent("../other/file.txt", Equals("global X")) - t.GlobalPress("Y") + t.GlobalPress(config.Keybinding{"Y"}) t.FileSystem().FileContent("../other/file.txt", Equals("local Y")) - t.GlobalPress("Z") + t.GlobalPress(config.Keybinding{"Z"}) t.FileSystem().FileContent("../other/file.txt", Equals("local Z")) }, }) diff --git a/pkg/integration/tests/custom_commands/access_commit_properties.go b/pkg/integration/tests/custom_commands/access_commit_properties.go index 22d1d0631..0823b1033 100644 --- a/pkg/integration/tests/custom_commands/access_commit_properties.go +++ b/pkg/integration/tests/custom_commands/access_commit_properties.go @@ -17,7 +17,7 @@ var AccessCommitProperties = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.GetUserConfig().CustomCommands = []config.CustomCommand{ { - Key: "X", + Key: config.Keybinding{"X"}, Context: "commits", Command: "printf '%s\n%s\n%s' '{{ .SelectedLocalCommit.Name }}' '{{ .SelectedLocalCommit.Hash }}' '{{ .SelectedLocalCommit.Sha }}' > file.txt", }, @@ -29,7 +29,7 @@ var AccessCommitProperties = NewIntegrationTest(NewIntegrationTestArgs{ Lines( Contains("my change").IsSelected(), ). - Press("X") + Press(config.Keybinding{"X"}) hash := t.Git().GetCommitHash("HEAD") t.FileSystem().FileContent("file.txt", Equals(fmt.Sprintf("my change\n%s\n%s", hash, hash))) diff --git a/pkg/integration/tests/custom_commands/basic_command.go b/pkg/integration/tests/custom_commands/basic_command.go index 10a9058b7..f5ae90a96 100644 --- a/pkg/integration/tests/custom_commands/basic_command.go +++ b/pkg/integration/tests/custom_commands/basic_command.go @@ -15,7 +15,7 @@ var BasicCommand = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.GetUserConfig().CustomCommands = []config.CustomCommand{ { - Key: "a", + Key: config.Keybinding{"a"}, Context: "files", Command: "touch myfile", }, @@ -25,7 +25,7 @@ var BasicCommand = NewIntegrationTest(NewIntegrationTestArgs{ t.Views().Files(). IsEmpty(). IsFocused(). - Press("a"). + Press(config.Keybinding{"a"}). Lines( Contains("myfile"), ) diff --git a/pkg/integration/tests/custom_commands/check_for_conflicts.go b/pkg/integration/tests/custom_commands/check_for_conflicts.go index d3376b456..5d7924c90 100644 --- a/pkg/integration/tests/custom_commands/check_for_conflicts.go +++ b/pkg/integration/tests/custom_commands/check_for_conflicts.go @@ -16,7 +16,7 @@ var CheckForConflicts = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.GetUserConfig().CustomCommands = []config.CustomCommand{ { - Key: "m", + Key: config.Keybinding{"m"}, Context: "localBranches", Command: "git merge {{ .SelectedLocalBranch.Name | quote }}", After: &config.CustomCommandAfterHook{ @@ -35,7 +35,7 @@ var CheckForConflicts = NewIntegrationTest(NewIntegrationTestArgs{ Contains("second-change-branch"), ). NavigateToLine(Contains("second-change-branch")). - Press("m") + Press(config.Keybinding{"m"}) t.Common().AcknowledgeConflicts() }, diff --git a/pkg/integration/tests/custom_commands/conditional_prompt_false_string.go b/pkg/integration/tests/custom_commands/conditional_prompt_false_string.go index a60db1036..e8a64ea0d 100644 --- a/pkg/integration/tests/custom_commands/conditional_prompt_false_string.go +++ b/pkg/integration/tests/custom_commands/conditional_prompt_false_string.go @@ -15,7 +15,7 @@ var ConditionalPromptFalseString = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.GetUserConfig().CustomCommands = []config.CustomCommand{ { - Key: "a", + Key: config.Keybinding{"a"}, Context: "files", Command: `echo "{{.Form.Choice}}" > result.txt`, Prompts: []config.CustomCommandPrompt{ @@ -55,7 +55,7 @@ var ConditionalPromptFalseString = NewIntegrationTest(NewIntegrationTestArgs{ Run: func(t *TestDriver, keys config.KeybindingConfig) { t.Views().Files(). IsFocused(). - Press("a") + Press(config.Keybinding{"a"}) t.ExpectPopup().Menu().Title(Equals("Pick one")).Select(Contains("foo")).Confirm() diff --git a/pkg/integration/tests/custom_commands/conditional_prompt_false_value.go b/pkg/integration/tests/custom_commands/conditional_prompt_false_value.go index 44378ce22..15379c7b4 100644 --- a/pkg/integration/tests/custom_commands/conditional_prompt_false_value.go +++ b/pkg/integration/tests/custom_commands/conditional_prompt_false_value.go @@ -15,7 +15,7 @@ var ConditionalPromptFalseValue = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.GetUserConfig().CustomCommands = []config.CustomCommand{ { - Key: "a", + Key: config.Keybinding{"a"}, Context: "files", Command: `echo "{{.Form.Word}} {{.Form.Extra}}" > result.txt`, Prompts: []config.CustomCommandPrompt{ @@ -37,7 +37,7 @@ var ConditionalPromptFalseValue = NewIntegrationTest(NewIntegrationTestArgs{ Run: func(t *TestDriver, keys config.KeybindingConfig) { t.Views().Files(). IsFocused(). - Press("a") + Press(config.Keybinding{"a"}) t.ExpectPopup().Prompt().Title(Equals("Enter a word")).Type("false").Confirm() diff --git a/pkg/integration/tests/custom_commands/conditional_prompts.go b/pkg/integration/tests/custom_commands/conditional_prompts.go index 36aab67df..ef743282a 100644 --- a/pkg/integration/tests/custom_commands/conditional_prompts.go +++ b/pkg/integration/tests/custom_commands/conditional_prompts.go @@ -15,7 +15,7 @@ var ConditionalPrompts = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.GetUserConfig().CustomCommands = []config.CustomCommand{ { - Key: "a", + Key: config.Keybinding{"a"}, Context: "files", Command: `echo "{{.Form.Choice}}{{if .Form.Detail}} {{.Form.Detail}}{{end}}" > result.txt`, Prompts: []config.CustomCommandPrompt{ @@ -28,13 +28,13 @@ var ConditionalPrompts = NewIntegrationTest(NewIntegrationTestArgs{ Name: "first", Description: "First option", Value: "FIRST", - Key: "1", + Key: config.Keybinding{"1"}, }, { Name: "second", Description: "Second option", Value: "SECOND", - Key: "H", + Key: config.Keybinding{"H"}, }, }, }, @@ -52,12 +52,12 @@ var ConditionalPrompts = NewIntegrationTest(NewIntegrationTestArgs{ // Test 1: Select "first" via key — conditional prompt should be skipped t.Views().Files(). IsFocused(). - Press("a") + Press(config.Keybinding{"a"}) t.ExpectPopup().Menu(). Title(Equals("Choose an option")) - t.Views().Menu().Press("1") + t.Views().Menu().Press(config.Keybinding{"1"}) // Detail prompt should be skipped, file should be created directly t.Views().Files(). @@ -75,12 +75,12 @@ var ConditionalPrompts = NewIntegrationTest(NewIntegrationTestArgs{ t.Views().Files(). IsEmpty(). IsFocused(). - Press("a") + Press(config.Keybinding{"a"}) t.ExpectPopup().Menu(). Title(Equals("Choose an option")) - t.Views().Menu().Press("H") + t.Views().Menu().Press(config.Keybinding{"H"}) // Detail prompt should appear because Choice == "SECOND" t.ExpectPopup().Prompt().Title(Equals("Enter detail for second option")).Type("extra").Confirm() diff --git a/pkg/integration/tests/custom_commands/custom_commands_submenu.go b/pkg/integration/tests/custom_commands/custom_commands_submenu.go index a8d13cf73..18dd51d83 100644 --- a/pkg/integration/tests/custom_commands/custom_commands_submenu.go +++ b/pkg/integration/tests/custom_commands/custom_commands_submenu.go @@ -13,21 +13,21 @@ var CustomCommandsSubmenu = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.GetUserConfig().CustomCommands = []config.CustomCommand{ { - Key: "x", + Key: config.Keybinding{"x"}, Description: "My Custom Commands", CommandMenu: []config.CustomCommand{ { - Key: "1", + Key: config.Keybinding{"1"}, Context: "global", Command: "touch myfile-global", }, { - Key: "2", + Key: config.Keybinding{"2"}, Context: "files", Command: "touch myfile-files", }, { - Key: "3", + Key: config.Keybinding{"3"}, Context: "commits", Command: "touch myfile-commits", }, @@ -39,7 +39,7 @@ var CustomCommandsSubmenu = NewIntegrationTest(NewIntegrationTestArgs{ t.Views().Files(). Focus(). IsEmpty(). - Press("x"). + Press(config.Keybinding{"x"}). Tap(func() { t.ExpectPopup().Menu(). Title(Equals("My Custom Commands")). @@ -55,7 +55,7 @@ var CustomCommandsSubmenu = NewIntegrationTest(NewIntegrationTestArgs{ t.Views().Commits(). Focus(). - Press("x"). + Press(config.Keybinding{"x"}). Tap(func() { t.ExpectPopup().Menu(). Title(Equals("My Custom Commands")). @@ -63,7 +63,7 @@ var CustomCommandsSubmenu = NewIntegrationTest(NewIntegrationTestArgs{ Contains("1 touch myfile-global"), Contains("3 touch myfile-commits"), ) - t.GlobalPress("3") + t.GlobalPress(config.Keybinding{"3"}) }) t.Views().Files(). diff --git a/pkg/integration/tests/custom_commands/custom_commands_submenu_with_special_keybindings.go b/pkg/integration/tests/custom_commands/custom_commands_submenu_with_special_keybindings.go index a1f62aff6..e79e05991 100644 --- a/pkg/integration/tests/custom_commands/custom_commands_submenu_with_special_keybindings.go +++ b/pkg/integration/tests/custom_commands/custom_commands_submenu_with_special_keybindings.go @@ -13,29 +13,29 @@ var CustomCommandsSubmenuWithSpecialKeybindings = NewIntegrationTest(NewIntegrat SetupConfig: func(cfg *config.AppConfig) { cfg.GetUserConfig().CustomCommands = []config.CustomCommand{ { - Key: "x", + Key: config.Keybinding{"x"}, Description: "My Custom Commands", CommandMenu: []config.CustomCommand{ { - Key: "j", + Key: config.Keybinding{"j"}, Context: "global", Command: "echo j", Output: "popup", }, { - Key: "H", + Key: config.Keybinding{"H"}, Context: "global", Command: "echo H", Output: "popup", }, { - Key: "y", + Key: config.Keybinding{"y"}, Context: "global", Command: "echo y", Output: "popup", }, { - Key: "", + Key: config.Keybinding{""}, Context: "global", Command: "echo down", Output: "popup", @@ -43,13 +43,13 @@ var CustomCommandsSubmenuWithSpecialKeybindings = NewIntegrationTest(NewIntegrat }, }, } - cfg.GetUserConfig().Keybinding.Universal.ConfirmMenu = "y" + cfg.GetUserConfig().Keybinding.Universal.ConfirmMenu = config.Keybinding{"y"} }, Run: func(t *TestDriver, keys config.KeybindingConfig) { t.Views().Files(). Focus(). IsEmpty(). - Press("x"). + Press(config.Keybinding{"x"}). Tap(func() { t.ExpectPopup().Menu(). Title(Equals("My Custom Commands")). @@ -59,14 +59,14 @@ var CustomCommandsSubmenuWithSpecialKeybindings = NewIntegrationTest(NewIntegrat Contains(" echo y"), Contains(" echo down"), ) - t.GlobalPress("j") + t.GlobalPress(config.Keybinding{"j"}) t.ExpectPopup().Alert().Title(Equals("echo j")).Content(Equals("j")).Confirm() }). - Press("x"). + Press(config.Keybinding{"x"}). Tap(func() { t.ExpectPopup().Menu(). Title(Equals("My Custom Commands")) - t.GlobalPress("H") + t.GlobalPress(config.Keybinding{"H"}) t.ExpectPopup().Alert().Title(Equals("echo H")).Content(Equals("H")).Confirm() }) }, diff --git a/pkg/integration/tests/custom_commands/form_prompts.go b/pkg/integration/tests/custom_commands/form_prompts.go index ccb2339de..43246b872 100644 --- a/pkg/integration/tests/custom_commands/form_prompts.go +++ b/pkg/integration/tests/custom_commands/form_prompts.go @@ -15,7 +15,7 @@ var FormPrompts = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.GetUserConfig().CustomCommands = []config.CustomCommand{ { - Key: "a", + Key: config.Keybinding{"a"}, Context: "files", Command: `echo {{.Form.FileContent | quote}} > {{.Form.FileName | quote}}`, Prompts: []config.CustomCommandPrompt{ @@ -59,7 +59,7 @@ var FormPrompts = NewIntegrationTest(NewIntegrationTestArgs{ t.Views().Files(). IsEmpty(). IsFocused(). - Press("a") + Press(config.Keybinding{"a"}) t.ExpectPopup().Prompt().Title(Equals("Enter a file name")).Type("my file").Confirm() diff --git a/pkg/integration/tests/custom_commands/global_context.go b/pkg/integration/tests/custom_commands/global_context.go index 82ef53010..2c4cd464f 100644 --- a/pkg/integration/tests/custom_commands/global_context.go +++ b/pkg/integration/tests/custom_commands/global_context.go @@ -15,7 +15,7 @@ var GlobalContext = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.GetUserConfig().CustomCommands = []config.CustomCommand{ { - Key: "X", + Key: config.Keybinding{"X"}, Context: "global", Command: "touch myfile", }, @@ -25,7 +25,7 @@ var GlobalContext = NewIntegrationTest(NewIntegrationTestArgs{ // commits t.Views().Commits(). Focus(). - Press("X") + Press(config.Keybinding{"X"}) t.Views().Files(). Focus(). @@ -37,7 +37,7 @@ var GlobalContext = NewIntegrationTest(NewIntegrationTestArgs{ // branches t.Views().Branches(). Focus(). - Press("X") + Press(config.Keybinding{"X"}) t.Views().Files(). Focus(). @@ -49,7 +49,7 @@ var GlobalContext = NewIntegrationTest(NewIntegrationTestArgs{ // files t.Views().Files(). Focus(). - Press("X") + Press(config.Keybinding{"X"}) t.Views().Files(). Focus(). diff --git a/pkg/integration/tests/custom_commands/menu_from_command.go b/pkg/integration/tests/custom_commands/menu_from_command.go index 10b8192ba..51122aa6f 100644 --- a/pkg/integration/tests/custom_commands/menu_from_command.go +++ b/pkg/integration/tests/custom_commands/menu_from_command.go @@ -21,7 +21,7 @@ var MenuFromCommand = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.GetUserConfig().CustomCommands = []config.CustomCommand{ { - Key: "a", + Key: config.Keybinding{"a"}, Context: "localBranches", Command: `echo "{{index .PromptResponses 0}} {{index .PromptResponses 1}} {{ .SelectedLocalBranch.Name }}" > output.txt`, Prompts: []config.CustomCommandPrompt{ @@ -48,7 +48,7 @@ var MenuFromCommand = NewIntegrationTest(NewIntegrationTestArgs{ t.Views().Branches(). Focus(). - Press("a") + Press(config.Keybinding{"a"}) t.ExpectPopup().Menu().Title(Equals("Choose commit message")).Select(Contains("bar")).Confirm() diff --git a/pkg/integration/tests/custom_commands/menu_from_commands_output.go b/pkg/integration/tests/custom_commands/menu_from_commands_output.go index 591daa5af..51444ad52 100644 --- a/pkg/integration/tests/custom_commands/menu_from_commands_output.go +++ b/pkg/integration/tests/custom_commands/menu_from_commands_output.go @@ -20,7 +20,7 @@ var MenuFromCommandsOutput = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.GetUserConfig().CustomCommands = []config.CustomCommand{ { - Key: "a", + Key: config.Keybinding{"a"}, Context: "localBranches", Command: "git checkout {{ index .PromptResponses 1 }}", Prompts: []config.CustomCommandPrompt{ @@ -46,7 +46,7 @@ var MenuFromCommandsOutput = NewIntegrationTest(NewIntegrationTestArgs{ t.Views().Branches(). Focus(). - Press("a") + Press(config.Keybinding{"a"}) t.ExpectPopup().Prompt(). Title(Equals("Which git command do you want to run?")). diff --git a/pkg/integration/tests/custom_commands/menu_prompt_with_keys.go b/pkg/integration/tests/custom_commands/menu_prompt_with_keys.go index f7f733d9c..4a217f924 100644 --- a/pkg/integration/tests/custom_commands/menu_prompt_with_keys.go +++ b/pkg/integration/tests/custom_commands/menu_prompt_with_keys.go @@ -15,7 +15,7 @@ var MenuPromptWithKeys = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.GetUserConfig().CustomCommands = []config.CustomCommand{ { - Key: "a", + Key: config.Keybinding{"a"}, Context: "files", Command: `echo {{.Form.Choice | quote}} > result.txt`, Prompts: []config.CustomCommandPrompt{ @@ -28,19 +28,19 @@ var MenuPromptWithKeys = NewIntegrationTest(NewIntegrationTestArgs{ Name: "first", Description: "First option", Value: "FIRST", - Key: "1", + Key: config.Keybinding{"1"}, }, { Name: "second", Description: "Second option", Value: "SECOND", - Key: "H", + Key: config.Keybinding{"H"}, }, { Name: "third", Description: "Third option", Value: "THIRD", - Key: "3", + Key: config.Keybinding{"3"}, }, }, }, @@ -51,14 +51,14 @@ var MenuPromptWithKeys = NewIntegrationTest(NewIntegrationTestArgs{ Run: func(t *TestDriver, keys config.KeybindingConfig) { t.Views().Files(). IsFocused(). - Press("a") + Press(config.Keybinding{"a"}) t.ExpectPopup().Menu(). Title(Equals("Choose an option")) // 'H' is normally a navigation key (ScrollLeft), so this tests that menu item // keybindings have proper precedence over non-essential navigation keys - t.Views().Menu().Press("H") + t.Views().Menu().Press(config.Keybinding{"H"}) t.FileSystem().FileContent("result.txt", Equals("SECOND\n")) }, diff --git a/pkg/integration/tests/custom_commands/multiple_contexts.go b/pkg/integration/tests/custom_commands/multiple_contexts.go index 61d46775b..3c7e07e84 100644 --- a/pkg/integration/tests/custom_commands/multiple_contexts.go +++ b/pkg/integration/tests/custom_commands/multiple_contexts.go @@ -15,7 +15,7 @@ var MultipleContexts = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.GetUserConfig().CustomCommands = []config.CustomCommand{ { - Key: "X", + Key: config.Keybinding{"X"}, Context: "commits, reflogCommits", Command: "touch myfile", }, @@ -25,7 +25,7 @@ var MultipleContexts = NewIntegrationTest(NewIntegrationTestArgs{ // commits t.Views().Commits(). Focus(). - Press("X") + Press(config.Keybinding{"X"}) t.Views().Files(). Focus(). @@ -37,7 +37,7 @@ var MultipleContexts = NewIntegrationTest(NewIntegrationTestArgs{ // branches t.Views().Branches(). Focus(). - Press("X") + Press(config.Keybinding{"X"}) t.Views().Files(). Focus(). @@ -46,7 +46,7 @@ var MultipleContexts = NewIntegrationTest(NewIntegrationTestArgs{ // files t.Views().ReflogCommits(). Focus(). - Press("X") + Press(config.Keybinding{"X"}) t.Views().Files(). Focus(). diff --git a/pkg/integration/tests/custom_commands/multiple_prompts.go b/pkg/integration/tests/custom_commands/multiple_prompts.go index b40aa77f2..8651a330a 100644 --- a/pkg/integration/tests/custom_commands/multiple_prompts.go +++ b/pkg/integration/tests/custom_commands/multiple_prompts.go @@ -15,7 +15,7 @@ var MultiplePrompts = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.GetUserConfig().CustomCommands = []config.CustomCommand{ { - Key: "a", + Key: config.Keybinding{"a"}, Context: "files", Command: `echo "{{index .PromptResponses 1}}" > {{index .PromptResponses 0}}`, Prompts: []config.CustomCommandPrompt{ @@ -57,7 +57,7 @@ var MultiplePrompts = NewIntegrationTest(NewIntegrationTestArgs{ t.Views().Files(). IsEmpty(). IsFocused(). - Press("a") + Press(config.Keybinding{"a"}) t.ExpectPopup().Prompt().Title(Equals("Enter a file name")).Type("myfile").Confirm() diff --git a/pkg/integration/tests/custom_commands/run_command.go b/pkg/integration/tests/custom_commands/run_command.go index 107b9e285..afff59590 100644 --- a/pkg/integration/tests/custom_commands/run_command.go +++ b/pkg/integration/tests/custom_commands/run_command.go @@ -15,7 +15,7 @@ var RunCommand = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.GetUserConfig().CustomCommands = []config.CustomCommand{ { - Key: "a", + Key: config.Keybinding{"a"}, Context: "localBranches", Command: `git checkout {{.Form.Branch}}`, Prompts: []config.CustomCommandPrompt{ @@ -32,7 +32,7 @@ var RunCommand = NewIntegrationTest(NewIntegrationTestArgs{ Run: func(t *TestDriver, keys config.KeybindingConfig) { t.Views().Branches(). Focus(). - Press("a") + Press(config.Keybinding{"a"}) t.ExpectPopup().Prompt(). Title(Equals("Enter a branch name")). diff --git a/pkg/integration/tests/custom_commands/selected_commit.go b/pkg/integration/tests/custom_commands/selected_commit.go index 6288634f1..0265759a8 100644 --- a/pkg/integration/tests/custom_commands/selected_commit.go +++ b/pkg/integration/tests/custom_commands/selected_commit.go @@ -15,7 +15,7 @@ var SelectedCommit = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.GetUserConfig().CustomCommands = []config.CustomCommand{ { - Key: "X", + Key: config.Keybinding{"X"}, Context: "global", Command: "printf '%s' '{{ .SelectedCommit.Name }}' > file.txt", }, @@ -34,34 +34,34 @@ var SelectedCommit = NewIntegrationTest(NewIntegrationTestArgs{ NavigateToLine(Contains("commit 03")) // SubCommits - t.GlobalPress("X") + t.GlobalPress(config.Keybinding{"X"}) t.FileSystem().FileContent("file.txt", Equals("commit 03")) t.Views().SubCommits().PressEnter() - t.GlobalPress("X") + t.GlobalPress(config.Keybinding{"X"}) t.FileSystem().FileContent("file.txt", Equals("commit 03")) // ReflogCommits t.Views().ReflogCommits().Focus() - t.GlobalPress("X") + t.GlobalPress(config.Keybinding{"X"}) t.FileSystem().FileContent("file.txt", Equals("commit: commit 02")) t.Views().ReflogCommits().PressEnter() - t.GlobalPress("X") + t.GlobalPress(config.Keybinding{"X"}) t.FileSystem().FileContent("file.txt", Equals("commit: commit 02")) // LocalCommits t.Views().Commits().Focus() - t.GlobalPress("X") + t.GlobalPress(config.Keybinding{"X"}) t.FileSystem().FileContent("file.txt", Equals("commit 01")) t.Views().Commits().PressEnter() - t.GlobalPress("X") + t.GlobalPress(config.Keybinding{"X"}) t.FileSystem().FileContent("file.txt", Equals("commit 01")) // None of these t.Views().Files().Focus() - t.GlobalPress("X") + t.GlobalPress(config.Keybinding{"X"}) t.FileSystem().FileContent("file.txt", Equals("commit 01")) }, }) diff --git a/pkg/integration/tests/custom_commands/selected_commit_range.go b/pkg/integration/tests/custom_commands/selected_commit_range.go index 1a4b3087c..6ef1305aa 100644 --- a/pkg/integration/tests/custom_commands/selected_commit_range.go +++ b/pkg/integration/tests/custom_commands/selected_commit_range.go @@ -15,7 +15,7 @@ var SelectedCommitRange = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.GetUserConfig().CustomCommands = []config.CustomCommand{ { - Key: "X", + Key: config.Keybinding{"X"}, Context: "global", Command: `git log --format="%s" {{.SelectedCommitRange.From}}^..{{.SelectedCommitRange.To}} > file.txt`, }, @@ -29,13 +29,13 @@ var SelectedCommitRange = NewIntegrationTest(NewIntegrationTestArgs{ Contains("commit 01"), ) - t.GlobalPress("X") + t.GlobalPress(config.Keybinding{"X"}) t.FileSystem().FileContent("file.txt", Equals("commit 03\n")) t.Views().Commits().Focus(). Press(keys.Universal.RangeSelectDown) - t.GlobalPress("X") + t.GlobalPress(config.Keybinding{"X"}) t.FileSystem().FileContent("file.txt", Equals("commit 03\ncommit 02\n")) }, }) diff --git a/pkg/integration/tests/custom_commands/selected_path.go b/pkg/integration/tests/custom_commands/selected_path.go index 9dc63ed43..cad479e4a 100644 --- a/pkg/integration/tests/custom_commands/selected_path.go +++ b/pkg/integration/tests/custom_commands/selected_path.go @@ -19,7 +19,7 @@ var SelectedPath = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.GetUserConfig().CustomCommands = []config.CustomCommand{ { - Key: "X", + Key: config.Keybinding{"X"}, Context: "global", Command: "printf '%s' '{{ .SelectedPath }}' > file.txt", }, @@ -29,7 +29,7 @@ var SelectedPath = NewIntegrationTest(NewIntegrationTestArgs{ t.Views().Files(). Focus(). NavigateToLine(Contains("file2")) - t.GlobalPress("X") + t.GlobalPress(config.Keybinding{"X"}) t.FileSystem().FileContent("file.txt", Equals("folder2/file2")) t.Views().Commits(). @@ -38,7 +38,7 @@ var SelectedPath = NewIntegrationTest(NewIntegrationTestArgs{ t.Views().CommitFiles(). IsFocused(). NavigateToLine(Contains("file1")) - t.GlobalPress("X") + t.GlobalPress(config.Keybinding{"X"}) t.FileSystem().FileContent("file.txt", Equals("folder1/file1")) }, }) diff --git a/pkg/integration/tests/custom_commands/selected_submodule.go b/pkg/integration/tests/custom_commands/selected_submodule.go index c4640017d..0671b4b83 100644 --- a/pkg/integration/tests/custom_commands/selected_submodule.go +++ b/pkg/integration/tests/custom_commands/selected_submodule.go @@ -17,17 +17,17 @@ var SelectedSubmodule = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.GetUserConfig().CustomCommands = []config.CustomCommand{ { - Key: "X", + Key: config.Keybinding{"X"}, Context: "submodules", Command: "printf '%s' '{{ .SelectedSubmodule.Path }}' > file.txt", }, { - Key: "U", + Key: config.Keybinding{"U"}, Context: "submodules", Command: "printf '%s' '{{ .SelectedSubmodule.Url }}' > file.txt", }, { - Key: "N", + Key: config.Keybinding{"N"}, Context: "submodules", Command: "printf '%s' '{{ .SelectedSubmodule.Name }}' > file.txt", }, @@ -40,13 +40,13 @@ var SelectedSubmodule = NewIntegrationTest(NewIntegrationTestArgs{ Contains("submodule").IsSelected(), ) - t.Views().Submodules().Press("X") + t.Views().Submodules().Press(config.Keybinding{"X"}) t.FileSystem().FileContent("file.txt", Equals("path/submodule")) - t.Views().Submodules().Press("U") + t.Views().Submodules().Press(config.Keybinding{"U"}) t.FileSystem().FileContent("file.txt", Equals("../submodule")) - t.Views().Submodules().Press("N") + t.Views().Submodules().Press(config.Keybinding{"N"}) t.FileSystem().FileContent("file.txt", Equals("submodule")) }, }) diff --git a/pkg/integration/tests/custom_commands/show_output_in_panel.go b/pkg/integration/tests/custom_commands/show_output_in_panel.go index 9fcab1be3..4654b9b51 100644 --- a/pkg/integration/tests/custom_commands/show_output_in_panel.go +++ b/pkg/integration/tests/custom_commands/show_output_in_panel.go @@ -17,13 +17,13 @@ var ShowOutputInPanel = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.GetUserConfig().CustomCommands = []config.CustomCommand{ { - Key: "X", + Key: config.Keybinding{"X"}, Context: "commits", Command: "printf '%s' '{{ .SelectedLocalCommit.Name }}'", Output: "popup", }, { - Key: "Y", + Key: config.Keybinding{"Y"}, Context: "commits", Command: "printf '%s' '{{ .SelectedLocalCommit.Name }}'", Output: "popup", @@ -37,7 +37,7 @@ var ShowOutputInPanel = NewIntegrationTest(NewIntegrationTestArgs{ Lines( Contains("my change").IsSelected(), ). - Press("X") + Press(config.Keybinding{"X"}) t.ExpectPopup().Alert(). // Uses cmd string as title if no outputTitle is provided @@ -46,7 +46,7 @@ var ShowOutputInPanel = NewIntegrationTest(NewIntegrationTestArgs{ Confirm() t.Views().Commits(). - Press("Y") + Press(config.Keybinding{"Y"}) hash := t.Git().GetCommitHash("HEAD") t.ExpectPopup().Alert(). diff --git a/pkg/integration/tests/custom_commands/suggestions_command.go b/pkg/integration/tests/custom_commands/suggestions_command.go index 51bbc2c65..e31d6dd8b 100644 --- a/pkg/integration/tests/custom_commands/suggestions_command.go +++ b/pkg/integration/tests/custom_commands/suggestions_command.go @@ -22,7 +22,7 @@ var SuggestionsCommand = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.GetUserConfig().CustomCommands = []config.CustomCommand{ { - Key: "a", + Key: config.Keybinding{"a"}, Context: "localBranches", Command: `git checkout {{.Form.Branch}}`, Prompts: []config.CustomCommandPrompt{ @@ -49,7 +49,7 @@ var SuggestionsCommand = NewIntegrationTest(NewIntegrationTestArgs{ Contains("branch-three"), Contains("branch-two"), ). - Press("a") + Press(config.Keybinding{"a"}) t.ExpectPopup().Prompt(). Title(Equals("Enter a branch name")). diff --git a/pkg/integration/tests/custom_commands/suggestions_preset.go b/pkg/integration/tests/custom_commands/suggestions_preset.go index 891ebf725..ebd9ee5da 100644 --- a/pkg/integration/tests/custom_commands/suggestions_preset.go +++ b/pkg/integration/tests/custom_commands/suggestions_preset.go @@ -22,7 +22,7 @@ var SuggestionsPreset = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.GetUserConfig().CustomCommands = []config.CustomCommand{ { - Key: "a", + Key: config.Keybinding{"a"}, Context: "localBranches", Command: `git checkout {{.Form.Branch}}`, Prompts: []config.CustomCommandPrompt{ @@ -49,7 +49,7 @@ var SuggestionsPreset = NewIntegrationTest(NewIntegrationTestArgs{ Contains("branch-three"), Contains("branch-two"), ). - Press("a") + Press(config.Keybinding{"a"}) t.ExpectPopup().Prompt(). Title(Equals("Enter a branch name")). diff --git a/pkg/integration/tests/demo/custom_command.go b/pkg/integration/tests/demo/custom_command.go index a65c2c073..486b3488a 100644 --- a/pkg/integration/tests/demo/custom_command.go +++ b/pkg/integration/tests/demo/custom_command.go @@ -28,7 +28,7 @@ var CustomCommand = NewIntegrationTest(NewIntegrationTestArgs{ cfg.GetUserConfig().CustomCommands = []config.CustomCommand{ { - Key: "a", + Key: config.Keybinding{"a"}, Context: "localBranches", Command: `git checkout {{.Form.Branch}}`, Prompts: []config.CustomCommandPrompt{ @@ -63,7 +63,7 @@ var CustomCommand = NewIntegrationTest(NewIntegrationTestArgs{ t.Views().Branches(). Focus(). Wait(500). - Press("a"). + Press(config.Keybinding{"a"}). Tap(func() { t.Wait(500) diff --git a/pkg/integration/tests/filter_and_search/filter_menu_with_no_keybindings.go b/pkg/integration/tests/filter_and_search/filter_menu_with_no_keybindings.go index 1d9ef589f..522ee7689 100644 --- a/pkg/integration/tests/filter_and_search/filter_menu_with_no_keybindings.go +++ b/pkg/integration/tests/filter_and_search/filter_menu_with_no_keybindings.go @@ -9,8 +9,8 @@ var FilterMenuWithNoKeybindings = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Filtering the keybindings menu so that only entries without keybinding are left", ExtraCmdArgs: []string{}, Skip: false, - SetupConfig: func(config *config.AppConfig) { - config.GetUserConfig().Keybinding.Universal.ToggleWhitespaceInDiffView = "" + SetupConfig: func(cfg *config.AppConfig) { + cfg.GetUserConfig().Keybinding.Universal.ToggleWhitespaceInDiffView = nil }, SetupRepo: func(shell *Shell) { }, diff --git a/pkg/integration/tests/interactive_rebase/show_exec_todos.go b/pkg/integration/tests/interactive_rebase/show_exec_todos.go index 74d3830f6..948bfb7d8 100644 --- a/pkg/integration/tests/interactive_rebase/show_exec_todos.go +++ b/pkg/integration/tests/interactive_rebase/show_exec_todos.go @@ -12,7 +12,7 @@ var ShowExecTodos = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.GetUserConfig().CustomCommands = []config.CustomCommand{ { - Key: "X", + Key: config.Keybinding{"X"}, Context: "commits", Command: "git -c core.editor=: rebase -i -x false HEAD^^", }, @@ -26,7 +26,7 @@ var ShowExecTodos = NewIntegrationTest(NewIntegrationTestArgs{ Run: func(t *TestDriver, keys config.KeybindingConfig) { t.Views().Commits(). Focus(). - Press("X"). + Press(config.Keybinding{"X"}). Tap(func() { t.ExpectPopup().Alert().Title(Equals("Error")).Content(Contains("Rebasing (2/4)Executing: false")).Confirm() }). diff --git a/pkg/integration/tests/misc/disabled_keybindings.go b/pkg/integration/tests/misc/disabled_keybindings.go deleted file mode 100644 index 7ab1ba42a..000000000 --- a/pkg/integration/tests/misc/disabled_keybindings.go +++ /dev/null @@ -1,26 +0,0 @@ -package misc - -import ( - "github.com/jesseduffield/lazygit/pkg/config" - . "github.com/jesseduffield/lazygit/pkg/integration/components" -) - -var DisabledKeybindings = NewIntegrationTest(NewIntegrationTestArgs{ - Description: "Confirms you can disable keybindings by setting them to ", - ExtraCmdArgs: []string{}, - Skip: false, - SetupConfig: func(config *config.AppConfig) { - config.GetUserConfig().Keybinding.Universal.PrevItem = "" - config.GetUserConfig().Keybinding.Universal.NextItem = "" - config.GetUserConfig().Keybinding.Universal.NextTab = "" - config.GetUserConfig().Keybinding.Universal.PrevTab = "" - }, - SetupRepo: func(shell *Shell) {}, - Run: func(t *TestDriver, keys config.KeybindingConfig) { - t.Views().Files(). - IsFocused(). - Press("") - - t.Views().Worktrees().IsFocused() - }, -}) diff --git a/pkg/integration/tests/submodule/enter.go b/pkg/integration/tests/submodule/enter.go index 588ae2049..b768ed40e 100644 --- a/pkg/integration/tests/submodule/enter.go +++ b/pkg/integration/tests/submodule/enter.go @@ -12,7 +12,7 @@ var Enter = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.GetUserConfig().CustomCommands = []config.CustomCommand{ { - Key: "e", + Key: config.Keybinding{"e"}, Context: "files", Command: "git commit --allow-empty -m \"empty commit\"", }, @@ -44,7 +44,7 @@ var Enter = NewIntegrationTest(NewIntegrationTestArgs{ assertInSubmodule() t.Views().Files().IsFocused(). - Press("e"). + Press(config.Keybinding{"e"}). Tap(func() { t.Views().Commits().Content(Contains("empty commit")) }). diff --git a/pkg/integration/tests/submodule/reset.go b/pkg/integration/tests/submodule/reset.go index 5cd6d58aa..d671066a1 100644 --- a/pkg/integration/tests/submodule/reset.go +++ b/pkg/integration/tests/submodule/reset.go @@ -12,7 +12,7 @@ var Reset = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.GetUserConfig().CustomCommands = []config.CustomCommand{ { - Key: "e", + Key: config.Keybinding{"e"}, Context: "files", Command: "git commit --allow-empty -m \"empty commit\" && echo \"my_file content\" > my_file", }, @@ -46,7 +46,7 @@ var Reset = NewIntegrationTest(NewIntegrationTestArgs{ assertInSubmodule() t.Views().Files().IsFocused(). - Press("e"). + Press(config.Keybinding{"e"}). Tap(func() { t.Views().Commits().Content(Contains("empty commit")) t.Views().Files().Content(Contains("my_file")) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index fdccb08bf..aea37515b 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -334,7 +334,6 @@ var tests = []*components.IntegrationTest{ misc.ConfirmOnQuit, misc.CopyConfirmationMessageToClipboard, misc.CopyToClipboard, - misc.DisabledKeybindings, misc.InitialOpen, misc.RecentReposOnLaunch, patch_building.Apply, diff --git a/pkg/integration/tests/ui/switch_tab_from_menu.go b/pkg/integration/tests/ui/switch_tab_from_menu.go index 61bd991ab..fbcdbd954 100644 --- a/pkg/integration/tests/ui/switch_tab_from_menu.go +++ b/pkg/integration/tests/ui/switch_tab_from_menu.go @@ -14,7 +14,7 @@ var SwitchTabFromMenu = NewIntegrationTest(NewIntegrationTestArgs{ }, Run: func(t *TestDriver, keys config.KeybindingConfig) { t.Views().Files().IsFocused(). - Press(keys.Universal.OptionMenuAlt1) + Press(keys.Universal.OptionMenu) t.ExpectPopup().Menu().Title(Equals("Keybindings")). Select(Contains("Next tab")). diff --git a/pkg/integration/tests/worktree/custom_command.go b/pkg/integration/tests/worktree/custom_command.go index 00c3c4c06..e00c162c3 100644 --- a/pkg/integration/tests/worktree/custom_command.go +++ b/pkg/integration/tests/worktree/custom_command.go @@ -12,7 +12,7 @@ var CustomCommand = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.GetUserConfig().CustomCommands = []config.CustomCommand{ { - Key: "d", + Key: config.Keybinding{"d"}, Context: "worktrees", Command: "git worktree remove {{ .SelectedWorktree.Path | quote }}", }, @@ -32,7 +32,7 @@ var CustomCommand = NewIntegrationTest(NewIntegrationTestArgs{ Contains("linked-worktree"), ). NavigateToLine(Contains("linked-worktree")). - Press("d"). + Press(config.Keybinding{"d"}). Lines( Contains("(main worktree)"), ) diff --git a/pkg/jsonschema/generate.go b/pkg/jsonschema/generate.go index 5e7267e3e..dc5045025 100644 --- a/pkg/jsonschema/generate.go +++ b/pkg/jsonschema/generate.go @@ -58,7 +58,9 @@ func customReflect(v *config.UserConfig) *jsonschema.Schema { } filterOutDevComments(r) schema := r.Reflect(v) + inlineKeybindingRefs(schema) defaultConfig := config.GetDefaultConfig() + defaultConfig.Keybinding.MergeLegacyAltKeybindings() userConfigSchema := schema.Definitions["UserConfig"] defaultValue := reflect.ValueOf(defaultConfig).Elem() @@ -77,6 +79,57 @@ func customReflect(v *config.UserConfig) *jsonschema.Schema { return schema } +// inlineKeybindingRefs replaces every `$ref: #/$defs/Keybinding` in the +// schema with the inlined oneOf union, then drops the Keybinding definition. +// +// The schema generator stores types that implement JSONSchema() as shared +// definitions and uses $ref to point at them. That works for most types +// (where every reference logically points at the same data), but for +// Keybinding fields each property carries its own description and default, +// and writing those onto the shared definition would clobber siblings. +// Inlining sidesteps the issue. +func inlineKeybindingRefs(schema *jsonschema.Schema) { + const ref = "#/$defs/Keybinding" + keybindingDef, ok := schema.Definitions["Keybinding"] + if !ok { + return + } + inline := func(s *jsonschema.Schema) { + desc := s.Description + *s = *keybindingDef + s.Description = desc + } + var visit func(s *jsonschema.Schema) + visit = func(s *jsonschema.Schema) { + if s == nil { + return + } + if s.Properties != nil { + for pair := s.Properties.Oldest(); pair != nil; pair = pair.Next() { + if pair.Value.Ref == ref { + inline(pair.Value) + } else { + visit(pair.Value) + } + } + } + if s.Items != nil { + if s.Items.Ref == ref { + inline(s.Items) + } else { + visit(s.Items) + } + } + if s.AdditionalProperties != nil { + visit(s.AdditionalProperties) + } + } + for _, def := range schema.Definitions { + visit(def) + } + delete(schema.Definitions, "Keybinding") +} + func filterOutDevComments(r *jsonschema.Reflector) { for k, v := range r.CommentMap { commentLines := strings.Split(v, "\n") diff --git a/schema-master/config.json b/schema-master/config.json index 549b80877..84fd2220e 100644 --- a/schema-master/config.json +++ b/schema-master/config.json @@ -60,8 +60,18 @@ "CustomCommand": { "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" + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "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. To bind several alternates to the same command, use a sequence (e.g. `[a, b]`)." }, "commandMenu": { "items": { @@ -165,8 +175,18 @@ ] }, "key": { - "type": "string", - "description": "Keybinding to invoke this menu option without needing to navigate to it" + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "description": "Keybinding to invoke this menu option without needing to navigate to it. Accepts either a single key or a sequence of alternates." } }, "additionalProperties": false, @@ -843,15 +863,45 @@ "KeybindingAmendAttributeConfig": { "properties": { "resetAuthor": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "a" }, "setAuthor": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "A" }, "addCoAuthor": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "c" } }, @@ -861,79 +911,269 @@ "KeybindingBranchesConfig": { "properties": { "createPullRequest": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "o" }, "viewPullRequestOptions": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "O" }, "openPullRequestInBrowser": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "G" }, "copyPullRequestURL": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "\u003cctrl+y\u003e" }, "checkoutBranchByName": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "c" }, "forceCheckoutBranch": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "F" }, "checkoutPreviousBranch": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "-" }, "rebaseBranch": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "r" }, "renameBranch": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "R" }, "mergeIntoCurrentBranch": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "M" }, "moveCommitsToNewBranch": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "N" }, "viewGitFlowOptions": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "i" }, "fastForward": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "f" }, "createTag": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "T" }, "pushTag": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "P" }, "setUpstream": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "u" }, "fetchRemote": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "f" }, "addForkRemote": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "F" }, "sortOrder": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "s" } }, @@ -943,7 +1183,17 @@ "KeybindingCommitFilesConfig": { "properties": { "checkoutCommitFile": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "c" } }, @@ -953,7 +1203,17 @@ "KeybindingCommitMessageConfig": { "properties": { "commitMenu": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "\u003cctrl+o\u003e" } }, @@ -963,111 +1223,381 @@ "KeybindingCommitsConfig": { "properties": { "squashDown": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "s" }, "renameCommit": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "r" }, "renameCommitWithEditor": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "R" }, "viewResetOptions": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "g" }, "markCommitAsFixup": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "f" }, "setFixupMessage": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "c" }, "createFixupCommit": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "F" }, "squashAboveCommits": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "S" }, "moveDownCommit": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "\u003cctrl+j\u003e" }, "moveUpCommit": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "\u003cctrl+k\u003e" }, "amendToCommit": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "A" }, "resetCommitAuthor": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "a" }, "pickCommit": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "p" }, "revertCommit": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "t" }, "cherryPickCopy": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "C" }, "pasteCommits": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "V" }, "markCommitAsBaseForRebase": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "B" }, "tagCommit": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "T" }, "checkoutCommit": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "\u003cspace\u003e" }, "resetCherryPick": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "\u003cctrl+r\u003e" }, "copyCommitAttributeToClipboard": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "y" }, "openLogMenu": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "\u003cctrl+l\u003e" }, "openInBrowser": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "o" }, "openPullRequestInBrowser": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "G" }, "viewBisectOptions": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "b" }, "startInteractiveRebase": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "i" }, "selectCommitsOfCurrentBranch": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "*" } }, @@ -1115,84 +1645,274 @@ }, "additionalProperties": false, "type": "object", - "description": "Keybindings" + "description": "Keybindings.\nEach binding can be a single key or a list of keys; see https://github.com/jesseduffield/lazygit/blob/master/docs/keybindings/Custom_Keybindings.md for the syntax." }, "KeybindingFilesConfig": { "properties": { "commitChanges": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "c" }, "commitChangesWithoutHook": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "w" }, "amendLastCommit": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "A" }, "commitChangesWithEditor": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "C" }, "findBaseCommitForFixup": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "\u003cctrl+f\u003e" }, "confirmDiscard": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "x" }, "ignoreFile": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "i" }, "refreshFiles": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "r" }, "stashAllChanges": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "s" }, "viewStashOptions": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "S" }, "toggleStagedAll": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "a" }, "viewResetOptions": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "D" }, "fetch": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "f" }, "toggleTreeView": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "`" }, "openMergeOptions": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "M" }, "openStatusFilter": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "\u003cctrl+b\u003e" }, "copyFileInfoToClipboard": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "y" }, "collapseAll": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "-" }, "expandAll": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "=" } }, @@ -1201,16 +1921,80 @@ }, "KeybindingMainConfig": { "properties": { + "prevHunk": { + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": [ + "\u003cleft\u003e", + "h" + ] + }, + "nextHunk": { + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": [ + "\u003cright\u003e", + "l" + ] + }, "toggleSelectHunk": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "a" }, "pickBothHunks": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "b" }, "editSelectHunk": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "E" } }, @@ -1220,11 +2004,31 @@ "KeybindingStashConfig": { "properties": { "popStash": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "g" }, "renameStash": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "r" } }, @@ -1234,19 +2038,59 @@ "KeybindingStatusConfig": { "properties": { "checkForUpdate": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "u" }, "recentRepos": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "\u003center\u003e" }, "allBranchesLogGraph": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "a" }, "allBranchesLogGraphReverse": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "A" } }, @@ -1256,15 +2100,45 @@ "KeybindingSubmodulesConfig": { "properties": { "init": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "i" }, "update": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "u" }, "bulkMenu": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "b" } }, @@ -1274,116 +2148,428 @@ "KeybindingUniversalConfig": { "properties": { "quit": { - "type": "string", - "default": "q" + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": [ + "q", + "\u003cctrl+c\u003e" + ] }, "quit-alt1": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "description": "Deprecated: add the key to `quit` instead.", "default": "\u003cctrl+c\u003e" }, "suspendApp": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "\u003cctrl+z\u003e" }, "return": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "\u003cesc\u003e" }, "quitWithoutChangingDirectory": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "Q" }, "togglePanel": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "\u003ctab\u003e" }, "prevItem": { - "type": "string", - "default": "\u003cup\u003e" + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": [ + "\u003cup\u003e", + "k" + ] }, "nextItem": { - "type": "string", - "default": "\u003cdown\u003e" + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": [ + "\u003cdown\u003e", + "j" + ] }, "prevItem-alt": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "description": "Deprecated: add the key to `prevItem` instead.", "default": "k" }, "nextItem-alt": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "description": "Deprecated: add the key to `nextItem` instead.", "default": "j" }, "prevPage": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "," }, "nextPage": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "." }, "scrollLeft": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "H" }, "scrollRight": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "L" }, "gotoTop": { - "type": "string", - "default": "\u003c" + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": [ + "\u003c", + "\u003chome\u003e" + ] }, "gotoBottom": { - "type": "string", - "default": "\u003e" + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": [ + "\u003e", + "\u003cend\u003e" + ] }, "gotoTop-alt": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "description": "Deprecated: add the key to `gotoTop` instead.", "default": "\u003chome\u003e" }, "gotoBottom-alt": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "description": "Deprecated: add the key to `gotoBottom` instead.", "default": "\u003cend\u003e" }, "toggleRangeSelect": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "v" }, "rangeSelectDown": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "\u003cshift+down\u003e" }, "rangeSelectUp": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "\u003cshift+up\u003e" }, "prevBlock": { - "type": "string", - "default": "\u003cleft\u003e" + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": [ + "\u003cleft\u003e", + "h", + "\u003cbacktab\u003e" + ] }, "nextBlock": { - "type": "string", - "default": "\u003cright\u003e" + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": [ + "\u003cright\u003e", + "l", + "\u003ctab\u003e" + ] }, "prevBlock-alt": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "description": "Deprecated: add the key to `prevBlock` instead.", "default": "h" }, "nextBlock-alt": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "description": "Deprecated: add the key to `nextBlock` instead.", "default": "l" }, "nextBlock-alt2": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "description": "Deprecated: add the key to `nextBlock` instead.", "default": "\u003ctab\u003e" }, "prevBlock-alt2": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "description": "Deprecated: add the key to `prevBlock` instead.", "default": "\u003cbacktab\u003e" }, "jumpToBlock": { "items": { - "type": "string" + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ] }, "type": "array", "default": [ @@ -1395,222 +2581,758 @@ ] }, "focusMainView": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "0" }, "nextMatch": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "n" }, "prevMatch": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "N" }, "startSearch": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "/" }, "moveWordLeft": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "description": "\u003calt+left\u003e on Mac", "default": "\u003cctrl+left\u003e" }, "moveWordRight": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "description": "\u003calt+right\u003e on Mac", "default": "\u003cctrl+right\u003e" }, "backspaceWord": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "description": "\u003calt+backspace\u003e on Mac", "default": "\u003cctrl+backspace\u003e" }, "forwardDeleteWord": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "description": "\u003calt+delete\u003e on Mac", "default": "\u003cctrl+delete\u003e" }, "optionMenu": { - "type": "string", - "default": "\u003cdisabled\u003e" - }, - "optionMenu-alt1": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "?" }, "select": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "\u003cspace\u003e" }, "goInto": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "\u003center\u003e" }, "confirm": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "\u003center\u003e" }, "confirmMenu": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "\u003center\u003e" }, "confirmSuggestion": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "\u003center\u003e" }, "confirmInEditor": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "description": "\u003cmeta+enter\u003e on Mac", - "default": "\u003cctrl+enter\u003e" + "default": [ + "\u003cctrl+enter\u003e", + "\u003cctrl+s\u003e" + ] }, "confirmInEditor-alt": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "description": "Deprecated: add the key to `confirmInEditor` instead.", "default": "\u003cctrl+s\u003e" }, "remove": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "d" }, "new": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "n" }, "edit": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "e" }, "openFile": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "o" }, "scrollUpMain": { - "type": "string", - "default": "\u003cpgup\u003e" + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": [ + "\u003cpgup\u003e", + "K", + "\u003cctrl+u\u003e" + ] }, "scrollDownMain": { - "type": "string", - "default": "\u003cpgdown\u003e" + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": [ + "\u003cpgdown\u003e", + "J", + "\u003cctrl+d\u003e" + ] }, "scrollUpMain-alt1": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "description": "Deprecated: add the key to `scrollUpMain` instead.", "default": "K" }, "scrollDownMain-alt1": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "description": "Deprecated: add the key to `scrollDownMain` instead.", "default": "J" }, "scrollUpMain-alt2": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "description": "Deprecated: add the key to `scrollUpMain` instead.", "default": "\u003cctrl+u\u003e" }, "scrollDownMain-alt2": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "description": "Deprecated: add the key to `scrollDownMain` instead.", "default": "\u003cctrl+d\u003e" }, "executeShellCommand": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": ":" }, "createRebaseOptionsMenu": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "m" }, "pushFiles": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "description": "'Files' appended for legacy reasons", "default": "P" }, "pullFiles": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "description": "'Files' appended for legacy reasons", "default": "p" }, "refresh": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "R" }, "createPatchOptionsMenu": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "\u003cctrl+p\u003e" }, "nextTab": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "]" }, "prevTab": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "[" }, "nextScreenMode": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "+" }, "prevScreenMode": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "_" }, "cyclePagers": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "|" }, "undo": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "z" }, "redo": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "Z" }, "filteringMenu": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "\u003cctrl+s\u003e" }, "diffingMenu": { - "type": "string", - "default": "W" + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": [ + "W", + "\u003cctrl+e\u003e" + ] }, "diffingMenu-alt": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "description": "Deprecated: add the key to `diffingMenu` instead.", "default": "\u003cctrl+e\u003e" }, "copyToClipboard": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "\u003cctrl+o\u003e" }, "openRecentRepos": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "\u003cctrl+r\u003e" }, "submitEditorText": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "\u003center\u003e" }, "extrasMenu": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "@" }, "toggleWhitespaceInDiffView": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "\u003cctrl+w\u003e" }, "increaseContextInDiffView": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "}" }, "decreaseContextInDiffView": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "{" }, "increaseRenameSimilarityThreshold": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": ")" }, "decreaseRenameSimilarityThreshold": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "(" }, "openDiffTool": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "\u003cctrl+t\u003e" } }, @@ -1620,7 +3342,17 @@ "KeybindingWorktreesConfig": { "properties": { "viewWorktreeOptions": { - "type": "string", + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], "default": "w" } }, @@ -2066,7 +3798,7 @@ }, "keybinding": { "$ref": "#/$defs/KeybindingConfig", - "description": "Keybindings" + "description": "Keybindings.\nEach binding can be a single key or a list of keys; see https://github.com/jesseduffield/lazygit/blob/master/docs/keybindings/Custom_Keybindings.md for the syntax." } }, "additionalProperties": false, diff --git a/scripts/check_commit.sh b/scripts/check_commit.sh new file mode 100755 index 000000000..9cc5e5c7b --- /dev/null +++ b/scripts/check_commit.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# Run some tests on the current commit, similar to what CI does; useful for +# checking every commit in a branch with `git rebase -x scripts/check_commit.sh master`. + +set -e + +git diff --quiet || { + echo "Error: there are unstaged changes. Please stage or stash them before running this script." + exit 1 +} + +just test +just lint +just generate +git diff --quiet || { + echo "Error: auto-generated files not up to date." + exit 1 +}