mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-03-19 21:28:28 +02:00
Merge branch 'refactor-better-encapsulation' into test-refactor
This commit is contained in:
commit
8d68ab41b6
@ -8,20 +8,20 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>ctrl+r</kbd>: switch to a recent repo
|
<kbd>ctrl+r</kbd>: switch to a recent repo
|
||||||
<kbd>pgup</kbd>: scroll up main panel (fn+up/shift+k)
|
<kbd>pgup</kbd>: scroll up main panel (fn+up/shift+k)
|
||||||
<kbd>pgdown</kbd>: scroll down main panel (fn+down/shift+j)
|
<kbd>pgdown</kbd>: scroll down main panel (fn+down/shift+j)
|
||||||
<kbd>m</kbd>: view merge/rebase options
|
|
||||||
<kbd>ctrl+p</kbd>: view custom patch options
|
|
||||||
<kbd>R</kbd>: refresh
|
|
||||||
<kbd>?</kbd>: open menu
|
|
||||||
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
|
|
||||||
<kbd>_</kbd>: prev screen mode
|
|
||||||
<kbd>ctrl+s</kbd>: view filter-by-path options
|
|
||||||
<kbd>W</kbd>: open diff menu
|
|
||||||
<kbd>ctrl+e</kbd>: open diff menu
|
|
||||||
<kbd>@</kbd>: open command log menu
|
<kbd>@</kbd>: open command log menu
|
||||||
<kbd>ctrl+w</kbd>: Toggle whether or not whitespace changes are shown in the diff view
|
|
||||||
<kbd>}</kbd>: Increase the size of the context shown around changes in the diff view
|
<kbd>}</kbd>: Increase the size of the context shown around changes in the diff view
|
||||||
<kbd>{</kbd>: Decrease the size of the context shown around changes in the diff view
|
<kbd>{</kbd>: Decrease the size of the context shown around changes in the diff view
|
||||||
<kbd>:</kbd>: execute custom command
|
<kbd>:</kbd>: execute custom command
|
||||||
|
<kbd>ctrl+p</kbd>: view custom patch options
|
||||||
|
<kbd>m</kbd>: view merge/rebase options
|
||||||
|
<kbd>R</kbd>: refresh
|
||||||
|
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
|
||||||
|
<kbd>_</kbd>: prev screen mode
|
||||||
|
<kbd>?</kbd>: open menu
|
||||||
|
<kbd>ctrl+s</kbd>: view filter-by-path options
|
||||||
|
<kbd>W</kbd>: open diff menu
|
||||||
|
<kbd>ctrl+e</kbd>: open diff menu
|
||||||
|
<kbd>ctrl+w</kbd>: Toggle whether or not whitespace changes are shown in the diff view
|
||||||
<kbd>z</kbd>: undo (via reflog) (experimental)
|
<kbd>z</kbd>: undo (via reflog) (experimental)
|
||||||
<kbd>ctrl+z</kbd>: redo (via reflog) (experimental)
|
<kbd>ctrl+z</kbd>: redo (via reflog) (experimental)
|
||||||
<kbd>P</kbd>: push
|
<kbd>P</kbd>: push
|
||||||
@ -56,6 +56,13 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>`</kbd>: toggle file tree view
|
<kbd>`</kbd>: toggle file tree view
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
## Commit Message
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>enter</kbd>: confirm
|
||||||
|
<kbd>esc</kbd>: close
|
||||||
|
</pre>
|
||||||
|
|
||||||
## Commits
|
## Commits
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
@ -89,6 +96,13 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>enter</kbd>: view selected item's files
|
<kbd>enter</kbd>: view selected item's files
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
## Confirmation Panel
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>enter</kbd>: confirm
|
||||||
|
<kbd>esc</kbd>: close/cancel
|
||||||
|
</pre>
|
||||||
|
|
||||||
## Files
|
## Files
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
@ -197,6 +211,13 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>C</kbd>: commit changes using git editor
|
<kbd>C</kbd>: commit changes using git editor
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
## Menu
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>enter</kbd>: execute
|
||||||
|
<kbd>esc</kbd>: close
|
||||||
|
</pre>
|
||||||
|
|
||||||
## Reflog
|
## Reflog
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
@ -250,8 +271,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
## Status
|
## Status
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>e</kbd>: edit config file
|
|
||||||
<kbd>o</kbd>: open config file
|
<kbd>o</kbd>: open config file
|
||||||
|
<kbd>e</kbd>: edit config file
|
||||||
<kbd>u</kbd>: check for update
|
<kbd>u</kbd>: check for update
|
||||||
<kbd>enter</kbd>: switch to a recent repo
|
<kbd>enter</kbd>: switch to a recent repo
|
||||||
<kbd>a</kbd>: show all branch logs
|
<kbd>a</kbd>: show all branch logs
|
||||||
|
@ -8,20 +8,20 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>ctrl+r</kbd>: 最近使用したリポジトリに切り替え
|
<kbd>ctrl+r</kbd>: 最近使用したリポジトリに切り替え
|
||||||
<kbd>pgup</kbd>: メインパネルを上にスクロール (fn+up/shift+k)
|
<kbd>pgup</kbd>: メインパネルを上にスクロール (fn+up/shift+k)
|
||||||
<kbd>pgdown</kbd>: メインパネルを下にスクロール (fn+down/shift+j)
|
<kbd>pgdown</kbd>: メインパネルを下にスクロール (fn+down/shift+j)
|
||||||
<kbd>m</kbd>: view merge/rebase options
|
|
||||||
<kbd>ctrl+p</kbd>: view custom patch options
|
|
||||||
<kbd>R</kbd>: リフレッシュ
|
|
||||||
<kbd>?</kbd>: メニューを開く
|
|
||||||
<kbd>+</kbd>: 次のスクリーンモード (normal/half/fullscreen)
|
|
||||||
<kbd>_</kbd>: 前のスクリーンモード
|
|
||||||
<kbd>ctrl+s</kbd>: view filter-by-path options
|
|
||||||
<kbd>W</kbd>: 差分メニューを開く
|
|
||||||
<kbd>ctrl+e</kbd>: 差分メニューを開く
|
|
||||||
<kbd>@</kbd>: コマンドログメニューを開く
|
<kbd>@</kbd>: コマンドログメニューを開く
|
||||||
<kbd>ctrl+w</kbd>: 空白文字の差分の表示有無を切り替え
|
|
||||||
<kbd>}</kbd>: Increase the size of the context shown around changes in the diff view
|
<kbd>}</kbd>: Increase the size of the context shown around changes in the diff view
|
||||||
<kbd>{</kbd>: Decrease the size of the context shown around changes in the diff view
|
<kbd>{</kbd>: Decrease the size of the context shown around changes in the diff view
|
||||||
<kbd>:</kbd>: カスタムコマンドを実行
|
<kbd>:</kbd>: カスタムコマンドを実行
|
||||||
|
<kbd>ctrl+p</kbd>: view custom patch options
|
||||||
|
<kbd>m</kbd>: view merge/rebase options
|
||||||
|
<kbd>R</kbd>: リフレッシュ
|
||||||
|
<kbd>+</kbd>: 次のスクリーンモード (normal/half/fullscreen)
|
||||||
|
<kbd>_</kbd>: 前のスクリーンモード
|
||||||
|
<kbd>?</kbd>: メニューを開く
|
||||||
|
<kbd>ctrl+s</kbd>: view filter-by-path options
|
||||||
|
<kbd>W</kbd>: 差分メニューを開く
|
||||||
|
<kbd>ctrl+e</kbd>: 差分メニューを開く
|
||||||
|
<kbd>ctrl+w</kbd>: 空白文字の差分の表示有無を切り替え
|
||||||
<kbd>z</kbd>: アンドゥ (via reflog) (experimental)
|
<kbd>z</kbd>: アンドゥ (via reflog) (experimental)
|
||||||
<kbd>ctrl+z</kbd>: リドゥ (via reflog) (experimental)
|
<kbd>ctrl+z</kbd>: リドゥ (via reflog) (experimental)
|
||||||
<kbd>P</kbd>: push
|
<kbd>P</kbd>: push
|
||||||
@ -115,6 +115,13 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>`</kbd>: ファイルツリーの表示を切り替え
|
<kbd>`</kbd>: ファイルツリーの表示を切り替え
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
## コミットメッセージ
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>enter</kbd>: 確認
|
||||||
|
<kbd>esc</kbd>: 閉じる
|
||||||
|
</pre>
|
||||||
|
|
||||||
## サブモジュール
|
## サブモジュール
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
@ -131,8 +138,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
## ステータス
|
## ステータス
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>e</kbd>: 設定ファイルを編集
|
|
||||||
<kbd>o</kbd>: 設定ファイルを開く
|
<kbd>o</kbd>: 設定ファイルを開く
|
||||||
|
<kbd>e</kbd>: 設定ファイルを編集
|
||||||
<kbd>u</kbd>: 更新を確認
|
<kbd>u</kbd>: 更新を確認
|
||||||
<kbd>enter</kbd>: 最近使用したリポジトリに切り替え
|
<kbd>enter</kbd>: 最近使用したリポジトリに切り替え
|
||||||
<kbd>a</kbd>: すべてのブランチログを表示
|
<kbd>a</kbd>: すべてのブランチログを表示
|
||||||
@ -257,6 +264,13 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>C</kbd>: gitエディタを使用して変更をコミット
|
<kbd>C</kbd>: gitエディタを使用して変更をコミット
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
## メニュー
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>enter</kbd>: 実行
|
||||||
|
<kbd>esc</kbd>: 閉じる
|
||||||
|
</pre>
|
||||||
|
|
||||||
## リモート
|
## リモート
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
@ -295,3 +309,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>ctrl+r</kbd>: reset cherry-picked (copied) commits selection
|
<kbd>ctrl+r</kbd>: reset cherry-picked (copied) commits selection
|
||||||
<kbd>enter</kbd>: コミットを閲覧
|
<kbd>enter</kbd>: コミットを閲覧
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
## 確認パネル
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>enter</kbd>: 確認
|
||||||
|
<kbd>esc</kbd>: 閉じる/キャンセル
|
||||||
|
</pre>
|
||||||
|
@ -8,20 +8,20 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>ctrl+r</kbd>: 최근에 사용한 저장소로 전환
|
<kbd>ctrl+r</kbd>: 최근에 사용한 저장소로 전환
|
||||||
<kbd>pgup</kbd>: 메인 패널을 위로 스크롤 (fn+up/shift+k)
|
<kbd>pgup</kbd>: 메인 패널을 위로 스크롤 (fn+up/shift+k)
|
||||||
<kbd>pgdown</kbd>: 메인 패널을 아래로로 스크롤 (fn+down/shift+j)
|
<kbd>pgdown</kbd>: 메인 패널을 아래로로 스크롤 (fn+down/shift+j)
|
||||||
<kbd>m</kbd>: view merge/rebase options
|
|
||||||
<kbd>ctrl+p</kbd>: 커스텀 Patch 옵션 보기
|
|
||||||
<kbd>R</kbd>: 새로고침
|
|
||||||
<kbd>?</kbd>: 매뉴 열기
|
|
||||||
<kbd>+</kbd>: 다음 스크린 모드 (normal/half/fullscreen)
|
|
||||||
<kbd>_</kbd>: 이전 스크린 모드
|
|
||||||
<kbd>ctrl+s</kbd>: view filter-by-path options
|
|
||||||
<kbd>W</kbd>: Diff 메뉴 열기
|
|
||||||
<kbd>ctrl+e</kbd>: Diff 메뉴 열기
|
|
||||||
<kbd>@</kbd>: 명령어 로그 메뉴 열기
|
<kbd>@</kbd>: 명령어 로그 메뉴 열기
|
||||||
<kbd>ctrl+w</kbd>: 공백문자를 Diff 뷰에서 표시 여부 전환
|
|
||||||
<kbd>}</kbd>: diff 보기의 변경 사항 주위에 표시되는 컨텍스트의 크기를 늘리기
|
<kbd>}</kbd>: diff 보기의 변경 사항 주위에 표시되는 컨텍스트의 크기를 늘리기
|
||||||
<kbd>{</kbd>: diff 보기의 변경 사항 주위에 표시되는 컨텍스트 크기 줄이기
|
<kbd>{</kbd>: diff 보기의 변경 사항 주위에 표시되는 컨텍스트 크기 줄이기
|
||||||
<kbd>:</kbd>: execute custom command
|
<kbd>:</kbd>: execute custom command
|
||||||
|
<kbd>ctrl+p</kbd>: 커스텀 Patch 옵션 보기
|
||||||
|
<kbd>m</kbd>: view merge/rebase options
|
||||||
|
<kbd>R</kbd>: 새로고침
|
||||||
|
<kbd>+</kbd>: 다음 스크린 모드 (normal/half/fullscreen)
|
||||||
|
<kbd>_</kbd>: 이전 스크린 모드
|
||||||
|
<kbd>?</kbd>: 매뉴 열기
|
||||||
|
<kbd>ctrl+s</kbd>: view filter-by-path options
|
||||||
|
<kbd>W</kbd>: Diff 메뉴 열기
|
||||||
|
<kbd>ctrl+e</kbd>: Diff 메뉴 열기
|
||||||
|
<kbd>ctrl+w</kbd>: 공백문자를 Diff 뷰에서 표시 여부 전환
|
||||||
<kbd>z</kbd>: 되돌리기 (reflog) (실험적)
|
<kbd>z</kbd>: 되돌리기 (reflog) (실험적)
|
||||||
<kbd>ctrl+z</kbd>: 다시 실행 (reflog) (실험적)
|
<kbd>ctrl+z</kbd>: 다시 실행 (reflog) (실험적)
|
||||||
<kbd>P</kbd>: 푸시
|
<kbd>P</kbd>: 푸시
|
||||||
@ -83,6 +83,13 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>enter</kbd>: view selected item's files
|
<kbd>enter</kbd>: view selected item's files
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
## 메뉴
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>enter</kbd>: 실행
|
||||||
|
<kbd>esc</kbd>: 닫기
|
||||||
|
</pre>
|
||||||
|
|
||||||
## 메인 패널 (Merging)
|
## 메인 패널 (Merging)
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
@ -168,8 +175,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
## 상태
|
## 상태
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>e</kbd>: 설정 파일 수정
|
|
||||||
<kbd>o</kbd>: 설정 파일 열기
|
<kbd>o</kbd>: 설정 파일 열기
|
||||||
|
<kbd>e</kbd>: 설정 파일 수정
|
||||||
<kbd>u</kbd>: 업데이트 확인
|
<kbd>u</kbd>: 업데이트 확인
|
||||||
<kbd>enter</kbd>: 최근에 사용한 저장소로 전환
|
<kbd>enter</kbd>: 최근에 사용한 저장소로 전환
|
||||||
<kbd>a</kbd>: 모든 브랜치 로그 표시
|
<kbd>a</kbd>: 모든 브랜치 로그 표시
|
||||||
@ -259,6 +266,13 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>`</kbd>: 파일 트리뷰로 전환
|
<kbd>`</kbd>: 파일 트리뷰로 전환
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
## 커밋메시지
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>enter</kbd>: 확인
|
||||||
|
<kbd>esc</kbd>: 닫기
|
||||||
|
</pre>
|
||||||
|
|
||||||
## 태그
|
## 태그
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
@ -295,3 +309,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>M</kbd>: git mergetool를 열기
|
<kbd>M</kbd>: git mergetool를 열기
|
||||||
<kbd>f</kbd>: fetch
|
<kbd>f</kbd>: fetch
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
## 확인 패널
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>enter</kbd>: 확인
|
||||||
|
<kbd>esc</kbd>: 닫기/취소
|
||||||
|
</pre>
|
||||||
|
@ -8,20 +8,20 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>ctrl+r</kbd>: wissel naar een recente repo
|
<kbd>ctrl+r</kbd>: wissel naar een recente repo
|
||||||
<kbd>pgup</kbd>: scroll naar beneden vanaf hoofdpaneel (fn+up/shift+k)
|
<kbd>pgup</kbd>: scroll naar beneden vanaf hoofdpaneel (fn+up/shift+k)
|
||||||
<kbd>pgdown</kbd>: scroll naar beneden vanaf hoofdpaneel (fn+down/shift+j)
|
<kbd>pgdown</kbd>: scroll naar beneden vanaf hoofdpaneel (fn+down/shift+j)
|
||||||
<kbd>m</kbd>: bekijk merge/rebase opties
|
|
||||||
<kbd>ctrl+p</kbd>: bekijk aangepaste patch opties
|
|
||||||
<kbd>R</kbd>: verversen
|
|
||||||
<kbd>?</kbd>: open menu
|
|
||||||
<kbd>+</kbd>: volgende scherm modus (normaal/half/groot)
|
|
||||||
<kbd>_</kbd>: vorige scherm modus
|
|
||||||
<kbd>ctrl+s</kbd>: bekijk scoping opties
|
|
||||||
<kbd>W</kbd>: open diff menu
|
|
||||||
<kbd>ctrl+e</kbd>: open diff menu
|
|
||||||
<kbd>@</kbd>: open command log menu
|
<kbd>@</kbd>: open command log menu
|
||||||
<kbd>ctrl+w</kbd>: Toggle whether or not whitespace changes are shown in the diff view
|
|
||||||
<kbd>}</kbd>: Increase the size of the context shown around changes in the diff view
|
<kbd>}</kbd>: Increase the size of the context shown around changes in the diff view
|
||||||
<kbd>{</kbd>: Decrease the size of the context shown around changes in the diff view
|
<kbd>{</kbd>: Decrease the size of the context shown around changes in the diff view
|
||||||
<kbd>:</kbd>: voer aangepaste commando uit
|
<kbd>:</kbd>: voer aangepaste commando uit
|
||||||
|
<kbd>ctrl+p</kbd>: bekijk aangepaste patch opties
|
||||||
|
<kbd>m</kbd>: bekijk merge/rebase opties
|
||||||
|
<kbd>R</kbd>: verversen
|
||||||
|
<kbd>+</kbd>: volgende scherm modus (normaal/half/groot)
|
||||||
|
<kbd>_</kbd>: vorige scherm modus
|
||||||
|
<kbd>?</kbd>: open menu
|
||||||
|
<kbd>ctrl+s</kbd>: bekijk scoping opties
|
||||||
|
<kbd>W</kbd>: open diff menu
|
||||||
|
<kbd>ctrl+e</kbd>: open diff menu
|
||||||
|
<kbd>ctrl+w</kbd>: Toggle whether or not whitespace changes are shown in the diff view
|
||||||
<kbd>z</kbd>: ongedaan maken (via reflog) (experimenteel)
|
<kbd>z</kbd>: ongedaan maken (via reflog) (experimenteel)
|
||||||
<kbd>ctrl+z</kbd>: redo (via reflog) (experimenteel)
|
<kbd>ctrl+z</kbd>: redo (via reflog) (experimenteel)
|
||||||
<kbd>P</kbd>: push
|
<kbd>P</kbd>: push
|
||||||
@ -68,6 +68,13 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>f</kbd>: fetch
|
<kbd>f</kbd>: fetch
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
## Bevestigingspaneel
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>enter</kbd>: bevestig
|
||||||
|
<kbd>esc</kbd>: sluiten
|
||||||
|
</pre>
|
||||||
|
|
||||||
## Branches
|
## Branches
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
@ -91,6 +98,13 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>enter</kbd>: bekijk commits
|
<kbd>enter</kbd>: bekijk commits
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
## Commit Bericht
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>enter</kbd>: bevestig
|
||||||
|
<kbd>esc</kbd>: sluiten
|
||||||
|
</pre>
|
||||||
|
|
||||||
## Commit bestanden
|
## Commit bestanden
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
@ -138,6 +152,13 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>enter</kbd>: bekijk gecommite bestanden
|
<kbd>enter</kbd>: bekijk gecommite bestanden
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
## Menu
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>enter</kbd>: uitvoeren
|
||||||
|
<kbd>esc</kbd>: sluiten
|
||||||
|
</pre>
|
||||||
|
|
||||||
## Mergen
|
## Mergen
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
@ -250,8 +271,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
## Status
|
## Status
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>e</kbd>: verander config bestand
|
|
||||||
<kbd>o</kbd>: open config bestand
|
<kbd>o</kbd>: open config bestand
|
||||||
|
<kbd>e</kbd>: verander config bestand
|
||||||
<kbd>u</kbd>: check voor updates
|
<kbd>u</kbd>: check voor updates
|
||||||
<kbd>enter</kbd>: wissel naar een recente repo
|
<kbd>enter</kbd>: wissel naar een recente repo
|
||||||
<kbd>a</kbd>: alle logs van de branch laten zien
|
<kbd>a</kbd>: alle logs van de branch laten zien
|
||||||
|
@ -8,20 +8,20 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>ctrl+r</kbd>: switch to a recent repo
|
<kbd>ctrl+r</kbd>: switch to a recent repo
|
||||||
<kbd>pgup</kbd>: scroll up main panel (fn+up/shift+k)
|
<kbd>pgup</kbd>: scroll up main panel (fn+up/shift+k)
|
||||||
<kbd>pgdown</kbd>: scroll down main panel (fn+down/shift+j)
|
<kbd>pgdown</kbd>: scroll down main panel (fn+down/shift+j)
|
||||||
<kbd>m</kbd>: widok scalenia/opcje zmiany bazy
|
|
||||||
<kbd>ctrl+p</kbd>: view custom patch options
|
|
||||||
<kbd>R</kbd>: odśwież
|
|
||||||
<kbd>?</kbd>: open menu
|
|
||||||
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
|
|
||||||
<kbd>_</kbd>: prev screen mode
|
|
||||||
<kbd>ctrl+s</kbd>: view filter-by-path options
|
|
||||||
<kbd>W</kbd>: open diff menu
|
|
||||||
<kbd>ctrl+e</kbd>: open diff menu
|
|
||||||
<kbd>@</kbd>: open command log menu
|
<kbd>@</kbd>: open command log menu
|
||||||
<kbd>ctrl+w</kbd>: Toggle whether or not whitespace changes are shown in the diff view
|
|
||||||
<kbd>}</kbd>: Increase the size of the context shown around changes in the diff view
|
<kbd>}</kbd>: Increase the size of the context shown around changes in the diff view
|
||||||
<kbd>{</kbd>: Decrease the size of the context shown around changes in the diff view
|
<kbd>{</kbd>: Decrease the size of the context shown around changes in the diff view
|
||||||
<kbd>:</kbd>: wykonaj własną komendę
|
<kbd>:</kbd>: wykonaj własną komendę
|
||||||
|
<kbd>ctrl+p</kbd>: view custom patch options
|
||||||
|
<kbd>m</kbd>: widok scalenia/opcje zmiany bazy
|
||||||
|
<kbd>R</kbd>: odśwież
|
||||||
|
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
|
||||||
|
<kbd>_</kbd>: prev screen mode
|
||||||
|
<kbd>?</kbd>: open menu
|
||||||
|
<kbd>ctrl+s</kbd>: view filter-by-path options
|
||||||
|
<kbd>W</kbd>: open diff menu
|
||||||
|
<kbd>ctrl+e</kbd>: open diff menu
|
||||||
|
<kbd>ctrl+w</kbd>: Toggle whether or not whitespace changes are shown in the diff view
|
||||||
<kbd>z</kbd>: undo (via reflog) (experimental)
|
<kbd>z</kbd>: undo (via reflog) (experimental)
|
||||||
<kbd>ctrl+z</kbd>: redo (via reflog) (experimental)
|
<kbd>ctrl+z</kbd>: redo (via reflog) (experimental)
|
||||||
<kbd>P</kbd>: push
|
<kbd>P</kbd>: push
|
||||||
@ -42,6 +42,13 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>[</kbd>: previous tab
|
<kbd>[</kbd>: previous tab
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
## Commit Message
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>enter</kbd>: potwierdź
|
||||||
|
<kbd>esc</kbd>: zamknij
|
||||||
|
</pre>
|
||||||
|
|
||||||
## Commity
|
## Commity
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
@ -75,6 +82,13 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>enter</kbd>: przeglądaj pliki commita
|
<kbd>enter</kbd>: przeglądaj pliki commita
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
## Confirmation Panel
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>enter</kbd>: potwierdź
|
||||||
|
<kbd>esc</kbd>: zamknij
|
||||||
|
</pre>
|
||||||
|
|
||||||
## Local Branches
|
## Local Branches
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
@ -113,6 +127,13 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>esc</kbd>: wyście z trybu "linia po linii"
|
<kbd>esc</kbd>: wyście z trybu "linia po linii"
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
## Menu
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>enter</kbd>: wykonaj
|
||||||
|
<kbd>esc</kbd>: zamknij
|
||||||
|
</pre>
|
||||||
|
|
||||||
## Pliki
|
## Pliki
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
@ -243,8 +264,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
## Status
|
## Status
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>e</kbd>: edytuj konfigurację
|
|
||||||
<kbd>o</kbd>: otwórz konfigurację
|
<kbd>o</kbd>: otwórz konfigurację
|
||||||
|
<kbd>e</kbd>: edytuj konfigurację
|
||||||
<kbd>u</kbd>: sprawdź aktualizacje
|
<kbd>u</kbd>: sprawdź aktualizacje
|
||||||
<kbd>enter</kbd>: switch to a recent repo
|
<kbd>enter</kbd>: switch to a recent repo
|
||||||
<kbd>a</kbd>: pokaż wszystkie logi gałęzi
|
<kbd>a</kbd>: pokaż wszystkie logi gałęzi
|
||||||
|
@ -8,20 +8,20 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>ctrl+r</kbd>: 切换到最近的仓库
|
<kbd>ctrl+r</kbd>: 切换到最近的仓库
|
||||||
<kbd>pgup</kbd>: 向上滚动主面板 (fn+up/shift+k)
|
<kbd>pgup</kbd>: 向上滚动主面板 (fn+up/shift+k)
|
||||||
<kbd>pgdown</kbd>: 向下滚动主面板 (fn+down/shift+j)
|
<kbd>pgdown</kbd>: 向下滚动主面板 (fn+down/shift+j)
|
||||||
<kbd>m</kbd>: 查看 合并/变基 选项
|
|
||||||
<kbd>ctrl+p</kbd>: 查看自定义补丁选项
|
|
||||||
<kbd>R</kbd>: 刷新
|
|
||||||
<kbd>?</kbd>: 打开菜单
|
|
||||||
<kbd>+</kbd>: 下一屏模式(正常/半屏/全屏)
|
|
||||||
<kbd>_</kbd>: 上一屏模式
|
|
||||||
<kbd>ctrl+s</kbd>: 查看按路径过滤选项
|
|
||||||
<kbd>W</kbd>: 打开 diff 菜单
|
|
||||||
<kbd>ctrl+e</kbd>: 打开 diff 菜单
|
|
||||||
<kbd>@</kbd>: 打开命令日志菜单
|
<kbd>@</kbd>: 打开命令日志菜单
|
||||||
<kbd>ctrl+w</kbd>: 切换是否在差异视图中显示空白字符差异
|
|
||||||
<kbd>}</kbd>: 扩大差异视图中显示的上下文范围
|
<kbd>}</kbd>: 扩大差异视图中显示的上下文范围
|
||||||
<kbd>{</kbd>: 缩小差异视图中显示的上下文范围
|
<kbd>{</kbd>: 缩小差异视图中显示的上下文范围
|
||||||
<kbd>:</kbd>: 执行自定义命令
|
<kbd>:</kbd>: 执行自定义命令
|
||||||
|
<kbd>ctrl+p</kbd>: 查看自定义补丁选项
|
||||||
|
<kbd>m</kbd>: 查看 合并/变基 选项
|
||||||
|
<kbd>R</kbd>: 刷新
|
||||||
|
<kbd>+</kbd>: 下一屏模式(正常/半屏/全屏)
|
||||||
|
<kbd>_</kbd>: 上一屏模式
|
||||||
|
<kbd>?</kbd>: 打开菜单
|
||||||
|
<kbd>ctrl+s</kbd>: 查看按路径过滤选项
|
||||||
|
<kbd>W</kbd>: 打开 diff 菜单
|
||||||
|
<kbd>ctrl+e</kbd>: 打开 diff 菜单
|
||||||
|
<kbd>ctrl+w</kbd>: 切换是否在差异视图中显示空白字符差异
|
||||||
<kbd>z</kbd>: (通过 reflog)撤销「实验功能」
|
<kbd>z</kbd>: (通过 reflog)撤销「实验功能」
|
||||||
<kbd>ctrl+z</kbd>: (通过 reflog)重做「实验功能」
|
<kbd>ctrl+z</kbd>: (通过 reflog)重做「实验功能」
|
||||||
<kbd>P</kbd>: 推送
|
<kbd>P</kbd>: 推送
|
||||||
@ -155,6 +155,13 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
<kbd>`</kbd>: 切换文件树视图
|
<kbd>`</kbd>: 切换文件树视图
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
## 提交讯息
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>enter</kbd>: 确认
|
||||||
|
<kbd>esc</kbd>: 关闭
|
||||||
|
</pre>
|
||||||
|
|
||||||
## 文件
|
## 文件
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
@ -254,13 +261,27 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||||||
## 状态
|
## 状态
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>e</kbd>: 编辑配置文件
|
|
||||||
<kbd>o</kbd>: 打开配置文件
|
<kbd>o</kbd>: 打开配置文件
|
||||||
|
<kbd>e</kbd>: 编辑配置文件
|
||||||
<kbd>u</kbd>: 检查更新
|
<kbd>u</kbd>: 检查更新
|
||||||
<kbd>enter</kbd>: 切换到最近的仓库
|
<kbd>enter</kbd>: 切换到最近的仓库
|
||||||
<kbd>a</kbd>: 显示所有分支的日志
|
<kbd>a</kbd>: 显示所有分支的日志
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
## 确认面板
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>enter</kbd>: 确认
|
||||||
|
<kbd>esc</kbd>: 关闭
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## 菜单
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>enter</kbd>: 执行
|
||||||
|
<kbd>esc</kbd>: 关闭
|
||||||
|
</pre>
|
||||||
|
|
||||||
## 贮藏
|
## 贮藏
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
|
@ -33,7 +33,6 @@ type App struct {
|
|||||||
Config config.AppConfigurer
|
Config config.AppConfigurer
|
||||||
OSCommand *oscommands.OSCommand
|
OSCommand *oscommands.OSCommand
|
||||||
Gui *gui.Gui
|
Gui *gui.Gui
|
||||||
Updater *updates.Updater // may only need this on the Gui
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Run(
|
func Run(
|
||||||
@ -87,8 +86,7 @@ func NewApp(config config.AppConfigurer, common *common.Common) (*App, error) {
|
|||||||
|
|
||||||
app.OSCommand = oscommands.NewOSCommand(common, config, oscommands.GetPlatform(), oscommands.NewNullGuiIO(app.Log))
|
app.OSCommand = oscommands.NewOSCommand(common, config, oscommands.GetPlatform(), oscommands.NewNullGuiIO(app.Log))
|
||||||
|
|
||||||
var err error
|
updater, err := updates.NewUpdater(common, config, app.OSCommand)
|
||||||
app.Updater, err = updates.NewUpdater(common, config, app.OSCommand)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return app, err
|
return app, err
|
||||||
}
|
}
|
||||||
@ -108,7 +106,7 @@ func NewApp(config config.AppConfigurer, common *common.Common) (*App, error) {
|
|||||||
return app, err
|
return app, err
|
||||||
}
|
}
|
||||||
|
|
||||||
app.Gui, err = gui.NewGui(common, config, gitVersion, app.Updater, showRecentRepos, dirName)
|
app.Gui, err = gui.NewGui(common, config, gitVersion, updater, showRecentRepos, dirName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return app, err
|
return app, err
|
||||||
}
|
}
|
||||||
|
@ -1,131 +0,0 @@
|
|||||||
package gui
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/jesseduffield/generics/slices"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
|
||||||
"github.com/sasha-s/go-deadlock"
|
|
||||||
)
|
|
||||||
|
|
||||||
// statusManager's job is to handle rendering of loading states and toast notifications
|
|
||||||
// that you see at the bottom left of the screen.
|
|
||||||
type statusManager struct {
|
|
||||||
statuses []appStatus
|
|
||||||
nextId int
|
|
||||||
mutex deadlock.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
type appStatus struct {
|
|
||||||
message string
|
|
||||||
statusType string
|
|
||||||
id int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *statusManager) removeStatus(id int) {
|
|
||||||
m.mutex.Lock()
|
|
||||||
defer m.mutex.Unlock()
|
|
||||||
|
|
||||||
m.statuses = slices.Filter(m.statuses, func(status appStatus) bool {
|
|
||||||
return status.id != id
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *statusManager) addWaitingStatus(message string) int {
|
|
||||||
m.mutex.Lock()
|
|
||||||
defer m.mutex.Unlock()
|
|
||||||
|
|
||||||
m.nextId += 1
|
|
||||||
id := m.nextId
|
|
||||||
|
|
||||||
newStatus := appStatus{
|
|
||||||
message: message,
|
|
||||||
statusType: "waiting",
|
|
||||||
id: id,
|
|
||||||
}
|
|
||||||
m.statuses = append([]appStatus{newStatus}, m.statuses...)
|
|
||||||
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *statusManager) addToastStatus(message string) int {
|
|
||||||
m.mutex.Lock()
|
|
||||||
defer m.mutex.Unlock()
|
|
||||||
|
|
||||||
m.nextId++
|
|
||||||
id := m.nextId
|
|
||||||
|
|
||||||
newStatus := appStatus{
|
|
||||||
message: message,
|
|
||||||
statusType: "toast",
|
|
||||||
id: id,
|
|
||||||
}
|
|
||||||
m.statuses = append([]appStatus{newStatus}, m.statuses...)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
time.Sleep(time.Second * 2)
|
|
||||||
|
|
||||||
m.removeStatus(id)
|
|
||||||
}()
|
|
||||||
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *statusManager) getStatusString() string {
|
|
||||||
if len(m.statuses) == 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
topStatus := m.statuses[0]
|
|
||||||
if topStatus.statusType == "waiting" {
|
|
||||||
return topStatus.message + " " + utils.Loader()
|
|
||||||
}
|
|
||||||
return topStatus.message
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *statusManager) showStatus() bool {
|
|
||||||
return len(m.statuses) > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) toast(message string) {
|
|
||||||
gui.statusManager.addToastStatus(message)
|
|
||||||
|
|
||||||
gui.renderAppStatus()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) renderAppStatus() {
|
|
||||||
go utils.Safe(func() {
|
|
||||||
ticker := time.NewTicker(time.Millisecond * 50)
|
|
||||||
defer ticker.Stop()
|
|
||||||
for range ticker.C {
|
|
||||||
appStatus := gui.statusManager.getStatusString()
|
|
||||||
gui.c.OnUIThread(func() error {
|
|
||||||
return gui.renderString(gui.Views.AppStatus, appStatus)
|
|
||||||
})
|
|
||||||
|
|
||||||
if appStatus == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// withWaitingStatus wraps a function and shows a waiting status while the function is still executing
|
|
||||||
func (gui *Gui) withWaitingStatus(message string, f func() error) error {
|
|
||||||
go utils.Safe(func() {
|
|
||||||
id := gui.statusManager.addWaitingStatus(message)
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
gui.statusManager.removeStatus(id)
|
|
||||||
}()
|
|
||||||
|
|
||||||
gui.renderAppStatus()
|
|
||||||
|
|
||||||
if err := f(); err != nil {
|
|
||||||
gui.c.OnUIThread(func() error {
|
|
||||||
return gui.c.Error(err)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -4,18 +4,33 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (gui *Gui) startBackgroundRoutines() {
|
type BackgroundRoutineMgr struct {
|
||||||
userConfig := gui.UserConfig
|
gui *Gui
|
||||||
|
|
||||||
|
// if we've suspended the gui (e.g. because we've switched to a subprocess)
|
||||||
|
// we typically want to pause some things that are running like background
|
||||||
|
// file refreshes
|
||||||
|
pauseBackgroundThreads bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BackgroundRoutineMgr) PauseBackgroundThreads(pause bool) {
|
||||||
|
self.pauseBackgroundThreads = pause
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BackgroundRoutineMgr) startBackgroundRoutines() {
|
||||||
|
userConfig := self.gui.UserConfig
|
||||||
|
|
||||||
if userConfig.Git.AutoFetch {
|
if userConfig.Git.AutoFetch {
|
||||||
fetchInterval := userConfig.Refresher.FetchInterval
|
fetchInterval := userConfig.Refresher.FetchInterval
|
||||||
if fetchInterval > 0 {
|
if fetchInterval > 0 {
|
||||||
go utils.Safe(gui.startBackgroundFetch)
|
go utils.Safe(self.startBackgroundFetch)
|
||||||
} else {
|
} else {
|
||||||
gui.c.Log.Errorf(
|
self.gui.c.Log.Errorf(
|
||||||
"Value of config option 'refresher.fetchInterval' (%d) is invalid, disabling auto-fetch",
|
"Value of config option 'refresher.fetchInterval' (%d) is invalid, disabling auto-fetch",
|
||||||
fetchInterval)
|
fetchInterval)
|
||||||
}
|
}
|
||||||
@ -24,42 +39,44 @@ func (gui *Gui) startBackgroundRoutines() {
|
|||||||
if userConfig.Git.AutoRefresh {
|
if userConfig.Git.AutoRefresh {
|
||||||
refreshInterval := userConfig.Refresher.RefreshInterval
|
refreshInterval := userConfig.Refresher.RefreshInterval
|
||||||
if refreshInterval > 0 {
|
if refreshInterval > 0 {
|
||||||
gui.goEvery(time.Second*time.Duration(refreshInterval), gui.stopChan, gui.refreshFilesAndSubmodules)
|
self.goEvery(time.Second*time.Duration(refreshInterval), self.gui.stopChan, func() error {
|
||||||
|
return self.gui.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.FILES}})
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
gui.c.Log.Errorf(
|
self.gui.c.Log.Errorf(
|
||||||
"Value of config option 'refresher.refreshInterval' (%d) is invalid, disabling auto-refresh",
|
"Value of config option 'refresher.refreshInterval' (%d) is invalid, disabling auto-refresh",
|
||||||
refreshInterval)
|
refreshInterval)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) startBackgroundFetch() {
|
func (self *BackgroundRoutineMgr) startBackgroundFetch() {
|
||||||
gui.waitForIntro.Wait()
|
self.gui.waitForIntro.Wait()
|
||||||
isNew := gui.IsNewRepo
|
isNew := self.gui.IsNewRepo
|
||||||
userConfig := gui.UserConfig
|
userConfig := self.gui.UserConfig
|
||||||
if !isNew {
|
if !isNew {
|
||||||
time.After(time.Duration(userConfig.Refresher.FetchInterval) * time.Second)
|
time.After(time.Duration(userConfig.Refresher.FetchInterval) * time.Second)
|
||||||
}
|
}
|
||||||
err := gui.backgroundFetch()
|
err := self.backgroundFetch()
|
||||||
if err != nil && strings.Contains(err.Error(), "exit status 128") && isNew {
|
if err != nil && strings.Contains(err.Error(), "exit status 128") && isNew {
|
||||||
_ = gui.c.Alert(gui.c.Tr.NoAutomaticGitFetchTitle, gui.c.Tr.NoAutomaticGitFetchBody)
|
_ = self.gui.c.Alert(self.gui.c.Tr.NoAutomaticGitFetchTitle, self.gui.c.Tr.NoAutomaticGitFetchBody)
|
||||||
} else {
|
} else {
|
||||||
gui.goEvery(time.Second*time.Duration(userConfig.Refresher.FetchInterval), gui.stopChan, func() error {
|
self.goEvery(time.Second*time.Duration(userConfig.Refresher.FetchInterval), self.gui.stopChan, func() error {
|
||||||
err := gui.backgroundFetch()
|
err := self.backgroundFetch()
|
||||||
gui.render()
|
self.gui.c.Render()
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) goEvery(interval time.Duration, stop chan struct{}, function func() error) {
|
func (self *BackgroundRoutineMgr) goEvery(interval time.Duration, stop chan struct{}, function func() error) {
|
||||||
go utils.Safe(func() {
|
go utils.Safe(func() {
|
||||||
ticker := time.NewTicker(interval)
|
ticker := time.NewTicker(interval)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
if gui.PauseBackgroundThreads {
|
if self.pauseBackgroundThreads {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
_ = function()
|
_ = function()
|
||||||
@ -69,3 +86,11 @@ func (gui *Gui) goEvery(interval time.Duration, stop chan struct{}, function fun
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *BackgroundRoutineMgr) backgroundFetch() (err error) {
|
||||||
|
err = self.gui.git.Sync.Fetch(git_commands.FetchOptions{Background: true})
|
||||||
|
|
||||||
|
_ = self.gui.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.BRANCHES, types.COMMITS, types.REMOTES, types.TAGS}, Mode: types.ASYNC})
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
package gui
|
|
||||||
|
|
||||||
import "github.com/jesseduffield/lazygit/pkg/gui/types"
|
|
||||||
|
|
||||||
func (gui *Gui) branchesRenderToMain() error {
|
|
||||||
var task types.UpdateTask
|
|
||||||
branch := gui.State.Contexts.Branches.GetSelected()
|
|
||||||
if branch == nil {
|
|
||||||
task = types.NewRenderStringTask(gui.c.Tr.NoBranchesThisRepo)
|
|
||||||
} else {
|
|
||||||
cmdObj := gui.git.Branch.GetGraphCmdObj(branch.FullRefName())
|
|
||||||
|
|
||||||
task = types.NewRunPtyTask(cmdObj.GetCmd())
|
|
||||||
}
|
|
||||||
|
|
||||||
return gui.c.RenderToMainViews(types.RefreshMainOpts{
|
|
||||||
Pair: gui.c.MainViewPairs().Normal,
|
|
||||||
Main: &types.ViewUpdateOpts{
|
|
||||||
Title: gui.c.Tr.LogTitle,
|
|
||||||
Task: task,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
package gui
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/controllers"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (gui *Gui) commitFilesRenderToMain() error {
|
|
||||||
node := gui.State.Contexts.CommitFiles.GetSelected()
|
|
||||||
if node == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ref := gui.State.Contexts.CommitFiles.GetRef()
|
|
||||||
to := ref.RefName()
|
|
||||||
from, reverse := gui.State.Modes.Diffing.GetFromAndReverseArgsForDiff(ref.ParentRefName())
|
|
||||||
|
|
||||||
cmdObj := gui.git.WorkingTree.ShowFileDiffCmdObj(from, to, reverse, node.GetPath(), false,
|
|
||||||
gui.IgnoreWhitespaceInDiffView)
|
|
||||||
task := types.NewRunPtyTask(cmdObj.GetCmd())
|
|
||||||
|
|
||||||
pair := gui.c.MainViewPairs().Normal
|
|
||||||
if node.File != nil {
|
|
||||||
pair = gui.c.MainViewPairs().PatchBuilding
|
|
||||||
}
|
|
||||||
|
|
||||||
return gui.c.RenderToMainViews(types.RefreshMainOpts{
|
|
||||||
Pair: pair,
|
|
||||||
Main: &types.ViewUpdateOpts{
|
|
||||||
Title: gui.Tr.Patch,
|
|
||||||
Task: task,
|
|
||||||
},
|
|
||||||
Secondary: gui.secondaryPatchPanelUpdateOpts(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) SwitchToCommitFilesContext(opts controllers.SwitchToCommitFilesContextOpts) error {
|
|
||||||
gui.State.Contexts.CommitFiles.SetSelectedLineIdx(0)
|
|
||||||
gui.State.Contexts.CommitFiles.SetRef(opts.Ref)
|
|
||||||
gui.State.Contexts.CommitFiles.SetTitleRef(opts.Ref.Description())
|
|
||||||
gui.State.Contexts.CommitFiles.SetCanRebase(opts.CanRebase)
|
|
||||||
gui.State.Contexts.CommitFiles.SetParentContext(opts.Context)
|
|
||||||
gui.State.Contexts.CommitFiles.SetWindowName(opts.Context.GetWindowName())
|
|
||||||
|
|
||||||
if err := gui.c.Refresh(types.RefreshOptions{
|
|
||||||
Scope: []types.RefreshableView{types.COMMIT_FILES},
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return gui.c.PushContext(gui.State.Contexts.CommitFiles)
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
package gui
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/jesseduffield/gocui"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/keybindings"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (gui *Gui) handleCommitMessageFocused() error {
|
|
||||||
message := utils.ResolvePlaceholderString(
|
|
||||||
gui.c.Tr.CommitMessageConfirm,
|
|
||||||
map[string]string{
|
|
||||||
"keyBindClose": keybindings.Label(gui.c.UserConfig.Keybinding.Universal.Return),
|
|
||||||
"keyBindConfirm": keybindings.Label(gui.c.UserConfig.Keybinding.Universal.Confirm),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
gui.RenderCommitLength()
|
|
||||||
|
|
||||||
return gui.renderString(gui.Views.Options, message)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) RenderCommitLength() {
|
|
||||||
if !gui.c.UserConfig.Gui.CommitLength.Show {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
gui.Views.CommitMessage.Subtitle = getBufferLength(gui.Views.CommitMessage)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getBufferLength(view *gocui.View) string {
|
|
||||||
return " " + strconv.Itoa(strings.Count(view.TextArea.GetContent(), "")-1) + " "
|
|
||||||
}
|
|
@ -1,88 +0,0 @@
|
|||||||
package gui
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/fsmiamoto/git-todo-parser/todo"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
// after selecting the 200th commit, we'll load in all the rest
|
|
||||||
const COMMIT_THRESHOLD = 200
|
|
||||||
|
|
||||||
// list panel functions
|
|
||||||
|
|
||||||
func (gui *Gui) getSelectedLocalCommit() *models.Commit {
|
|
||||||
return gui.State.Contexts.LocalCommits.GetSelected()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) onCommitFocus() error {
|
|
||||||
context := gui.State.Contexts.LocalCommits
|
|
||||||
if context.GetSelectedLineIdx() > COMMIT_THRESHOLD && context.GetLimitCommits() {
|
|
||||||
context.SetLimitCommits(false)
|
|
||||||
go utils.Safe(func() {
|
|
||||||
if err := gui.refreshCommitsWithLimit(); err != nil {
|
|
||||||
_ = gui.c.Error(err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) branchCommitsRenderToMain() error {
|
|
||||||
var task types.UpdateTask
|
|
||||||
commit := gui.State.Contexts.LocalCommits.GetSelected()
|
|
||||||
if commit == nil {
|
|
||||||
task = types.NewRenderStringTask(gui.c.Tr.NoCommitsThisBranch)
|
|
||||||
} else if commit.Action == todo.UpdateRef {
|
|
||||||
task = types.NewRenderStringTask(
|
|
||||||
utils.ResolvePlaceholderString(
|
|
||||||
gui.c.Tr.UpdateRefHere,
|
|
||||||
map[string]string{
|
|
||||||
"ref": commit.Name,
|
|
||||||
}))
|
|
||||||
} else {
|
|
||||||
cmdObj := gui.git.Commit.ShowCmdObj(commit.Sha, gui.State.Modes.Filtering.GetPath(),
|
|
||||||
gui.IgnoreWhitespaceInDiffView)
|
|
||||||
task = types.NewRunPtyTask(cmdObj.GetCmd())
|
|
||||||
}
|
|
||||||
|
|
||||||
return gui.c.RenderToMainViews(types.RefreshMainOpts{
|
|
||||||
Pair: gui.c.MainViewPairs().Normal,
|
|
||||||
Main: &types.ViewUpdateOpts{
|
|
||||||
Title: "Patch",
|
|
||||||
Task: task,
|
|
||||||
},
|
|
||||||
Secondary: gui.secondaryPatchPanelUpdateOpts(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) secondaryPatchPanelUpdateOpts() *types.ViewUpdateOpts {
|
|
||||||
if gui.git.Patch.PatchBuilder.Active() {
|
|
||||||
patch := gui.git.Patch.PatchBuilder.RenderAggregatedPatch(false)
|
|
||||||
|
|
||||||
return &types.ViewUpdateOpts{
|
|
||||||
Task: types.NewRenderStringWithoutScrollTask(patch),
|
|
||||||
Title: gui.Tr.CustomPatch,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) refForLog() string {
|
|
||||||
bisectInfo := gui.git.Bisect.GetInfo()
|
|
||||||
gui.State.Model.BisectInfo = bisectInfo
|
|
||||||
|
|
||||||
if !bisectInfo.Started() {
|
|
||||||
return "HEAD"
|
|
||||||
}
|
|
||||||
|
|
||||||
// need to see if our bisect's current commit is reachable from our 'new' ref.
|
|
||||||
if bisectInfo.Bisecting() && !gui.git.Bisect.ReachableFromStart(bisectInfo) {
|
|
||||||
return bisectInfo.GetNewSha()
|
|
||||||
}
|
|
||||||
|
|
||||||
return bisectInfo.GetStartSha()
|
|
||||||
}
|
|
@ -1,311 +0,0 @@
|
|||||||
package gui
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/jesseduffield/gocui"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/keybindings"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
|
||||||
"github.com/mattn/go-runewidth"
|
|
||||||
)
|
|
||||||
|
|
||||||
// This file is for the rendering of confirmation panels along with setting and handling associated
|
|
||||||
// keybindings.
|
|
||||||
|
|
||||||
func (gui *Gui) wrappedConfirmationFunction(cancel context.CancelFunc, function func() error) func() error {
|
|
||||||
return func() error {
|
|
||||||
cancel()
|
|
||||||
|
|
||||||
if err := gui.c.PopContext(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if function != nil {
|
|
||||||
if err := function(); err != nil {
|
|
||||||
return gui.c.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) wrappedPromptConfirmationFunction(cancel context.CancelFunc, function func(string) error, getResponse func() string) func() error {
|
|
||||||
return func() error {
|
|
||||||
cancel()
|
|
||||||
|
|
||||||
if err := gui.c.PopContext(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if function != nil {
|
|
||||||
if err := function(getResponse()); err != nil {
|
|
||||||
return gui.c.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) deactivateConfirmationPrompt() {
|
|
||||||
gui.Mutexes.PopupMutex.Lock()
|
|
||||||
gui.State.CurrentPopupOpts = nil
|
|
||||||
gui.Mutexes.PopupMutex.Unlock()
|
|
||||||
|
|
||||||
gui.Views.Confirmation.Visible = false
|
|
||||||
gui.Views.Suggestions.Visible = false
|
|
||||||
|
|
||||||
gui.clearConfirmationViewKeyBindings()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) getMessageHeight(wrap bool, message string, width int) int {
|
|
||||||
lines := strings.Split(message, "\n")
|
|
||||||
lineCount := 0
|
|
||||||
// if we need to wrap, calculate height to fit content within view's width
|
|
||||||
if wrap {
|
|
||||||
for _, line := range lines {
|
|
||||||
lineCount += runewidth.StringWidth(line)/width + 1
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
lineCount = len(lines)
|
|
||||||
}
|
|
||||||
return lineCount
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) getPopupPanelDimensionsForContentHeight(panelWidth, contentHeight int) (int, int, int, int) {
|
|
||||||
return gui.getPopupPanelDimensionsAux(panelWidth, contentHeight)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) getPopupPanelDimensionsAux(panelWidth int, panelHeight int) (int, int, int, int) {
|
|
||||||
width, height := gui.g.Size()
|
|
||||||
if panelHeight > height*3/4 {
|
|
||||||
panelHeight = height * 3 / 4
|
|
||||||
}
|
|
||||||
return width/2 - panelWidth/2,
|
|
||||||
height/2 - panelHeight/2 - panelHeight%2 - 1,
|
|
||||||
width/2 + panelWidth/2,
|
|
||||||
height/2 + panelHeight/2
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) getConfirmationPanelWidth() int {
|
|
||||||
width, _ := gui.g.Size()
|
|
||||||
// we want a minimum width up to a point, then we do it based on ratio.
|
|
||||||
panelWidth := 4 * width / 7
|
|
||||||
minWidth := 80
|
|
||||||
if panelWidth < minWidth {
|
|
||||||
if width-2 < minWidth {
|
|
||||||
panelWidth = width - 2
|
|
||||||
} else {
|
|
||||||
panelWidth = minWidth
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return panelWidth
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) prepareConfirmationPanel(
|
|
||||||
ctx context.Context,
|
|
||||||
opts types.ConfirmOpts,
|
|
||||||
) error {
|
|
||||||
gui.Views.Confirmation.HasLoader = opts.HasLoader
|
|
||||||
if opts.HasLoader {
|
|
||||||
gui.g.StartTicking(ctx)
|
|
||||||
}
|
|
||||||
gui.Views.Confirmation.Title = opts.Title
|
|
||||||
// for now we do not support wrapping in our editor
|
|
||||||
gui.Views.Confirmation.Wrap = !opts.Editable
|
|
||||||
gui.Views.Confirmation.FgColor = theme.GocuiDefaultTextColor
|
|
||||||
gui.Views.Confirmation.Mask = runeForMask(opts.Mask)
|
|
||||||
_ = gui.Views.Confirmation.SetOrigin(0, 0)
|
|
||||||
|
|
||||||
gui.findSuggestions = opts.FindSuggestionsFunc
|
|
||||||
if opts.FindSuggestionsFunc != nil {
|
|
||||||
suggestionsView := gui.Views.Suggestions
|
|
||||||
suggestionsView.Wrap = false
|
|
||||||
suggestionsView.FgColor = theme.GocuiDefaultTextColor
|
|
||||||
gui.setSuggestions(opts.FindSuggestionsFunc(""))
|
|
||||||
suggestionsView.Visible = true
|
|
||||||
suggestionsView.Title = fmt.Sprintf(gui.c.Tr.SuggestionsTitle, gui.c.UserConfig.Keybinding.Universal.TogglePanel)
|
|
||||||
}
|
|
||||||
|
|
||||||
gui.resizeConfirmationPanel()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func runeForMask(mask bool) rune {
|
|
||||||
if mask {
|
|
||||||
return '*'
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) createPopupPanel(ctx context.Context, opts types.CreatePopupPanelOpts) error {
|
|
||||||
gui.Mutexes.PopupMutex.Lock()
|
|
||||||
defer gui.Mutexes.PopupMutex.Unlock()
|
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
|
||||||
|
|
||||||
// we don't allow interruptions of non-loader popups in case we get stuck somehow
|
|
||||||
// e.g. a credentials popup never gets its required user input so a process hangs
|
|
||||||
// forever.
|
|
||||||
// The proper solution is to have a queue of popup options
|
|
||||||
if gui.State.CurrentPopupOpts != nil && !gui.State.CurrentPopupOpts.HasLoader {
|
|
||||||
gui.Log.Error("ignoring create popup panel because a popup panel is already open")
|
|
||||||
cancel()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove any previous keybindings
|
|
||||||
gui.clearConfirmationViewKeyBindings()
|
|
||||||
|
|
||||||
err := gui.prepareConfirmationPanel(
|
|
||||||
ctx,
|
|
||||||
types.ConfirmOpts{
|
|
||||||
Title: opts.Title,
|
|
||||||
Prompt: opts.Prompt,
|
|
||||||
HasLoader: opts.HasLoader,
|
|
||||||
FindSuggestionsFunc: opts.FindSuggestionsFunc,
|
|
||||||
Editable: opts.Editable,
|
|
||||||
Mask: opts.Mask,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
cancel()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
confirmationView := gui.Views.Confirmation
|
|
||||||
confirmationView.Editable = opts.Editable
|
|
||||||
confirmationView.Editor = gocui.EditorFunc(gui.promptEditor)
|
|
||||||
|
|
||||||
if opts.Editable {
|
|
||||||
textArea := confirmationView.TextArea
|
|
||||||
textArea.Clear()
|
|
||||||
textArea.TypeString(opts.Prompt)
|
|
||||||
gui.resizeConfirmationPanel()
|
|
||||||
confirmationView.RenderTextArea()
|
|
||||||
} else {
|
|
||||||
if err := gui.renderString(confirmationView, style.AttrBold.Sprint(opts.Prompt)); err != nil {
|
|
||||||
cancel()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := gui.setKeyBindings(cancel, opts); err != nil {
|
|
||||||
cancel()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
gui.State.CurrentPopupOpts = &opts
|
|
||||||
|
|
||||||
return gui.c.PushContext(gui.State.Contexts.Confirmation)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) setKeyBindings(cancel context.CancelFunc, opts types.CreatePopupPanelOpts) error {
|
|
||||||
actions := utils.ResolvePlaceholderString(
|
|
||||||
gui.c.Tr.CloseConfirm,
|
|
||||||
map[string]string{
|
|
||||||
"keyBindClose": "esc",
|
|
||||||
"keyBindConfirm": "enter",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
_ = gui.renderString(gui.Views.Options, actions)
|
|
||||||
var onConfirm func() error
|
|
||||||
if opts.HandleConfirmPrompt != nil {
|
|
||||||
onConfirm = gui.wrappedPromptConfirmationFunction(cancel, opts.HandleConfirmPrompt, func() string { return gui.Views.Confirmation.TextArea.GetContent() })
|
|
||||||
} else {
|
|
||||||
onConfirm = gui.wrappedConfirmationFunction(cancel, opts.HandleConfirm)
|
|
||||||
}
|
|
||||||
|
|
||||||
keybindingConfig := gui.c.UserConfig.Keybinding
|
|
||||||
onSuggestionConfirm := gui.wrappedPromptConfirmationFunction(
|
|
||||||
cancel,
|
|
||||||
opts.HandleConfirmPrompt,
|
|
||||||
gui.getSelectedSuggestionValue,
|
|
||||||
)
|
|
||||||
|
|
||||||
bindings := []*types.Binding{
|
|
||||||
{
|
|
||||||
ViewName: "confirmation",
|
|
||||||
Key: keybindings.GetKey(keybindingConfig.Universal.Confirm),
|
|
||||||
Handler: onConfirm,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ViewName: "confirmation",
|
|
||||||
Key: keybindings.GetKey(keybindingConfig.Universal.Return),
|
|
||||||
Handler: gui.wrappedConfirmationFunction(cancel, opts.HandleClose),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ViewName: "confirmation",
|
|
||||||
Key: keybindings.GetKey(keybindingConfig.Universal.TogglePanel),
|
|
||||||
Handler: func() error {
|
|
||||||
if len(gui.State.Suggestions) > 0 {
|
|
||||||
return gui.replaceContext(gui.State.Contexts.Suggestions)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ViewName: "suggestions",
|
|
||||||
Key: keybindings.GetKey(keybindingConfig.Universal.Confirm),
|
|
||||||
Handler: onSuggestionConfirm,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ViewName: "suggestions",
|
|
||||||
Key: keybindings.GetKey(keybindingConfig.Universal.Return),
|
|
||||||
Handler: gui.wrappedConfirmationFunction(cancel, opts.HandleClose),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ViewName: "suggestions",
|
|
||||||
Key: keybindings.GetKey(keybindingConfig.Universal.TogglePanel),
|
|
||||||
Handler: func() error { return gui.replaceContext(gui.State.Contexts.Confirmation) },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, binding := range bindings {
|
|
||||||
if err := gui.SetKeybinding(binding); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) clearConfirmationViewKeyBindings() {
|
|
||||||
keybindingConfig := gui.c.UserConfig.Keybinding
|
|
||||||
_ = gui.g.DeleteKeybinding("confirmation", keybindings.GetKey(keybindingConfig.Universal.Confirm), gocui.ModNone)
|
|
||||||
_ = gui.g.DeleteKeybinding("confirmation", keybindings.GetKey(keybindingConfig.Universal.Return), gocui.ModNone)
|
|
||||||
_ = gui.g.DeleteKeybinding("suggestions", keybindings.GetKey(keybindingConfig.Universal.Confirm), gocui.ModNone)
|
|
||||||
_ = gui.g.DeleteKeybinding("suggestions", keybindings.GetKey(keybindingConfig.Universal.Return), gocui.ModNone)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) refreshSuggestions() {
|
|
||||||
gui.suggestionsAsyncHandler.Do(func() func() {
|
|
||||||
findSuggestionsFn := gui.findSuggestions
|
|
||||||
if findSuggestionsFn != nil {
|
|
||||||
suggestions := gui.findSuggestions(gui.c.GetPromptInput())
|
|
||||||
return func() { gui.setSuggestions(suggestions) }
|
|
||||||
} else {
|
|
||||||
return func() {}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) handleAskFocused() error {
|
|
||||||
keybindingConfig := gui.c.UserConfig.Keybinding
|
|
||||||
|
|
||||||
message := utils.ResolvePlaceholderString(
|
|
||||||
gui.c.Tr.CloseConfirm,
|
|
||||||
map[string]string{
|
|
||||||
"keyBindClose": keybindings.Label(keybindingConfig.Universal.Return),
|
|
||||||
"keyBindConfirm": keybindings.Label(keybindingConfig.Universal.Confirm),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
return gui.renderString(gui.Views.Options, message)
|
|
||||||
}
|
|
3
pkg/gui/constants/constants.go
Normal file
3
pkg/gui/constants/constants.go
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
package constants
|
||||||
|
|
||||||
|
const SEARCH_PREFIX = "search: "
|
@ -1,12 +1,10 @@
|
|||||||
package gui
|
package gui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sort"
|
"errors"
|
||||||
"strings"
|
"sync"
|
||||||
|
|
||||||
"github.com/jesseduffield/generics/maps"
|
|
||||||
"github.com/jesseduffield/generics/slices"
|
"github.com/jesseduffield/generics/slices"
|
||||||
"github.com/jesseduffield/gocui"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
@ -17,46 +15,66 @@ import (
|
|||||||
// you in the menu context. When contexts are activated/deactivated certain things need
|
// you in the menu context. When contexts are activated/deactivated certain things need
|
||||||
// to happen like showing/hiding views and rendering content.
|
// to happen like showing/hiding views and rendering content.
|
||||||
|
|
||||||
func (gui *Gui) popupViewNames() []string {
|
type ContextMgr struct {
|
||||||
popups := slices.Filter(gui.State.Contexts.Flatten(), func(c types.Context) bool {
|
ContextStack []types.Context
|
||||||
return c.GetKind() == types.PERSISTENT_POPUP || c.GetKind() == types.TEMPORARY_POPUP
|
sync.RWMutex
|
||||||
})
|
gui *Gui
|
||||||
|
|
||||||
return slices.Map(popups, func(c types.Context) string {
|
allContexts *context.ContextTree
|
||||||
return c.GetViewName()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// use replaceContext when you don't want to return to the original context upon
|
func NewContextMgr(
|
||||||
|
gui *Gui,
|
||||||
|
allContexts *context.ContextTree,
|
||||||
|
) *ContextMgr {
|
||||||
|
return &ContextMgr{
|
||||||
|
ContextStack: []types.Context{},
|
||||||
|
RWMutex: sync.RWMutex{},
|
||||||
|
gui: gui,
|
||||||
|
allContexts: allContexts,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// use when you don't want to return to the original context upon
|
||||||
// hitting escape: you want to go that context's parent instead.
|
// hitting escape: you want to go that context's parent instead.
|
||||||
func (gui *Gui) replaceContext(c types.Context) error {
|
func (self *ContextMgr) Replace(c types.Context) error {
|
||||||
if !c.IsFocusable() {
|
if !c.IsFocusable() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.State.ContextManager.Lock()
|
self.Lock()
|
||||||
|
|
||||||
if len(gui.State.ContextManager.ContextStack) == 0 {
|
if len(self.ContextStack) == 0 {
|
||||||
gui.State.ContextManager.ContextStack = []types.Context{c}
|
self.ContextStack = []types.Context{c}
|
||||||
} else {
|
} else {
|
||||||
// replace the last item with the given item
|
// replace the last item with the given item
|
||||||
gui.State.ContextManager.ContextStack = append(gui.State.ContextManager.ContextStack[0:len(gui.State.ContextManager.ContextStack)-1], c)
|
self.ContextStack = append(self.ContextStack[0:len(self.ContextStack)-1], c)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer gui.State.ContextManager.Unlock()
|
defer self.Unlock()
|
||||||
|
|
||||||
return gui.activateContext(c, types.OnFocusOpts{})
|
return self.activateContext(c, types.OnFocusOpts{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) pushContext(c types.Context, opts types.OnFocusOpts) error {
|
func (self *ContextMgr) Push(c types.Context, opts ...types.OnFocusOpts) error {
|
||||||
|
if len(opts) > 1 {
|
||||||
|
return errors.New("cannot pass multiple opts to Push")
|
||||||
|
}
|
||||||
|
|
||||||
|
singleOpts := types.OnFocusOpts{}
|
||||||
|
if len(opts) > 0 {
|
||||||
|
// using triple dot but you should only ever pass one of these opt structs
|
||||||
|
singleOpts = opts[0]
|
||||||
|
}
|
||||||
|
|
||||||
if !c.IsFocusable() {
|
if !c.IsFocusable() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
contextsToDeactivate, contextToActivate := gui.pushToContextStack(c)
|
contextsToDeactivate, contextToActivate := self.pushToContextStack(c)
|
||||||
|
|
||||||
for _, contextToDeactivate := range contextsToDeactivate {
|
for _, contextToDeactivate := range contextsToDeactivate {
|
||||||
if err := gui.deactivateContext(contextToDeactivate, types.OnFocusLostOpts{NewContextKey: c.GetKey()}); err != nil {
|
if err := self.deactivateContext(contextToDeactivate, types.OnFocusLostOpts{NewContextKey: c.GetKey()}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -65,43 +83,43 @@ func (gui *Gui) pushContext(c types.Context, opts types.OnFocusOpts) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.activateContext(contextToActivate, opts)
|
return self.activateContext(contextToActivate, singleOpts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adjusts the context stack based on the context that's being pushed and
|
// Adjusts the context stack based on the context that's being pushed and
|
||||||
// returns (contexts to deactivate, context to activate)
|
// returns (contexts to deactivate, context to activate)
|
||||||
func (gui *Gui) pushToContextStack(c types.Context) ([]types.Context, types.Context) {
|
func (self *ContextMgr) pushToContextStack(c types.Context) ([]types.Context, types.Context) {
|
||||||
contextsToDeactivate := []types.Context{}
|
contextsToDeactivate := []types.Context{}
|
||||||
|
|
||||||
gui.State.ContextManager.Lock()
|
self.Lock()
|
||||||
defer gui.State.ContextManager.Unlock()
|
defer self.Unlock()
|
||||||
|
|
||||||
if len(gui.State.ContextManager.ContextStack) > 0 &&
|
if len(self.ContextStack) > 0 &&
|
||||||
c == gui.State.ContextManager.ContextStack[len(gui.State.ContextManager.ContextStack)-1] {
|
c == self.ContextStack[len(self.ContextStack)-1] {
|
||||||
// Context being pushed is already on top of the stack: nothing to
|
// Context being pushed is already on top of the stack: nothing to
|
||||||
// deactivate or activate
|
// deactivate or activate
|
||||||
return contextsToDeactivate, nil
|
return contextsToDeactivate, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(gui.State.ContextManager.ContextStack) == 0 {
|
if len(self.ContextStack) == 0 {
|
||||||
gui.State.ContextManager.ContextStack = append(gui.State.ContextManager.ContextStack, c)
|
self.ContextStack = append(self.ContextStack, c)
|
||||||
} else if c.GetKind() == types.SIDE_CONTEXT {
|
} else if c.GetKind() == types.SIDE_CONTEXT {
|
||||||
// if we are switching to a side context, remove all other contexts in the stack
|
// if we are switching to a side context, remove all other contexts in the stack
|
||||||
contextsToDeactivate = gui.State.ContextManager.ContextStack
|
contextsToDeactivate = self.ContextStack
|
||||||
gui.State.ContextManager.ContextStack = []types.Context{c}
|
self.ContextStack = []types.Context{c}
|
||||||
} else if c.GetKind() == types.MAIN_CONTEXT {
|
} else if c.GetKind() == types.MAIN_CONTEXT {
|
||||||
// if we're switching to a main context, remove all other main contexts in the stack
|
// if we're switching to a main context, remove all other main contexts in the stack
|
||||||
contextsToKeep := []types.Context{}
|
contextsToKeep := []types.Context{}
|
||||||
for _, stackContext := range gui.State.ContextManager.ContextStack {
|
for _, stackContext := range self.ContextStack {
|
||||||
if stackContext.GetKind() == types.MAIN_CONTEXT {
|
if stackContext.GetKind() == types.MAIN_CONTEXT {
|
||||||
contextsToDeactivate = append(contextsToDeactivate, stackContext)
|
contextsToDeactivate = append(contextsToDeactivate, stackContext)
|
||||||
} else {
|
} else {
|
||||||
contextsToKeep = append(contextsToKeep, stackContext)
|
contextsToKeep = append(contextsToKeep, stackContext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
gui.State.ContextManager.ContextStack = append(contextsToKeep, c)
|
self.ContextStack = append(contextsToKeep, c)
|
||||||
} else {
|
} else {
|
||||||
topContext := gui.currentContextWithoutLock()
|
topContext := self.currentContextWithoutLock()
|
||||||
|
|
||||||
// if we're pushing the same context on, we do nothing.
|
// if we're pushing the same context on, we do nothing.
|
||||||
if topContext.GetKey() != c.GetKey() {
|
if topContext.GetKey() != c.GetKey() {
|
||||||
@ -114,48 +132,48 @@ func (gui *Gui) pushToContextStack(c types.Context) ([]types.Context, types.Cont
|
|||||||
(topContext.GetKind() == types.MAIN_CONTEXT && c.GetKind() == types.MAIN_CONTEXT) {
|
(topContext.GetKind() == types.MAIN_CONTEXT && c.GetKind() == types.MAIN_CONTEXT) {
|
||||||
|
|
||||||
contextsToDeactivate = append(contextsToDeactivate, topContext)
|
contextsToDeactivate = append(contextsToDeactivate, topContext)
|
||||||
_, gui.State.ContextManager.ContextStack = slices.Pop(gui.State.ContextManager.ContextStack)
|
_, self.ContextStack = slices.Pop(self.ContextStack)
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.State.ContextManager.ContextStack = append(gui.State.ContextManager.ContextStack, c)
|
self.ContextStack = append(self.ContextStack, c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return contextsToDeactivate, c
|
return contextsToDeactivate, c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) popContext() error {
|
func (self *ContextMgr) Pop() error {
|
||||||
gui.State.ContextManager.Lock()
|
self.Lock()
|
||||||
|
|
||||||
if len(gui.State.ContextManager.ContextStack) == 1 {
|
if len(self.ContextStack) == 1 {
|
||||||
// cannot escape from bottommost context
|
// cannot escape from bottommost context
|
||||||
gui.State.ContextManager.Unlock()
|
self.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentContext types.Context
|
var currentContext types.Context
|
||||||
currentContext, gui.State.ContextManager.ContextStack = slices.Pop(gui.State.ContextManager.ContextStack)
|
currentContext, self.ContextStack = slices.Pop(self.ContextStack)
|
||||||
|
|
||||||
newContext := gui.State.ContextManager.ContextStack[len(gui.State.ContextManager.ContextStack)-1]
|
newContext := self.ContextStack[len(self.ContextStack)-1]
|
||||||
|
|
||||||
gui.State.ContextManager.Unlock()
|
self.Unlock()
|
||||||
|
|
||||||
if err := gui.deactivateContext(currentContext, types.OnFocusLostOpts{NewContextKey: newContext.GetKey()}); err != nil {
|
if err := self.deactivateContext(currentContext, types.OnFocusLostOpts{NewContextKey: newContext.GetKey()}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.activateContext(newContext, types.OnFocusOpts{})
|
return self.activateContext(newContext, types.OnFocusOpts{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) removeContexts(contextsToRemove []types.Context) error {
|
func (self *ContextMgr) RemoveContexts(contextsToRemove []types.Context) error {
|
||||||
gui.State.ContextManager.Lock()
|
self.Lock()
|
||||||
|
|
||||||
if len(gui.State.ContextManager.ContextStack) == 1 {
|
if len(self.ContextStack) == 1 {
|
||||||
gui.State.ContextManager.Unlock()
|
self.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
rest := lo.Filter(gui.State.ContextManager.ContextStack, func(context types.Context, _ int) bool {
|
rest := lo.Filter(self.ContextStack, func(context types.Context, _ int) bool {
|
||||||
for _, contextToRemove := range contextsToRemove {
|
for _, contextToRemove := range contextsToRemove {
|
||||||
if context.GetKey() == contextToRemove.GetKey() {
|
if context.GetKey() == contextToRemove.GetKey() {
|
||||||
return false
|
return false
|
||||||
@ -163,25 +181,25 @@ func (gui *Gui) removeContexts(contextsToRemove []types.Context) error {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
gui.State.ContextManager.ContextStack = rest
|
self.ContextStack = rest
|
||||||
contextToActivate := rest[len(rest)-1]
|
contextToActivate := rest[len(rest)-1]
|
||||||
gui.State.ContextManager.Unlock()
|
self.Unlock()
|
||||||
|
|
||||||
for _, context := range contextsToRemove {
|
for _, context := range contextsToRemove {
|
||||||
if err := gui.deactivateContext(context, types.OnFocusLostOpts{NewContextKey: contextToActivate.GetKey()}); err != nil {
|
if err := self.deactivateContext(context, types.OnFocusLostOpts{NewContextKey: contextToActivate.GetKey()}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// activate the item at the top of the stack
|
// activate the item at the top of the stack
|
||||||
return gui.activateContext(contextToActivate, types.OnFocusOpts{})
|
return self.activateContext(contextToActivate, types.OnFocusOpts{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) deactivateContext(c types.Context, opts types.OnFocusLostOpts) error {
|
func (self *ContextMgr) deactivateContext(c types.Context, opts types.OnFocusLostOpts) error {
|
||||||
view, _ := gui.g.View(c.GetViewName())
|
view, _ := self.gui.c.GocuiGui().View(c.GetViewName())
|
||||||
|
|
||||||
if view != nil && view.IsSearching() {
|
if view != nil && view.IsSearching() {
|
||||||
if err := gui.onSearchEscape(); err != nil {
|
if err := self.gui.onSearchEscape(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -200,34 +218,17 @@ func (gui *Gui) deactivateContext(c types.Context, opts types.OnFocusLostOpts) e
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// postRefreshUpdate is to be called on a context after the state that it depends on has been refreshed
|
func (self *ContextMgr) activateContext(c types.Context, opts types.OnFocusOpts) error {
|
||||||
// if the context's view is set to another context we do nothing.
|
|
||||||
// if the context's view is the current view we trigger a focus; re-selecting the current item.
|
|
||||||
func (gui *Gui) postRefreshUpdate(c types.Context) error {
|
|
||||||
if err := c.HandleRender(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if gui.currentViewName() == c.GetViewName() {
|
|
||||||
if err := c.HandleFocus(types.OnFocusOpts{}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) activateContext(c types.Context, opts types.OnFocusOpts) error {
|
|
||||||
viewName := c.GetViewName()
|
viewName := c.GetViewName()
|
||||||
v, err := gui.g.View(viewName)
|
v, err := self.gui.c.GocuiGui().View(viewName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.setWindowContext(c)
|
self.gui.helpers.Window.SetWindowContext(c)
|
||||||
|
|
||||||
gui.moveToTopOfWindow(c)
|
self.gui.helpers.Window.MoveToTopOfWindow(c)
|
||||||
if _, err := gui.g.SetCurrentView(viewName); err != nil {
|
if _, err := self.gui.c.GocuiGui().SetCurrentView(viewName); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,14 +239,9 @@ func (gui *Gui) activateContext(c types.Context, opts types.OnFocusOpts) error {
|
|||||||
|
|
||||||
v.Visible = true
|
v.Visible = true
|
||||||
|
|
||||||
gui.g.Cursor = v.Editable
|
self.gui.c.GocuiGui().Cursor = v.Editable
|
||||||
|
|
||||||
// render the options available for the current context at the bottom of the screen
|
self.gui.renderContextOptionsMap(c)
|
||||||
optionsMap := c.GetOptionsMap()
|
|
||||||
if optionsMap == nil {
|
|
||||||
optionsMap = gui.globalOptionsMap()
|
|
||||||
}
|
|
||||||
gui.renderOptionsMap(optionsMap)
|
|
||||||
|
|
||||||
if err := c.HandleFocus(opts); err != nil {
|
if err := c.HandleFocus(opts); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -254,63 +250,27 @@ func (gui *Gui) activateContext(c types.Context, opts types.OnFocusOpts) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) optionsMapToString(optionsMap map[string]string) string {
|
func (self *ContextMgr) Current() types.Context {
|
||||||
options := maps.MapToSlice(optionsMap, func(key string, description string) string {
|
self.RLock()
|
||||||
return key + ": " + description
|
defer self.RUnlock()
|
||||||
})
|
|
||||||
sort.Strings(options)
|
return self.currentContextWithoutLock()
|
||||||
return strings.Join(options, ", ")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) renderOptionsMap(optionsMap map[string]string) {
|
func (self *ContextMgr) currentContextWithoutLock() types.Context {
|
||||||
_ = gui.renderString(gui.Views.Options, gui.optionsMapToString(optionsMap))
|
if len(self.ContextStack) == 0 {
|
||||||
}
|
return self.gui.defaultSideContext()
|
||||||
|
|
||||||
// // currently unused
|
|
||||||
// func (gui *Gui) renderContextStack() string {
|
|
||||||
// result := ""
|
|
||||||
// for _, context := range gui.State.ContextManager.ContextStack {
|
|
||||||
// result += string(context.GetKey()) + "\n"
|
|
||||||
// }
|
|
||||||
// return result
|
|
||||||
// }
|
|
||||||
|
|
||||||
func (gui *Gui) currentContext() types.Context {
|
|
||||||
gui.State.ContextManager.RLock()
|
|
||||||
defer gui.State.ContextManager.RUnlock()
|
|
||||||
|
|
||||||
return gui.currentContextWithoutLock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) currentContextWithoutLock() types.Context {
|
|
||||||
if len(gui.State.ContextManager.ContextStack) == 0 {
|
|
||||||
return gui.defaultSideContext()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.State.ContextManager.ContextStack[len(gui.State.ContextManager.ContextStack)-1]
|
return self.ContextStack[len(self.ContextStack)-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
// the status panel is not yet a list context (and may never be), so this method is not
|
// Note that this could return the 'status' context which is not itself a list context.
|
||||||
// quite the same as currentSideContext()
|
func (self *ContextMgr) CurrentSide() types.Context {
|
||||||
func (gui *Gui) currentSideListContext() types.IListContext {
|
self.RLock()
|
||||||
context := gui.currentSideContext()
|
defer self.RUnlock()
|
||||||
listContext, ok := context.(types.IListContext)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return listContext
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) currentSideContext() types.Context {
|
stack := self.ContextStack
|
||||||
gui.State.ContextManager.RLock()
|
|
||||||
defer gui.State.ContextManager.RUnlock()
|
|
||||||
|
|
||||||
stack := gui.State.ContextManager.ContextStack
|
|
||||||
|
|
||||||
// on startup the stack can be empty so we'll return an empty string in that case
|
|
||||||
if len(stack) == 0 {
|
|
||||||
return gui.defaultSideContext()
|
|
||||||
}
|
|
||||||
|
|
||||||
// find the first context in the stack with the type of types.SIDE_CONTEXT
|
// find the first context in the stack with the type of types.SIDE_CONTEXT
|
||||||
for i := range stack {
|
for i := range stack {
|
||||||
@ -321,22 +281,22 @@ func (gui *Gui) currentSideContext() types.Context {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.defaultSideContext()
|
return self.gui.defaultSideContext()
|
||||||
}
|
}
|
||||||
|
|
||||||
// static as opposed to popup
|
// static as opposed to popup
|
||||||
func (gui *Gui) currentStaticContext() types.Context {
|
func (self *ContextMgr) CurrentStatic() types.Context {
|
||||||
gui.State.ContextManager.RLock()
|
self.RLock()
|
||||||
defer gui.State.ContextManager.RUnlock()
|
defer self.RUnlock()
|
||||||
|
|
||||||
return gui.currentStaticContextWithoutLock()
|
return self.currentStaticContextWithoutLock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) currentStaticContextWithoutLock() types.Context {
|
func (self *ContextMgr) currentStaticContextWithoutLock() types.Context {
|
||||||
stack := gui.State.ContextManager.ContextStack
|
stack := self.ContextStack
|
||||||
|
|
||||||
if len(stack) == 0 {
|
if len(stack) == 0 {
|
||||||
return gui.defaultSideContext()
|
return self.gui.defaultSideContext()
|
||||||
}
|
}
|
||||||
|
|
||||||
// find the first context in the stack without a popup type
|
// find the first context in the stack without a popup type
|
||||||
@ -348,88 +308,43 @@ func (gui *Gui) currentStaticContextWithoutLock() types.Context {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.defaultSideContext()
|
return self.gui.defaultSideContext()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) defaultSideContext() types.Context {
|
func (self *ContextMgr) ForEach(f func(types.Context)) {
|
||||||
if gui.State.Modes.Filtering.Active() {
|
self.RLock()
|
||||||
return gui.State.Contexts.LocalCommits
|
defer self.RUnlock()
|
||||||
} else {
|
|
||||||
return gui.State.Contexts.Files
|
for _, context := range self.gui.State.ContextMgr.ContextStack {
|
||||||
|
f(context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getFocusLayout returns a manager function for when view gain and lose focus
|
func (self *ContextMgr) IsCurrent(c types.Context) bool {
|
||||||
func (gui *Gui) getFocusLayout() func(g *gocui.Gui) error {
|
return self.Current().GetKey() == c.GetKey()
|
||||||
var previousView *gocui.View
|
}
|
||||||
return func(g *gocui.Gui) error {
|
|
||||||
newView := gui.g.CurrentView()
|
|
||||||
// for now we don't consider losing focus to a popup panel as actually losing focus
|
|
||||||
if newView != previousView && !gui.isPopupPanel(newView.Name()) {
|
|
||||||
if err := gui.onViewFocusLost(previousView); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
previousView = newView
|
// all list contexts
|
||||||
|
func (self *ContextMgr) AllList() []types.IListContext {
|
||||||
|
var listContexts []types.IListContext
|
||||||
|
|
||||||
|
for _, context := range self.allContexts.Flatten() {
|
||||||
|
if listContext, ok := context.(types.IListContext); ok {
|
||||||
|
listContexts = append(listContexts, listContext)
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) onViewFocusLost(oldView *gocui.View) error {
|
|
||||||
if oldView == nil {
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
oldView.Highlight = false
|
return listContexts
|
||||||
|
|
||||||
_ = oldView.SetOriginX(0)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) TransientContexts() []types.Context {
|
func (self *ContextMgr) AllPatchExplorer() []types.IPatchExplorerContext {
|
||||||
return slices.Filter(gui.State.Contexts.Flatten(), func(context types.Context) bool {
|
var listContexts []types.IPatchExplorerContext
|
||||||
return context.IsTransient()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) rerenderView(view *gocui.View) error {
|
for _, context := range self.allContexts.Flatten() {
|
||||||
context, ok := gui.contextForView(view.Name())
|
if listContext, ok := context.(types.IPatchExplorerContext); ok {
|
||||||
if !ok {
|
listContexts = append(listContexts, listContext)
|
||||||
gui.Log.Errorf("no context found for view %s", view.Name())
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return context.HandleRender()
|
return listContexts
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) getSideContextSelectedItemId() string {
|
|
||||||
currentSideContext := gui.currentSideListContext()
|
|
||||||
if currentSideContext == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return currentSideContext.GetSelectedItemId()
|
|
||||||
}
|
|
||||||
|
|
||||||
// currently unused
|
|
||||||
// func (gui *Gui) getCurrentSideView() *gocui.View {
|
|
||||||
// currentSideContext := gui.currentSideContext()
|
|
||||||
// if currentSideContext == nil {
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
// view, _ := gui.g.View(currentSideContext.GetViewName())
|
|
||||||
|
|
||||||
// return view
|
|
||||||
// }
|
|
||||||
|
|
||||||
// currently unused
|
|
||||||
// func (gui *Gui) renderContextStack() string {
|
|
||||||
// result := ""
|
|
||||||
// for _, context := range gui.State.ContextManager.ContextStack {
|
|
||||||
// result += context.GetViewName() + "\n"
|
|
||||||
// }
|
|
||||||
// return result
|
|
||||||
// }
|
|
||||||
|
@ -16,6 +16,9 @@ type BaseContext struct {
|
|||||||
keybindingsFns []types.KeybindingsFn
|
keybindingsFns []types.KeybindingsFn
|
||||||
mouseKeybindingsFns []types.MouseKeybindingsFn
|
mouseKeybindingsFns []types.MouseKeybindingsFn
|
||||||
onClickFn func() error
|
onClickFn func() error
|
||||||
|
onRenderToMainFn func() error
|
||||||
|
onFocusFn onFocusFn
|
||||||
|
onFocusLostFn onFocusLostFn
|
||||||
|
|
||||||
focusable bool
|
focusable bool
|
||||||
transient bool
|
transient bool
|
||||||
@ -25,6 +28,11 @@ type BaseContext struct {
|
|||||||
*ParentContextMgr
|
*ParentContextMgr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
onFocusFn = func(types.OnFocusOpts) error
|
||||||
|
onFocusLostFn = func(types.OnFocusLostOpts) error
|
||||||
|
)
|
||||||
|
|
||||||
var _ types.IBaseContext = &BaseContext{}
|
var _ types.IBaseContext = &BaseContext{}
|
||||||
|
|
||||||
type NewBaseContextOpts struct {
|
type NewBaseContextOpts struct {
|
||||||
@ -129,6 +137,36 @@ func (self *BaseContext) GetOnClick() func() error {
|
|||||||
return self.onClickFn
|
return self.onClickFn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *BaseContext) AddOnRenderToMainFn(fn func() error) {
|
||||||
|
if fn != nil {
|
||||||
|
self.onRenderToMainFn = fn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BaseContext) GetOnRenderToMain() func() error {
|
||||||
|
return self.onRenderToMainFn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BaseContext) AddOnFocusFn(fn onFocusFn) {
|
||||||
|
if fn != nil {
|
||||||
|
self.onFocusFn = fn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BaseContext) GetOnFocus() onFocusFn {
|
||||||
|
return self.onFocusFn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BaseContext) AddOnFocusLostFn(fn onFocusLostFn) {
|
||||||
|
if fn != nil {
|
||||||
|
self.onFocusLostFn = fn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BaseContext) GetOnFocusLost() onFocusLostFn {
|
||||||
|
return self.onFocusLostFn
|
||||||
|
}
|
||||||
|
|
||||||
func (self *BaseContext) GetMouseKeybindings(opts types.KeybindingsOpts) []*gocui.ViewMouseBinding {
|
func (self *BaseContext) GetMouseKeybindings(opts types.KeybindingsOpts) []*gocui.ViewMouseBinding {
|
||||||
bindings := []*gocui.ViewMouseBinding{}
|
bindings := []*gocui.ViewMouseBinding{}
|
||||||
for i := range self.mouseKeybindingsFns {
|
for i := range self.mouseKeybindingsFns {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package context
|
package context
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jesseduffield/gocui"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -11,40 +11,40 @@ type BranchesContext struct {
|
|||||||
*ListContextTrait
|
*ListContextTrait
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ types.IListContext = (*BranchesContext)(nil)
|
var (
|
||||||
|
_ types.IListContext = (*BranchesContext)(nil)
|
||||||
|
_ types.DiffableContext = (*BranchesContext)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
func NewBranchesContext(
|
func NewBranchesContext(c *ContextCommon) *BranchesContext {
|
||||||
getModel func() []*models.Branch,
|
viewModel := NewBasicViewModel(func() []*models.Branch { return c.Model().Branches })
|
||||||
view *gocui.View,
|
|
||||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
|
||||||
|
|
||||||
onFocus func(types.OnFocusOpts) error,
|
getDisplayStrings := func(startIdx int, length int) [][]string {
|
||||||
onRenderToMain func() error,
|
return presentation.GetBranchListDisplayStrings(
|
||||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
c.Model().Branches,
|
||||||
|
c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL,
|
||||||
|
c.Modes().Diffing.Ref,
|
||||||
|
c.Tr,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
c *types.HelperCommon,
|
self := &BranchesContext{
|
||||||
) *BranchesContext {
|
|
||||||
viewModel := NewBasicViewModel(getModel)
|
|
||||||
|
|
||||||
return &BranchesContext{
|
|
||||||
BasicViewModel: viewModel,
|
BasicViewModel: viewModel,
|
||||||
ListContextTrait: &ListContextTrait{
|
ListContextTrait: &ListContextTrait{
|
||||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||||
View: view,
|
View: c.Views().Branches,
|
||||||
WindowName: "branches",
|
WindowName: "branches",
|
||||||
Key: LOCAL_BRANCHES_CONTEXT_KEY,
|
Key: LOCAL_BRANCHES_CONTEXT_KEY,
|
||||||
Kind: types.SIDE_CONTEXT,
|
Kind: types.SIDE_CONTEXT,
|
||||||
Focusable: true,
|
Focusable: true,
|
||||||
}), ContextCallbackOpts{
|
})),
|
||||||
OnFocus: onFocus,
|
|
||||||
OnFocusLost: onFocusLost,
|
|
||||||
OnRenderToMain: onRenderToMain,
|
|
||||||
}),
|
|
||||||
list: viewModel,
|
list: viewModel,
|
||||||
getDisplayStrings: getDisplayStrings,
|
getDisplayStrings: getDisplayStrings,
|
||||||
c: c,
|
c: c,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *BranchesContext) GetSelectedItemId() string {
|
func (self *BranchesContext) GetSelectedItemId() string {
|
||||||
@ -63,3 +63,16 @@ func (self *BranchesContext) GetSelectedRef() types.Ref {
|
|||||||
}
|
}
|
||||||
return branch
|
return branch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *BranchesContext) GetDiffTerminals() []string {
|
||||||
|
// for our local branches we want to include both the branch and its upstream
|
||||||
|
branch := self.GetSelected()
|
||||||
|
if branch != nil {
|
||||||
|
names := []string{branch.ID()}
|
||||||
|
if branch.IsTrackingRemote() {
|
||||||
|
names = append(names, branch.ID()+"@{u}")
|
||||||
|
}
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
package context
|
package context
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/generics/slices"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -13,20 +15,28 @@ type CommitFilesContext struct {
|
|||||||
*DynamicTitleBuilder
|
*DynamicTitleBuilder
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ types.IListContext = (*CommitFilesContext)(nil)
|
var (
|
||||||
|
_ types.IListContext = (*CommitFilesContext)(nil)
|
||||||
|
_ types.DiffableContext = (*CommitFilesContext)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
func NewCommitFilesContext(
|
func NewCommitFilesContext(c *ContextCommon) *CommitFilesContext {
|
||||||
getModel func() []*models.CommitFile,
|
viewModel := filetree.NewCommitFileTreeViewModel(
|
||||||
view *gocui.View,
|
func() []*models.CommitFile { return c.Model().CommitFiles },
|
||||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
c.Log,
|
||||||
|
c.UserConfig.Gui.ShowFileTree,
|
||||||
|
)
|
||||||
|
|
||||||
onFocus func(types.OnFocusOpts) error,
|
getDisplayStrings := func(startIdx int, length int) [][]string {
|
||||||
onRenderToMain func() error,
|
if viewModel.Len() == 0 {
|
||||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
return [][]string{{style.FgRed.Sprint("(none)")}}
|
||||||
|
}
|
||||||
|
|
||||||
c *types.HelperCommon,
|
lines := presentation.RenderCommitFileTree(viewModel, c.Modes().Diffing.Ref, c.Git().Patch.PatchBuilder)
|
||||||
) *CommitFilesContext {
|
return slices.Map(lines, func(line string) []string {
|
||||||
viewModel := filetree.NewCommitFileTreeViewModel(getModel, c.Log, c.UserConfig.Gui.ShowFileTree)
|
return []string{line}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return &CommitFilesContext{
|
return &CommitFilesContext{
|
||||||
CommitFileTreeViewModel: viewModel,
|
CommitFileTreeViewModel: viewModel,
|
||||||
@ -34,18 +44,14 @@ func NewCommitFilesContext(
|
|||||||
ListContextTrait: &ListContextTrait{
|
ListContextTrait: &ListContextTrait{
|
||||||
Context: NewSimpleContext(
|
Context: NewSimpleContext(
|
||||||
NewBaseContext(NewBaseContextOpts{
|
NewBaseContext(NewBaseContextOpts{
|
||||||
View: view,
|
View: c.Views().CommitFiles,
|
||||||
WindowName: "commits",
|
WindowName: "commits",
|
||||||
Key: COMMIT_FILES_CONTEXT_KEY,
|
Key: COMMIT_FILES_CONTEXT_KEY,
|
||||||
Kind: types.SIDE_CONTEXT,
|
Kind: types.SIDE_CONTEXT,
|
||||||
Focusable: true,
|
Focusable: true,
|
||||||
Transient: true,
|
Transient: true,
|
||||||
}),
|
}),
|
||||||
ContextCallbackOpts{
|
),
|
||||||
OnFocus: onFocus,
|
|
||||||
OnFocusLost: onFocusLost,
|
|
||||||
OnRenderToMain: onRenderToMain,
|
|
||||||
}),
|
|
||||||
list: viewModel,
|
list: viewModel,
|
||||||
getDisplayStrings: getDisplayStrings,
|
getDisplayStrings: getDisplayStrings,
|
||||||
c: c,
|
c: c,
|
||||||
@ -61,3 +67,7 @@ func (self *CommitFilesContext) GetSelectedItemId() string {
|
|||||||
|
|
||||||
return item.ID()
|
return item.ID()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *CommitFilesContext) GetDiffTerminals() []string {
|
||||||
|
return []string{self.GetRef().RefName()}
|
||||||
|
}
|
||||||
|
@ -1,15 +1,21 @@
|
|||||||
package context
|
package context
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CommitMessageContext struct {
|
type CommitMessageContext struct {
|
||||||
|
c *ContextCommon
|
||||||
types.Context
|
types.Context
|
||||||
viewModel *CommitMessageViewModel
|
viewModel *CommitMessageViewModel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ types.Context = (*CommitMessageContext)(nil)
|
||||||
|
|
||||||
// when selectedIndex (see below) is set to this value, it means that we're not
|
// when selectedIndex (see below) is set to this value, it means that we're not
|
||||||
// currently viewing a commit message of an existing commit: instead we're making our own
|
// currently viewing a commit message of an existing commit: instead we're making our own
|
||||||
// new commit message
|
// new commit message
|
||||||
@ -35,22 +41,21 @@ type CommitMessageViewModel struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewCommitMessageContext(
|
func NewCommitMessageContext(
|
||||||
view *gocui.View,
|
c *ContextCommon,
|
||||||
opts ContextCallbackOpts,
|
|
||||||
) *CommitMessageContext {
|
) *CommitMessageContext {
|
||||||
viewModel := &CommitMessageViewModel{}
|
viewModel := &CommitMessageViewModel{}
|
||||||
return &CommitMessageContext{
|
return &CommitMessageContext{
|
||||||
|
c: c,
|
||||||
viewModel: viewModel,
|
viewModel: viewModel,
|
||||||
Context: NewSimpleContext(
|
Context: NewSimpleContext(
|
||||||
NewBaseContext(NewBaseContextOpts{
|
NewBaseContext(NewBaseContextOpts{
|
||||||
Kind: types.PERSISTENT_POPUP,
|
Kind: types.PERSISTENT_POPUP,
|
||||||
View: view,
|
View: c.Views().CommitMessage,
|
||||||
WindowName: "commitMessage",
|
WindowName: "commitMessage",
|
||||||
Key: COMMIT_MESSAGE_CONTEXT_KEY,
|
Key: COMMIT_MESSAGE_CONTEXT_KEY,
|
||||||
Focusable: true,
|
Focusable: true,
|
||||||
HasUncontrolledBounds: true,
|
HasUncontrolledBounds: true,
|
||||||
}),
|
}),
|
||||||
opts,
|
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -93,3 +98,15 @@ func (self *CommitMessageContext) SetPanelState(index int, title string, preserv
|
|||||||
self.viewModel.onConfirm = onConfirm
|
self.viewModel.onConfirm = onConfirm
|
||||||
self.GetView().Title = title
|
self.GetView().Title = title
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *CommitMessageContext) RenderCommitLength() {
|
||||||
|
if !self.c.UserConfig.Gui.CommitLength.Show {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.c.Views().CommitMessage.Subtitle = getBufferLength(self.c.Views().CommitMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBufferLength(view *gocui.View) string {
|
||||||
|
return " " + strconv.Itoa(strings.Count(view.TextArea.GetContent(), "")-1) + " "
|
||||||
|
}
|
||||||
|
35
pkg/gui/context/confirmation_context.go
Normal file
35
pkg/gui/context/confirmation_context.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package context
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ConfirmationContext struct {
|
||||||
|
*SimpleContext
|
||||||
|
c *ContextCommon
|
||||||
|
|
||||||
|
State ConfirmationContextState
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConfirmationContextState struct {
|
||||||
|
OnConfirm func() error
|
||||||
|
OnClose func() error
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ types.Context = (*ConfirmationContext)(nil)
|
||||||
|
|
||||||
|
func NewConfirmationContext(
|
||||||
|
c *ContextCommon,
|
||||||
|
) *ConfirmationContext {
|
||||||
|
return &ConfirmationContext{
|
||||||
|
c: c,
|
||||||
|
SimpleContext: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||||
|
View: c.Views().Confirmation,
|
||||||
|
WindowName: "confirmation",
|
||||||
|
Key: CONFIRMATION_CONTEXT_KEY,
|
||||||
|
Kind: types.TEMPORARY_POPUP,
|
||||||
|
Focusable: true,
|
||||||
|
HasUncontrolledBounds: true,
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
@ -97,7 +97,7 @@ type ContextTree struct {
|
|||||||
CustomPatchBuilder *PatchExplorerContext
|
CustomPatchBuilder *PatchExplorerContext
|
||||||
CustomPatchBuilderSecondary types.Context
|
CustomPatchBuilderSecondary types.Context
|
||||||
MergeConflicts *MergeConflictsContext
|
MergeConflicts *MergeConflictsContext
|
||||||
Confirmation types.Context
|
Confirmation *ConfirmationContext
|
||||||
CommitMessage *CommitMessageContext
|
CommitMessage *CommitMessageContext
|
||||||
CommitDescription types.Context
|
CommitDescription types.Context
|
||||||
CommandLog types.Context
|
CommandLog types.Context
|
||||||
|
11
pkg/gui/context/context_common.go
Normal file
11
pkg/gui/context/context_common.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package context
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/common"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ContextCommon struct {
|
||||||
|
*common.Common
|
||||||
|
types.IGuiCommon
|
||||||
|
}
|
@ -10,11 +10,13 @@ import (
|
|||||||
type ListContextTrait struct {
|
type ListContextTrait struct {
|
||||||
types.Context
|
types.Context
|
||||||
|
|
||||||
c *types.HelperCommon
|
c *ContextCommon
|
||||||
list types.IList
|
list types.IList
|
||||||
getDisplayStrings func(startIdx int, length int) [][]string
|
getDisplayStrings func(startIdx int, length int) [][]string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *ListContextTrait) IsListContext() {}
|
||||||
|
|
||||||
func (self *ListContextTrait) GetList() types.IList {
|
func (self *ListContextTrait) GetList() types.IList {
|
||||||
return self.list
|
return self.list
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
package context
|
package context
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jesseduffield/gocui"
|
"log"
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -11,36 +14,57 @@ type LocalCommitsContext struct {
|
|||||||
*ViewportListContextTrait
|
*ViewportListContextTrait
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ types.IListContext = (*LocalCommitsContext)(nil)
|
var (
|
||||||
|
_ types.IListContext = (*LocalCommitsContext)(nil)
|
||||||
|
_ types.DiffableContext = (*LocalCommitsContext)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
func NewLocalCommitsContext(
|
func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext {
|
||||||
getModel func() []*models.Commit,
|
viewModel := NewLocalCommitsViewModel(
|
||||||
view *gocui.View,
|
func() []*models.Commit { return c.Model().Commits },
|
||||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
c,
|
||||||
|
)
|
||||||
|
|
||||||
onFocus func(types.OnFocusOpts) error,
|
getDisplayStrings := func(startIdx int, length int) [][]string {
|
||||||
onRenderToMain func() error,
|
selectedCommitSha := ""
|
||||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
|
||||||
|
|
||||||
c *types.HelperCommon,
|
if c.CurrentContext().GetKey() == LOCAL_COMMITS_CONTEXT_KEY {
|
||||||
) *LocalCommitsContext {
|
selectedCommit := viewModel.GetSelected()
|
||||||
viewModel := NewLocalCommitsViewModel(getModel, c)
|
if selectedCommit != nil {
|
||||||
|
selectedCommitSha = selectedCommit.Sha
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showYouAreHereLabel := c.Model().WorkingTreeStateAtLastCommitRefresh == enums.REBASE_MODE_REBASING
|
||||||
|
|
||||||
|
return presentation.GetCommitListDisplayStrings(
|
||||||
|
c.Common,
|
||||||
|
c.Model().Commits,
|
||||||
|
c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL,
|
||||||
|
c.Modes().CherryPicking.SelectedShaSet(),
|
||||||
|
c.Modes().Diffing.Ref,
|
||||||
|
c.UserConfig.Gui.TimeFormat,
|
||||||
|
c.UserConfig.Git.ParseEmoji,
|
||||||
|
selectedCommitSha,
|
||||||
|
startIdx,
|
||||||
|
length,
|
||||||
|
shouldShowGraph(c),
|
||||||
|
c.Model().BisectInfo,
|
||||||
|
showYouAreHereLabel,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return &LocalCommitsContext{
|
return &LocalCommitsContext{
|
||||||
LocalCommitsViewModel: viewModel,
|
LocalCommitsViewModel: viewModel,
|
||||||
ViewportListContextTrait: &ViewportListContextTrait{
|
ViewportListContextTrait: &ViewportListContextTrait{
|
||||||
ListContextTrait: &ListContextTrait{
|
ListContextTrait: &ListContextTrait{
|
||||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||||
View: view,
|
View: c.Views().Commits,
|
||||||
WindowName: "commits",
|
WindowName: "commits",
|
||||||
Key: LOCAL_COMMITS_CONTEXT_KEY,
|
Key: LOCAL_COMMITS_CONTEXT_KEY,
|
||||||
Kind: types.SIDE_CONTEXT,
|
Kind: types.SIDE_CONTEXT,
|
||||||
Focusable: true,
|
Focusable: true,
|
||||||
}), ContextCallbackOpts{
|
})),
|
||||||
OnFocus: onFocus,
|
|
||||||
OnFocusLost: onFocusLost,
|
|
||||||
OnRenderToMain: onRenderToMain,
|
|
||||||
}),
|
|
||||||
list: viewModel,
|
list: viewModel,
|
||||||
getDisplayStrings: getDisplayStrings,
|
getDisplayStrings: getDisplayStrings,
|
||||||
c: c,
|
c: c,
|
||||||
@ -69,7 +93,7 @@ type LocalCommitsViewModel struct {
|
|||||||
showWholeGitGraph bool
|
showWholeGitGraph bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLocalCommitsViewModel(getModel func() []*models.Commit, c *types.HelperCommon) *LocalCommitsViewModel {
|
func NewLocalCommitsViewModel(getModel func() []*models.Commit, c *ContextCommon) *LocalCommitsViewModel {
|
||||||
self := &LocalCommitsViewModel{
|
self := &LocalCommitsViewModel{
|
||||||
BasicViewModel: NewBasicViewModel(getModel),
|
BasicViewModel: NewBasicViewModel(getModel),
|
||||||
limitCommits: true,
|
limitCommits: true,
|
||||||
@ -91,6 +115,12 @@ func (self *LocalCommitsContext) GetSelectedRef() types.Ref {
|
|||||||
return commit
|
return commit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *LocalCommitsContext) GetDiffTerminals() []string {
|
||||||
|
itemId := self.GetSelectedItemId()
|
||||||
|
|
||||||
|
return []string{itemId}
|
||||||
|
}
|
||||||
|
|
||||||
func (self *LocalCommitsViewModel) SetLimitCommits(value bool) {
|
func (self *LocalCommitsViewModel) SetLimitCommits(value bool) {
|
||||||
self.limitCommits = value
|
self.limitCommits = value
|
||||||
}
|
}
|
||||||
@ -110,3 +140,22 @@ func (self *LocalCommitsViewModel) GetShowWholeGitGraph() bool {
|
|||||||
func (self *LocalCommitsViewModel) GetCommits() []*models.Commit {
|
func (self *LocalCommitsViewModel) GetCommits() []*models.Commit {
|
||||||
return self.getModel()
|
return self.getModel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func shouldShowGraph(c *ContextCommon) bool {
|
||||||
|
if c.Modes().Filtering.Active() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
value := c.UserConfig.Git.Log.ShowGraph
|
||||||
|
switch value {
|
||||||
|
case "always":
|
||||||
|
return true
|
||||||
|
case "never":
|
||||||
|
return false
|
||||||
|
case "when-maximised":
|
||||||
|
return c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Fatalf("Unknown value for git.log.showGraph: %s. Expected one of: 'always', 'never', 'when-maximised'", value)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@ -2,7 +2,6 @@ package context
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jesseduffield/generics/slices"
|
"github.com/jesseduffield/generics/slices"
|
||||||
"github.com/jesseduffield/gocui"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/keybindings"
|
"github.com/jesseduffield/lazygit/pkg/gui/keybindings"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
@ -16,34 +15,21 @@ type MenuContext struct {
|
|||||||
var _ types.IListContext = (*MenuContext)(nil)
|
var _ types.IListContext = (*MenuContext)(nil)
|
||||||
|
|
||||||
func NewMenuContext(
|
func NewMenuContext(
|
||||||
view *gocui.View,
|
c *ContextCommon,
|
||||||
|
|
||||||
c *types.HelperCommon,
|
|
||||||
getOptionsMap func() map[string]string,
|
|
||||||
renderToDescriptionView func(string),
|
|
||||||
) *MenuContext {
|
) *MenuContext {
|
||||||
viewModel := NewMenuViewModel()
|
viewModel := NewMenuViewModel()
|
||||||
|
|
||||||
onFocus := func(types.OnFocusOpts) error {
|
|
||||||
selectedMenuItem := viewModel.GetSelected()
|
|
||||||
renderToDescriptionView(selectedMenuItem.Tooltip)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &MenuContext{
|
return &MenuContext{
|
||||||
MenuViewModel: viewModel,
|
MenuViewModel: viewModel,
|
||||||
ListContextTrait: &ListContextTrait{
|
ListContextTrait: &ListContextTrait{
|
||||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||||
View: view,
|
View: c.Views().Menu,
|
||||||
WindowName: "menu",
|
WindowName: "menu",
|
||||||
Key: "menu",
|
Key: "menu",
|
||||||
Kind: types.TEMPORARY_POPUP,
|
Kind: types.TEMPORARY_POPUP,
|
||||||
OnGetOptionsMap: getOptionsMap,
|
|
||||||
Focusable: true,
|
Focusable: true,
|
||||||
HasUncontrolledBounds: true,
|
HasUncontrolledBounds: true,
|
||||||
}), ContextCallbackOpts{
|
})),
|
||||||
OnFocus: onFocus,
|
|
||||||
}),
|
|
||||||
getDisplayStrings: viewModel.GetDisplayStrings,
|
getDisplayStrings: viewModel.GetDisplayStrings,
|
||||||
list: viewModel,
|
list: viewModel,
|
||||||
c: c,
|
c: c,
|
||||||
|
@ -3,7 +3,6 @@ package context
|
|||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
"github.com/jesseduffield/gocui"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/mergeconflicts"
|
"github.com/jesseduffield/lazygit/pkg/gui/mergeconflicts"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
"github.com/sasha-s/go-deadlock"
|
"github.com/sasha-s/go-deadlock"
|
||||||
@ -12,7 +11,7 @@ import (
|
|||||||
type MergeConflictsContext struct {
|
type MergeConflictsContext struct {
|
||||||
types.Context
|
types.Context
|
||||||
viewModel *ConflictsViewModel
|
viewModel *ConflictsViewModel
|
||||||
c *types.HelperCommon
|
c *ContextCommon
|
||||||
mutex *deadlock.Mutex
|
mutex *deadlock.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,12 +24,7 @@ type ConflictsViewModel struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewMergeConflictsContext(
|
func NewMergeConflictsContext(
|
||||||
view *gocui.View,
|
c *ContextCommon,
|
||||||
|
|
||||||
opts ContextCallbackOpts,
|
|
||||||
|
|
||||||
c *types.HelperCommon,
|
|
||||||
getOptionsMap func() map[string]string,
|
|
||||||
) *MergeConflictsContext {
|
) *MergeConflictsContext {
|
||||||
viewModel := &ConflictsViewModel{
|
viewModel := &ConflictsViewModel{
|
||||||
state: mergeconflicts.NewState(),
|
state: mergeconflicts.NewState(),
|
||||||
@ -43,14 +37,12 @@ func NewMergeConflictsContext(
|
|||||||
Context: NewSimpleContext(
|
Context: NewSimpleContext(
|
||||||
NewBaseContext(NewBaseContextOpts{
|
NewBaseContext(NewBaseContextOpts{
|
||||||
Kind: types.MAIN_CONTEXT,
|
Kind: types.MAIN_CONTEXT,
|
||||||
View: view,
|
View: c.Views().MergeConflicts,
|
||||||
WindowName: "main",
|
WindowName: "main",
|
||||||
Key: MERGE_CONFLICTS_CONTEXT_KEY,
|
Key: MERGE_CONFLICTS_CONTEXT_KEY,
|
||||||
OnGetOptionsMap: getOptionsMap,
|
|
||||||
Focusable: true,
|
Focusable: true,
|
||||||
HighlightOnFocus: true,
|
HighlightOnFocus: true,
|
||||||
}),
|
}),
|
||||||
opts,
|
|
||||||
),
|
),
|
||||||
c: c,
|
c: c,
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ type PatchExplorerContext struct {
|
|||||||
state *patch_exploring.State
|
state *patch_exploring.State
|
||||||
viewTrait *ViewTrait
|
viewTrait *ViewTrait
|
||||||
getIncludedLineIndices func() []int
|
getIncludedLineIndices func() []int
|
||||||
c *types.HelperCommon
|
c *ContextCommon
|
||||||
mutex *deadlock.Mutex
|
mutex *deadlock.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,11 +24,9 @@ func NewPatchExplorerContext(
|
|||||||
windowName string,
|
windowName string,
|
||||||
key types.ContextKey,
|
key types.ContextKey,
|
||||||
|
|
||||||
onFocus func(types.OnFocusOpts) error,
|
|
||||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
|
||||||
getIncludedLineIndices func() []int,
|
getIncludedLineIndices func() []int,
|
||||||
|
|
||||||
c *types.HelperCommon,
|
c *ContextCommon,
|
||||||
) *PatchExplorerContext {
|
) *PatchExplorerContext {
|
||||||
return &PatchExplorerContext{
|
return &PatchExplorerContext{
|
||||||
state: nil,
|
state: nil,
|
||||||
@ -43,13 +41,12 @@ func NewPatchExplorerContext(
|
|||||||
Kind: types.MAIN_CONTEXT,
|
Kind: types.MAIN_CONTEXT,
|
||||||
Focusable: true,
|
Focusable: true,
|
||||||
HighlightOnFocus: true,
|
HighlightOnFocus: true,
|
||||||
}), ContextCallbackOpts{
|
})),
|
||||||
OnFocus: onFocus,
|
|
||||||
OnFocusLost: onFocusLost,
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *PatchExplorerContext) IsPatchExplorerContext() {}
|
||||||
|
|
||||||
func (self *PatchExplorerContext) GetState() *patch_exploring.State {
|
func (self *PatchExplorerContext) GetState() *patch_exploring.State {
|
||||||
return self.state
|
return self.state
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package context
|
package context
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jesseduffield/gocui"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -11,35 +11,35 @@ type ReflogCommitsContext struct {
|
|||||||
*ListContextTrait
|
*ListContextTrait
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ types.IListContext = (*ReflogCommitsContext)(nil)
|
var (
|
||||||
|
_ types.IListContext = (*ReflogCommitsContext)(nil)
|
||||||
|
_ types.DiffableContext = (*ReflogCommitsContext)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
func NewReflogCommitsContext(
|
func NewReflogCommitsContext(c *ContextCommon) *ReflogCommitsContext {
|
||||||
getModel func() []*models.Commit,
|
viewModel := NewBasicViewModel(func() []*models.Commit { return c.Model().FilteredReflogCommits })
|
||||||
view *gocui.View,
|
|
||||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
|
||||||
|
|
||||||
onFocus func(types.OnFocusOpts) error,
|
getDisplayStrings := func(startIdx int, length int) [][]string {
|
||||||
onRenderToMain func() error,
|
return presentation.GetReflogCommitListDisplayStrings(
|
||||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
c.Model().FilteredReflogCommits,
|
||||||
|
c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL,
|
||||||
c *types.HelperCommon,
|
c.Modes().CherryPicking.SelectedShaSet(),
|
||||||
) *ReflogCommitsContext {
|
c.Modes().Diffing.Ref,
|
||||||
viewModel := NewBasicViewModel(getModel)
|
c.UserConfig.Gui.TimeFormat,
|
||||||
|
c.UserConfig.Git.ParseEmoji,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return &ReflogCommitsContext{
|
return &ReflogCommitsContext{
|
||||||
BasicViewModel: viewModel,
|
BasicViewModel: viewModel,
|
||||||
ListContextTrait: &ListContextTrait{
|
ListContextTrait: &ListContextTrait{
|
||||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||||
View: view,
|
View: c.Views().ReflogCommits,
|
||||||
WindowName: "commits",
|
WindowName: "commits",
|
||||||
Key: REFLOG_COMMITS_CONTEXT_KEY,
|
Key: REFLOG_COMMITS_CONTEXT_KEY,
|
||||||
Kind: types.SIDE_CONTEXT,
|
Kind: types.SIDE_CONTEXT,
|
||||||
Focusable: true,
|
Focusable: true,
|
||||||
}), ContextCallbackOpts{
|
})),
|
||||||
OnFocus: onFocus,
|
|
||||||
OnFocusLost: onFocusLost,
|
|
||||||
OnRenderToMain: onRenderToMain,
|
|
||||||
}),
|
|
||||||
list: viewModel,
|
list: viewModel,
|
||||||
getDisplayStrings: getDisplayStrings,
|
getDisplayStrings: getDisplayStrings,
|
||||||
c: c,
|
c: c,
|
||||||
@ -71,3 +71,9 @@ func (self *ReflogCommitsContext) GetSelectedRef() types.Ref {
|
|||||||
func (self *ReflogCommitsContext) GetCommits() []*models.Commit {
|
func (self *ReflogCommitsContext) GetCommits() []*models.Commit {
|
||||||
return self.getModel()
|
return self.getModel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *ReflogCommitsContext) GetDiffTerminals() []string {
|
||||||
|
itemId := self.GetSelectedItemId()
|
||||||
|
|
||||||
|
return []string{itemId}
|
||||||
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package context
|
package context
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jesseduffield/gocui"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -12,37 +12,32 @@ type RemoteBranchesContext struct {
|
|||||||
*DynamicTitleBuilder
|
*DynamicTitleBuilder
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ types.IListContext = (*RemoteBranchesContext)(nil)
|
var (
|
||||||
|
_ types.IListContext = (*RemoteBranchesContext)(nil)
|
||||||
|
_ types.DiffableContext = (*RemoteBranchesContext)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
func NewRemoteBranchesContext(
|
func NewRemoteBranchesContext(
|
||||||
getModel func() []*models.RemoteBranch,
|
c *ContextCommon,
|
||||||
view *gocui.View,
|
|
||||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
|
||||||
|
|
||||||
onFocus func(types.OnFocusOpts) error,
|
|
||||||
onRenderToMain func() error,
|
|
||||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
|
||||||
|
|
||||||
c *types.HelperCommon,
|
|
||||||
) *RemoteBranchesContext {
|
) *RemoteBranchesContext {
|
||||||
viewModel := NewBasicViewModel(getModel)
|
viewModel := NewBasicViewModel(func() []*models.RemoteBranch { return c.Model().RemoteBranches })
|
||||||
|
|
||||||
|
getDisplayStrings := func(startIdx int, length int) [][]string {
|
||||||
|
return presentation.GetRemoteBranchListDisplayStrings(c.Model().RemoteBranches, c.Modes().Diffing.Ref)
|
||||||
|
}
|
||||||
|
|
||||||
return &RemoteBranchesContext{
|
return &RemoteBranchesContext{
|
||||||
BasicViewModel: viewModel,
|
BasicViewModel: viewModel,
|
||||||
DynamicTitleBuilder: NewDynamicTitleBuilder(c.Tr.RemoteBranchesDynamicTitle),
|
DynamicTitleBuilder: NewDynamicTitleBuilder(c.Tr.RemoteBranchesDynamicTitle),
|
||||||
ListContextTrait: &ListContextTrait{
|
ListContextTrait: &ListContextTrait{
|
||||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||||
View: view,
|
View: c.Views().RemoteBranches,
|
||||||
WindowName: "branches",
|
WindowName: "branches",
|
||||||
Key: REMOTE_BRANCHES_CONTEXT_KEY,
|
Key: REMOTE_BRANCHES_CONTEXT_KEY,
|
||||||
Kind: types.SIDE_CONTEXT,
|
Kind: types.SIDE_CONTEXT,
|
||||||
Focusable: true,
|
Focusable: true,
|
||||||
Transient: true,
|
Transient: true,
|
||||||
}), ContextCallbackOpts{
|
})),
|
||||||
OnFocus: onFocus,
|
|
||||||
OnFocusLost: onFocusLost,
|
|
||||||
OnRenderToMain: onRenderToMain,
|
|
||||||
}),
|
|
||||||
list: viewModel,
|
list: viewModel,
|
||||||
getDisplayStrings: getDisplayStrings,
|
getDisplayStrings: getDisplayStrings,
|
||||||
c: c,
|
c: c,
|
||||||
@ -66,3 +61,9 @@ func (self *RemoteBranchesContext) GetSelectedRef() types.Ref {
|
|||||||
}
|
}
|
||||||
return remoteBranch
|
return remoteBranch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *RemoteBranchesContext) GetDiffTerminals() []string {
|
||||||
|
itemId := self.GetSelectedItemId()
|
||||||
|
|
||||||
|
return []string{itemId}
|
||||||
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package context
|
package context
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jesseduffield/gocui"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -11,35 +11,28 @@ type RemotesContext struct {
|
|||||||
*ListContextTrait
|
*ListContextTrait
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ types.IListContext = (*RemotesContext)(nil)
|
var (
|
||||||
|
_ types.IListContext = (*RemotesContext)(nil)
|
||||||
|
_ types.DiffableContext = (*RemotesContext)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
func NewRemotesContext(
|
func NewRemotesContext(c *ContextCommon) *RemotesContext {
|
||||||
getModel func() []*models.Remote,
|
viewModel := NewBasicViewModel(func() []*models.Remote { return c.Model().Remotes })
|
||||||
view *gocui.View,
|
|
||||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
|
||||||
|
|
||||||
onFocus func(types.OnFocusOpts) error,
|
getDisplayStrings := func(startIdx int, length int) [][]string {
|
||||||
onRenderToMain func() error,
|
return presentation.GetRemoteListDisplayStrings(c.Model().Remotes, c.Modes().Diffing.Ref)
|
||||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
}
|
||||||
|
|
||||||
c *types.HelperCommon,
|
|
||||||
) *RemotesContext {
|
|
||||||
viewModel := NewBasicViewModel(getModel)
|
|
||||||
|
|
||||||
return &RemotesContext{
|
return &RemotesContext{
|
||||||
BasicViewModel: viewModel,
|
BasicViewModel: viewModel,
|
||||||
ListContextTrait: &ListContextTrait{
|
ListContextTrait: &ListContextTrait{
|
||||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||||
View: view,
|
View: c.Views().Remotes,
|
||||||
WindowName: "branches",
|
WindowName: "branches",
|
||||||
Key: REMOTES_CONTEXT_KEY,
|
Key: REMOTES_CONTEXT_KEY,
|
||||||
Kind: types.SIDE_CONTEXT,
|
Kind: types.SIDE_CONTEXT,
|
||||||
Focusable: true,
|
Focusable: true,
|
||||||
}), ContextCallbackOpts{
|
})),
|
||||||
OnFocus: onFocus,
|
|
||||||
OnFocusLost: onFocusLost,
|
|
||||||
OnRenderToMain: onRenderToMain,
|
|
||||||
}),
|
|
||||||
list: viewModel,
|
list: viewModel,
|
||||||
getDisplayStrings: getDisplayStrings,
|
getDisplayStrings: getDisplayStrings,
|
||||||
c: c,
|
c: c,
|
||||||
@ -55,3 +48,9 @@ func (self *RemotesContext) GetSelectedItemId() string {
|
|||||||
|
|
||||||
return item.ID()
|
return item.ID()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *RemotesContext) GetDiffTerminals() []string {
|
||||||
|
itemId := self.GetSelectedItemId()
|
||||||
|
|
||||||
|
return []string{itemId}
|
||||||
|
}
|
||||||
|
146
pkg/gui/context/setup.go
Normal file
146
pkg/gui/context/setup.go
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
package context
|
||||||
|
|
||||||
|
import "github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
|
|
||||||
|
func NewContextTree(c *ContextCommon) *ContextTree {
|
||||||
|
commitFilesContext := NewCommitFilesContext(c)
|
||||||
|
|
||||||
|
return &ContextTree{
|
||||||
|
Global: NewSimpleContext(
|
||||||
|
NewBaseContext(NewBaseContextOpts{
|
||||||
|
Kind: types.GLOBAL_CONTEXT,
|
||||||
|
View: nil, // TODO: see if this breaks anything
|
||||||
|
WindowName: "",
|
||||||
|
Key: GLOBAL_CONTEXT_KEY,
|
||||||
|
Focusable: false,
|
||||||
|
HasUncontrolledBounds: true, // setting to true because the global context doesn't even have a view
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
Status: NewSimpleContext(
|
||||||
|
NewBaseContext(NewBaseContextOpts{
|
||||||
|
Kind: types.SIDE_CONTEXT,
|
||||||
|
View: c.Views().Status,
|
||||||
|
WindowName: "status",
|
||||||
|
Key: STATUS_CONTEXT_KEY,
|
||||||
|
Focusable: true,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
Files: NewWorkingTreeContext(c),
|
||||||
|
Submodules: NewSubmodulesContext(c),
|
||||||
|
Menu: NewMenuContext(c),
|
||||||
|
Remotes: NewRemotesContext(c),
|
||||||
|
RemoteBranches: NewRemoteBranchesContext(c),
|
||||||
|
LocalCommits: NewLocalCommitsContext(c),
|
||||||
|
CommitFiles: commitFilesContext,
|
||||||
|
ReflogCommits: NewReflogCommitsContext(c),
|
||||||
|
SubCommits: NewSubCommitsContext(c),
|
||||||
|
Branches: NewBranchesContext(c),
|
||||||
|
Tags: NewTagsContext(c),
|
||||||
|
Stash: NewStashContext(c),
|
||||||
|
Suggestions: NewSuggestionsContext(c),
|
||||||
|
Normal: NewSimpleContext(
|
||||||
|
NewBaseContext(NewBaseContextOpts{
|
||||||
|
Kind: types.MAIN_CONTEXT,
|
||||||
|
View: c.Views().Main,
|
||||||
|
WindowName: "main",
|
||||||
|
Key: NORMAL_MAIN_CONTEXT_KEY,
|
||||||
|
Focusable: false,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
NormalSecondary: NewSimpleContext(
|
||||||
|
NewBaseContext(NewBaseContextOpts{
|
||||||
|
Kind: types.MAIN_CONTEXT,
|
||||||
|
View: c.Views().Secondary,
|
||||||
|
WindowName: "secondary",
|
||||||
|
Key: NORMAL_SECONDARY_CONTEXT_KEY,
|
||||||
|
Focusable: false,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
Staging: NewPatchExplorerContext(
|
||||||
|
c.Views().Staging,
|
||||||
|
"main",
|
||||||
|
STAGING_MAIN_CONTEXT_KEY,
|
||||||
|
func() []int { return nil },
|
||||||
|
c,
|
||||||
|
),
|
||||||
|
StagingSecondary: NewPatchExplorerContext(
|
||||||
|
c.Views().StagingSecondary,
|
||||||
|
"secondary",
|
||||||
|
STAGING_SECONDARY_CONTEXT_KEY,
|
||||||
|
func() []int { return nil },
|
||||||
|
c,
|
||||||
|
),
|
||||||
|
CustomPatchBuilder: NewPatchExplorerContext(
|
||||||
|
c.Views().PatchBuilding,
|
||||||
|
"main",
|
||||||
|
PATCH_BUILDING_MAIN_CONTEXT_KEY,
|
||||||
|
func() []int {
|
||||||
|
filename := commitFilesContext.GetSelectedPath()
|
||||||
|
includedLineIndices, err := c.Git().Patch.PatchBuilder.GetFileIncLineIndices(filename)
|
||||||
|
if err != nil {
|
||||||
|
c.Log.Error(err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return includedLineIndices
|
||||||
|
},
|
||||||
|
c,
|
||||||
|
),
|
||||||
|
CustomPatchBuilderSecondary: NewSimpleContext(
|
||||||
|
NewBaseContext(NewBaseContextOpts{
|
||||||
|
Kind: types.MAIN_CONTEXT,
|
||||||
|
View: c.Views().PatchBuildingSecondary,
|
||||||
|
WindowName: "secondary",
|
||||||
|
Key: PATCH_BUILDING_SECONDARY_CONTEXT_KEY,
|
||||||
|
Focusable: false,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
MergeConflicts: NewMergeConflictsContext(
|
||||||
|
c,
|
||||||
|
),
|
||||||
|
Confirmation: NewConfirmationContext(c),
|
||||||
|
CommitMessage: NewCommitMessageContext(c),
|
||||||
|
CommitDescription: NewSimpleContext(
|
||||||
|
NewBaseContext(NewBaseContextOpts{
|
||||||
|
Kind: types.PERSISTENT_POPUP,
|
||||||
|
View: c.Views().CommitDescription,
|
||||||
|
WindowName: "commitDescription",
|
||||||
|
Key: COMMIT_DESCRIPTION_CONTEXT_KEY,
|
||||||
|
Focusable: true,
|
||||||
|
HasUncontrolledBounds: true,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
Search: NewSimpleContext(
|
||||||
|
NewBaseContext(NewBaseContextOpts{
|
||||||
|
Kind: types.PERSISTENT_POPUP,
|
||||||
|
View: c.Views().Search,
|
||||||
|
WindowName: "search",
|
||||||
|
Key: SEARCH_CONTEXT_KEY,
|
||||||
|
Focusable: true,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
CommandLog: NewSimpleContext(
|
||||||
|
NewBaseContext(NewBaseContextOpts{
|
||||||
|
Kind: types.EXTRAS_CONTEXT,
|
||||||
|
View: c.Views().Extras,
|
||||||
|
WindowName: "extras",
|
||||||
|
Key: COMMAND_LOG_CONTEXT_KEY,
|
||||||
|
Focusable: true,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
Snake: NewSimpleContext(
|
||||||
|
NewBaseContext(NewBaseContextOpts{
|
||||||
|
Kind: types.SIDE_CONTEXT,
|
||||||
|
View: c.Views().Snake,
|
||||||
|
WindowName: "files",
|
||||||
|
Key: SNAKE_CONTEXT_KEY,
|
||||||
|
Focusable: true,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
Options: NewDisplayContext(OPTIONS_CONTEXT_KEY, c.Views().Options, "options"),
|
||||||
|
AppStatus: NewDisplayContext(APP_STATUS_CONTEXT_KEY, c.Views().AppStatus, "appStatus"),
|
||||||
|
SearchPrefix: NewDisplayContext(SEARCH_PREFIX_CONTEXT_KEY, c.Views().SearchPrefix, "searchPrefix"),
|
||||||
|
Information: NewDisplayContext(INFORMATION_CONTEXT_KEY, c.Views().Information, "information"),
|
||||||
|
Limit: NewDisplayContext(LIMIT_CONTEXT_KEY, c.Views().Limit, "limit"),
|
||||||
|
}
|
||||||
|
}
|
@ -6,29 +6,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type SimpleContext struct {
|
type SimpleContext struct {
|
||||||
OnFocus func(opts types.OnFocusOpts) error
|
|
||||||
OnFocusLost func(opts types.OnFocusLostOpts) error
|
|
||||||
OnRender func() error
|
|
||||||
// this is for pushing some content to the main view
|
|
||||||
OnRenderToMain func() error
|
|
||||||
|
|
||||||
*BaseContext
|
*BaseContext
|
||||||
}
|
}
|
||||||
|
|
||||||
type ContextCallbackOpts struct {
|
func NewSimpleContext(baseContext *BaseContext) *SimpleContext {
|
||||||
OnFocus func(opts types.OnFocusOpts) error
|
|
||||||
OnFocusLost func(opts types.OnFocusLostOpts) error
|
|
||||||
OnRender func() error
|
|
||||||
OnRenderToMain func() error
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSimpleContext(baseContext *BaseContext, opts ContextCallbackOpts) *SimpleContext {
|
|
||||||
return &SimpleContext{
|
return &SimpleContext{
|
||||||
OnFocus: opts.OnFocus,
|
BaseContext: baseContext,
|
||||||
OnFocusLost: opts.OnFocusLost,
|
|
||||||
OnRender: opts.OnRender,
|
|
||||||
OnRenderToMain: opts.OnRenderToMain,
|
|
||||||
BaseContext: baseContext,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +28,6 @@ func NewDisplayContext(key types.ContextKey, view *gocui.View, windowName string
|
|||||||
Focusable: false,
|
Focusable: false,
|
||||||
Transient: false,
|
Transient: false,
|
||||||
}),
|
}),
|
||||||
ContextCallbackOpts{},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,14 +36,14 @@ func (self *SimpleContext) HandleFocus(opts types.OnFocusOpts) error {
|
|||||||
self.GetViewTrait().SetHighlight(true)
|
self.GetViewTrait().SetHighlight(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.OnFocus != nil {
|
if self.onFocusFn != nil {
|
||||||
if err := self.OnFocus(opts); err != nil {
|
if err := self.onFocusFn(opts); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.OnRenderToMain != nil {
|
if self.onRenderToMainFn != nil {
|
||||||
if err := self.OnRenderToMain(); err != nil {
|
if err := self.onRenderToMainFn(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -70,22 +52,19 @@ func (self *SimpleContext) HandleFocus(opts types.OnFocusOpts) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *SimpleContext) HandleFocusLost(opts types.OnFocusLostOpts) error {
|
func (self *SimpleContext) HandleFocusLost(opts types.OnFocusLostOpts) error {
|
||||||
if self.OnFocusLost != nil {
|
if self.onFocusLostFn != nil {
|
||||||
return self.OnFocusLost(opts)
|
return self.onFocusLostFn(opts)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *SimpleContext) HandleRender() error {
|
func (self *SimpleContext) HandleRender() error {
|
||||||
if self.OnRender != nil {
|
|
||||||
return self.OnRender()
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *SimpleContext) HandleRenderToMain() error {
|
func (self *SimpleContext) HandleRenderToMain() error {
|
||||||
if self.OnRenderToMain != nil {
|
if self.onRenderToMainFn != nil {
|
||||||
return self.OnRenderToMain()
|
return self.onRenderToMainFn()
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package context
|
package context
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jesseduffield/gocui"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -11,35 +11,30 @@ type StashContext struct {
|
|||||||
*ListContextTrait
|
*ListContextTrait
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ types.IListContext = (*StashContext)(nil)
|
var (
|
||||||
|
_ types.IListContext = (*StashContext)(nil)
|
||||||
|
_ types.DiffableContext = (*StashContext)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
func NewStashContext(
|
func NewStashContext(
|
||||||
getModel func() []*models.StashEntry,
|
c *ContextCommon,
|
||||||
view *gocui.View,
|
|
||||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
|
||||||
|
|
||||||
onFocus func(types.OnFocusOpts) error,
|
|
||||||
onRenderToMain func() error,
|
|
||||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
|
||||||
|
|
||||||
c *types.HelperCommon,
|
|
||||||
) *StashContext {
|
) *StashContext {
|
||||||
viewModel := NewBasicViewModel(getModel)
|
viewModel := NewBasicViewModel(func() []*models.StashEntry { return c.Model().StashEntries })
|
||||||
|
|
||||||
|
getDisplayStrings := func(startIdx int, length int) [][]string {
|
||||||
|
return presentation.GetStashEntryListDisplayStrings(c.Model().StashEntries, c.Modes().Diffing.Ref)
|
||||||
|
}
|
||||||
|
|
||||||
return &StashContext{
|
return &StashContext{
|
||||||
BasicViewModel: viewModel,
|
BasicViewModel: viewModel,
|
||||||
ListContextTrait: &ListContextTrait{
|
ListContextTrait: &ListContextTrait{
|
||||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||||
View: view,
|
View: c.Views().Stash,
|
||||||
WindowName: "stash",
|
WindowName: "stash",
|
||||||
Key: STASH_CONTEXT_KEY,
|
Key: STASH_CONTEXT_KEY,
|
||||||
Kind: types.SIDE_CONTEXT,
|
Kind: types.SIDE_CONTEXT,
|
||||||
Focusable: true,
|
Focusable: true,
|
||||||
}), ContextCallbackOpts{
|
})),
|
||||||
OnFocus: onFocus,
|
|
||||||
OnFocusLost: onFocusLost,
|
|
||||||
OnRenderToMain: onRenderToMain,
|
|
||||||
}),
|
|
||||||
list: viewModel,
|
list: viewModel,
|
||||||
getDisplayStrings: getDisplayStrings,
|
getDisplayStrings: getDisplayStrings,
|
||||||
c: c,
|
c: c,
|
||||||
@ -67,3 +62,9 @@ func (self *StashContext) GetSelectedRef() types.Ref {
|
|||||||
}
|
}
|
||||||
return stash
|
return stash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *StashContext) GetDiffTerminals() []string {
|
||||||
|
itemId := self.GetSelectedItemId()
|
||||||
|
|
||||||
|
return []string{itemId}
|
||||||
|
}
|
||||||
|
@ -3,8 +3,9 @@ package context
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
@ -15,23 +16,45 @@ type SubCommitsContext struct {
|
|||||||
*DynamicTitleBuilder
|
*DynamicTitleBuilder
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ types.IListContext = (*SubCommitsContext)(nil)
|
var (
|
||||||
|
_ types.IListContext = (*SubCommitsContext)(nil)
|
||||||
|
_ types.DiffableContext = (*SubCommitsContext)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
func NewSubCommitsContext(
|
func NewSubCommitsContext(
|
||||||
getModel func() []*models.Commit,
|
c *ContextCommon,
|
||||||
view *gocui.View,
|
|
||||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
|
||||||
|
|
||||||
onFocus func(types.OnFocusOpts) error,
|
|
||||||
onRenderToMain func() error,
|
|
||||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
|
||||||
|
|
||||||
c *types.HelperCommon,
|
|
||||||
) *SubCommitsContext {
|
) *SubCommitsContext {
|
||||||
viewModel := &SubCommitsViewModel{
|
viewModel := &SubCommitsViewModel{
|
||||||
BasicViewModel: NewBasicViewModel(getModel),
|
BasicViewModel: NewBasicViewModel(
|
||||||
ref: nil,
|
func() []*models.Commit { return c.Model().SubCommits },
|
||||||
limitCommits: true,
|
),
|
||||||
|
ref: nil,
|
||||||
|
limitCommits: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
getDisplayStrings := func(startIdx int, length int) [][]string {
|
||||||
|
selectedCommitSha := ""
|
||||||
|
if c.CurrentContext().GetKey() == SUB_COMMITS_CONTEXT_KEY {
|
||||||
|
selectedCommit := viewModel.GetSelected()
|
||||||
|
if selectedCommit != nil {
|
||||||
|
selectedCommitSha = selectedCommit.Sha
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return presentation.GetCommitListDisplayStrings(
|
||||||
|
c.Common,
|
||||||
|
c.Model().SubCommits,
|
||||||
|
c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL,
|
||||||
|
c.Modes().CherryPicking.SelectedShaSet(),
|
||||||
|
c.Modes().Diffing.Ref,
|
||||||
|
c.UserConfig.Gui.TimeFormat,
|
||||||
|
c.UserConfig.Git.ParseEmoji,
|
||||||
|
selectedCommitSha,
|
||||||
|
startIdx,
|
||||||
|
length,
|
||||||
|
shouldShowGraph(c),
|
||||||
|
git_commands.NewNullBisectInfo(),
|
||||||
|
false,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &SubCommitsContext{
|
return &SubCommitsContext{
|
||||||
@ -40,17 +63,13 @@ func NewSubCommitsContext(
|
|||||||
ViewportListContextTrait: &ViewportListContextTrait{
|
ViewportListContextTrait: &ViewportListContextTrait{
|
||||||
ListContextTrait: &ListContextTrait{
|
ListContextTrait: &ListContextTrait{
|
||||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||||
View: view,
|
View: c.Views().SubCommits,
|
||||||
WindowName: "branches",
|
WindowName: "branches",
|
||||||
Key: SUB_COMMITS_CONTEXT_KEY,
|
Key: SUB_COMMITS_CONTEXT_KEY,
|
||||||
Kind: types.SIDE_CONTEXT,
|
Kind: types.SIDE_CONTEXT,
|
||||||
Focusable: true,
|
Focusable: true,
|
||||||
Transient: true,
|
Transient: true,
|
||||||
}), ContextCallbackOpts{
|
})),
|
||||||
OnFocus: onFocus,
|
|
||||||
OnFocusLost: onFocusLost,
|
|
||||||
OnRenderToMain: onRenderToMain,
|
|
||||||
}),
|
|
||||||
list: viewModel,
|
list: viewModel,
|
||||||
getDisplayStrings: getDisplayStrings,
|
getDisplayStrings: getDisplayStrings,
|
||||||
c: c,
|
c: c,
|
||||||
@ -111,3 +130,9 @@ func (self *SubCommitsContext) SetLimitCommits(value bool) {
|
|||||||
func (self *SubCommitsContext) GetLimitCommits() bool {
|
func (self *SubCommitsContext) GetLimitCommits() bool {
|
||||||
return self.limitCommits
|
return self.limitCommits
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *SubCommitsContext) GetDiffTerminals() []string {
|
||||||
|
itemId := self.GetSelectedItemId()
|
||||||
|
|
||||||
|
return []string{itemId}
|
||||||
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package context
|
package context
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jesseduffield/gocui"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -13,33 +13,23 @@ type SubmodulesContext struct {
|
|||||||
|
|
||||||
var _ types.IListContext = (*SubmodulesContext)(nil)
|
var _ types.IListContext = (*SubmodulesContext)(nil)
|
||||||
|
|
||||||
func NewSubmodulesContext(
|
func NewSubmodulesContext(c *ContextCommon) *SubmodulesContext {
|
||||||
getModel func() []*models.SubmoduleConfig,
|
viewModel := NewBasicViewModel(func() []*models.SubmoduleConfig { return c.Model().Submodules })
|
||||||
view *gocui.View,
|
|
||||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
|
||||||
|
|
||||||
onFocus func(types.OnFocusOpts) error,
|
getDisplayStrings := func(startIdx int, length int) [][]string {
|
||||||
onRenderToMain func() error,
|
return presentation.GetSubmoduleListDisplayStrings(c.Model().Submodules)
|
||||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
}
|
||||||
|
|
||||||
c *types.HelperCommon,
|
|
||||||
) *SubmodulesContext {
|
|
||||||
viewModel := NewBasicViewModel(getModel)
|
|
||||||
|
|
||||||
return &SubmodulesContext{
|
return &SubmodulesContext{
|
||||||
BasicViewModel: viewModel,
|
BasicViewModel: viewModel,
|
||||||
ListContextTrait: &ListContextTrait{
|
ListContextTrait: &ListContextTrait{
|
||||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||||
View: view,
|
View: c.Views().Submodules,
|
||||||
WindowName: "files",
|
WindowName: "files",
|
||||||
Key: SUBMODULES_CONTEXT_KEY,
|
Key: SUBMODULES_CONTEXT_KEY,
|
||||||
Kind: types.SIDE_CONTEXT,
|
Kind: types.SIDE_CONTEXT,
|
||||||
Focusable: true,
|
Focusable: true,
|
||||||
}), ContextCallbackOpts{
|
})),
|
||||||
OnFocus: onFocus,
|
|
||||||
OnFocusLost: onFocusLost,
|
|
||||||
OnRenderToMain: onRenderToMain,
|
|
||||||
}),
|
|
||||||
list: viewModel,
|
list: viewModel,
|
||||||
getDisplayStrings: getDisplayStrings,
|
getDisplayStrings: getDisplayStrings,
|
||||||
c: c,
|
c: c,
|
||||||
|
@ -1,45 +1,59 @@
|
|||||||
package context
|
package context
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/tasks"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SuggestionsContext struct {
|
type SuggestionsContext struct {
|
||||||
*BasicViewModel[*types.Suggestion]
|
*BasicViewModel[*types.Suggestion]
|
||||||
*ListContextTrait
|
*ListContextTrait
|
||||||
|
|
||||||
|
State *SuggestionsContextState
|
||||||
|
}
|
||||||
|
|
||||||
|
type SuggestionsContextState struct {
|
||||||
|
Suggestions []*types.Suggestion
|
||||||
|
OnConfirm func() error
|
||||||
|
OnClose func() error
|
||||||
|
AsyncHandler *tasks.AsyncHandler
|
||||||
|
|
||||||
|
// FindSuggestions will take a string that the user has typed into a prompt
|
||||||
|
// and return a slice of suggestions which match that string.
|
||||||
|
FindSuggestions func(string) []*types.Suggestion
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ types.IListContext = (*SuggestionsContext)(nil)
|
var _ types.IListContext = (*SuggestionsContext)(nil)
|
||||||
|
|
||||||
func NewSuggestionsContext(
|
func NewSuggestionsContext(
|
||||||
getModel func() []*types.Suggestion,
|
c *ContextCommon,
|
||||||
view *gocui.View,
|
|
||||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
|
||||||
|
|
||||||
onFocus func(types.OnFocusOpts) error,
|
|
||||||
onRenderToMain func() error,
|
|
||||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
|
||||||
|
|
||||||
c *types.HelperCommon,
|
|
||||||
) *SuggestionsContext {
|
) *SuggestionsContext {
|
||||||
|
state := &SuggestionsContextState{
|
||||||
|
AsyncHandler: tasks.NewAsyncHandler(),
|
||||||
|
}
|
||||||
|
getModel := func() []*types.Suggestion {
|
||||||
|
return state.Suggestions
|
||||||
|
}
|
||||||
|
|
||||||
|
getDisplayStrings := func(startIdx int, length int) [][]string {
|
||||||
|
return presentation.GetSuggestionListDisplayStrings(state.Suggestions)
|
||||||
|
}
|
||||||
|
|
||||||
viewModel := NewBasicViewModel(getModel)
|
viewModel := NewBasicViewModel(getModel)
|
||||||
|
|
||||||
return &SuggestionsContext{
|
return &SuggestionsContext{
|
||||||
|
State: state,
|
||||||
BasicViewModel: viewModel,
|
BasicViewModel: viewModel,
|
||||||
ListContextTrait: &ListContextTrait{
|
ListContextTrait: &ListContextTrait{
|
||||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||||
View: view,
|
View: c.Views().Suggestions,
|
||||||
WindowName: "suggestions",
|
WindowName: "suggestions",
|
||||||
Key: SUGGESTIONS_CONTEXT_KEY,
|
Key: SUGGESTIONS_CONTEXT_KEY,
|
||||||
Kind: types.PERSISTENT_POPUP,
|
Kind: types.PERSISTENT_POPUP,
|
||||||
Focusable: true,
|
Focusable: true,
|
||||||
HasUncontrolledBounds: true,
|
HasUncontrolledBounds: true,
|
||||||
}), ContextCallbackOpts{
|
})),
|
||||||
OnFocus: onFocus,
|
|
||||||
OnFocusLost: onFocusLost,
|
|
||||||
OnRenderToMain: onRenderToMain,
|
|
||||||
}),
|
|
||||||
list: viewModel,
|
list: viewModel,
|
||||||
getDisplayStrings: getDisplayStrings,
|
getDisplayStrings: getDisplayStrings,
|
||||||
c: c,
|
c: c,
|
||||||
@ -55,3 +69,22 @@ func (self *SuggestionsContext) GetSelectedItemId() string {
|
|||||||
|
|
||||||
return item.Value
|
return item.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *SuggestionsContext) SetSuggestions(suggestions []*types.Suggestion) {
|
||||||
|
self.State.Suggestions = suggestions
|
||||||
|
self.SetSelectedLineIdx(0)
|
||||||
|
self.c.ResetViewOrigin(self.GetView())
|
||||||
|
_ = self.HandleRender()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *SuggestionsContext) RefreshSuggestions() {
|
||||||
|
self.State.AsyncHandler.Do(func() func() {
|
||||||
|
findSuggestionsFn := self.State.FindSuggestions
|
||||||
|
if findSuggestionsFn != nil {
|
||||||
|
suggestions := findSuggestionsFn(self.c.GetPromptInput())
|
||||||
|
return func() { self.SetSuggestions(suggestions) }
|
||||||
|
} else {
|
||||||
|
return func() {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package context
|
package context
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jesseduffield/gocui"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -11,35 +11,30 @@ type TagsContext struct {
|
|||||||
*ListContextTrait
|
*ListContextTrait
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ types.IListContext = (*TagsContext)(nil)
|
var (
|
||||||
|
_ types.IListContext = (*TagsContext)(nil)
|
||||||
|
_ types.DiffableContext = (*TagsContext)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
func NewTagsContext(
|
func NewTagsContext(
|
||||||
getModel func() []*models.Tag,
|
c *ContextCommon,
|
||||||
view *gocui.View,
|
|
||||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
|
||||||
|
|
||||||
onFocus func(types.OnFocusOpts) error,
|
|
||||||
onRenderToMain func() error,
|
|
||||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
|
||||||
|
|
||||||
c *types.HelperCommon,
|
|
||||||
) *TagsContext {
|
) *TagsContext {
|
||||||
viewModel := NewBasicViewModel(getModel)
|
viewModel := NewBasicViewModel(func() []*models.Tag { return c.Model().Tags })
|
||||||
|
|
||||||
|
getDisplayStrings := func(startIdx int, length int) [][]string {
|
||||||
|
return presentation.GetTagListDisplayStrings(c.Model().Tags, c.Modes().Diffing.Ref)
|
||||||
|
}
|
||||||
|
|
||||||
return &TagsContext{
|
return &TagsContext{
|
||||||
BasicViewModel: viewModel,
|
BasicViewModel: viewModel,
|
||||||
ListContextTrait: &ListContextTrait{
|
ListContextTrait: &ListContextTrait{
|
||||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||||
View: view,
|
View: c.Views().Tags,
|
||||||
WindowName: "branches",
|
WindowName: "branches",
|
||||||
Key: TAGS_CONTEXT_KEY,
|
Key: TAGS_CONTEXT_KEY,
|
||||||
Kind: types.SIDE_CONTEXT,
|
Kind: types.SIDE_CONTEXT,
|
||||||
Focusable: true,
|
Focusable: true,
|
||||||
}), ContextCallbackOpts{
|
})),
|
||||||
OnFocus: onFocus,
|
|
||||||
OnFocusLost: onFocusLost,
|
|
||||||
OnRenderToMain: onRenderToMain,
|
|
||||||
}),
|
|
||||||
list: viewModel,
|
list: viewModel,
|
||||||
getDisplayStrings: getDisplayStrings,
|
getDisplayStrings: getDisplayStrings,
|
||||||
c: c,
|
c: c,
|
||||||
@ -63,3 +58,9 @@ func (self *TagsContext) GetSelectedRef() types.Ref {
|
|||||||
}
|
}
|
||||||
return tag
|
return tag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *TagsContext) GetDiffTerminals() []string {
|
||||||
|
itemId := self.GetSelectedItemId()
|
||||||
|
|
||||||
|
return []string{itemId}
|
||||||
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
package context
|
package context
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/generics/slices"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -14,33 +15,30 @@ type WorkingTreeContext struct {
|
|||||||
|
|
||||||
var _ types.IListContext = (*WorkingTreeContext)(nil)
|
var _ types.IListContext = (*WorkingTreeContext)(nil)
|
||||||
|
|
||||||
func NewWorkingTreeContext(
|
func NewWorkingTreeContext(c *ContextCommon) *WorkingTreeContext {
|
||||||
getModel func() []*models.File,
|
viewModel := filetree.NewFileTreeViewModel(
|
||||||
view *gocui.View,
|
func() []*models.File { return c.Model().Files },
|
||||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
c.Log,
|
||||||
|
c.UserConfig.Gui.ShowFileTree,
|
||||||
|
)
|
||||||
|
|
||||||
onFocus func(types.OnFocusOpts) error,
|
getDisplayStrings := func(startIdx int, length int) [][]string {
|
||||||
onRenderToMain func() error,
|
lines := presentation.RenderFileTree(viewModel, c.Modes().Diffing.Ref, c.Model().Submodules)
|
||||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
return slices.Map(lines, func(line string) []string {
|
||||||
|
return []string{line}
|
||||||
c *types.HelperCommon,
|
})
|
||||||
) *WorkingTreeContext {
|
}
|
||||||
viewModel := filetree.NewFileTreeViewModel(getModel, c.Log, c.UserConfig.Gui.ShowFileTree)
|
|
||||||
|
|
||||||
return &WorkingTreeContext{
|
return &WorkingTreeContext{
|
||||||
FileTreeViewModel: viewModel,
|
FileTreeViewModel: viewModel,
|
||||||
ListContextTrait: &ListContextTrait{
|
ListContextTrait: &ListContextTrait{
|
||||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||||
View: view,
|
View: c.Views().Files,
|
||||||
WindowName: "files",
|
WindowName: "files",
|
||||||
Key: FILES_CONTEXT_KEY,
|
Key: FILES_CONTEXT_KEY,
|
||||||
Kind: types.SIDE_CONTEXT,
|
Kind: types.SIDE_CONTEXT,
|
||||||
Focusable: true,
|
Focusable: true,
|
||||||
}), ContextCallbackOpts{
|
})),
|
||||||
OnFocus: onFocus,
|
|
||||||
OnFocusLost: onFocusLost,
|
|
||||||
OnRenderToMain: onRenderToMain,
|
|
||||||
}),
|
|
||||||
list: viewModel,
|
list: viewModel,
|
||||||
getDisplayStrings: getDisplayStrings,
|
getDisplayStrings: getDisplayStrings,
|
||||||
c: c,
|
c: c,
|
||||||
|
@ -6,273 +6,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (gui *Gui) contextTree() *context.ContextTree {
|
func (gui *Gui) contextTree() *context.ContextTree {
|
||||||
return &context.ContextTree{
|
contextCommon := &context.ContextCommon{
|
||||||
Global: context.NewSimpleContext(
|
IGuiCommon: gui.c.IGuiCommon,
|
||||||
context.NewBaseContext(context.NewBaseContextOpts{
|
Common: gui.c.Common,
|
||||||
Kind: types.GLOBAL_CONTEXT,
|
|
||||||
View: nil, // TODO: see if this breaks anything
|
|
||||||
WindowName: "",
|
|
||||||
Key: context.GLOBAL_CONTEXT_KEY,
|
|
||||||
Focusable: false,
|
|
||||||
HasUncontrolledBounds: true, // setting to true because the global context doesn't even have a view
|
|
||||||
}),
|
|
||||||
context.ContextCallbackOpts{
|
|
||||||
OnRenderToMain: gui.statusRenderToMain,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Status: context.NewSimpleContext(
|
|
||||||
context.NewBaseContext(context.NewBaseContextOpts{
|
|
||||||
Kind: types.SIDE_CONTEXT,
|
|
||||||
View: gui.Views.Status,
|
|
||||||
WindowName: "status",
|
|
||||||
Key: context.STATUS_CONTEXT_KEY,
|
|
||||||
Focusable: true,
|
|
||||||
}),
|
|
||||||
context.ContextCallbackOpts{
|
|
||||||
OnRenderToMain: gui.statusRenderToMain,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Snake: context.NewSimpleContext(
|
|
||||||
context.NewBaseContext(context.NewBaseContextOpts{
|
|
||||||
Kind: types.SIDE_CONTEXT,
|
|
||||||
View: gui.Views.Snake,
|
|
||||||
WindowName: "files",
|
|
||||||
Key: context.SNAKE_CONTEXT_KEY,
|
|
||||||
Focusable: true,
|
|
||||||
}),
|
|
||||||
context.ContextCallbackOpts{
|
|
||||||
OnFocus: func(opts types.OnFocusOpts) error {
|
|
||||||
gui.startSnake()
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
OnFocusLost: func(opts types.OnFocusLostOpts) error {
|
|
||||||
gui.snakeGame.Exit()
|
|
||||||
gui.moveToTopOfWindow(gui.State.Contexts.Submodules)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Files: gui.filesListContext(),
|
|
||||||
Submodules: gui.submodulesListContext(),
|
|
||||||
Menu: gui.menuListContext(),
|
|
||||||
Remotes: gui.remotesListContext(),
|
|
||||||
RemoteBranches: gui.remoteBranchesListContext(),
|
|
||||||
LocalCommits: gui.branchCommitsListContext(),
|
|
||||||
CommitFiles: gui.commitFilesListContext(),
|
|
||||||
ReflogCommits: gui.reflogCommitsListContext(),
|
|
||||||
SubCommits: gui.subCommitsListContext(),
|
|
||||||
Branches: gui.branchesListContext(),
|
|
||||||
Tags: gui.tagsListContext(),
|
|
||||||
Stash: gui.stashListContext(),
|
|
||||||
Suggestions: gui.suggestionsListContext(),
|
|
||||||
Normal: context.NewSimpleContext(
|
|
||||||
context.NewBaseContext(context.NewBaseContextOpts{
|
|
||||||
Kind: types.MAIN_CONTEXT,
|
|
||||||
View: gui.Views.Main,
|
|
||||||
WindowName: "main",
|
|
||||||
Key: context.NORMAL_MAIN_CONTEXT_KEY,
|
|
||||||
Focusable: false,
|
|
||||||
}),
|
|
||||||
context.ContextCallbackOpts{
|
|
||||||
OnFocus: func(opts types.OnFocusOpts) error {
|
|
||||||
return nil // TODO: should we do something here? We should allow for scrolling the panel
|
|
||||||
},
|
|
||||||
},
|
|
||||||
),
|
|
||||||
NormalSecondary: context.NewSimpleContext(
|
|
||||||
context.NewBaseContext(context.NewBaseContextOpts{
|
|
||||||
Kind: types.MAIN_CONTEXT,
|
|
||||||
View: gui.Views.Secondary,
|
|
||||||
WindowName: "secondary",
|
|
||||||
Key: context.NORMAL_SECONDARY_CONTEXT_KEY,
|
|
||||||
Focusable: false,
|
|
||||||
}),
|
|
||||||
context.ContextCallbackOpts{},
|
|
||||||
),
|
|
||||||
Staging: context.NewPatchExplorerContext(
|
|
||||||
gui.Views.Staging,
|
|
||||||
"main",
|
|
||||||
context.STAGING_MAIN_CONTEXT_KEY,
|
|
||||||
func(opts types.OnFocusOpts) error {
|
|
||||||
gui.Views.Staging.Wrap = false
|
|
||||||
gui.Views.StagingSecondary.Wrap = false
|
|
||||||
|
|
||||||
return gui.refreshStagingPanel(opts)
|
|
||||||
},
|
|
||||||
func(opts types.OnFocusLostOpts) error {
|
|
||||||
gui.State.Contexts.Staging.SetState(nil)
|
|
||||||
|
|
||||||
if opts.NewContextKey != context.STAGING_SECONDARY_CONTEXT_KEY {
|
|
||||||
gui.Views.Staging.Wrap = true
|
|
||||||
gui.Views.StagingSecondary.Wrap = true
|
|
||||||
_ = gui.State.Contexts.Staging.Render(false)
|
|
||||||
_ = gui.State.Contexts.StagingSecondary.Render(false)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
func() []int { return nil },
|
|
||||||
gui.c,
|
|
||||||
),
|
|
||||||
StagingSecondary: context.NewPatchExplorerContext(
|
|
||||||
gui.Views.StagingSecondary,
|
|
||||||
"secondary",
|
|
||||||
context.STAGING_SECONDARY_CONTEXT_KEY,
|
|
||||||
func(opts types.OnFocusOpts) error {
|
|
||||||
gui.Views.Staging.Wrap = false
|
|
||||||
gui.Views.StagingSecondary.Wrap = false
|
|
||||||
|
|
||||||
return gui.refreshStagingPanel(opts)
|
|
||||||
},
|
|
||||||
func(opts types.OnFocusLostOpts) error {
|
|
||||||
gui.State.Contexts.StagingSecondary.SetState(nil)
|
|
||||||
|
|
||||||
if opts.NewContextKey != context.STAGING_MAIN_CONTEXT_KEY {
|
|
||||||
gui.Views.Staging.Wrap = true
|
|
||||||
gui.Views.StagingSecondary.Wrap = true
|
|
||||||
_ = gui.State.Contexts.Staging.Render(false)
|
|
||||||
_ = gui.State.Contexts.StagingSecondary.Render(false)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
func() []int { return nil },
|
|
||||||
gui.c,
|
|
||||||
),
|
|
||||||
CustomPatchBuilder: context.NewPatchExplorerContext(
|
|
||||||
gui.Views.PatchBuilding,
|
|
||||||
"main",
|
|
||||||
context.PATCH_BUILDING_MAIN_CONTEXT_KEY,
|
|
||||||
func(opts types.OnFocusOpts) error {
|
|
||||||
// no need to change wrap on the secondary view because it can't be interacted with
|
|
||||||
gui.Views.PatchBuilding.Wrap = false
|
|
||||||
|
|
||||||
return gui.refreshPatchBuildingPanel(opts)
|
|
||||||
},
|
|
||||||
func(opts types.OnFocusLostOpts) error {
|
|
||||||
gui.Views.PatchBuilding.Wrap = true
|
|
||||||
|
|
||||||
if gui.git.Patch.PatchBuilder.IsEmpty() {
|
|
||||||
gui.git.Patch.PatchBuilder.Reset()
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
func() []int {
|
|
||||||
filename := gui.State.Contexts.CommitFiles.GetSelectedPath()
|
|
||||||
includedLineIndices, err := gui.git.Patch.PatchBuilder.GetFileIncLineIndices(filename)
|
|
||||||
if err != nil {
|
|
||||||
gui.Log.Error(err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return includedLineIndices
|
|
||||||
},
|
|
||||||
gui.c,
|
|
||||||
),
|
|
||||||
CustomPatchBuilderSecondary: context.NewSimpleContext(
|
|
||||||
context.NewBaseContext(context.NewBaseContextOpts{
|
|
||||||
Kind: types.MAIN_CONTEXT,
|
|
||||||
View: gui.Views.PatchBuildingSecondary,
|
|
||||||
WindowName: "secondary",
|
|
||||||
Key: context.PATCH_BUILDING_SECONDARY_CONTEXT_KEY,
|
|
||||||
Focusable: false,
|
|
||||||
}),
|
|
||||||
context.ContextCallbackOpts{},
|
|
||||||
),
|
|
||||||
MergeConflicts: context.NewMergeConflictsContext(
|
|
||||||
gui.Views.MergeConflicts,
|
|
||||||
context.ContextCallbackOpts{
|
|
||||||
OnFocus: OnFocusWrapper(func() error {
|
|
||||||
gui.Views.MergeConflicts.Wrap = false
|
|
||||||
|
|
||||||
return gui.refreshMergePanel(true)
|
|
||||||
}),
|
|
||||||
OnFocusLost: func(opts types.OnFocusLostOpts) error {
|
|
||||||
gui.State.Contexts.MergeConflicts.SetUserScrolling(false)
|
|
||||||
gui.State.Contexts.MergeConflicts.GetState().ResetConflictSelection()
|
|
||||||
gui.Views.MergeConflicts.Wrap = true
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
gui.c,
|
|
||||||
func() map[string]string {
|
|
||||||
// wrapping in a function because contexts are initialized before helpers
|
|
||||||
return gui.helpers.MergeConflicts.GetMergingOptions()
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Confirmation: context.NewSimpleContext(
|
|
||||||
context.NewBaseContext(context.NewBaseContextOpts{
|
|
||||||
Kind: types.TEMPORARY_POPUP,
|
|
||||||
View: gui.Views.Confirmation,
|
|
||||||
WindowName: "confirmation",
|
|
||||||
Key: context.CONFIRMATION_CONTEXT_KEY,
|
|
||||||
Focusable: true,
|
|
||||||
HasUncontrolledBounds: true,
|
|
||||||
}),
|
|
||||||
context.ContextCallbackOpts{
|
|
||||||
OnFocus: OnFocusWrapper(gui.handleAskFocused),
|
|
||||||
OnFocusLost: func(types.OnFocusLostOpts) error {
|
|
||||||
gui.deactivateConfirmationPrompt()
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
),
|
|
||||||
CommitMessage: context.NewCommitMessageContext(
|
|
||||||
gui.Views.CommitMessage,
|
|
||||||
context.ContextCallbackOpts{
|
|
||||||
OnFocus: OnFocusWrapper(gui.handleCommitMessageFocused),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
CommitDescription: context.NewSimpleContext(
|
|
||||||
context.NewBaseContext(context.NewBaseContextOpts{
|
|
||||||
Kind: types.PERSISTENT_POPUP,
|
|
||||||
View: gui.Views.CommitDescription,
|
|
||||||
WindowName: "commitDescription",
|
|
||||||
Key: context.COMMIT_DESCRIPTION_CONTEXT_KEY,
|
|
||||||
Focusable: true,
|
|
||||||
HasUncontrolledBounds: true,
|
|
||||||
}),
|
|
||||||
context.ContextCallbackOpts{
|
|
||||||
OnFocus: func(opts types.OnFocusOpts) error {
|
|
||||||
_, err := gui.g.SetViewBeneath("commitDescription", "commitMessage", 10)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Search: context.NewSimpleContext(
|
|
||||||
context.NewBaseContext(context.NewBaseContextOpts{
|
|
||||||
Kind: types.PERSISTENT_POPUP,
|
|
||||||
View: gui.Views.Search,
|
|
||||||
WindowName: "search",
|
|
||||||
Key: context.SEARCH_CONTEXT_KEY,
|
|
||||||
Focusable: true,
|
|
||||||
}),
|
|
||||||
context.ContextCallbackOpts{},
|
|
||||||
),
|
|
||||||
CommandLog: context.NewSimpleContext(
|
|
||||||
context.NewBaseContext(context.NewBaseContextOpts{
|
|
||||||
Kind: types.EXTRAS_CONTEXT,
|
|
||||||
View: gui.Views.Extras,
|
|
||||||
WindowName: "extras",
|
|
||||||
Key: context.COMMAND_LOG_CONTEXT_KEY,
|
|
||||||
Focusable: true,
|
|
||||||
}),
|
|
||||||
context.ContextCallbackOpts{
|
|
||||||
OnFocusLost: func(opts types.OnFocusLostOpts) error {
|
|
||||||
gui.Views.Extras.Autoscroll = true
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Options: context.NewDisplayContext(context.OPTIONS_CONTEXT_KEY, gui.Views.Options, "options"),
|
|
||||||
AppStatus: context.NewDisplayContext(context.APP_STATUS_CONTEXT_KEY, gui.Views.AppStatus, "appStatus"),
|
|
||||||
SearchPrefix: context.NewDisplayContext(context.SEARCH_PREFIX_CONTEXT_KEY, gui.Views.SearchPrefix, "searchPrefix"),
|
|
||||||
Information: context.NewDisplayContext(context.INFORMATION_CONTEXT_KEY, gui.Views.Information, "information"),
|
|
||||||
Limit: context.NewDisplayContext(context.LIMIT_CONTEXT_KEY, gui.Views.Limit, "limit"),
|
|
||||||
}
|
}
|
||||||
|
return context.NewContextTree(contextCommon)
|
||||||
}
|
}
|
||||||
|
|
||||||
// using this wrapper for when an onFocus function doesn't care about any potential
|
// using this wrapper for when an onFocus function doesn't care about any potential
|
||||||
@ -283,10 +21,10 @@ func OnFocusWrapper(f func() error) func(opts types.OnFocusOpts) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) getPatchExplorerContexts() []types.IPatchExplorerContext {
|
func (gui *Gui) defaultSideContext() types.Context {
|
||||||
return []types.IPatchExplorerContext{
|
if gui.State.Modes.Filtering.Active() {
|
||||||
gui.State.Contexts.Staging,
|
return gui.State.Contexts.LocalCommits
|
||||||
gui.State.Contexts.StagingSecondary,
|
} else {
|
||||||
gui.State.Contexts.CustomPatchBuilder,
|
return gui.State.Contexts.Files
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,24 +7,22 @@ import (
|
|||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/controllers"
|
"github.com/jesseduffield/lazygit/pkg/gui/controllers"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers"
|
"github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/modes/cherrypicking"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/services/custom_commands"
|
"github.com/jesseduffield/lazygit/pkg/gui/services/custom_commands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/snake"
|
"github.com/jesseduffield/lazygit/pkg/gui/status"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (gui *Gui) resetControllers() {
|
func (gui *Gui) Helpers() *helpers.Helpers {
|
||||||
helperCommon := gui.c
|
return gui.helpers
|
||||||
osCommand := gui.os
|
}
|
||||||
model := gui.State.Model
|
|
||||||
refsHelper := helpers.NewRefsHelper(
|
func (gui *Gui) resetHelpersAndControllers() {
|
||||||
helperCommon,
|
helperCommon := gui.c
|
||||||
gui.git,
|
refsHelper := helpers.NewRefsHelper(helperCommon)
|
||||||
gui.State.Contexts,
|
|
||||||
model,
|
rebaseHelper := helpers.NewMergeAndRebaseHelper(helperCommon, refsHelper)
|
||||||
)
|
suggestionsHelper := helpers.NewSuggestionsHelper(helperCommon)
|
||||||
|
|
||||||
rebaseHelper := helpers.NewMergeAndRebaseHelper(helperCommon, gui.State.Contexts, gui.git, refsHelper)
|
|
||||||
suggestionsHelper := helpers.NewSuggestionsHelper(helperCommon, model, gui.refreshSuggestions)
|
|
||||||
setCommitSummary := gui.getCommitMessageSetTextareaTextFn(func() *gocui.View { return gui.Views.CommitMessage })
|
setCommitSummary := gui.getCommitMessageSetTextareaTextFn(func() *gocui.View { return gui.Views.CommitMessage })
|
||||||
setCommitDescription := gui.getCommitMessageSetTextareaTextFn(func() *gocui.View { return gui.Views.CommitDescription })
|
setCommitDescription := gui.getCommitMessageSetTextareaTextFn(func() *gocui.View { return gui.Views.CommitDescription })
|
||||||
getCommitSummary := func() string {
|
getCommitSummary := func() string {
|
||||||
@ -35,66 +33,86 @@ func (gui *Gui) resetControllers() {
|
|||||||
return strings.TrimSpace(gui.Views.CommitDescription.TextArea.GetContent())
|
return strings.TrimSpace(gui.Views.CommitDescription.TextArea.GetContent())
|
||||||
}
|
}
|
||||||
commitsHelper := helpers.NewCommitsHelper(helperCommon,
|
commitsHelper := helpers.NewCommitsHelper(helperCommon,
|
||||||
gui.State.Model,
|
|
||||||
gui.State.Contexts,
|
|
||||||
getCommitSummary,
|
getCommitSummary,
|
||||||
setCommitSummary,
|
setCommitSummary,
|
||||||
getCommitDescription,
|
getCommitDescription,
|
||||||
setCommitDescription,
|
setCommitDescription,
|
||||||
gui.RenderCommitLength,
|
|
||||||
)
|
)
|
||||||
gpgHelper := helpers.NewGpgHelper(helperCommon, gui.os, gui.git)
|
|
||||||
|
gpgHelper := helpers.NewGpgHelper(helperCommon)
|
||||||
|
viewHelper := helpers.NewViewHelper(helperCommon, gui.State.Contexts)
|
||||||
|
recordDirectoryHelper := helpers.NewRecordDirectoryHelper(helperCommon)
|
||||||
|
patchBuildingHelper := helpers.NewPatchBuildingHelper(helperCommon)
|
||||||
|
stagingHelper := helpers.NewStagingHelper(helperCommon)
|
||||||
|
mergeConflictsHelper := helpers.NewMergeConflictsHelper(helperCommon)
|
||||||
|
refreshHelper := helpers.NewRefreshHelper(helperCommon, refsHelper, rebaseHelper, patchBuildingHelper, stagingHelper, mergeConflictsHelper, gui.fileWatcher)
|
||||||
|
diffHelper := helpers.NewDiffHelper(helperCommon)
|
||||||
|
cherryPickHelper := helpers.NewCherryPickHelper(
|
||||||
|
helperCommon,
|
||||||
|
rebaseHelper,
|
||||||
|
)
|
||||||
|
bisectHelper := helpers.NewBisectHelper(helperCommon)
|
||||||
|
windowHelper := helpers.NewWindowHelper(helperCommon, viewHelper)
|
||||||
|
modeHelper := helpers.NewModeHelper(
|
||||||
|
helperCommon,
|
||||||
|
diffHelper,
|
||||||
|
patchBuildingHelper,
|
||||||
|
cherryPickHelper,
|
||||||
|
rebaseHelper,
|
||||||
|
bisectHelper,
|
||||||
|
)
|
||||||
|
appStatusHelper := helpers.NewAppStatusHelper(
|
||||||
|
helperCommon,
|
||||||
|
func() *status.StatusManager { return gui.statusManager },
|
||||||
|
)
|
||||||
gui.helpers = &helpers.Helpers{
|
gui.helpers = &helpers.Helpers{
|
||||||
Refs: refsHelper,
|
Refs: refsHelper,
|
||||||
Host: helpers.NewHostHelper(helperCommon, gui.git),
|
Host: helpers.NewHostHelper(helperCommon),
|
||||||
PatchBuilding: helpers.NewPatchBuildingHelper(helperCommon, gui.git, gui.State.Contexts),
|
PatchBuilding: patchBuildingHelper,
|
||||||
Bisect: helpers.NewBisectHelper(helperCommon, gui.git),
|
Staging: stagingHelper,
|
||||||
Suggestions: suggestionsHelper,
|
Bisect: bisectHelper,
|
||||||
Files: helpers.NewFilesHelper(helperCommon, gui.git, osCommand),
|
Suggestions: suggestionsHelper,
|
||||||
WorkingTree: helpers.NewWorkingTreeHelper(helperCommon, gui.git, gui.State.Contexts, refsHelper, model, setCommitSummary, commitsHelper, gpgHelper),
|
Files: helpers.NewFilesHelper(helperCommon),
|
||||||
Tags: helpers.NewTagsHelper(helperCommon, gui.git),
|
WorkingTree: helpers.NewWorkingTreeHelper(helperCommon, refsHelper, commitsHelper, gpgHelper),
|
||||||
GPG: gpgHelper,
|
Tags: helpers.NewTagsHelper(helperCommon),
|
||||||
MergeAndRebase: rebaseHelper,
|
GPG: helpers.NewGpgHelper(helperCommon),
|
||||||
MergeConflicts: helpers.NewMergeConflictsHelper(helperCommon, gui.State.Contexts, gui.git),
|
MergeAndRebase: rebaseHelper,
|
||||||
CherryPick: helpers.NewCherryPickHelper(
|
MergeConflicts: mergeConflictsHelper,
|
||||||
helperCommon,
|
CherryPick: cherryPickHelper,
|
||||||
gui.git,
|
Upstream: helpers.NewUpstreamHelper(helperCommon, suggestionsHelper.GetRemoteBranchesSuggestionsFunc),
|
||||||
gui.State.Contexts,
|
AmendHelper: helpers.NewAmendHelper(helperCommon, gpgHelper),
|
||||||
func() *cherrypicking.CherryPicking { return gui.State.Modes.CherryPicking },
|
Commits: commitsHelper,
|
||||||
rebaseHelper,
|
Snake: helpers.NewSnakeHelper(helperCommon),
|
||||||
|
Diff: diffHelper,
|
||||||
|
Repos: helpers.NewRecentReposHelper(helperCommon, recordDirectoryHelper, gui.onNewRepo),
|
||||||
|
RecordDirectory: recordDirectoryHelper,
|
||||||
|
Update: helpers.NewUpdateHelper(helperCommon, gui.Updater),
|
||||||
|
Window: windowHelper,
|
||||||
|
View: viewHelper,
|
||||||
|
Refresh: refreshHelper,
|
||||||
|
Confirmation: helpers.NewConfirmationHelper(helperCommon),
|
||||||
|
Mode: modeHelper,
|
||||||
|
AppStatus: appStatusHelper,
|
||||||
|
WindowArrangement: helpers.NewWindowArrangementHelper(
|
||||||
|
gui.c,
|
||||||
|
windowHelper,
|
||||||
|
modeHelper,
|
||||||
|
appStatusHelper,
|
||||||
),
|
),
|
||||||
Upstream: helpers.NewUpstreamHelper(helperCommon, model, suggestionsHelper.GetRemoteBranchesSuggestionsFunc),
|
|
||||||
AmendHelper: helpers.NewAmendHelper(helperCommon, gui.git, gpgHelper),
|
|
||||||
Commits: commitsHelper,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.CustomCommandsClient = custom_commands.NewClient(
|
gui.CustomCommandsClient = custom_commands.NewClient(
|
||||||
helperCommon,
|
helperCommon,
|
||||||
gui.os,
|
|
||||||
gui.git,
|
|
||||||
gui.State.Contexts,
|
|
||||||
gui.helpers,
|
gui.helpers,
|
||||||
)
|
)
|
||||||
|
|
||||||
common := controllers.NewControllerCommon(
|
common := controllers.NewControllerCommon(helperCommon, gui)
|
||||||
helperCommon,
|
|
||||||
osCommand,
|
|
||||||
gui.git,
|
|
||||||
gui.helpers,
|
|
||||||
model,
|
|
||||||
gui.State.Contexts,
|
|
||||||
gui.State.Modes,
|
|
||||||
&gui.Mutexes,
|
|
||||||
)
|
|
||||||
|
|
||||||
syncController := controllers.NewSyncController(
|
syncController := controllers.NewSyncController(
|
||||||
common,
|
common,
|
||||||
)
|
)
|
||||||
|
|
||||||
submodulesController := controllers.NewSubmodulesController(
|
submodulesController := controllers.NewSubmodulesController(common)
|
||||||
common,
|
|
||||||
gui.enterSubmodule,
|
|
||||||
)
|
|
||||||
|
|
||||||
bisectController := controllers.NewBisectController(common)
|
bisectController := controllers.NewBisectController(common)
|
||||||
|
|
||||||
@ -113,8 +131,6 @@ func (gui *Gui) resetControllers() {
|
|||||||
tagsController := controllers.NewTagsController(common)
|
tagsController := controllers.NewTagsController(common)
|
||||||
filesController := controllers.NewFilesController(
|
filesController := controllers.NewFilesController(
|
||||||
common,
|
common,
|
||||||
gui.enterSubmodule,
|
|
||||||
setCommitSummary,
|
|
||||||
)
|
)
|
||||||
mergeConflictsController := controllers.NewMergeConflictsController(common)
|
mergeConflictsController := controllers.NewMergeConflictsController(common)
|
||||||
remotesController := controllers.NewRemotesController(
|
remotesController := controllers.NewRemotesController(
|
||||||
@ -135,7 +151,34 @@ func (gui *Gui) resetControllers() {
|
|||||||
stagingController := controllers.NewStagingController(common, gui.State.Contexts.Staging, gui.State.Contexts.StagingSecondary, false)
|
stagingController := controllers.NewStagingController(common, gui.State.Contexts.Staging, gui.State.Contexts.StagingSecondary, false)
|
||||||
stagingSecondaryController := controllers.NewStagingController(common, gui.State.Contexts.StagingSecondary, gui.State.Contexts.Staging, true)
|
stagingSecondaryController := controllers.NewStagingController(common, gui.State.Contexts.StagingSecondary, gui.State.Contexts.Staging, true)
|
||||||
patchBuildingController := controllers.NewPatchBuildingController(common)
|
patchBuildingController := controllers.NewPatchBuildingController(common)
|
||||||
snakeController := controllers.NewSnakeController(common, func() *snake.Game { return gui.snakeGame })
|
snakeController := controllers.NewSnakeController(common)
|
||||||
|
reflogCommitsController := controllers.NewReflogCommitsController(common)
|
||||||
|
subCommitsController := controllers.NewSubCommitsController(common)
|
||||||
|
statusController := controllers.NewStatusController(common)
|
||||||
|
commandLogController := controllers.NewCommandLogController(common)
|
||||||
|
confirmationController := controllers.NewConfirmationController(common)
|
||||||
|
suggestionsController := controllers.NewSuggestionsController(common)
|
||||||
|
jumpToSideWindowController := controllers.NewJumpToSideWindowController(common)
|
||||||
|
|
||||||
|
sideWindowControllerFactory := controllers.NewSideWindowControllerFactory(common)
|
||||||
|
|
||||||
|
// allow for navigating between side window contexts
|
||||||
|
for _, context := range []types.Context{
|
||||||
|
gui.State.Contexts.Status,
|
||||||
|
gui.State.Contexts.Remotes,
|
||||||
|
gui.State.Contexts.Tags,
|
||||||
|
gui.State.Contexts.Branches,
|
||||||
|
gui.State.Contexts.RemoteBranches,
|
||||||
|
gui.State.Contexts.Files,
|
||||||
|
gui.State.Contexts.Submodules,
|
||||||
|
gui.State.Contexts.ReflogCommits,
|
||||||
|
gui.State.Contexts.LocalCommits,
|
||||||
|
gui.State.Contexts.CommitFiles,
|
||||||
|
gui.State.Contexts.SubCommits,
|
||||||
|
gui.State.Contexts.Stash,
|
||||||
|
} {
|
||||||
|
controllers.AttachControllers(context, sideWindowControllerFactory.Create(context))
|
||||||
|
}
|
||||||
|
|
||||||
setSubCommits := func(commits []*models.Commit) {
|
setSubCommits := func(commits []*models.Commit) {
|
||||||
gui.Mutexes.SubCommitsMutex.Lock()
|
gui.Mutexes.SubCommitsMutex.Lock()
|
||||||
@ -161,7 +204,7 @@ func (gui *Gui) resetControllers() {
|
|||||||
gui.State.Contexts.Stash,
|
gui.State.Contexts.Stash,
|
||||||
} {
|
} {
|
||||||
controllers.AttachControllers(context, controllers.NewSwitchToDiffFilesController(
|
controllers.AttachControllers(context, controllers.NewSwitchToDiffFilesController(
|
||||||
common, gui.SwitchToCommitFilesContext, context,
|
common, context, gui.State.Contexts.CommitFiles,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,6 +216,14 @@ func (gui *Gui) resetControllers() {
|
|||||||
controllers.AttachControllers(context, controllers.NewBasicCommitsController(common, context))
|
controllers.AttachControllers(context, controllers.NewBasicCommitsController(common, context))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
controllers.AttachControllers(gui.State.Contexts.ReflogCommits,
|
||||||
|
reflogCommitsController,
|
||||||
|
)
|
||||||
|
|
||||||
|
controllers.AttachControllers(gui.State.Contexts.SubCommits,
|
||||||
|
subCommitsController,
|
||||||
|
)
|
||||||
|
|
||||||
// TODO: add scroll controllers for main panels (need to bring some more functionality across for that e.g. reading more from the currently displayed git command)
|
// TODO: add scroll controllers for main panels (need to bring some more functionality across for that e.g. reading more from the currently displayed git command)
|
||||||
controllers.AttachControllers(gui.State.Contexts.Staging,
|
controllers.AttachControllers(gui.State.Contexts.Staging,
|
||||||
stagingController,
|
stagingController,
|
||||||
@ -256,11 +307,28 @@ func (gui *Gui) resetControllers() {
|
|||||||
remoteBranchesController,
|
remoteBranchesController,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
controllers.AttachControllers(gui.State.Contexts.Status,
|
||||||
|
statusController,
|
||||||
|
)
|
||||||
|
|
||||||
|
controllers.AttachControllers(gui.State.Contexts.CommandLog,
|
||||||
|
commandLogController,
|
||||||
|
)
|
||||||
|
|
||||||
|
controllers.AttachControllers(gui.State.Contexts.Confirmation,
|
||||||
|
confirmationController,
|
||||||
|
)
|
||||||
|
|
||||||
|
controllers.AttachControllers(gui.State.Contexts.Suggestions,
|
||||||
|
suggestionsController,
|
||||||
|
)
|
||||||
|
|
||||||
controllers.AttachControllers(gui.State.Contexts.Global,
|
controllers.AttachControllers(gui.State.Contexts.Global,
|
||||||
syncController,
|
syncController,
|
||||||
undoController,
|
undoController,
|
||||||
globalController,
|
globalController,
|
||||||
contextLinesController,
|
contextLinesController,
|
||||||
|
jumpToSideWindowController,
|
||||||
)
|
)
|
||||||
|
|
||||||
controllers.AttachControllers(gui.State.Contexts.Snake,
|
controllers.AttachControllers(gui.State.Contexts.Snake,
|
||||||
@ -268,8 +336,19 @@ func (gui *Gui) resetControllers() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// this must come last so that we've got our click handlers defined against the context
|
// this must come last so that we've got our click handlers defined against the context
|
||||||
listControllerFactory := controllers.NewListControllerFactory(gui.c)
|
listControllerFactory := controllers.NewListControllerFactory(common)
|
||||||
for _, context := range gui.getListContexts() {
|
for _, context := range gui.c.Context().AllList() {
|
||||||
controllers.AttachControllers(context, listControllerFactory.Create(context))
|
controllers.AttachControllers(context, listControllerFactory.Create(context))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) getCommitMessageSetTextareaTextFn(getView func() *gocui.View) func(string) {
|
||||||
|
return func(text string) {
|
||||||
|
// using a getView function so that we don't need to worry about when the view is created
|
||||||
|
view := getView()
|
||||||
|
view.ClearTextArea()
|
||||||
|
view.TextArea.TypeString(text)
|
||||||
|
gui.helpers.Confirmation.ResizeCommitMessagePanels()
|
||||||
|
view.RenderTextArea()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -7,5 +7,8 @@ func AttachControllers(context types.Context, controllers ...types.IController)
|
|||||||
context.AddKeybindingsFn(controller.GetKeybindings)
|
context.AddKeybindingsFn(controller.GetKeybindings)
|
||||||
context.AddMouseKeybindingsFn(controller.GetMouseKeybindings)
|
context.AddMouseKeybindingsFn(controller.GetMouseKeybindings)
|
||||||
context.AddOnClickFn(controller.GetOnClick())
|
context.AddOnClickFn(controller.GetOnClick())
|
||||||
|
context.AddOnRenderToMainFn(controller.GetOnRenderToMain())
|
||||||
|
context.AddOnFocusFn(controller.GetOnFocus())
|
||||||
|
context.AddOnFocusLostFn(controller.GetOnFocusLost())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,3 +18,15 @@ func (self *baseController) GetMouseKeybindings(opts types.KeybindingsOpts) []*g
|
|||||||
func (self *baseController) GetOnClick() func() error {
|
func (self *baseController) GetOnClick() func() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *baseController) GetOnRenderToMain() func() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *baseController) GetOnFocus() func(types.OnFocusOpts) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *baseController) GetOnFocusLost() func(types.OnFocusLostOpts) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -20,15 +20,15 @@ type ContainsCommits interface {
|
|||||||
|
|
||||||
type BasicCommitsController struct {
|
type BasicCommitsController struct {
|
||||||
baseController
|
baseController
|
||||||
*controllerCommon
|
c *ControllerCommon
|
||||||
context ContainsCommits
|
context ContainsCommits
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBasicCommitsController(controllerCommon *controllerCommon, context ContainsCommits) *BasicCommitsController {
|
func NewBasicCommitsController(controllerCommon *ControllerCommon, context ContainsCommits) *BasicCommitsController {
|
||||||
return &BasicCommitsController{
|
return &BasicCommitsController{
|
||||||
baseController: baseController{},
|
baseController: baseController{},
|
||||||
controllerCommon: controllerCommon,
|
c: controllerCommon,
|
||||||
context: context,
|
context: context,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ func (self *BasicCommitsController) GetKeybindings(opts types.KeybindingsOpts) [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: opts.GetKey(opts.Config.Commits.ResetCherryPick),
|
Key: opts.GetKey(opts.Config.Commits.ResetCherryPick),
|
||||||
Handler: self.helpers.CherryPick.Reset,
|
Handler: self.c.Helpers().CherryPick.Reset,
|
||||||
Description: self.c.Tr.LcResetCherryPick,
|
Description: self.c.Tr.LcResetCherryPick,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -141,7 +141,7 @@ func (self *BasicCommitsController) copyCommitAttribute(commit *models.Commit) e
|
|||||||
|
|
||||||
func (self *BasicCommitsController) copyCommitSHAToClipboard(commit *models.Commit) error {
|
func (self *BasicCommitsController) copyCommitSHAToClipboard(commit *models.Commit) error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.CopyCommitSHAToClipboard)
|
self.c.LogAction(self.c.Tr.Actions.CopyCommitSHAToClipboard)
|
||||||
if err := self.os.CopyToClipboard(commit.Sha); err != nil {
|
if err := self.c.OS().CopyToClipboard(commit.Sha); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,13 +150,13 @@ func (self *BasicCommitsController) copyCommitSHAToClipboard(commit *models.Comm
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *BasicCommitsController) copyCommitURLToClipboard(commit *models.Commit) error {
|
func (self *BasicCommitsController) copyCommitURLToClipboard(commit *models.Commit) error {
|
||||||
url, err := self.helpers.Host.GetCommitURL(commit.Sha)
|
url, err := self.c.Helpers().Host.GetCommitURL(commit.Sha)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
self.c.LogAction(self.c.Tr.Actions.CopyCommitURLToClipboard)
|
self.c.LogAction(self.c.Tr.Actions.CopyCommitURLToClipboard)
|
||||||
if err := self.os.CopyToClipboard(url); err != nil {
|
if err := self.c.OS().CopyToClipboard(url); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,13 +165,13 @@ func (self *BasicCommitsController) copyCommitURLToClipboard(commit *models.Comm
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *BasicCommitsController) copyCommitDiffToClipboard(commit *models.Commit) error {
|
func (self *BasicCommitsController) copyCommitDiffToClipboard(commit *models.Commit) error {
|
||||||
diff, err := self.git.Commit.GetCommitDiff(commit.Sha)
|
diff, err := self.c.Git().Commit.GetCommitDiff(commit.Sha)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.c.LogAction(self.c.Tr.Actions.CopyCommitDiffToClipboard)
|
self.c.LogAction(self.c.Tr.Actions.CopyCommitDiffToClipboard)
|
||||||
if err := self.os.CopyToClipboard(diff); err != nil {
|
if err := self.c.OS().CopyToClipboard(diff); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,7 +180,7 @@ func (self *BasicCommitsController) copyCommitDiffToClipboard(commit *models.Com
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *BasicCommitsController) copyAuthorToClipboard(commit *models.Commit) error {
|
func (self *BasicCommitsController) copyAuthorToClipboard(commit *models.Commit) error {
|
||||||
author, err := self.git.Commit.GetCommitAuthor(commit.Sha)
|
author, err := self.c.Git().Commit.GetCommitAuthor(commit.Sha)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
@ -188,7 +188,7 @@ func (self *BasicCommitsController) copyAuthorToClipboard(commit *models.Commit)
|
|||||||
formattedAuthor := fmt.Sprintf("%s <%s>", author.Name, author.Email)
|
formattedAuthor := fmt.Sprintf("%s <%s>", author.Name, author.Email)
|
||||||
|
|
||||||
self.c.LogAction(self.c.Tr.Actions.CopyCommitAuthorToClipboard)
|
self.c.LogAction(self.c.Tr.Actions.CopyCommitAuthorToClipboard)
|
||||||
if err := self.os.CopyToClipboard(formattedAuthor); err != nil {
|
if err := self.c.OS().CopyToClipboard(formattedAuthor); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,13 +197,13 @@ func (self *BasicCommitsController) copyAuthorToClipboard(commit *models.Commit)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *BasicCommitsController) copyCommitMessageToClipboard(commit *models.Commit) error {
|
func (self *BasicCommitsController) copyCommitMessageToClipboard(commit *models.Commit) error {
|
||||||
message, err := self.git.Commit.GetCommitMessage(commit.Sha)
|
message, err := self.c.Git().Commit.GetCommitMessage(commit.Sha)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.c.LogAction(self.c.Tr.Actions.CopyCommitMessageToClipboard)
|
self.c.LogAction(self.c.Tr.Actions.CopyCommitMessageToClipboard)
|
||||||
if err := self.os.CopyToClipboard(message); err != nil {
|
if err := self.c.OS().CopyToClipboard(message); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,13 +212,13 @@ func (self *BasicCommitsController) copyCommitMessageToClipboard(commit *models.
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *BasicCommitsController) openInBrowser(commit *models.Commit) error {
|
func (self *BasicCommitsController) openInBrowser(commit *models.Commit) error {
|
||||||
url, err := self.helpers.Host.GetCommitURL(commit.Sha)
|
url, err := self.c.Helpers().Host.GetCommitURL(commit.Sha)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.c.LogAction(self.c.Tr.Actions.OpenCommitInBrowser)
|
self.c.LogAction(self.c.Tr.Actions.OpenCommitInBrowser)
|
||||||
if err := self.os.OpenLink(url); err != nil {
|
if err := self.c.OS().OpenLink(url); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,11 +226,11 @@ func (self *BasicCommitsController) openInBrowser(commit *models.Commit) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *BasicCommitsController) newBranch(commit *models.Commit) error {
|
func (self *BasicCommitsController) newBranch(commit *models.Commit) error {
|
||||||
return self.helpers.Refs.NewBranch(commit.RefName(), commit.Description(), "")
|
return self.c.Helpers().Refs.NewBranch(commit.RefName(), commit.Description(), "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *BasicCommitsController) createResetMenu(commit *models.Commit) error {
|
func (self *BasicCommitsController) createResetMenu(commit *models.Commit) error {
|
||||||
return self.helpers.Refs.CreateGitResetMenu(commit.Sha)
|
return self.c.Helpers().Refs.CreateGitResetMenu(commit.Sha)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *BasicCommitsController) checkout(commit *models.Commit) error {
|
func (self *BasicCommitsController) checkout(commit *models.Commit) error {
|
||||||
@ -239,15 +239,15 @@ func (self *BasicCommitsController) checkout(commit *models.Commit) error {
|
|||||||
Prompt: self.c.Tr.SureCheckoutThisCommit,
|
Prompt: self.c.Tr.SureCheckoutThisCommit,
|
||||||
HandleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.CheckoutCommit)
|
self.c.LogAction(self.c.Tr.Actions.CheckoutCommit)
|
||||||
return self.helpers.Refs.CheckoutRef(commit.Sha, types.CheckoutRefOptions{})
|
return self.c.Helpers().Refs.CheckoutRef(commit.Sha, types.CheckoutRefOptions{})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *BasicCommitsController) copy(commit *models.Commit) error {
|
func (self *BasicCommitsController) copy(commit *models.Commit) error {
|
||||||
return self.helpers.CherryPick.Copy(commit, self.context.GetCommits(), self.context)
|
return self.c.Helpers().CherryPick.Copy(commit, self.context.GetCommits(), self.context)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *BasicCommitsController) copyRange(*models.Commit) error {
|
func (self *BasicCommitsController) copyRange(*models.Commit) error {
|
||||||
return self.helpers.CherryPick.CopyRange(self.context.GetSelectedLineIdx(), self.context.GetCommits(), self.context)
|
return self.c.Helpers().CherryPick.CopyRange(self.context.GetSelectedLineIdx(), self.context.GetCommits(), self.context)
|
||||||
}
|
}
|
||||||
|
@ -12,17 +12,17 @@ import (
|
|||||||
|
|
||||||
type BisectController struct {
|
type BisectController struct {
|
||||||
baseController
|
baseController
|
||||||
*controllerCommon
|
c *ControllerCommon
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ types.IController = &BisectController{}
|
var _ types.IController = &BisectController{}
|
||||||
|
|
||||||
func NewBisectController(
|
func NewBisectController(
|
||||||
common *controllerCommon,
|
common *ControllerCommon,
|
||||||
) *BisectController {
|
) *BisectController {
|
||||||
return &BisectController{
|
return &BisectController{
|
||||||
baseController: baseController{},
|
baseController: baseController{},
|
||||||
controllerCommon: common,
|
c: common,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ func (self *BisectController) GetKeybindings(opts types.KeybindingsOpts) []*type
|
|||||||
func (self *BisectController) openMenu(commit *models.Commit) error {
|
func (self *BisectController) openMenu(commit *models.Commit) error {
|
||||||
// no shame in getting this directly rather than using the cached value
|
// no shame in getting this directly rather than using the cached value
|
||||||
// given how cheap it is to obtain
|
// given how cheap it is to obtain
|
||||||
info := self.git.Bisect.GetInfo()
|
info := self.c.Git().Bisect.GetInfo()
|
||||||
if info.Started() {
|
if info.Started() {
|
||||||
return self.openMidBisectMenu(info, commit)
|
return self.openMidBisectMenu(info, commit)
|
||||||
} else {
|
} else {
|
||||||
@ -63,14 +63,14 @@ func (self *BisectController) openMidBisectMenu(info *git_commands.BisectInfo, c
|
|||||||
selectCurrentAfter := info.GetCurrentSha() == "" || info.GetCurrentSha() == commit.Sha
|
selectCurrentAfter := info.GetCurrentSha() == "" || info.GetCurrentSha() == commit.Sha
|
||||||
// we need to wait to reselect if our bisect commits aren't ancestors of our 'start'
|
// we need to wait to reselect if our bisect commits aren't ancestors of our 'start'
|
||||||
// ref, because we'll be reloading our commits in that case.
|
// ref, because we'll be reloading our commits in that case.
|
||||||
waitToReselect := selectCurrentAfter && !self.git.Bisect.ReachableFromStart(info)
|
waitToReselect := selectCurrentAfter && !self.c.Git().Bisect.ReachableFromStart(info)
|
||||||
|
|
||||||
menuItems := []*types.MenuItem{
|
menuItems := []*types.MenuItem{
|
||||||
{
|
{
|
||||||
Label: fmt.Sprintf(self.c.Tr.Bisect.Mark, commit.ShortSha(), info.NewTerm()),
|
Label: fmt.Sprintf(self.c.Tr.Bisect.Mark, commit.ShortSha(), info.NewTerm()),
|
||||||
OnPress: func() error {
|
OnPress: func() error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.BisectMark)
|
self.c.LogAction(self.c.Tr.Actions.BisectMark)
|
||||||
if err := self.git.Bisect.Mark(commit.Sha, info.NewTerm()); err != nil {
|
if err := self.c.Git().Bisect.Mark(commit.Sha, info.NewTerm()); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,7 +82,7 @@ func (self *BisectController) openMidBisectMenu(info *git_commands.BisectInfo, c
|
|||||||
Label: fmt.Sprintf(self.c.Tr.Bisect.Mark, commit.ShortSha(), info.OldTerm()),
|
Label: fmt.Sprintf(self.c.Tr.Bisect.Mark, commit.ShortSha(), info.OldTerm()),
|
||||||
OnPress: func() error {
|
OnPress: func() error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.BisectMark)
|
self.c.LogAction(self.c.Tr.Actions.BisectMark)
|
||||||
if err := self.git.Bisect.Mark(commit.Sha, info.OldTerm()); err != nil {
|
if err := self.c.Git().Bisect.Mark(commit.Sha, info.OldTerm()); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,7 +94,7 @@ func (self *BisectController) openMidBisectMenu(info *git_commands.BisectInfo, c
|
|||||||
Label: fmt.Sprintf(self.c.Tr.Bisect.Skip, commit.ShortSha()),
|
Label: fmt.Sprintf(self.c.Tr.Bisect.Skip, commit.ShortSha()),
|
||||||
OnPress: func() error {
|
OnPress: func() error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.BisectSkip)
|
self.c.LogAction(self.c.Tr.Actions.BisectSkip)
|
||||||
if err := self.git.Bisect.Skip(commit.Sha); err != nil {
|
if err := self.c.Git().Bisect.Skip(commit.Sha); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,7 +105,7 @@ func (self *BisectController) openMidBisectMenu(info *git_commands.BisectInfo, c
|
|||||||
{
|
{
|
||||||
Label: self.c.Tr.Bisect.ResetOption,
|
Label: self.c.Tr.Bisect.ResetOption,
|
||||||
OnPress: func() error {
|
OnPress: func() error {
|
||||||
return self.helpers.Bisect.Reset()
|
return self.c.Helpers().Bisect.Reset()
|
||||||
},
|
},
|
||||||
Key: 'r',
|
Key: 'r',
|
||||||
},
|
},
|
||||||
@ -125,15 +125,15 @@ func (self *BisectController) openStartBisectMenu(info *git_commands.BisectInfo,
|
|||||||
Label: fmt.Sprintf(self.c.Tr.Bisect.MarkStart, commit.ShortSha(), info.NewTerm()),
|
Label: fmt.Sprintf(self.c.Tr.Bisect.MarkStart, commit.ShortSha(), info.NewTerm()),
|
||||||
OnPress: func() error {
|
OnPress: func() error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.StartBisect)
|
self.c.LogAction(self.c.Tr.Actions.StartBisect)
|
||||||
if err := self.git.Bisect.Start(); err != nil {
|
if err := self.c.Git().Bisect.Start(); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := self.git.Bisect.Mark(commit.Sha, info.NewTerm()); err != nil {
|
if err := self.c.Git().Bisect.Mark(commit.Sha, info.NewTerm()); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.helpers.Bisect.PostBisectCommandRefresh()
|
return self.c.Helpers().Bisect.PostBisectCommandRefresh()
|
||||||
},
|
},
|
||||||
Key: 'b',
|
Key: 'b',
|
||||||
},
|
},
|
||||||
@ -141,15 +141,15 @@ func (self *BisectController) openStartBisectMenu(info *git_commands.BisectInfo,
|
|||||||
Label: fmt.Sprintf(self.c.Tr.Bisect.MarkStart, commit.ShortSha(), info.OldTerm()),
|
Label: fmt.Sprintf(self.c.Tr.Bisect.MarkStart, commit.ShortSha(), info.OldTerm()),
|
||||||
OnPress: func() error {
|
OnPress: func() error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.StartBisect)
|
self.c.LogAction(self.c.Tr.Actions.StartBisect)
|
||||||
if err := self.git.Bisect.Start(); err != nil {
|
if err := self.c.Git().Bisect.Start(); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := self.git.Bisect.Mark(commit.Sha, info.OldTerm()); err != nil {
|
if err := self.c.Git().Bisect.Mark(commit.Sha, info.OldTerm()); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.helpers.Bisect.PostBisectCommandRefresh()
|
return self.c.Helpers().Bisect.PostBisectCommandRefresh()
|
||||||
},
|
},
|
||||||
Key: 'g',
|
Key: 'g',
|
||||||
},
|
},
|
||||||
@ -163,7 +163,7 @@ func (self *BisectController) showBisectCompleteMessage(candidateShas []string)
|
|||||||
prompt = self.c.Tr.Bisect.CompletePromptIndeterminate
|
prompt = self.c.Tr.Bisect.CompletePromptIndeterminate
|
||||||
}
|
}
|
||||||
|
|
||||||
formattedCommits, err := self.git.Commit.GetCommitsOneline(candidateShas)
|
formattedCommits, err := self.c.Git().Commit.GetCommitsOneline(candidateShas)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
@ -173,17 +173,17 @@ func (self *BisectController) showBisectCompleteMessage(candidateShas []string)
|
|||||||
Prompt: fmt.Sprintf(prompt, strings.TrimSpace(formattedCommits)),
|
Prompt: fmt.Sprintf(prompt, strings.TrimSpace(formattedCommits)),
|
||||||
HandleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.ResetBisect)
|
self.c.LogAction(self.c.Tr.Actions.ResetBisect)
|
||||||
if err := self.git.Bisect.Reset(); err != nil {
|
if err := self.c.Git().Bisect.Reset(); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.helpers.Bisect.PostBisectCommandRefresh()
|
return self.c.Helpers().Bisect.PostBisectCommandRefresh()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *BisectController) afterMark(selectCurrent bool, waitToReselect bool) error {
|
func (self *BisectController) afterMark(selectCurrent bool, waitToReselect bool) error {
|
||||||
done, candidateShas, err := self.git.Bisect.IsDone()
|
done, candidateShas, err := self.c.Git().Bisect.IsDone()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
@ -211,15 +211,15 @@ func (self *BisectController) afterBisectMarkRefresh(selectCurrent bool, waitToR
|
|||||||
} else {
|
} else {
|
||||||
selectFn()
|
selectFn()
|
||||||
|
|
||||||
return self.helpers.Bisect.PostBisectCommandRefresh()
|
return self.c.Helpers().Bisect.PostBisectCommandRefresh()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *BisectController) selectCurrentBisectCommit() {
|
func (self *BisectController) selectCurrentBisectCommit() {
|
||||||
info := self.git.Bisect.GetInfo()
|
info := self.c.Git().Bisect.GetInfo()
|
||||||
if info.GetCurrentSha() != "" {
|
if info.GetCurrentSha() != "" {
|
||||||
// find index of commit with that sha, move cursor to that.
|
// find index of commit with that sha, move cursor to that.
|
||||||
for i, commit := range self.model.Commits {
|
for i, commit := range self.c.Model().Commits {
|
||||||
if commit.Sha == info.GetCurrentSha() {
|
if commit.Sha == info.GetCurrentSha() {
|
||||||
self.context().SetSelectedLineIdx(i)
|
self.context().SetSelectedLineIdx(i)
|
||||||
_ = self.context().HandleFocus(types.OnFocusOpts{})
|
_ = self.context().HandleFocus(types.OnFocusOpts{})
|
||||||
@ -245,5 +245,5 @@ func (self *BisectController) Context() types.Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *BisectController) context() *context.LocalCommitsContext {
|
func (self *BisectController) context() *context.LocalCommitsContext {
|
||||||
return self.contexts.LocalCommits
|
return self.c.Contexts().LocalCommits
|
||||||
}
|
}
|
||||||
|
@ -14,17 +14,17 @@ import (
|
|||||||
|
|
||||||
type BranchesController struct {
|
type BranchesController struct {
|
||||||
baseController
|
baseController
|
||||||
*controllerCommon
|
c *ControllerCommon
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ types.IController = &BranchesController{}
|
var _ types.IController = &BranchesController{}
|
||||||
|
|
||||||
func NewBranchesController(
|
func NewBranchesController(
|
||||||
common *controllerCommon,
|
common *ControllerCommon,
|
||||||
) *BranchesController {
|
) *BranchesController {
|
||||||
return &BranchesController{
|
return &BranchesController{
|
||||||
baseController: baseController{},
|
baseController: baseController{},
|
||||||
controllerCommon: common,
|
c: common,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,6 +111,30 @@ func (self *BranchesController) GetKeybindings(opts types.KeybindingsOpts) []*ty
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *BranchesController) GetOnRenderToMain() func() error {
|
||||||
|
return func() error {
|
||||||
|
return self.c.Helpers().Diff.WithDiffModeCheck(func() error {
|
||||||
|
var task types.UpdateTask
|
||||||
|
branch := self.context().GetSelected()
|
||||||
|
if branch == nil {
|
||||||
|
task = types.NewRenderStringTask(self.c.Tr.NoBranchesThisRepo)
|
||||||
|
} else {
|
||||||
|
cmdObj := self.c.Git().Branch.GetGraphCmdObj(branch.FullRefName())
|
||||||
|
|
||||||
|
task = types.NewRunPtyTask(cmdObj.GetCmd())
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.c.RenderToMainViews(types.RefreshMainOpts{
|
||||||
|
Pair: self.c.MainViewPairs().Normal,
|
||||||
|
Main: &types.ViewUpdateOpts{
|
||||||
|
Title: self.c.Tr.LogTitle,
|
||||||
|
Task: task,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (self *BranchesController) setUpstream(selectedBranch *models.Branch) error {
|
func (self *BranchesController) setUpstream(selectedBranch *models.Branch) error {
|
||||||
return self.c.Menu(types.CreateMenuOptions{
|
return self.c.Menu(types.CreateMenuOptions{
|
||||||
Title: self.c.Tr.Actions.SetUnsetUpstream,
|
Title: self.c.Tr.Actions.SetUnsetUpstream,
|
||||||
@ -118,7 +142,7 @@ func (self *BranchesController) setUpstream(selectedBranch *models.Branch) error
|
|||||||
{
|
{
|
||||||
LabelColumns: []string{self.c.Tr.LcUnsetUpstream},
|
LabelColumns: []string{self.c.Tr.LcUnsetUpstream},
|
||||||
OnPress: func() error {
|
OnPress: func() error {
|
||||||
if err := self.git.Branch.UnsetUpstream(selectedBranch.Name); err != nil {
|
if err := self.c.Git().Branch.UnsetUpstream(selectedBranch.Name); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
if err := self.c.Refresh(types.RefreshOptions{
|
if err := self.c.Refresh(types.RefreshOptions{
|
||||||
@ -137,13 +161,13 @@ func (self *BranchesController) setUpstream(selectedBranch *models.Branch) error
|
|||||||
{
|
{
|
||||||
LabelColumns: []string{self.c.Tr.LcSetUpstream},
|
LabelColumns: []string{self.c.Tr.LcSetUpstream},
|
||||||
OnPress: func() error {
|
OnPress: func() error {
|
||||||
return self.helpers.Upstream.PromptForUpstreamWithoutInitialContent(selectedBranch, func(upstream string) error {
|
return self.c.Helpers().Upstream.PromptForUpstreamWithoutInitialContent(selectedBranch, func(upstream string) error {
|
||||||
upstreamRemote, upstreamBranch, err := self.helpers.Upstream.ParseUpstream(upstream)
|
upstreamRemote, upstreamBranch, err := self.c.Helpers().Upstream.ParseUpstream(upstream)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := self.git.Branch.SetUpstream(upstreamRemote, upstreamBranch, selectedBranch.Name); err != nil {
|
if err := self.c.Git().Branch.SetUpstream(upstreamRemote, upstreamBranch, selectedBranch.Name); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
if err := self.c.Refresh(types.RefreshOptions{
|
if err := self.c.Refresh(types.RefreshOptions{
|
||||||
@ -169,16 +193,16 @@ func (self *BranchesController) Context() types.Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *BranchesController) context() *context.BranchesContext {
|
func (self *BranchesController) context() *context.BranchesContext {
|
||||||
return self.contexts.Branches
|
return self.c.Contexts().Branches
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *BranchesController) press(selectedBranch *models.Branch) error {
|
func (self *BranchesController) press(selectedBranch *models.Branch) error {
|
||||||
if selectedBranch == self.helpers.Refs.GetCheckedOutRef() {
|
if selectedBranch == self.c.Helpers().Refs.GetCheckedOutRef() {
|
||||||
return self.c.ErrorMsg(self.c.Tr.AlreadyCheckedOutBranch)
|
return self.c.ErrorMsg(self.c.Tr.AlreadyCheckedOutBranch)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.c.LogAction(self.c.Tr.Actions.CheckoutBranch)
|
self.c.LogAction(self.c.Tr.Actions.CheckoutBranch)
|
||||||
return self.helpers.Refs.CheckoutRef(selectedBranch.Name, types.CheckoutRefOptions{})
|
return self.c.Helpers().Refs.CheckoutRef(selectedBranch.Name, types.CheckoutRefOptions{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *BranchesController) handleCreatePullRequest(selectedBranch *models.Branch) error {
|
func (self *BranchesController) handleCreatePullRequest(selectedBranch *models.Branch) error {
|
||||||
@ -186,7 +210,7 @@ func (self *BranchesController) handleCreatePullRequest(selectedBranch *models.B
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *BranchesController) handleCreatePullRequestMenu(selectedBranch *models.Branch) error {
|
func (self *BranchesController) handleCreatePullRequestMenu(selectedBranch *models.Branch) error {
|
||||||
checkedOutBranch := self.helpers.Refs.GetCheckedOutRef()
|
checkedOutBranch := self.c.Helpers().Refs.GetCheckedOutRef()
|
||||||
|
|
||||||
return self.createPullRequestMenu(selectedBranch, checkedOutBranch)
|
return self.createPullRequestMenu(selectedBranch, checkedOutBranch)
|
||||||
}
|
}
|
||||||
@ -194,18 +218,18 @@ func (self *BranchesController) handleCreatePullRequestMenu(selectedBranch *mode
|
|||||||
func (self *BranchesController) copyPullRequestURL() error {
|
func (self *BranchesController) copyPullRequestURL() error {
|
||||||
branch := self.context().GetSelected()
|
branch := self.context().GetSelected()
|
||||||
|
|
||||||
branchExistsOnRemote := self.git.Remote.CheckRemoteBranchExists(branch.Name)
|
branchExistsOnRemote := self.c.Git().Remote.CheckRemoteBranchExists(branch.Name)
|
||||||
|
|
||||||
if !branchExistsOnRemote {
|
if !branchExistsOnRemote {
|
||||||
return self.c.Error(errors.New(self.c.Tr.NoBranchOnRemote))
|
return self.c.Error(errors.New(self.c.Tr.NoBranchOnRemote))
|
||||||
}
|
}
|
||||||
|
|
||||||
url, err := self.helpers.Host.GetPullRequestURL(branch.Name, "")
|
url, err := self.c.Helpers().Host.GetPullRequestURL(branch.Name, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
self.c.LogAction(self.c.Tr.Actions.CopyPullRequestURL)
|
self.c.LogAction(self.c.Tr.Actions.CopyPullRequestURL)
|
||||||
if err := self.os.CopyToClipboard(url); err != nil {
|
if err := self.c.OS().CopyToClipboard(url); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,7 +248,7 @@ func (self *BranchesController) forceCheckout() error {
|
|||||||
Prompt: message,
|
Prompt: message,
|
||||||
HandleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.ForceCheckoutBranch)
|
self.c.LogAction(self.c.Tr.Actions.ForceCheckoutBranch)
|
||||||
if err := self.git.Branch.Checkout(branch.Name, git_commands.CheckoutOptions{Force: true}); err != nil {
|
if err := self.c.Git().Branch.Checkout(branch.Name, git_commands.CheckoutOptions{Force: true}); err != nil {
|
||||||
_ = self.c.Error(err)
|
_ = self.c.Error(err)
|
||||||
}
|
}
|
||||||
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||||
@ -235,10 +259,10 @@ func (self *BranchesController) forceCheckout() error {
|
|||||||
func (self *BranchesController) checkoutByName() error {
|
func (self *BranchesController) checkoutByName() error {
|
||||||
return self.c.Prompt(types.PromptOpts{
|
return self.c.Prompt(types.PromptOpts{
|
||||||
Title: self.c.Tr.BranchName + ":",
|
Title: self.c.Tr.BranchName + ":",
|
||||||
FindSuggestionsFunc: self.helpers.Suggestions.GetRefsSuggestionsFunc(),
|
FindSuggestionsFunc: self.c.Helpers().Suggestions.GetRefsSuggestionsFunc(),
|
||||||
HandleConfirm: func(response string) error {
|
HandleConfirm: func(response string) error {
|
||||||
self.c.LogAction("Checkout branch")
|
self.c.LogAction("Checkout branch")
|
||||||
return self.helpers.Refs.CheckoutRef(response, types.CheckoutRefOptions{
|
return self.c.Helpers().Refs.CheckoutRef(response, types.CheckoutRefOptions{
|
||||||
OnRefNotFound: func(ref string) error {
|
OnRefNotFound: func(ref string) error {
|
||||||
return self.c.Confirm(types.ConfirmOpts{
|
return self.c.Confirm(types.ConfirmOpts{
|
||||||
Title: self.c.Tr.BranchNotFoundTitle,
|
Title: self.c.Tr.BranchNotFoundTitle,
|
||||||
@ -260,7 +284,7 @@ func (self *BranchesController) createNewBranchWithName(newBranchName string) er
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := self.git.Branch.New(newBranchName, branch.FullRefName()); err != nil {
|
if err := self.c.Git().Branch.New(newBranchName, branch.FullRefName()); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,7 +293,7 @@ func (self *BranchesController) createNewBranchWithName(newBranchName string) er
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *BranchesController) delete(branch *models.Branch) error {
|
func (self *BranchesController) delete(branch *models.Branch) error {
|
||||||
checkedOutBranch := self.helpers.Refs.GetCheckedOutRef()
|
checkedOutBranch := self.c.Helpers().Refs.GetCheckedOutRef()
|
||||||
if checkedOutBranch.Name == branch.Name {
|
if checkedOutBranch.Name == branch.Name {
|
||||||
return self.c.ErrorMsg(self.c.Tr.CantDeleteCheckOutBranch)
|
return self.c.ErrorMsg(self.c.Tr.CantDeleteCheckOutBranch)
|
||||||
}
|
}
|
||||||
@ -296,7 +320,7 @@ func (self *BranchesController) deleteWithForce(selectedBranch *models.Branch, f
|
|||||||
Prompt: message,
|
Prompt: message,
|
||||||
HandleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.DeleteBranch)
|
self.c.LogAction(self.c.Tr.Actions.DeleteBranch)
|
||||||
if err := self.git.Branch.Delete(selectedBranch.Name, force); err != nil {
|
if err := self.c.Git().Branch.Delete(selectedBranch.Name, force); err != nil {
|
||||||
errMessage := err.Error()
|
errMessage := err.Error()
|
||||||
if !force && strings.Contains(errMessage, "git branch -D ") {
|
if !force && strings.Contains(errMessage, "git branch -D ") {
|
||||||
return self.deleteWithForce(selectedBranch, true)
|
return self.deleteWithForce(selectedBranch, true)
|
||||||
@ -310,12 +334,12 @@ func (self *BranchesController) deleteWithForce(selectedBranch *models.Branch, f
|
|||||||
|
|
||||||
func (self *BranchesController) merge() error {
|
func (self *BranchesController) merge() error {
|
||||||
selectedBranchName := self.context().GetSelected().Name
|
selectedBranchName := self.context().GetSelected().Name
|
||||||
return self.helpers.MergeAndRebase.MergeRefIntoCheckedOutBranch(selectedBranchName)
|
return self.c.Helpers().MergeAndRebase.MergeRefIntoCheckedOutBranch(selectedBranchName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *BranchesController) rebase() error {
|
func (self *BranchesController) rebase() error {
|
||||||
selectedBranchName := self.context().GetSelected().Name
|
selectedBranchName := self.context().GetSelected().Name
|
||||||
return self.helpers.MergeAndRebase.RebaseOntoRef(selectedBranchName)
|
return self.c.Helpers().MergeAndRebase.RebaseOntoRef(selectedBranchName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *BranchesController) fastForward(branch *models.Branch) error {
|
func (self *BranchesController) fastForward(branch *models.Branch) error {
|
||||||
@ -340,10 +364,10 @@ func (self *BranchesController) fastForward(branch *models.Branch) error {
|
|||||||
)
|
)
|
||||||
|
|
||||||
return self.c.WithLoaderPanel(message, func() error {
|
return self.c.WithLoaderPanel(message, func() error {
|
||||||
if branch == self.helpers.Refs.GetCheckedOutRef() {
|
if branch == self.c.Helpers().Refs.GetCheckedOutRef() {
|
||||||
self.c.LogAction(action)
|
self.c.LogAction(action)
|
||||||
|
|
||||||
err := self.git.Sync.Pull(
|
err := self.c.Git().Sync.Pull(
|
||||||
git_commands.PullOptions{
|
git_commands.PullOptions{
|
||||||
RemoteName: branch.UpstreamRemote,
|
RemoteName: branch.UpstreamRemote,
|
||||||
BranchName: branch.UpstreamBranch,
|
BranchName: branch.UpstreamBranch,
|
||||||
@ -357,7 +381,7 @@ func (self *BranchesController) fastForward(branch *models.Branch) error {
|
|||||||
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||||
} else {
|
} else {
|
||||||
self.c.LogAction(action)
|
self.c.LogAction(action)
|
||||||
err := self.git.Sync.FastForward(branch.Name, branch.UpstreamRemote, branch.UpstreamBranch)
|
err := self.c.Git().Sync.FastForward(branch.Name, branch.UpstreamRemote, branch.UpstreamBranch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = self.c.Error(err)
|
_ = self.c.Error(err)
|
||||||
}
|
}
|
||||||
@ -369,11 +393,11 @@ func (self *BranchesController) fastForward(branch *models.Branch) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *BranchesController) createTag(branch *models.Branch) error {
|
func (self *BranchesController) createTag(branch *models.Branch) error {
|
||||||
return self.helpers.Tags.CreateTagMenu(branch.FullRefName(), func() {})
|
return self.c.Helpers().Tags.CreateTagMenu(branch.FullRefName(), func() {})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *BranchesController) createResetMenu(selectedBranch *models.Branch) error {
|
func (self *BranchesController) createResetMenu(selectedBranch *models.Branch) error {
|
||||||
return self.helpers.Refs.CreateGitResetMenu(selectedBranch.Name)
|
return self.c.Helpers().Refs.CreateGitResetMenu(selectedBranch.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *BranchesController) rename(branch *models.Branch) error {
|
func (self *BranchesController) rename(branch *models.Branch) error {
|
||||||
@ -383,7 +407,7 @@ func (self *BranchesController) rename(branch *models.Branch) error {
|
|||||||
InitialContent: branch.Name,
|
InitialContent: branch.Name,
|
||||||
HandleConfirm: func(newBranchName string) error {
|
HandleConfirm: func(newBranchName string) error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.RenameBranch)
|
self.c.LogAction(self.c.Tr.Actions.RenameBranch)
|
||||||
if err := self.git.Branch.Rename(branch.Name, newBranchName); err != nil {
|
if err := self.c.Git().Branch.Rename(branch.Name, newBranchName); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,7 +415,7 @@ func (self *BranchesController) rename(branch *models.Branch) error {
|
|||||||
_ = self.c.Refresh(types.RefreshOptions{Mode: types.SYNC, Scope: []types.RefreshableView{types.BRANCHES}})
|
_ = self.c.Refresh(types.RefreshOptions{Mode: types.SYNC, Scope: []types.RefreshableView{types.BRANCHES}})
|
||||||
|
|
||||||
// now that we've got our stuff again we need to find that branch and reselect it.
|
// now that we've got our stuff again we need to find that branch and reselect it.
|
||||||
for i, newBranch := range self.model.Branches {
|
for i, newBranch := range self.c.Model().Branches {
|
||||||
if newBranch.Name == newBranchName {
|
if newBranch.Name == newBranchName {
|
||||||
self.context().SetSelectedLineIdx(i)
|
self.context().SetSelectedLineIdx(i)
|
||||||
if err := self.context().HandleRender(); err != nil {
|
if err := self.context().HandleRender(); err != nil {
|
||||||
@ -420,7 +444,7 @@ func (self *BranchesController) rename(branch *models.Branch) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *BranchesController) newBranch(selectedBranch *models.Branch) error {
|
func (self *BranchesController) newBranch(selectedBranch *models.Branch) error {
|
||||||
return self.helpers.Refs.NewBranch(selectedBranch.FullRefName(), selectedBranch.RefName(), "")
|
return self.c.Helpers().Refs.NewBranch(selectedBranch.FullRefName(), selectedBranch.RefName(), "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *BranchesController) createPullRequestMenu(selectedBranch *models.Branch, checkedOutBranch *models.Branch) error {
|
func (self *BranchesController) createPullRequestMenu(selectedBranch *models.Branch, checkedOutBranch *models.Branch) error {
|
||||||
@ -443,7 +467,7 @@ func (self *BranchesController) createPullRequestMenu(selectedBranch *models.Bra
|
|||||||
OnPress: func() error {
|
OnPress: func() error {
|
||||||
return self.c.Prompt(types.PromptOpts{
|
return self.c.Prompt(types.PromptOpts{
|
||||||
Title: branch.Name + " →",
|
Title: branch.Name + " →",
|
||||||
FindSuggestionsFunc: self.helpers.Suggestions.GetBranchNameSuggestionsFunc(),
|
FindSuggestionsFunc: self.c.Helpers().Suggestions.GetBranchNameSuggestionsFunc(),
|
||||||
HandleConfirm: func(targetBranchName string) error {
|
HandleConfirm: func(targetBranchName string) error {
|
||||||
return self.createPullRequest(branch.Name, targetBranchName)
|
return self.createPullRequest(branch.Name, targetBranchName)
|
||||||
},
|
},
|
||||||
@ -471,14 +495,14 @@ func (self *BranchesController) createPullRequestMenu(selectedBranch *models.Bra
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *BranchesController) createPullRequest(from string, to string) error {
|
func (self *BranchesController) createPullRequest(from string, to string) error {
|
||||||
url, err := self.helpers.Host.GetPullRequestURL(from, to)
|
url, err := self.c.Helpers().Host.GetPullRequestURL(from, to)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.c.LogAction(self.c.Tr.Actions.OpenPullRequest)
|
self.c.LogAction(self.c.Tr.Actions.OpenPullRequest)
|
||||||
|
|
||||||
if err := self.os.OpenLink(url); err != nil {
|
if err := self.c.OS().OpenLink(url); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
42
pkg/gui/controllers/command_log_controller.go
Normal file
42
pkg/gui/controllers/command_log_controller.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CommandLogController struct {
|
||||||
|
baseController
|
||||||
|
c *ControllerCommon
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ types.IController = &CommandLogController{}
|
||||||
|
|
||||||
|
func NewCommandLogController(
|
||||||
|
common *ControllerCommon,
|
||||||
|
) *CommandLogController {
|
||||||
|
return &CommandLogController{
|
||||||
|
baseController: baseController{},
|
||||||
|
c: common,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *CommandLogController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
|
||||||
|
bindings := []*types.Binding{}
|
||||||
|
|
||||||
|
return bindings
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *CommandLogController) GetOnFocusLost() func(types.OnFocusLostOpts) error {
|
||||||
|
return func(types.OnFocusLostOpts) error {
|
||||||
|
self.c.Views().Extras.Autoscroll = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *CommandLogController) Context() types.Context {
|
||||||
|
return self.context()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *CommandLogController) context() types.Context {
|
||||||
|
return self.c.Contexts().CommandLog
|
||||||
|
}
|
@ -6,17 +6,17 @@ import (
|
|||||||
|
|
||||||
type CommitDescriptionController struct {
|
type CommitDescriptionController struct {
|
||||||
baseController
|
baseController
|
||||||
*controllerCommon
|
c *ControllerCommon
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ types.IController = &CommitMessageController{}
|
var _ types.IController = &CommitMessageController{}
|
||||||
|
|
||||||
func NewCommitDescriptionController(
|
func NewCommitDescriptionController(
|
||||||
common *controllerCommon,
|
common *ControllerCommon,
|
||||||
) *CommitDescriptionController {
|
) *CommitDescriptionController {
|
||||||
return &CommitDescriptionController{
|
return &CommitDescriptionController{
|
||||||
baseController: baseController{},
|
baseController: baseController{},
|
||||||
controllerCommon: common,
|
c: common,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,17 +44,17 @@ func (self *CommitDescriptionController) Context() types.Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitDescriptionController) context() types.Context {
|
func (self *CommitDescriptionController) context() types.Context {
|
||||||
return self.contexts.CommitMessage
|
return self.c.Contexts().CommitMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitDescriptionController) switchToCommitMessage() error {
|
func (self *CommitDescriptionController) switchToCommitMessage() error {
|
||||||
return self.c.PushContext(self.contexts.CommitMessage)
|
return self.c.PushContext(self.c.Contexts().CommitMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitDescriptionController) close() error {
|
func (self *CommitDescriptionController) close() error {
|
||||||
return self.helpers.Commits.CloseCommitMessagePanel()
|
return self.c.Helpers().Commits.CloseCommitMessagePanel()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitDescriptionController) confirm() error {
|
func (self *CommitDescriptionController) confirm() error {
|
||||||
return self.helpers.Commits.HandleCommitConfirm()
|
return self.c.Helpers().Commits.HandleCommitConfirm()
|
||||||
}
|
}
|
||||||
|
@ -8,29 +8,33 @@ import (
|
|||||||
|
|
||||||
type CommitMessageController struct {
|
type CommitMessageController struct {
|
||||||
baseController
|
baseController
|
||||||
*controllerCommon
|
c *ControllerCommon
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ types.IController = &CommitMessageController{}
|
var _ types.IController = &CommitMessageController{}
|
||||||
|
|
||||||
func NewCommitMessageController(
|
func NewCommitMessageController(
|
||||||
common *controllerCommon,
|
common *ControllerCommon,
|
||||||
) *CommitMessageController {
|
) *CommitMessageController {
|
||||||
return &CommitMessageController{
|
return &CommitMessageController{
|
||||||
baseController: baseController{},
|
baseController: baseController{},
|
||||||
controllerCommon: common,
|
c: common,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: merge that commit panel PR because we're not currently showing how to add a newline as it's
|
||||||
|
// handled by the editor func rather than by the controller here.
|
||||||
func (self *CommitMessageController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
|
func (self *CommitMessageController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
|
||||||
bindings := []*types.Binding{
|
bindings := []*types.Binding{
|
||||||
{
|
{
|
||||||
Key: opts.GetKey(opts.Config.Universal.SubmitEditorText),
|
Key: opts.GetKey(opts.Config.Universal.SubmitEditorText),
|
||||||
Handler: self.confirm,
|
Handler: self.confirm,
|
||||||
|
Description: self.c.Tr.LcConfirm,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: opts.GetKey(opts.Config.Universal.Return),
|
Key: opts.GetKey(opts.Config.Universal.Return),
|
||||||
Handler: self.close,
|
Handler: self.close,
|
||||||
|
Description: self.c.Tr.LcClose,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: opts.GetKey(opts.Config.Universal.PrevItem),
|
Key: opts.GetKey(opts.Config.Universal.PrevItem),
|
||||||
@ -49,12 +53,19 @@ func (self *CommitMessageController) GetKeybindings(opts types.KeybindingsOpts)
|
|||||||
return bindings
|
return bindings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *CommitMessageController) GetOnFocusLost() func(types.OnFocusLostOpts) error {
|
||||||
|
return func(types.OnFocusLostOpts) error {
|
||||||
|
self.context().RenderCommitLength()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (self *CommitMessageController) Context() types.Context {
|
func (self *CommitMessageController) Context() types.Context {
|
||||||
return self.context()
|
return self.context()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitMessageController) context() *context.CommitMessageContext {
|
func (self *CommitMessageController) context() *context.CommitMessageContext {
|
||||||
return self.contexts.CommitMessage
|
return self.c.Contexts().CommitMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitMessageController) handlePreviousCommit() error {
|
func (self *CommitMessageController) handlePreviousCommit() error {
|
||||||
@ -69,7 +80,7 @@ func (self *CommitMessageController) handleNextCommit() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitMessageController) switchToCommitDescription() error {
|
func (self *CommitMessageController) switchToCommitDescription() error {
|
||||||
if err := self.c.PushContext(self.contexts.CommitDescription); err != nil {
|
if err := self.c.PushContext(self.c.Contexts().CommitDescription); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -80,10 +91,10 @@ func (self *CommitMessageController) handleCommitIndexChange(value int) error {
|
|||||||
newIndex := currentIndex + value
|
newIndex := currentIndex + value
|
||||||
if newIndex == context.NoCommitIndex {
|
if newIndex == context.NoCommitIndex {
|
||||||
self.context().SetSelectedIndex(newIndex)
|
self.context().SetSelectedIndex(newIndex)
|
||||||
self.helpers.Commits.SetMessageAndDescriptionInView(self.context().GetHistoryMessage())
|
self.c.Helpers().Commits.SetMessageAndDescriptionInView(self.context().GetHistoryMessage())
|
||||||
return nil
|
return nil
|
||||||
} else if currentIndex == context.NoCommitIndex {
|
} else if currentIndex == context.NoCommitIndex {
|
||||||
self.context().SetHistoryMessage(self.helpers.Commits.JoinCommitMessageAndDescription())
|
self.context().SetHistoryMessage(self.c.Helpers().Commits.JoinCommitMessageAndDescription())
|
||||||
}
|
}
|
||||||
|
|
||||||
validCommit, err := self.setCommitMessageAtIndex(newIndex)
|
validCommit, err := self.setCommitMessageAtIndex(newIndex)
|
||||||
@ -95,21 +106,21 @@ func (self *CommitMessageController) handleCommitIndexChange(value int) error {
|
|||||||
|
|
||||||
// returns true if the given index is for a valid commit
|
// returns true if the given index is for a valid commit
|
||||||
func (self *CommitMessageController) setCommitMessageAtIndex(index int) (bool, error) {
|
func (self *CommitMessageController) setCommitMessageAtIndex(index int) (bool, error) {
|
||||||
commitMessage, err := self.git.Commit.GetCommitMessageFromHistory(index)
|
commitMessage, err := self.c.Git().Commit.GetCommitMessageFromHistory(index)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == git_commands.ErrInvalidCommitIndex {
|
if err == git_commands.ErrInvalidCommitIndex {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
return false, self.c.ErrorMsg(self.c.Tr.CommitWithoutMessageErr)
|
return false, self.c.ErrorMsg(self.c.Tr.CommitWithoutMessageErr)
|
||||||
}
|
}
|
||||||
self.helpers.Commits.UpdateCommitPanelView(commitMessage)
|
self.c.Helpers().Commits.UpdateCommitPanelView(commitMessage)
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitMessageController) confirm() error {
|
func (self *CommitMessageController) confirm() error {
|
||||||
return self.helpers.Commits.HandleCommitConfirm()
|
return self.c.Helpers().Commits.HandleCommitConfirm()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitMessageController) close() error {
|
func (self *CommitMessageController) close() error {
|
||||||
return self.helpers.Commits.CloseCommitMessagePanel()
|
return self.c.Helpers().Commits.CloseCommitMessagePanel()
|
||||||
}
|
}
|
||||||
|
@ -11,17 +11,17 @@ import (
|
|||||||
|
|
||||||
type CommitFilesController struct {
|
type CommitFilesController struct {
|
||||||
baseController
|
baseController
|
||||||
*controllerCommon
|
c *ControllerCommon
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ types.IController = &CommitFilesController{}
|
var _ types.IController = &CommitFilesController{}
|
||||||
|
|
||||||
func NewCommitFilesController(
|
func NewCommitFilesController(
|
||||||
common *controllerCommon,
|
common *ControllerCommon,
|
||||||
) *CommitFilesController {
|
) *CommitFilesController {
|
||||||
return &CommitFilesController{
|
return &CommitFilesController{
|
||||||
baseController: baseController{},
|
baseController: baseController{},
|
||||||
controllerCommon: common,
|
c: common,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,7 +99,39 @@ func (self *CommitFilesController) Context() types.Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitFilesController) context() *context.CommitFilesContext {
|
func (self *CommitFilesController) context() *context.CommitFilesContext {
|
||||||
return self.contexts.CommitFiles
|
return self.c.Contexts().CommitFiles
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *CommitFilesController) GetOnRenderToMain() func() error {
|
||||||
|
return func() error {
|
||||||
|
node := self.context().GetSelected()
|
||||||
|
if node == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ref := self.context().GetRef()
|
||||||
|
to := ref.RefName()
|
||||||
|
from, reverse := self.c.Modes().Diffing.GetFromAndReverseArgsForDiff(ref.ParentRefName())
|
||||||
|
|
||||||
|
cmdObj := self.c.Git().WorkingTree.ShowFileDiffCmdObj(
|
||||||
|
from, to, reverse, node.GetPath(), false, self.c.State().GetIgnoreWhitespaceInDiffView(),
|
||||||
|
)
|
||||||
|
task := types.NewRunPtyTask(cmdObj.GetCmd())
|
||||||
|
|
||||||
|
pair := self.c.MainViewPairs().Normal
|
||||||
|
if node.File != nil {
|
||||||
|
pair = self.c.MainViewPairs().PatchBuilding
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.c.RenderToMainViews(types.RefreshMainOpts{
|
||||||
|
Pair: pair,
|
||||||
|
Main: &types.ViewUpdateOpts{
|
||||||
|
Title: self.c.Tr.Patch,
|
||||||
|
Task: task,
|
||||||
|
},
|
||||||
|
Secondary: secondaryPatchPanelUpdateOpts(self.c),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitFilesController) onClickMain(opts gocui.ViewMouseBindingOpts) error {
|
func (self *CommitFilesController) onClickMain(opts gocui.ViewMouseBindingOpts) error {
|
||||||
@ -112,7 +144,7 @@ func (self *CommitFilesController) onClickMain(opts gocui.ViewMouseBindingOpts)
|
|||||||
|
|
||||||
func (self *CommitFilesController) checkout(node *filetree.CommitFileNode) error {
|
func (self *CommitFilesController) checkout(node *filetree.CommitFileNode) error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.CheckoutFile)
|
self.c.LogAction(self.c.Tr.Actions.CheckoutFile)
|
||||||
if err := self.git.WorkingTree.CheckoutFile(self.context().GetRef().RefName(), node.GetPath()); err != nil {
|
if err := self.c.Git().WorkingTree.CheckoutFile(self.context().GetRef().RefName(), node.GetPath()); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +152,7 @@ func (self *CommitFilesController) checkout(node *filetree.CommitFileNode) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitFilesController) discard(node *filetree.CommitFileNode) error {
|
func (self *CommitFilesController) discard(node *filetree.CommitFileNode) error {
|
||||||
if ok, err := self.helpers.PatchBuilding.ValidateNormalWorkingTreeState(); !ok {
|
if ok, err := self.c.Helpers().PatchBuilding.ValidateNormalWorkingTreeState(); !ok {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,8 +162,8 @@ func (self *CommitFilesController) discard(node *filetree.CommitFileNode) error
|
|||||||
HandleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func() error {
|
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func() error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.DiscardOldFileChange)
|
self.c.LogAction(self.c.Tr.Actions.DiscardOldFileChange)
|
||||||
if err := self.git.Rebase.DiscardOldFileChanges(self.model.Commits, self.contexts.LocalCommits.GetSelectedLineIdx(), node.GetPath()); err != nil {
|
if err := self.c.Git().Rebase.DiscardOldFileChanges(self.c.Model().Commits, self.c.Contexts().LocalCommits.GetSelectedLineIdx(), node.GetPath()); err != nil {
|
||||||
if err := self.helpers.MergeAndRebase.CheckMergeOrRebase(err); err != nil {
|
if err := self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -143,7 +175,7 @@ func (self *CommitFilesController) discard(node *filetree.CommitFileNode) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitFilesController) open(node *filetree.CommitFileNode) error {
|
func (self *CommitFilesController) open(node *filetree.CommitFileNode) error {
|
||||||
return self.helpers.Files.OpenFile(node.GetPath())
|
return self.c.Helpers().Files.OpenFile(node.GetPath())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitFilesController) edit(node *filetree.CommitFileNode) error {
|
func (self *CommitFilesController) edit(node *filetree.CommitFileNode) error {
|
||||||
@ -151,13 +183,13 @@ func (self *CommitFilesController) edit(node *filetree.CommitFileNode) error {
|
|||||||
return self.c.ErrorMsg(self.c.Tr.ErrCannotEditDirectory)
|
return self.c.ErrorMsg(self.c.Tr.ErrCannotEditDirectory)
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.helpers.Files.EditFile(node.GetPath())
|
return self.c.Helpers().Files.EditFile(node.GetPath())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitFilesController) toggleForPatch(node *filetree.CommitFileNode) error {
|
func (self *CommitFilesController) toggleForPatch(node *filetree.CommitFileNode) error {
|
||||||
toggle := func() error {
|
toggle := func() error {
|
||||||
return self.c.WithWaitingStatus(self.c.Tr.LcUpdatingPatch, func() error {
|
return self.c.WithWaitingStatus(self.c.Tr.LcUpdatingPatch, func() error {
|
||||||
if !self.git.Patch.PatchBuilder.Active() {
|
if !self.c.Git().Patch.PatchBuilder.Active() {
|
||||||
if err := self.startPatchBuilder(); err != nil {
|
if err := self.startPatchBuilder(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -166,34 +198,34 @@ func (self *CommitFilesController) toggleForPatch(node *filetree.CommitFileNode)
|
|||||||
// if there is any file that hasn't been fully added we'll fully add everything,
|
// if there is any file that hasn't been fully added we'll fully add everything,
|
||||||
// otherwise we'll remove everything
|
// otherwise we'll remove everything
|
||||||
adding := node.SomeFile(func(file *models.CommitFile) bool {
|
adding := node.SomeFile(func(file *models.CommitFile) bool {
|
||||||
return self.git.Patch.PatchBuilder.GetFileStatus(file.Name, self.context().GetRef().RefName()) != patch.WHOLE
|
return self.c.Git().Patch.PatchBuilder.GetFileStatus(file.Name, self.context().GetRef().RefName()) != patch.WHOLE
|
||||||
})
|
})
|
||||||
|
|
||||||
err := node.ForEachFile(func(file *models.CommitFile) error {
|
err := node.ForEachFile(func(file *models.CommitFile) error {
|
||||||
if adding {
|
if adding {
|
||||||
return self.git.Patch.PatchBuilder.AddFileWhole(file.Name)
|
return self.c.Git().Patch.PatchBuilder.AddFileWhole(file.Name)
|
||||||
} else {
|
} else {
|
||||||
return self.git.Patch.PatchBuilder.RemoveFile(file.Name)
|
return self.c.Git().Patch.PatchBuilder.RemoveFile(file.Name)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.git.Patch.PatchBuilder.IsEmpty() {
|
if self.c.Git().Patch.PatchBuilder.IsEmpty() {
|
||||||
self.git.Patch.PatchBuilder.Reset()
|
self.c.Git().Patch.PatchBuilder.Reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.c.PostRefreshUpdate(self.context())
|
return self.c.PostRefreshUpdate(self.context())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.git.Patch.PatchBuilder.Active() && self.git.Patch.PatchBuilder.To != self.context().GetRef().RefName() {
|
if self.c.Git().Patch.PatchBuilder.Active() && self.c.Git().Patch.PatchBuilder.To != self.context().GetRef().RefName() {
|
||||||
return self.c.Confirm(types.ConfirmOpts{
|
return self.c.Confirm(types.ConfirmOpts{
|
||||||
Title: self.c.Tr.DiscardPatch,
|
Title: self.c.Tr.DiscardPatch,
|
||||||
Prompt: self.c.Tr.DiscardPatchConfirm,
|
Prompt: self.c.Tr.DiscardPatchConfirm,
|
||||||
HandleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
self.git.Patch.PatchBuilder.Reset()
|
self.c.Git().Patch.PatchBuilder.Reset()
|
||||||
return toggle()
|
return toggle()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -213,9 +245,9 @@ func (self *CommitFilesController) startPatchBuilder() error {
|
|||||||
canRebase := commitFilesContext.GetCanRebase()
|
canRebase := commitFilesContext.GetCanRebase()
|
||||||
ref := commitFilesContext.GetRef()
|
ref := commitFilesContext.GetRef()
|
||||||
to := ref.RefName()
|
to := ref.RefName()
|
||||||
from, reverse := self.modes.Diffing.GetFromAndReverseArgsForDiff(ref.ParentRefName())
|
from, reverse := self.c.Modes().Diffing.GetFromAndReverseArgsForDiff(ref.ParentRefName())
|
||||||
|
|
||||||
self.git.Patch.PatchBuilder.Start(from, to, reverse, canRebase)
|
self.c.Git().Patch.PatchBuilder.Start(from, to, reverse, canRebase)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,21 +261,21 @@ func (self *CommitFilesController) enterCommitFile(node *filetree.CommitFileNode
|
|||||||
}
|
}
|
||||||
|
|
||||||
enterTheFile := func() error {
|
enterTheFile := func() error {
|
||||||
if !self.git.Patch.PatchBuilder.Active() {
|
if !self.c.Git().Patch.PatchBuilder.Active() {
|
||||||
if err := self.startPatchBuilder(); err != nil {
|
if err := self.startPatchBuilder(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.c.PushContext(self.contexts.CustomPatchBuilder, opts)
|
return self.c.PushContext(self.c.Contexts().CustomPatchBuilder, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.git.Patch.PatchBuilder.Active() && self.git.Patch.PatchBuilder.To != self.context().GetRef().RefName() {
|
if self.c.Git().Patch.PatchBuilder.Active() && self.c.Git().Patch.PatchBuilder.To != self.context().GetRef().RefName() {
|
||||||
return self.c.Confirm(types.ConfirmOpts{
|
return self.c.Confirm(types.ConfirmOpts{
|
||||||
Title: self.c.Tr.DiscardPatch,
|
Title: self.c.Tr.DiscardPatch,
|
||||||
Prompt: self.c.Tr.DiscardPatchConfirm,
|
Prompt: self.c.Tr.DiscardPatchConfirm,
|
||||||
HandleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
self.git.Patch.PatchBuilder.Reset()
|
self.c.Git().Patch.PatchBuilder.Reset()
|
||||||
return enterTheFile()
|
return enterTheFile()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -1,42 +1,24 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers"
|
"github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type controllerCommon struct {
|
type ControllerCommon struct {
|
||||||
c *types.HelperCommon
|
*helpers.HelperCommon
|
||||||
os *oscommands.OSCommand
|
IGetHelpers
|
||||||
git *commands.GitCommand
|
}
|
||||||
helpers *helpers.Helpers
|
|
||||||
model *types.Model
|
type IGetHelpers interface {
|
||||||
contexts *context.ContextTree
|
Helpers() *helpers.Helpers
|
||||||
modes *types.Modes
|
|
||||||
mutexes *types.Mutexes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewControllerCommon(
|
func NewControllerCommon(
|
||||||
c *types.HelperCommon,
|
c *helpers.HelperCommon,
|
||||||
os *oscommands.OSCommand,
|
IGetHelpers IGetHelpers,
|
||||||
git *commands.GitCommand,
|
) *ControllerCommon {
|
||||||
helpers *helpers.Helpers,
|
return &ControllerCommon{
|
||||||
model *types.Model,
|
HelperCommon: c,
|
||||||
contexts *context.ContextTree,
|
IGetHelpers: IGetHelpers,
|
||||||
modes *types.Modes,
|
|
||||||
mutexes *types.Mutexes,
|
|
||||||
) *controllerCommon {
|
|
||||||
return &controllerCommon{
|
|
||||||
c: c,
|
|
||||||
os: os,
|
|
||||||
git: git,
|
|
||||||
helpers: helpers,
|
|
||||||
model: model,
|
|
||||||
contexts: contexts,
|
|
||||||
modes: modes,
|
|
||||||
mutexes: mutexes,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
65
pkg/gui/controllers/confirmation_controller.go
Normal file
65
pkg/gui/controllers/confirmation_controller.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ConfirmationController struct {
|
||||||
|
baseController
|
||||||
|
c *ControllerCommon
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ types.IController = &ConfirmationController{}
|
||||||
|
|
||||||
|
func NewConfirmationController(
|
||||||
|
common *ControllerCommon,
|
||||||
|
) *ConfirmationController {
|
||||||
|
return &ConfirmationController{
|
||||||
|
baseController: baseController{},
|
||||||
|
c: common,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ConfirmationController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
|
||||||
|
bindings := []*types.Binding{
|
||||||
|
{
|
||||||
|
Key: opts.GetKey(opts.Config.Universal.Confirm),
|
||||||
|
Handler: func() error { return self.context().State.OnConfirm() },
|
||||||
|
Description: self.c.Tr.LcConfirm,
|
||||||
|
Display: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: opts.GetKey(opts.Config.Universal.Return),
|
||||||
|
Handler: func() error { return self.context().State.OnClose() },
|
||||||
|
Description: self.c.Tr.LcCloseCancel,
|
||||||
|
Display: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: opts.GetKey(opts.Config.Universal.TogglePanel),
|
||||||
|
Handler: func() error {
|
||||||
|
if len(self.c.Contexts().Suggestions.State.Suggestions) > 0 {
|
||||||
|
return self.c.ReplaceContext(self.c.Contexts().Suggestions)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return bindings
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ConfirmationController) GetOnFocusLost() func(types.OnFocusLostOpts) error {
|
||||||
|
return func(types.OnFocusLostOpts) error {
|
||||||
|
self.c.Helpers().Confirmation.DeactivateConfirmationPrompt()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ConfirmationController) Context() types.Context {
|
||||||
|
return self.context()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ConfirmationController) context() *context.ConfirmationContext {
|
||||||
|
return self.c.Contexts().Confirmation
|
||||||
|
}
|
@ -24,17 +24,17 @@ var CONTEXT_KEYS_SHOWING_DIFFS = []types.ContextKey{
|
|||||||
|
|
||||||
type ContextLinesController struct {
|
type ContextLinesController struct {
|
||||||
baseController
|
baseController
|
||||||
*controllerCommon
|
c *ControllerCommon
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ types.IController = &ContextLinesController{}
|
var _ types.IController = &ContextLinesController{}
|
||||||
|
|
||||||
func NewContextLinesController(
|
func NewContextLinesController(
|
||||||
common *controllerCommon,
|
common *ControllerCommon,
|
||||||
) *ContextLinesController {
|
) *ContextLinesController {
|
||||||
return &ContextLinesController{
|
return &ContextLinesController{
|
||||||
baseController: baseController{},
|
baseController: baseController{},
|
||||||
controllerCommon: common,
|
c: common,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +101,7 @@ func (self *ContextLinesController) applyChange() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *ContextLinesController) checkCanChangeContext() error {
|
func (self *ContextLinesController) checkCanChangeContext() error {
|
||||||
if self.git.Patch.PatchBuilder.Active() {
|
if self.c.Git().Patch.PatchBuilder.Active() {
|
||||||
return errors.New(self.c.Tr.CantChangeContextSizeError)
|
return errors.New(self.c.Tr.CantChangeContextSizeError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
53
pkg/gui/controllers/custom_command_action.go
Normal file
53
pkg/gui/controllers/custom_command_action.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/generics/slices"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
|
"github.com/samber/lo"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CustomCommandAction struct {
|
||||||
|
c *ControllerCommon
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *CustomCommandAction) Call() error {
|
||||||
|
return self.c.Prompt(types.PromptOpts{
|
||||||
|
Title: self.c.Tr.CustomCommand,
|
||||||
|
FindSuggestionsFunc: self.GetCustomCommandsHistorySuggestionsFunc(),
|
||||||
|
HandleConfirm: func(command string) error {
|
||||||
|
if self.shouldSaveCommand(command) {
|
||||||
|
self.c.GetAppState().CustomCommandsHistory = utils.Limit(
|
||||||
|
lo.Uniq(append(self.c.GetAppState().CustomCommandsHistory, command)),
|
||||||
|
1000,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := self.c.SaveAppState()
|
||||||
|
if err != nil {
|
||||||
|
self.c.Log.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.c.LogAction(self.c.Tr.Actions.CustomCommand)
|
||||||
|
return self.c.RunSubprocessAndRefresh(
|
||||||
|
self.c.OS().Cmd.NewShell(command),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *CustomCommandAction) GetCustomCommandsHistorySuggestionsFunc() func(string) []*types.Suggestion {
|
||||||
|
// reversing so that we display the latest command first
|
||||||
|
history := slices.Reverse(self.c.GetAppState().CustomCommandsHistory)
|
||||||
|
|
||||||
|
return helpers.FuzzySearchFunc(history)
|
||||||
|
}
|
||||||
|
|
||||||
|
// this mimics the shell functionality `ignorespace`
|
||||||
|
// which doesn't save a command to history if it starts with a space
|
||||||
|
func (self *CustomCommandAction) shouldSaveCommand(command string) bool {
|
||||||
|
return !strings.HasPrefix(command, " ")
|
||||||
|
}
|
219
pkg/gui/controllers/custom_patch_options_menu_action.go
Normal file
219
pkg/gui/controllers/custom_patch_options_menu_action.go
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CustomPatchOptionsMenuAction struct {
|
||||||
|
c *ControllerCommon
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *CustomPatchOptionsMenuAction) Call() error {
|
||||||
|
if !self.c.Git().Patch.PatchBuilder.Active() {
|
||||||
|
return self.c.ErrorMsg(self.c.Tr.NoPatchError)
|
||||||
|
}
|
||||||
|
|
||||||
|
menuItems := []*types.MenuItem{
|
||||||
|
{
|
||||||
|
Label: "reset patch",
|
||||||
|
OnPress: self.c.Helpers().PatchBuilding.Reset,
|
||||||
|
Key: 'c',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Label: "apply patch",
|
||||||
|
OnPress: func() error { return self.handleApplyPatch(false) },
|
||||||
|
Key: 'a',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Label: "apply patch in reverse",
|
||||||
|
OnPress: func() error { return self.handleApplyPatch(true) },
|
||||||
|
Key: 'r',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.c.Git().Patch.PatchBuilder.CanRebase && self.c.Git().Status.WorkingTreeState() == enums.REBASE_MODE_NONE {
|
||||||
|
menuItems = append(menuItems, []*types.MenuItem{
|
||||||
|
{
|
||||||
|
Label: fmt.Sprintf("remove patch from original commit (%s)", self.c.Git().Patch.PatchBuilder.To),
|
||||||
|
OnPress: self.handleDeletePatchFromCommit,
|
||||||
|
Key: 'd',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Label: "move patch out into index",
|
||||||
|
OnPress: self.handleMovePatchIntoWorkingTree,
|
||||||
|
Key: 'i',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Label: "move patch into new commit",
|
||||||
|
OnPress: self.handlePullPatchIntoNewCommit,
|
||||||
|
Key: 'n',
|
||||||
|
},
|
||||||
|
}...)
|
||||||
|
|
||||||
|
if self.c.CurrentContext().GetKey() == self.c.Contexts().LocalCommits.GetKey() {
|
||||||
|
selectedCommit := self.c.Contexts().LocalCommits.GetSelected()
|
||||||
|
if selectedCommit != nil && self.c.Git().Patch.PatchBuilder.To != selectedCommit.Sha {
|
||||||
|
// adding this option to index 1
|
||||||
|
menuItems = append(
|
||||||
|
menuItems[:1],
|
||||||
|
append(
|
||||||
|
[]*types.MenuItem{
|
||||||
|
{
|
||||||
|
Label: fmt.Sprintf("move patch to selected commit (%s)", selectedCommit.Sha),
|
||||||
|
OnPress: self.handleMovePatchToSelectedCommit,
|
||||||
|
Key: 'm',
|
||||||
|
},
|
||||||
|
}, menuItems[1:]...,
|
||||||
|
)...,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
menuItems = append(menuItems, []*types.MenuItem{
|
||||||
|
{
|
||||||
|
Label: "copy patch to clipboard",
|
||||||
|
OnPress: func() error { return self.copyPatchToClipboard() },
|
||||||
|
Key: 'y',
|
||||||
|
},
|
||||||
|
}...)
|
||||||
|
|
||||||
|
return self.c.Menu(types.CreateMenuOptions{Title: self.c.Tr.PatchOptionsTitle, Items: menuItems})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *CustomPatchOptionsMenuAction) getPatchCommitIndex() int {
|
||||||
|
for index, commit := range self.c.Model().Commits {
|
||||||
|
if commit.Sha == self.c.Git().Patch.PatchBuilder.To {
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *CustomPatchOptionsMenuAction) validateNormalWorkingTreeState() (bool, error) {
|
||||||
|
if self.c.Git().Status.WorkingTreeState() != enums.REBASE_MODE_NONE {
|
||||||
|
return false, self.c.ErrorMsg(self.c.Tr.CantPatchWhileRebasingError)
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *CustomPatchOptionsMenuAction) returnFocusFromPatchExplorerIfNecessary() error {
|
||||||
|
if self.c.CurrentContext().GetKey() == self.c.Contexts().CustomPatchBuilder.GetKey() {
|
||||||
|
return self.c.Helpers().PatchBuilding.Escape()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *CustomPatchOptionsMenuAction) handleDeletePatchFromCommit() error {
|
||||||
|
if ok, err := self.validateNormalWorkingTreeState(); !ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := self.returnFocusFromPatchExplorerIfNecessary(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func() error {
|
||||||
|
commitIndex := self.getPatchCommitIndex()
|
||||||
|
self.c.LogAction(self.c.Tr.Actions.RemovePatchFromCommit)
|
||||||
|
err := self.c.Git().Patch.DeletePatchesFromCommit(self.c.Model().Commits, commitIndex)
|
||||||
|
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *CustomPatchOptionsMenuAction) handleMovePatchToSelectedCommit() error {
|
||||||
|
if ok, err := self.validateNormalWorkingTreeState(); !ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := self.returnFocusFromPatchExplorerIfNecessary(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func() error {
|
||||||
|
commitIndex := self.getPatchCommitIndex()
|
||||||
|
self.c.LogAction(self.c.Tr.Actions.MovePatchToSelectedCommit)
|
||||||
|
err := self.c.Git().Patch.MovePatchToSelectedCommit(self.c.Model().Commits, commitIndex, self.c.Contexts().LocalCommits.GetSelectedLineIdx())
|
||||||
|
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *CustomPatchOptionsMenuAction) handleMovePatchIntoWorkingTree() error {
|
||||||
|
if ok, err := self.validateNormalWorkingTreeState(); !ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := self.returnFocusFromPatchExplorerIfNecessary(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
pull := func(stash bool) error {
|
||||||
|
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func() error {
|
||||||
|
commitIndex := self.getPatchCommitIndex()
|
||||||
|
self.c.LogAction(self.c.Tr.Actions.MovePatchIntoIndex)
|
||||||
|
err := self.c.Git().Patch.MovePatchIntoIndex(self.c.Model().Commits, commitIndex, stash)
|
||||||
|
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.c.Helpers().WorkingTree.IsWorkingTreeDirty() {
|
||||||
|
return self.c.Confirm(types.ConfirmOpts{
|
||||||
|
Title: self.c.Tr.MustStashTitle,
|
||||||
|
Prompt: self.c.Tr.MustStashWarning,
|
||||||
|
HandleConfirm: func() error {
|
||||||
|
return pull(true)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return pull(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *CustomPatchOptionsMenuAction) handlePullPatchIntoNewCommit() error {
|
||||||
|
if ok, err := self.validateNormalWorkingTreeState(); !ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := self.returnFocusFromPatchExplorerIfNecessary(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func() error {
|
||||||
|
commitIndex := self.getPatchCommitIndex()
|
||||||
|
self.c.LogAction(self.c.Tr.Actions.MovePatchIntoNewCommit)
|
||||||
|
err := self.c.Git().Patch.PullPatchIntoNewCommit(self.c.Model().Commits, commitIndex)
|
||||||
|
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *CustomPatchOptionsMenuAction) handleApplyPatch(reverse bool) error {
|
||||||
|
if err := self.returnFocusFromPatchExplorerIfNecessary(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
action := self.c.Tr.Actions.ApplyPatch
|
||||||
|
if reverse {
|
||||||
|
action = "Apply patch in reverse"
|
||||||
|
}
|
||||||
|
self.c.LogAction(action)
|
||||||
|
if err := self.c.Git().Patch.PatchBuilder.ApplyPatches(reverse); err != nil {
|
||||||
|
return self.c.Error(err)
|
||||||
|
}
|
||||||
|
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *CustomPatchOptionsMenuAction) copyPatchToClipboard() error {
|
||||||
|
patch := self.c.Git().Patch.PatchBuilder.RenderAggregatedPatch(true)
|
||||||
|
|
||||||
|
self.c.LogAction(self.c.Tr.Actions.CopyPatchToClipboard)
|
||||||
|
if err := self.c.OS().CopyToClipboard(patch); err != nil {
|
||||||
|
return self.c.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.c.Toast(self.c.Tr.PatchCopiedToClipboard)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
69
pkg/gui/controllers/diffing_menu_action.go
Normal file
69
pkg/gui/controllers/diffing_menu_action.go
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/modes/diffing"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DiffingMenuAction struct {
|
||||||
|
c *ControllerCommon
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *DiffingMenuAction) Call() error {
|
||||||
|
names := self.c.Helpers().Diff.CurrentDiffTerminals()
|
||||||
|
|
||||||
|
menuItems := []*types.MenuItem{}
|
||||||
|
for _, name := range names {
|
||||||
|
name := name
|
||||||
|
menuItems = append(menuItems, []*types.MenuItem{
|
||||||
|
{
|
||||||
|
Label: fmt.Sprintf("%s %s", self.c.Tr.LcDiff, name),
|
||||||
|
OnPress: func() error {
|
||||||
|
self.c.Modes().Diffing.Ref = name
|
||||||
|
// can scope this down based on current view but too lazy right now
|
||||||
|
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}...)
|
||||||
|
}
|
||||||
|
|
||||||
|
menuItems = append(menuItems, []*types.MenuItem{
|
||||||
|
{
|
||||||
|
Label: self.c.Tr.LcEnterRefToDiff,
|
||||||
|
OnPress: func() error {
|
||||||
|
return self.c.Prompt(types.PromptOpts{
|
||||||
|
Title: self.c.Tr.LcEnteRefName,
|
||||||
|
FindSuggestionsFunc: self.c.Helpers().Suggestions.GetRefsSuggestionsFunc(),
|
||||||
|
HandleConfirm: func(response string) error {
|
||||||
|
self.c.Modes().Diffing.Ref = strings.TrimSpace(response)
|
||||||
|
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}...)
|
||||||
|
|
||||||
|
if self.c.Modes().Diffing.Active() {
|
||||||
|
menuItems = append(menuItems, []*types.MenuItem{
|
||||||
|
{
|
||||||
|
Label: self.c.Tr.LcSwapDiff,
|
||||||
|
OnPress: func() error {
|
||||||
|
self.c.Modes().Diffing.Reverse = !self.c.Modes().Diffing.Reverse
|
||||||
|
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Label: self.c.Tr.LcExitDiffMode,
|
||||||
|
OnPress: func() error {
|
||||||
|
self.c.Modes().Diffing = diffing.New()
|
||||||
|
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.c.Menu(types.CreateMenuOptions{Title: self.c.Tr.DiffingMenuTitle, Items: menuItems})
|
||||||
|
}
|
@ -13,23 +13,16 @@ import (
|
|||||||
|
|
||||||
type FilesController struct {
|
type FilesController struct {
|
||||||
baseController // nolint: unused
|
baseController // nolint: unused
|
||||||
*controllerCommon
|
c *ControllerCommon
|
||||||
|
|
||||||
enterSubmodule func(submodule *models.SubmoduleConfig) error
|
|
||||||
setCommitMessage func(message string)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ types.IController = &FilesController{}
|
var _ types.IController = &FilesController{}
|
||||||
|
|
||||||
func NewFilesController(
|
func NewFilesController(
|
||||||
common *controllerCommon,
|
common *ControllerCommon,
|
||||||
enterSubmodule func(submodule *models.SubmoduleConfig) error,
|
|
||||||
setCommitMessage func(message string),
|
|
||||||
) *FilesController {
|
) *FilesController {
|
||||||
return &FilesController{
|
return &FilesController{
|
||||||
controllerCommon: common,
|
c: common,
|
||||||
enterSubmodule: enterSubmodule,
|
|
||||||
setCommitMessage: setCommitMessage,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,12 +40,12 @@ func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: opts.GetKey(opts.Config.Files.CommitChanges),
|
Key: opts.GetKey(opts.Config.Files.CommitChanges),
|
||||||
Handler: self.helpers.WorkingTree.HandleCommitPress,
|
Handler: self.c.Helpers().WorkingTree.HandleCommitPress,
|
||||||
Description: self.c.Tr.CommitChanges,
|
Description: self.c.Tr.CommitChanges,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: opts.GetKey(opts.Config.Files.CommitChangesWithoutHook),
|
Key: opts.GetKey(opts.Config.Files.CommitChangesWithoutHook),
|
||||||
Handler: self.helpers.WorkingTree.HandleWIPCommitPress,
|
Handler: self.c.Helpers().WorkingTree.HandleWIPCommitPress,
|
||||||
Description: self.c.Tr.LcCommitChangesWithoutHook,
|
Description: self.c.Tr.LcCommitChangesWithoutHook,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -62,7 +55,7 @@ func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: opts.GetKey(opts.Config.Files.CommitChangesWithEditor),
|
Key: opts.GetKey(opts.Config.Files.CommitChangesWithEditor),
|
||||||
Handler: self.helpers.WorkingTree.HandleCommitEditorPress,
|
Handler: self.c.Helpers().WorkingTree.HandleCommitEditorPress,
|
||||||
Description: self.c.Tr.CommitChangesWithEditor,
|
Description: self.c.Tr.CommitChangesWithEditor,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -126,7 +119,7 @@ func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: opts.GetKey(opts.Config.Files.OpenMergeTool),
|
Key: opts.GetKey(opts.Config.Files.OpenMergeTool),
|
||||||
Handler: self.helpers.WorkingTree.OpenMergeTool,
|
Handler: self.c.Helpers().WorkingTree.OpenMergeTool,
|
||||||
Description: self.c.Tr.LcOpenMergeTool,
|
Description: self.c.Tr.LcOpenMergeTool,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -172,6 +165,74 @@ func (self *FilesController) GetMouseKeybindings(opts types.KeybindingsOpts) []*
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *FilesController) GetOnRenderToMain() func() error {
|
||||||
|
return func() error {
|
||||||
|
return self.c.Helpers().Diff.WithDiffModeCheck(func() error {
|
||||||
|
node := self.context().GetSelected()
|
||||||
|
|
||||||
|
if node == nil {
|
||||||
|
return self.c.RenderToMainViews(types.RefreshMainOpts{
|
||||||
|
Pair: self.c.MainViewPairs().Normal,
|
||||||
|
Main: &types.ViewUpdateOpts{
|
||||||
|
Title: self.c.Tr.DiffTitle,
|
||||||
|
Task: types.NewRenderStringTask(self.c.Tr.NoChangedFiles),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if node.File != nil && node.File.HasInlineMergeConflicts {
|
||||||
|
hasConflicts, err := self.c.Helpers().MergeConflicts.SetMergeState(node.GetPath())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasConflicts {
|
||||||
|
return self.c.Helpers().MergeConflicts.Render(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.c.Helpers().MergeConflicts.ResetMergeState()
|
||||||
|
|
||||||
|
pair := self.c.MainViewPairs().Normal
|
||||||
|
if node.File != nil {
|
||||||
|
pair = self.c.MainViewPairs().Staging
|
||||||
|
}
|
||||||
|
|
||||||
|
split := self.c.UserConfig.Gui.SplitDiff == "always" || (node.GetHasUnstagedChanges() && node.GetHasStagedChanges())
|
||||||
|
mainShowsStaged := !split && node.GetHasStagedChanges()
|
||||||
|
|
||||||
|
cmdObj := self.c.Git().WorkingTree.WorktreeFileDiffCmdObj(node, false, mainShowsStaged, self.c.State().GetIgnoreWhitespaceInDiffView())
|
||||||
|
title := self.c.Tr.UnstagedChanges
|
||||||
|
if mainShowsStaged {
|
||||||
|
title = self.c.Tr.StagedChanges
|
||||||
|
}
|
||||||
|
refreshOpts := types.RefreshMainOpts{
|
||||||
|
Pair: pair,
|
||||||
|
Main: &types.ViewUpdateOpts{
|
||||||
|
Task: types.NewRunPtyTask(cmdObj.GetCmd()),
|
||||||
|
Title: title,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if split {
|
||||||
|
cmdObj := self.c.Git().WorkingTree.WorktreeFileDiffCmdObj(node, false, true, self.c.State().GetIgnoreWhitespaceInDiffView())
|
||||||
|
|
||||||
|
title := self.c.Tr.StagedChanges
|
||||||
|
if mainShowsStaged {
|
||||||
|
title = self.c.Tr.UnstagedChanges
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshOpts.Secondary = &types.ViewUpdateOpts{
|
||||||
|
Title: title,
|
||||||
|
Task: types.NewRunPtyTask(cmdObj.GetCmd()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.c.RenderToMainViews(refreshOpts)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (self *FilesController) GetOnClick() func() error {
|
func (self *FilesController) GetOnClick() func() error {
|
||||||
return self.checkSelectedFileNode(self.press)
|
return self.checkSelectedFileNode(self.press)
|
||||||
}
|
}
|
||||||
@ -227,7 +288,7 @@ func (self *FilesController) optimisticChange(node *filetree.FileNode, optimisti
|
|||||||
rerender := false
|
rerender := false
|
||||||
err := node.ForEachFile(func(f *models.File) error {
|
err := node.ForEachFile(func(f *models.File) error {
|
||||||
// can't act on the file itself: we need to update the original model file
|
// can't act on the file itself: we need to update the original model file
|
||||||
for _, modelFile := range self.model.Files {
|
for _, modelFile := range self.c.Model().Files {
|
||||||
if modelFile.Name == f.Name {
|
if modelFile.Name == f.Name {
|
||||||
if optimisticChangeFn(modelFile) {
|
if optimisticChangeFn(modelFile) {
|
||||||
rerender = true
|
rerender = true
|
||||||
@ -242,7 +303,7 @@ func (self *FilesController) optimisticChange(node *filetree.FileNode, optimisti
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if rerender {
|
if rerender {
|
||||||
if err := self.c.PostRefreshUpdate(self.contexts.Files); err != nil {
|
if err := self.c.PostRefreshUpdate(self.c.Contexts().Files); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -253,8 +314,8 @@ func (self *FilesController) optimisticChange(node *filetree.FileNode, optimisti
|
|||||||
func (self *FilesController) pressWithLock(node *filetree.FileNode) error {
|
func (self *FilesController) pressWithLock(node *filetree.FileNode) error {
|
||||||
// Obtaining this lock because optimistic rendering requires us to mutate
|
// Obtaining this lock because optimistic rendering requires us to mutate
|
||||||
// the files in our model.
|
// the files in our model.
|
||||||
self.mutexes.RefreshingFilesMutex.Lock()
|
self.c.Mutexes().RefreshingFilesMutex.Lock()
|
||||||
defer self.mutexes.RefreshingFilesMutex.Unlock()
|
defer self.c.Mutexes().RefreshingFilesMutex.Unlock()
|
||||||
|
|
||||||
if node.IsFile() {
|
if node.IsFile() {
|
||||||
file := node.File
|
file := node.File
|
||||||
@ -266,7 +327,7 @@ func (self *FilesController) pressWithLock(node *filetree.FileNode) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := self.git.WorkingTree.StageFile(file.Name); err != nil {
|
if err := self.c.Git().WorkingTree.StageFile(file.Name); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -276,7 +337,7 @@ func (self *FilesController) pressWithLock(node *filetree.FileNode) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := self.git.WorkingTree.UnStageFile(file.Names(), file.Tracked); err != nil {
|
if err := self.c.Git().WorkingTree.UnStageFile(file.Names(), file.Tracked); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -294,7 +355,7 @@ func (self *FilesController) pressWithLock(node *filetree.FileNode) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := self.git.WorkingTree.StageFile(node.Path); err != nil {
|
if err := self.c.Git().WorkingTree.StageFile(node.Path); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -305,7 +366,7 @@ func (self *FilesController) pressWithLock(node *filetree.FileNode) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// pretty sure it doesn't matter that we're always passing true here
|
// pretty sure it doesn't matter that we're always passing true here
|
||||||
if err := self.git.WorkingTree.UnStageFile([]string{node.Path}, true); err != nil {
|
if err := self.c.Git().WorkingTree.UnStageFile([]string{node.Path}, true); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -346,7 +407,7 @@ func (self *FilesController) Context() types.Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *FilesController) context() *context.WorkingTreeContext {
|
func (self *FilesController) context() *context.WorkingTreeContext {
|
||||||
return self.contexts.Files
|
return self.c.Contexts().Files
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *FilesController) getSelectedFile() *models.File {
|
func (self *FilesController) getSelectedFile() *models.File {
|
||||||
@ -373,10 +434,10 @@ func (self *FilesController) EnterFile(opts types.OnFocusOpts) error {
|
|||||||
|
|
||||||
file := node.File
|
file := node.File
|
||||||
|
|
||||||
submoduleConfigs := self.model.Submodules
|
submoduleConfigs := self.c.Model().Submodules
|
||||||
if file.IsSubmodule(submoduleConfigs) {
|
if file.IsSubmodule(submoduleConfigs) {
|
||||||
submoduleConfig := file.SubmoduleConfig(submoduleConfigs)
|
submoduleConfig := file.SubmoduleConfig(submoduleConfigs)
|
||||||
return self.enterSubmodule(submoduleConfig)
|
return self.c.Helpers().Repos.EnterSubmodule(submoduleConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
if file.HasInlineMergeConflicts {
|
if file.HasInlineMergeConflicts {
|
||||||
@ -386,7 +447,7 @@ func (self *FilesController) EnterFile(opts types.OnFocusOpts) error {
|
|||||||
return self.c.ErrorMsg(self.c.Tr.FileStagingRequirements)
|
return self.c.ErrorMsg(self.c.Tr.FileStagingRequirements)
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.c.PushContext(self.contexts.Staging, opts)
|
return self.c.PushContext(self.c.Contexts().Staging, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *FilesController) toggleStagedAll() error {
|
func (self *FilesController) toggleStagedAll() error {
|
||||||
@ -402,8 +463,8 @@ func (self *FilesController) toggleStagedAll() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *FilesController) toggleStagedAllWithLock() error {
|
func (self *FilesController) toggleStagedAllWithLock() error {
|
||||||
self.mutexes.RefreshingFilesMutex.Lock()
|
self.c.Mutexes().RefreshingFilesMutex.Lock()
|
||||||
defer self.mutexes.RefreshingFilesMutex.Unlock()
|
defer self.c.Mutexes().RefreshingFilesMutex.Unlock()
|
||||||
|
|
||||||
root := self.context().FileTreeViewModel.GetRoot()
|
root := self.context().FileTreeViewModel.GetRoot()
|
||||||
|
|
||||||
@ -420,7 +481,7 @@ func (self *FilesController) toggleStagedAllWithLock() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := self.git.WorkingTree.StageAll(); err != nil {
|
if err := self.c.Git().WorkingTree.StageAll(); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -430,7 +491,7 @@ func (self *FilesController) toggleStagedAllWithLock() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := self.git.WorkingTree.UnstageAll(); err != nil {
|
if err := self.c.Git().WorkingTree.UnstageAll(); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -441,7 +502,7 @@ func (self *FilesController) toggleStagedAllWithLock() error {
|
|||||||
func (self *FilesController) unstageFiles(node *filetree.FileNode) error {
|
func (self *FilesController) unstageFiles(node *filetree.FileNode) error {
|
||||||
return node.ForEachFile(func(file *models.File) error {
|
return node.ForEachFile(func(file *models.File) error {
|
||||||
if file.HasStagedChanges {
|
if file.HasStagedChanges {
|
||||||
if err := self.git.WorkingTree.UnStageFile(file.Names(), file.Tracked); err != nil {
|
if err := self.c.Git().WorkingTree.UnStageFile(file.Names(), file.Tracked); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -457,7 +518,7 @@ func (self *FilesController) ignoreOrExcludeTracked(node *filetree.FileNode, trA
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := self.git.WorkingTree.RemoveTrackedFiles(node.GetPath()); err != nil {
|
if err := self.c.Git().WorkingTree.RemoveTrackedFiles(node.GetPath()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -495,7 +556,7 @@ func (self *FilesController) ignore(node *filetree.FileNode) error {
|
|||||||
if node.GetPath() == ".gitignore" {
|
if node.GetPath() == ".gitignore" {
|
||||||
return self.c.ErrorMsg(self.c.Tr.Actions.IgnoreFileErr)
|
return self.c.ErrorMsg(self.c.Tr.Actions.IgnoreFileErr)
|
||||||
}
|
}
|
||||||
err := self.ignoreOrExcludeFile(node, self.c.Tr.IgnoreTracked, self.c.Tr.IgnoreTrackedPrompt, self.c.Tr.Actions.LcIgnoreExcludeFile, self.git.WorkingTree.Ignore)
|
err := self.ignoreOrExcludeFile(node, self.c.Tr.IgnoreTracked, self.c.Tr.IgnoreTrackedPrompt, self.c.Tr.Actions.LcIgnoreExcludeFile, self.c.Git().WorkingTree.Ignore)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -512,7 +573,7 @@ func (self *FilesController) exclude(node *filetree.FileNode) error {
|
|||||||
return self.c.ErrorMsg(self.c.Tr.Actions.ExcludeGitIgnoreErr)
|
return self.c.ErrorMsg(self.c.Tr.Actions.ExcludeGitIgnoreErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := self.ignoreOrExcludeFile(node, self.c.Tr.ExcludeTracked, self.c.Tr.ExcludeTrackedPrompt, self.c.Tr.Actions.ExcludeFile, self.git.WorkingTree.Exclude)
|
err := self.ignoreOrExcludeFile(node, self.c.Tr.ExcludeTracked, self.c.Tr.ExcludeTrackedPrompt, self.c.Tr.Actions.ExcludeFile, self.c.Git().WorkingTree.Exclude)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -552,19 +613,19 @@ func (self *FilesController) refresh() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *FilesController) handleAmendCommitPress() error {
|
func (self *FilesController) handleAmendCommitPress() error {
|
||||||
if len(self.model.Files) == 0 {
|
if len(self.c.Model().Files) == 0 {
|
||||||
return self.c.ErrorMsg(self.c.Tr.NoFilesStagedTitle)
|
return self.c.ErrorMsg(self.c.Tr.NoFilesStagedTitle)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.helpers.WorkingTree.AnyStagedFiles() {
|
if !self.c.Helpers().WorkingTree.AnyStagedFiles() {
|
||||||
return self.helpers.WorkingTree.PromptToStageAllAndRetry(self.handleAmendCommitPress)
|
return self.c.Helpers().WorkingTree.PromptToStageAllAndRetry(self.handleAmendCommitPress)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(self.model.Commits) == 0 {
|
if len(self.c.Model().Commits) == 0 {
|
||||||
return self.c.ErrorMsg(self.c.Tr.NoCommitToAmend)
|
return self.c.ErrorMsg(self.c.Tr.NoCommitToAmend)
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.helpers.AmendHelper.AmendHead()
|
return self.c.Helpers().AmendHelper.AmendHead()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *FilesController) handleStatusFilterPressed() error {
|
func (self *FilesController) handleStatusFilterPressed() error {
|
||||||
@ -603,7 +664,7 @@ func (self *FilesController) edit(node *filetree.FileNode) error {
|
|||||||
return self.c.ErrorMsg(self.c.Tr.ErrCannotEditDirectory)
|
return self.c.ErrorMsg(self.c.Tr.ErrCannotEditDirectory)
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.helpers.Files.EditFile(node.GetPath())
|
return self.c.Helpers().Files.EditFile(node.GetPath())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *FilesController) Open() error {
|
func (self *FilesController) Open() error {
|
||||||
@ -612,7 +673,7 @@ func (self *FilesController) Open() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.helpers.Files.OpenFile(node.GetPath())
|
return self.c.Helpers().Files.OpenFile(node.GetPath())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *FilesController) switchToMerge() error {
|
func (self *FilesController) switchToMerge() error {
|
||||||
@ -621,7 +682,7 @@ func (self *FilesController) switchToMerge() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.helpers.MergeConflicts.SwitchToMerge(file.Name)
|
return self.c.Helpers().MergeConflicts.SwitchToMerge(file.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *FilesController) createStashMenu() error {
|
func (self *FilesController) createStashMenu() error {
|
||||||
@ -631,28 +692,28 @@ func (self *FilesController) createStashMenu() error {
|
|||||||
{
|
{
|
||||||
Label: self.c.Tr.LcStashAllChanges,
|
Label: self.c.Tr.LcStashAllChanges,
|
||||||
OnPress: func() error {
|
OnPress: func() error {
|
||||||
if !self.helpers.WorkingTree.IsWorkingTreeDirty() {
|
if !self.c.Helpers().WorkingTree.IsWorkingTreeDirty() {
|
||||||
return self.c.ErrorMsg(self.c.Tr.NoFilesToStash)
|
return self.c.ErrorMsg(self.c.Tr.NoFilesToStash)
|
||||||
}
|
}
|
||||||
return self.handleStashSave(self.git.Stash.Save, self.c.Tr.Actions.StashAllChanges)
|
return self.handleStashSave(self.c.Git().Stash.Save, self.c.Tr.Actions.StashAllChanges)
|
||||||
},
|
},
|
||||||
Key: 'a',
|
Key: 'a',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Label: self.c.Tr.LcStashAllChangesKeepIndex,
|
Label: self.c.Tr.LcStashAllChangesKeepIndex,
|
||||||
OnPress: func() error {
|
OnPress: func() error {
|
||||||
if !self.helpers.WorkingTree.IsWorkingTreeDirty() {
|
if !self.c.Helpers().WorkingTree.IsWorkingTreeDirty() {
|
||||||
return self.c.ErrorMsg(self.c.Tr.NoFilesToStash)
|
return self.c.ErrorMsg(self.c.Tr.NoFilesToStash)
|
||||||
}
|
}
|
||||||
// if there are no staged files it behaves the same as Stash.Save
|
// if there are no staged files it behaves the same as Stash.Save
|
||||||
return self.handleStashSave(self.git.Stash.StashAndKeepIndex, self.c.Tr.Actions.StashAllChangesKeepIndex)
|
return self.handleStashSave(self.c.Git().Stash.StashAndKeepIndex, self.c.Tr.Actions.StashAllChangesKeepIndex)
|
||||||
},
|
},
|
||||||
Key: 'i',
|
Key: 'i',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Label: self.c.Tr.LcStashIncludeUntrackedChanges,
|
Label: self.c.Tr.LcStashIncludeUntrackedChanges,
|
||||||
OnPress: func() error {
|
OnPress: func() error {
|
||||||
return self.handleStashSave(self.git.Stash.StashIncludeUntrackedChanges, self.c.Tr.Actions.StashIncludeUntrackedChanges)
|
return self.handleStashSave(self.c.Git().Stash.StashIncludeUntrackedChanges, self.c.Tr.Actions.StashIncludeUntrackedChanges)
|
||||||
},
|
},
|
||||||
Key: 'U',
|
Key: 'U',
|
||||||
},
|
},
|
||||||
@ -660,24 +721,24 @@ func (self *FilesController) createStashMenu() error {
|
|||||||
Label: self.c.Tr.LcStashStagedChanges,
|
Label: self.c.Tr.LcStashStagedChanges,
|
||||||
OnPress: func() error {
|
OnPress: func() error {
|
||||||
// there must be something in staging otherwise the current implementation mucks the stash up
|
// there must be something in staging otherwise the current implementation mucks the stash up
|
||||||
if !self.helpers.WorkingTree.AnyStagedFiles() {
|
if !self.c.Helpers().WorkingTree.AnyStagedFiles() {
|
||||||
return self.c.ErrorMsg(self.c.Tr.NoTrackedStagedFilesStash)
|
return self.c.ErrorMsg(self.c.Tr.NoTrackedStagedFilesStash)
|
||||||
}
|
}
|
||||||
return self.handleStashSave(self.git.Stash.SaveStagedChanges, self.c.Tr.Actions.StashStagedChanges)
|
return self.handleStashSave(self.c.Git().Stash.SaveStagedChanges, self.c.Tr.Actions.StashStagedChanges)
|
||||||
},
|
},
|
||||||
Key: 's',
|
Key: 's',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Label: self.c.Tr.LcStashUnstagedChanges,
|
Label: self.c.Tr.LcStashUnstagedChanges,
|
||||||
OnPress: func() error {
|
OnPress: func() error {
|
||||||
if !self.helpers.WorkingTree.IsWorkingTreeDirty() {
|
if !self.c.Helpers().WorkingTree.IsWorkingTreeDirty() {
|
||||||
return self.c.ErrorMsg(self.c.Tr.NoFilesToStash)
|
return self.c.ErrorMsg(self.c.Tr.NoFilesToStash)
|
||||||
}
|
}
|
||||||
if self.helpers.WorkingTree.AnyStagedFiles() {
|
if self.c.Helpers().WorkingTree.AnyStagedFiles() {
|
||||||
return self.handleStashSave(self.git.Stash.StashUnstagedChanges, self.c.Tr.Actions.StashUnstagedChanges)
|
return self.handleStashSave(self.c.Git().Stash.StashUnstagedChanges, self.c.Tr.Actions.StashUnstagedChanges)
|
||||||
}
|
}
|
||||||
// ordinary stash
|
// ordinary stash
|
||||||
return self.handleStashSave(self.git.Stash.Save, self.c.Tr.Actions.StashUnstagedChanges)
|
return self.handleStashSave(self.c.Git().Stash.Save, self.c.Tr.Actions.StashUnstagedChanges)
|
||||||
},
|
},
|
||||||
Key: 'u',
|
Key: 'u',
|
||||||
},
|
},
|
||||||
@ -686,11 +747,11 @@ func (self *FilesController) createStashMenu() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *FilesController) stash() error {
|
func (self *FilesController) stash() error {
|
||||||
return self.handleStashSave(self.git.Stash.Save, self.c.Tr.Actions.StashAllChanges)
|
return self.handleStashSave(self.c.Git().Stash.Save, self.c.Tr.Actions.StashAllChanges)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *FilesController) createResetToUpstreamMenu() error {
|
func (self *FilesController) createResetToUpstreamMenu() error {
|
||||||
return self.helpers.Refs.CreateGitResetMenu("@{upstream}")
|
return self.c.Helpers().Refs.CreateGitResetMenu("@{upstream}")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *FilesController) handleToggleDirCollapsed() error {
|
func (self *FilesController) handleToggleDirCollapsed() error {
|
||||||
@ -701,7 +762,7 @@ func (self *FilesController) handleToggleDirCollapsed() error {
|
|||||||
|
|
||||||
self.context().FileTreeViewModel.ToggleCollapsed(node.GetPath())
|
self.context().FileTreeViewModel.ToggleCollapsed(node.GetPath())
|
||||||
|
|
||||||
if err := self.c.PostRefreshUpdate(self.contexts.Files); err != nil {
|
if err := self.c.PostRefreshUpdate(self.c.Contexts().Files); err != nil {
|
||||||
self.c.Log.Error(err)
|
self.c.Log.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -747,7 +808,7 @@ func (self *FilesController) fetch() error {
|
|||||||
|
|
||||||
func (self *FilesController) fetchAux() (err error) {
|
func (self *FilesController) fetchAux() (err error) {
|
||||||
self.c.LogAction("Fetch")
|
self.c.LogAction("Fetch")
|
||||||
err = self.git.Sync.Fetch(git_commands.FetchOptions{})
|
err = self.c.Git().Sync.Fetch(git_commands.FetchOptions{})
|
||||||
|
|
||||||
if err != nil && strings.Contains(err.Error(), "exit status 128") {
|
if err != nil && strings.Contains(err.Error(), "exit status 128") {
|
||||||
_ = self.c.ErrorMsg(self.c.Tr.PassUnameWrong)
|
_ = self.c.ErrorMsg(self.c.Tr.PassUnameWrong)
|
||||||
|
@ -12,17 +12,17 @@ import (
|
|||||||
|
|
||||||
type FilesRemoveController struct {
|
type FilesRemoveController struct {
|
||||||
baseController
|
baseController
|
||||||
*controllerCommon
|
c *ControllerCommon
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ types.IController = &FilesRemoveController{}
|
var _ types.IController = &FilesRemoveController{}
|
||||||
|
|
||||||
func NewFilesRemoveController(
|
func NewFilesRemoveController(
|
||||||
common *controllerCommon,
|
common *ControllerCommon,
|
||||||
) *FilesRemoveController {
|
) *FilesRemoveController {
|
||||||
return &FilesRemoveController{
|
return &FilesRemoveController{
|
||||||
baseController: baseController{},
|
baseController: baseController{},
|
||||||
controllerCommon: common,
|
c: common,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ func (self *FilesRemoveController) remove(node *filetree.FileNode) error {
|
|||||||
Label: self.c.Tr.LcDiscardAllChanges,
|
Label: self.c.Tr.LcDiscardAllChanges,
|
||||||
OnPress: func() error {
|
OnPress: func() error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.DiscardAllChangesInDirectory)
|
self.c.LogAction(self.c.Tr.Actions.DiscardAllChangesInDirectory)
|
||||||
if err := self.git.WorkingTree.DiscardAllDirChanges(node); err != nil {
|
if err := self.c.Git().WorkingTree.DiscardAllDirChanges(node); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}})
|
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}})
|
||||||
@ -67,7 +67,7 @@ func (self *FilesRemoveController) remove(node *filetree.FileNode) error {
|
|||||||
Label: self.c.Tr.LcDiscardUnstagedChanges,
|
Label: self.c.Tr.LcDiscardUnstagedChanges,
|
||||||
OnPress: func() error {
|
OnPress: func() error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.DiscardUnstagedChangesInDirectory)
|
self.c.LogAction(self.c.Tr.Actions.DiscardUnstagedChangesInDirectory)
|
||||||
if err := self.git.WorkingTree.DiscardUnstagedDirChanges(node); err != nil {
|
if err := self.c.Git().WorkingTree.DiscardUnstagedDirChanges(node); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ func (self *FilesRemoveController) remove(node *filetree.FileNode) error {
|
|||||||
} else {
|
} else {
|
||||||
file := node.File
|
file := node.File
|
||||||
|
|
||||||
submodules := self.model.Submodules
|
submodules := self.c.Model().Submodules
|
||||||
if file.IsSubmodule(submodules) {
|
if file.IsSubmodule(submodules) {
|
||||||
submodule := file.SubmoduleConfig(submodules)
|
submodule := file.SubmoduleConfig(submodules)
|
||||||
|
|
||||||
@ -103,7 +103,7 @@ func (self *FilesRemoveController) remove(node *filetree.FileNode) error {
|
|||||||
Label: self.c.Tr.LcDiscardAllChanges,
|
Label: self.c.Tr.LcDiscardAllChanges,
|
||||||
OnPress: func() error {
|
OnPress: func() error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.DiscardAllChangesInFile)
|
self.c.LogAction(self.c.Tr.Actions.DiscardAllChangesInFile)
|
||||||
if err := self.git.WorkingTree.DiscardAllFileChanges(file); err != nil {
|
if err := self.c.Git().WorkingTree.DiscardAllFileChanges(file); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}})
|
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}})
|
||||||
@ -123,7 +123,7 @@ func (self *FilesRemoveController) remove(node *filetree.FileNode) error {
|
|||||||
Label: self.c.Tr.LcDiscardUnstagedChanges,
|
Label: self.c.Tr.LcDiscardUnstagedChanges,
|
||||||
OnPress: func() error {
|
OnPress: func() error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.DiscardAllUnstagedChangesInFile)
|
self.c.LogAction(self.c.Tr.Actions.DiscardAllUnstagedChangesInFile)
|
||||||
if err := self.git.WorkingTree.DiscardUnstagedFileChanges(file); err != nil {
|
if err := self.c.Git().WorkingTree.DiscardUnstagedFileChanges(file); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,17 +148,17 @@ func (self *FilesRemoveController) ResetSubmodule(submodule *models.SubmoduleCon
|
|||||||
return self.c.WithWaitingStatus(self.c.Tr.LcResettingSubmoduleStatus, func() error {
|
return self.c.WithWaitingStatus(self.c.Tr.LcResettingSubmoduleStatus, func() error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.ResetSubmodule)
|
self.c.LogAction(self.c.Tr.Actions.ResetSubmodule)
|
||||||
|
|
||||||
file := self.helpers.WorkingTree.FileForSubmodule(submodule)
|
file := self.c.Helpers().WorkingTree.FileForSubmodule(submodule)
|
||||||
if file != nil {
|
if file != nil {
|
||||||
if err := self.git.WorkingTree.UnStageFile(file.Names(), file.Tracked); err != nil {
|
if err := self.c.Git().WorkingTree.UnStageFile(file.Names(), file.Tracked); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := self.git.Submodule.Stash(submodule); err != nil {
|
if err := self.c.Git().Submodule.Stash(submodule); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
if err := self.git.Submodule.Reset(submodule); err != nil {
|
if err := self.c.Git().Submodule.Reset(submodule); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,5 +182,5 @@ func (self *FilesRemoveController) Context() types.Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *FilesRemoveController) context() *context.WorkingTreeContext {
|
func (self *FilesRemoveController) context() *context.WorkingTreeContext {
|
||||||
return self.contexts.Files
|
return self.c.Contexts().Files
|
||||||
}
|
}
|
||||||
|
78
pkg/gui/controllers/filtering_menu_action.go
Normal file
78
pkg/gui/controllers/filtering_menu_action.go
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FilteringMenuAction struct {
|
||||||
|
c *ControllerCommon
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *FilteringMenuAction) Call() error {
|
||||||
|
fileName := ""
|
||||||
|
switch self.c.CurrentSideContext() {
|
||||||
|
case self.c.Contexts().Files:
|
||||||
|
node := self.c.Contexts().Files.GetSelected()
|
||||||
|
if node != nil {
|
||||||
|
fileName = node.GetPath()
|
||||||
|
}
|
||||||
|
case self.c.Contexts().CommitFiles:
|
||||||
|
node := self.c.Contexts().CommitFiles.GetSelected()
|
||||||
|
if node != nil {
|
||||||
|
fileName = node.GetPath()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
menuItems := []*types.MenuItem{}
|
||||||
|
|
||||||
|
if fileName != "" {
|
||||||
|
menuItems = append(menuItems, &types.MenuItem{
|
||||||
|
Label: fmt.Sprintf("%s '%s'", self.c.Tr.LcFilterBy, fileName),
|
||||||
|
OnPress: func() error {
|
||||||
|
return self.setFiltering(fileName)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
menuItems = append(menuItems, &types.MenuItem{
|
||||||
|
Label: self.c.Tr.LcFilterPathOption,
|
||||||
|
OnPress: func() error {
|
||||||
|
return self.c.Prompt(types.PromptOpts{
|
||||||
|
FindSuggestionsFunc: self.c.Helpers().Suggestions.GetFilePathSuggestionsFunc(),
|
||||||
|
Title: self.c.Tr.EnterFileName,
|
||||||
|
HandleConfirm: func(response string) error {
|
||||||
|
return self.setFiltering(strings.TrimSpace(response))
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if self.c.Modes().Filtering.Active() {
|
||||||
|
menuItems = append(menuItems, &types.MenuItem{
|
||||||
|
Label: self.c.Tr.LcExitFilterMode,
|
||||||
|
OnPress: self.c.Helpers().Mode.ClearFiltering,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.c.Menu(types.CreateMenuOptions{Title: self.c.Tr.FilteringMenuTitle, Items: menuItems})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *FilteringMenuAction) setFiltering(path string) error {
|
||||||
|
self.c.Modes().Filtering.SetPath(path)
|
||||||
|
|
||||||
|
repoState := self.c.State().GetRepoState()
|
||||||
|
if repoState.GetScreenMode() == types.SCREEN_NORMAL {
|
||||||
|
repoState.SetScreenMode(types.SCREEN_HALF)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := self.c.PushContext(self.c.Contexts().LocalCommits); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.COMMITS}, Then: func() {
|
||||||
|
self.c.Contexts().LocalCommits.SetSelectedLineIdx(0)
|
||||||
|
}})
|
||||||
|
}
|
@ -11,17 +11,17 @@ import (
|
|||||||
|
|
||||||
type GitFlowController struct {
|
type GitFlowController struct {
|
||||||
baseController
|
baseController
|
||||||
*controllerCommon
|
c *ControllerCommon
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ types.IController = &GitFlowController{}
|
var _ types.IController = &GitFlowController{}
|
||||||
|
|
||||||
func NewGitFlowController(
|
func NewGitFlowController(
|
||||||
common *controllerCommon,
|
common *ControllerCommon,
|
||||||
) *GitFlowController {
|
) *GitFlowController {
|
||||||
return &GitFlowController{
|
return &GitFlowController{
|
||||||
baseController: baseController{},
|
baseController: baseController{},
|
||||||
controllerCommon: common,
|
c: common,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ func (self *GitFlowController) GetKeybindings(opts types.KeybindingsOpts) []*typ
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *GitFlowController) handleCreateGitFlowMenu(branch *models.Branch) error {
|
func (self *GitFlowController) handleCreateGitFlowMenu(branch *models.Branch) error {
|
||||||
if !self.git.Flow.GitFlowEnabled() {
|
if !self.c.Git().Flow.GitFlowEnabled() {
|
||||||
return self.c.ErrorMsg("You need to install git-flow and enable it in this repo to use git-flow features")
|
return self.c.ErrorMsg("You need to install git-flow and enable it in this repo to use git-flow features")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ func (self *GitFlowController) handleCreateGitFlowMenu(branch *models.Branch) er
|
|||||||
HandleConfirm: func(name string) error {
|
HandleConfirm: func(name string) error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.GitFlowStart)
|
self.c.LogAction(self.c.Tr.Actions.GitFlowStart)
|
||||||
return self.c.RunSubprocessAndRefresh(
|
return self.c.RunSubprocessAndRefresh(
|
||||||
self.git.Flow.StartCmdObj(branchType, name),
|
self.c.Git().Flow.StartCmdObj(branchType, name),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -94,7 +94,7 @@ func (self *GitFlowController) handleCreateGitFlowMenu(branch *models.Branch) er
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *GitFlowController) gitFlowFinishBranch(branchName string) error {
|
func (self *GitFlowController) gitFlowFinishBranch(branchName string) error {
|
||||||
cmdObj, err := self.git.Flow.FinishCmdObj(branchName)
|
cmdObj, err := self.c.Git().Flow.FinishCmdObj(branchName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
@ -119,5 +119,5 @@ func (self *GitFlowController) Context() types.Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *GitFlowController) context() *context.BranchesContext {
|
func (self *GitFlowController) context() *context.BranchesContext {
|
||||||
return self.contexts.Branches
|
return self.c.Contexts().Branches
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,21 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"github.com/jesseduffield/gocui"
|
||||||
|
|
||||||
"github.com/jesseduffield/generics/slices"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
|
||||||
"github.com/samber/lo"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type GlobalController struct {
|
type GlobalController struct {
|
||||||
baseController
|
baseController
|
||||||
*controllerCommon
|
c *ControllerCommon
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGlobalController(
|
func NewGlobalController(
|
||||||
common *controllerCommon,
|
common *ControllerCommon,
|
||||||
) *GlobalController {
|
) *GlobalController {
|
||||||
return &GlobalController{
|
return &GlobalController{
|
||||||
baseController: baseController{},
|
baseController: baseController{},
|
||||||
controllerCommon: common,
|
c: common,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,47 +26,143 @@ func (self *GlobalController) GetKeybindings(opts types.KeybindingsOpts) []*type
|
|||||||
Handler: self.customCommand,
|
Handler: self.customCommand,
|
||||||
Description: self.c.Tr.LcExecuteCustomCommand,
|
Description: self.c.Tr.LcExecuteCustomCommand,
|
||||||
},
|
},
|
||||||
}
|
{
|
||||||
}
|
Key: opts.GetKey(opts.Config.Universal.CreatePatchOptionsMenu),
|
||||||
|
Handler: self.createCustomPatchOptionsMenu,
|
||||||
func (self *GlobalController) customCommand() error {
|
Description: self.c.Tr.ViewPatchOptions,
|
||||||
return self.c.Prompt(types.PromptOpts{
|
OpensMenu: true,
|
||||||
Title: self.c.Tr.CustomCommand,
|
|
||||||
FindSuggestionsFunc: self.GetCustomCommandsHistorySuggestionsFunc(),
|
|
||||||
HandleConfirm: func(command string) error {
|
|
||||||
if self.shouldSaveCommand(command) {
|
|
||||||
self.c.GetAppState().CustomCommandsHistory = utils.Limit(
|
|
||||||
lo.Uniq(append(self.c.GetAppState().CustomCommandsHistory, command)),
|
|
||||||
1000,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
err := self.c.SaveAppState()
|
|
||||||
if err != nil {
|
|
||||||
self.c.Log.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
self.c.LogAction(self.c.Tr.Actions.CustomCommand)
|
|
||||||
return self.c.RunSubprocessAndRefresh(
|
|
||||||
self.os.Cmd.NewShell(command),
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
})
|
{
|
||||||
}
|
Key: opts.GetKey(opts.Config.Universal.CreateRebaseOptionsMenu),
|
||||||
|
Handler: self.c.Helpers().MergeAndRebase.CreateRebaseOptionsMenu,
|
||||||
// this mimics the shell functionality `ignorespace`
|
Description: self.c.Tr.ViewMergeRebaseOptions,
|
||||||
// which doesn't save a command to history if it starts with a space
|
OpensMenu: true,
|
||||||
func (self *GlobalController) shouldSaveCommand(command string) bool {
|
},
|
||||||
return !strings.HasPrefix(command, " ")
|
{
|
||||||
}
|
Key: opts.GetKey(opts.Config.Universal.Refresh),
|
||||||
|
Handler: self.refresh,
|
||||||
func (self *GlobalController) GetCustomCommandsHistorySuggestionsFunc() func(string) []*types.Suggestion {
|
Description: self.c.Tr.LcRefresh,
|
||||||
// reversing so that we display the latest command first
|
},
|
||||||
history := slices.Reverse(self.c.GetAppState().CustomCommandsHistory)
|
{
|
||||||
|
Key: opts.GetKey(opts.Config.Universal.NextScreenMode),
|
||||||
return helpers.FuzzySearchFunc(history)
|
Handler: self.nextScreenMode,
|
||||||
|
Description: self.c.Tr.LcNextScreenMode,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: opts.GetKey(opts.Config.Universal.PrevScreenMode),
|
||||||
|
Handler: self.prevScreenMode,
|
||||||
|
Description: self.c.Tr.LcPrevScreenMode,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ViewName: "",
|
||||||
|
Key: opts.GetKey(opts.Config.Universal.OptionMenu),
|
||||||
|
Handler: self.createOptionsMenu,
|
||||||
|
OpensMenu: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ViewName: "",
|
||||||
|
Key: opts.GetKey(opts.Config.Universal.OptionMenuAlt1),
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
// 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)
|
||||||
|
Description: self.c.Tr.LcOpenMenu,
|
||||||
|
Handler: self.createOptionsMenu,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ViewName: "",
|
||||||
|
Key: opts.GetKey(opts.Config.Universal.FilteringMenu),
|
||||||
|
Handler: self.createFilteringMenu,
|
||||||
|
Description: self.c.Tr.LcOpenFilteringMenu,
|
||||||
|
OpensMenu: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: opts.GetKey(opts.Config.Universal.DiffingMenu),
|
||||||
|
Handler: self.createDiffingMenu,
|
||||||
|
Description: self.c.Tr.LcOpenDiffingMenu,
|
||||||
|
OpensMenu: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: opts.GetKey(opts.Config.Universal.DiffingMenuAlt),
|
||||||
|
Handler: self.createDiffingMenu,
|
||||||
|
Description: self.c.Tr.LcOpenDiffingMenu,
|
||||||
|
OpensMenu: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: opts.GetKey(opts.Config.Universal.Quit),
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: self.quit,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: opts.GetKey(opts.Config.Universal.QuitAlt1),
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: self.quit,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: opts.GetKey(opts.Config.Universal.QuitWithoutChangingDirectory),
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: self.quitWithoutChangingDirectory,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: opts.GetKey(opts.Config.Universal.Return),
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: self.escape,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: opts.GetKey(opts.Config.Universal.ToggleWhitespaceInDiffView),
|
||||||
|
Handler: self.toggleWhitespace,
|
||||||
|
Description: self.c.Tr.ToggleWhitespaceInDiffView,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *GlobalController) Context() types.Context {
|
func (self *GlobalController) Context() types.Context {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *GlobalController) customCommand() error {
|
||||||
|
return (&CustomCommandAction{c: self.c}).Call()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *GlobalController) createCustomPatchOptionsMenu() error {
|
||||||
|
return (&CustomPatchOptionsMenuAction{c: self.c}).Call()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *GlobalController) refresh() error {
|
||||||
|
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *GlobalController) nextScreenMode() error {
|
||||||
|
return (&ScreenModeActions{c: self.c}).Next()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *GlobalController) prevScreenMode() error {
|
||||||
|
return (&ScreenModeActions{c: self.c}).Prev()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *GlobalController) createOptionsMenu() error {
|
||||||
|
return (&OptionsMenuAction{c: self.c}).Call()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *GlobalController) createFilteringMenu() error {
|
||||||
|
return (&FilteringMenuAction{c: self.c}).Call()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *GlobalController) createDiffingMenu() error {
|
||||||
|
return (&DiffingMenuAction{c: self.c}).Call()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *GlobalController) quit() error {
|
||||||
|
return (&QuitActions{c: self.c}).Quit()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *GlobalController) quitWithoutChangingDirectory() error {
|
||||||
|
return (&QuitActions{c: self.c}).QuitWithoutChangingDirectory()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *GlobalController) escape() error {
|
||||||
|
return (&QuitActions{c: self.c}).Escape()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *GlobalController) toggleWhitespace() error {
|
||||||
|
return (&ToggleWhitespaceAction{c: self.c}).Call()
|
||||||
|
}
|
||||||
|
@ -1,24 +1,20 @@
|
|||||||
package helpers
|
package helpers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AmendHelper struct {
|
type AmendHelper struct {
|
||||||
c *types.HelperCommon
|
c *HelperCommon
|
||||||
git *commands.GitCommand
|
|
||||||
gpg *GpgHelper
|
gpg *GpgHelper
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAmendHelper(
|
func NewAmendHelper(
|
||||||
c *types.HelperCommon,
|
c *HelperCommon,
|
||||||
git *commands.GitCommand,
|
|
||||||
gpg *GpgHelper,
|
gpg *GpgHelper,
|
||||||
) *AmendHelper {
|
) *AmendHelper {
|
||||||
return &AmendHelper{
|
return &AmendHelper{
|
||||||
c: c,
|
c: c,
|
||||||
git: git,
|
|
||||||
gpg: gpg,
|
gpg: gpg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -28,7 +24,7 @@ func (self *AmendHelper) AmendHead() error {
|
|||||||
Title: self.c.Tr.AmendLastCommitTitle,
|
Title: self.c.Tr.AmendLastCommitTitle,
|
||||||
Prompt: self.c.Tr.SureToAmend,
|
Prompt: self.c.Tr.SureToAmend,
|
||||||
HandleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
cmdObj := self.git.Commit.AmendHeadCmdObj()
|
cmdObj := self.c.Git().Commit.AmendHeadCmdObj()
|
||||||
self.c.LogAction(self.c.Tr.Actions.AmendCommit)
|
self.c.LogAction(self.c.Tr.Actions.AmendCommit)
|
||||||
return self.gpg.WithGpgHandling(cmdObj, self.c.Tr.AmendingStatus, nil)
|
return self.gpg.WithGpgHandling(cmdObj, self.c.Tr.AmendingStatus, nil)
|
||||||
},
|
},
|
||||||
|
68
pkg/gui/controllers/helpers/app_status_helper.go
Normal file
68
pkg/gui/controllers/helpers/app_status_helper.go
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/status"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AppStatusHelper struct {
|
||||||
|
c *HelperCommon
|
||||||
|
|
||||||
|
statusMgr func() *status.StatusManager
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAppStatusHelper(c *HelperCommon, statusMgr func() *status.StatusManager) *AppStatusHelper {
|
||||||
|
return &AppStatusHelper{
|
||||||
|
c: c,
|
||||||
|
statusMgr: statusMgr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *AppStatusHelper) Toast(message string) {
|
||||||
|
self.statusMgr().AddToastStatus(message)
|
||||||
|
|
||||||
|
self.renderAppStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
// withWaitingStatus wraps a function and shows a waiting status while the function is still executing
|
||||||
|
func (self *AppStatusHelper) WithWaitingStatus(message string, f func() error) {
|
||||||
|
go utils.Safe(func() {
|
||||||
|
self.statusMgr().WithWaitingStatus(message, func() {
|
||||||
|
self.renderAppStatus()
|
||||||
|
|
||||||
|
if err := f(); err != nil {
|
||||||
|
self.c.OnUIThread(func() error {
|
||||||
|
return self.c.Error(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *AppStatusHelper) HasStatus() bool {
|
||||||
|
return self.statusMgr().HasStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *AppStatusHelper) GetStatusString() string {
|
||||||
|
return self.statusMgr().GetStatusString()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *AppStatusHelper) renderAppStatus() {
|
||||||
|
go utils.Safe(func() {
|
||||||
|
ticker := time.NewTicker(time.Millisecond * 50)
|
||||||
|
defer ticker.Stop()
|
||||||
|
for range ticker.C {
|
||||||
|
appStatus := self.statusMgr().GetStatusString()
|
||||||
|
self.c.OnUIThread(func() error {
|
||||||
|
self.c.SetViewContent(self.c.Views().AppStatus, appStatus)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if appStatus == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
@ -1,23 +1,15 @@
|
|||||||
package helpers
|
package helpers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type BisectHelper struct {
|
type BisectHelper struct {
|
||||||
c *types.HelperCommon
|
c *HelperCommon
|
||||||
git *commands.GitCommand
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBisectHelper(
|
func NewBisectHelper(c *HelperCommon) *BisectHelper {
|
||||||
c *types.HelperCommon,
|
return &BisectHelper{c: c}
|
||||||
git *commands.GitCommand,
|
|
||||||
) *BisectHelper {
|
|
||||||
return &BisectHelper{
|
|
||||||
c: c,
|
|
||||||
git: git,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *BisectHelper) Reset() error {
|
func (self *BisectHelper) Reset() error {
|
||||||
@ -26,7 +18,7 @@ func (self *BisectHelper) Reset() error {
|
|||||||
Prompt: self.c.Tr.Bisect.ResetPrompt,
|
Prompt: self.c.Tr.Bisect.ResetPrompt,
|
||||||
HandleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.ResetBisect)
|
self.c.LogAction(self.c.Tr.Actions.ResetBisect)
|
||||||
if err := self.git.Bisect.Reset(); err != nil {
|
if err := self.c.Git().Bisect.Reset(); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,22 +1,13 @@
|
|||||||
package helpers
|
package helpers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jesseduffield/generics/set"
|
|
||||||
"github.com/jesseduffield/generics/slices"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/modes/cherrypicking"
|
"github.com/jesseduffield/lazygit/pkg/gui/modes/cherrypicking"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CherryPickHelper struct {
|
type CherryPickHelper struct {
|
||||||
c *types.HelperCommon
|
c *HelperCommon
|
||||||
|
|
||||||
git *commands.GitCommand
|
|
||||||
|
|
||||||
contexts *context.ContextTree
|
|
||||||
getData func() *cherrypicking.CherryPicking
|
|
||||||
|
|
||||||
rebaseHelper *MergeAndRebaseHelper
|
rebaseHelper *MergeAndRebaseHelper
|
||||||
}
|
}
|
||||||
@ -25,38 +16,31 @@ type CherryPickHelper struct {
|
|||||||
// even if in truth we're running git cherry-pick
|
// even if in truth we're running git cherry-pick
|
||||||
|
|
||||||
func NewCherryPickHelper(
|
func NewCherryPickHelper(
|
||||||
c *types.HelperCommon,
|
c *HelperCommon,
|
||||||
git *commands.GitCommand,
|
|
||||||
contexts *context.ContextTree,
|
|
||||||
getData func() *cherrypicking.CherryPicking,
|
|
||||||
rebaseHelper *MergeAndRebaseHelper,
|
rebaseHelper *MergeAndRebaseHelper,
|
||||||
) *CherryPickHelper {
|
) *CherryPickHelper {
|
||||||
return &CherryPickHelper{
|
return &CherryPickHelper{
|
||||||
c: c,
|
c: c,
|
||||||
git: git,
|
|
||||||
contexts: contexts,
|
|
||||||
getData: getData,
|
|
||||||
rebaseHelper: rebaseHelper,
|
rebaseHelper: rebaseHelper,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *CherryPickHelper) getData() *cherrypicking.CherryPicking {
|
||||||
|
return self.c.Modes().CherryPicking
|
||||||
|
}
|
||||||
|
|
||||||
func (self *CherryPickHelper) Copy(commit *models.Commit, commitsList []*models.Commit, context types.Context) error {
|
func (self *CherryPickHelper) Copy(commit *models.Commit, commitsList []*models.Commit, context types.Context) error {
|
||||||
if err := self.resetIfNecessary(context); err != nil {
|
if err := self.resetIfNecessary(context); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// we will un-copy it if it's already copied
|
// we will un-copy it if it's already copied
|
||||||
for index, cherryPickedCommit := range self.getData().CherryPickedCommits {
|
if self.getData().SelectedShaSet().Includes(commit.Sha) {
|
||||||
if commit.Sha == cherryPickedCommit.Sha {
|
self.getData().Remove(commit, commitsList)
|
||||||
self.getData().CherryPickedCommits = append(
|
} else {
|
||||||
self.getData().CherryPickedCommits[0:index],
|
self.getData().Add(commit, commitsList)
|
||||||
self.getData().CherryPickedCommits[index+1:]...,
|
|
||||||
)
|
|
||||||
return self.rerender()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.add(commit, commitsList)
|
|
||||||
return self.rerender()
|
return self.rerender()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +49,7 @@ func (self *CherryPickHelper) CopyRange(selectedIndex int, commitsList []*models
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
commitSet := self.CherryPickedCommitShaSet()
|
commitSet := self.getData().SelectedShaSet()
|
||||||
|
|
||||||
// find the last commit that is copied that's above our position
|
// find the last commit that is copied that's above our position
|
||||||
// if there are none, startIndex = 0
|
// if there are none, startIndex = 0
|
||||||
@ -78,7 +62,7 @@ func (self *CherryPickHelper) CopyRange(selectedIndex int, commitsList []*models
|
|||||||
|
|
||||||
for index := startIndex; index <= selectedIndex; index++ {
|
for index := startIndex; index <= selectedIndex; index++ {
|
||||||
commit := commitsList[index]
|
commit := commitsList[index]
|
||||||
self.add(commit, commitsList)
|
self.getData().Add(commit, commitsList)
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.rerender()
|
return self.rerender()
|
||||||
@ -93,7 +77,7 @@ func (self *CherryPickHelper) Paste() error {
|
|||||||
HandleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
return self.c.WithWaitingStatus(self.c.Tr.CherryPickingStatus, func() error {
|
return self.c.WithWaitingStatus(self.c.Tr.CherryPickingStatus, func() error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.CherryPick)
|
self.c.LogAction(self.c.Tr.Actions.CherryPick)
|
||||||
err := self.git.Rebase.CherryPickCommits(self.getData().CherryPickedCommits)
|
err := self.c.Git().Rebase.CherryPickCommits(self.getData().CherryPickedCommits)
|
||||||
return self.rebaseHelper.CheckMergeOrRebase(err)
|
return self.rebaseHelper.CheckMergeOrRebase(err)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@ -107,26 +91,6 @@ func (self *CherryPickHelper) Reset() error {
|
|||||||
return self.rerender()
|
return self.rerender()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CherryPickHelper) CherryPickedCommitShaSet() *set.Set[string] {
|
|
||||||
shas := slices.Map(self.getData().CherryPickedCommits, func(commit *models.Commit) string {
|
|
||||||
return commit.Sha
|
|
||||||
})
|
|
||||||
return set.NewFromSlice(shas)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *CherryPickHelper) add(selectedCommit *models.Commit, commitsList []*models.Commit) {
|
|
||||||
commitSet := self.CherryPickedCommitShaSet()
|
|
||||||
commitSet.Add(selectedCommit.Sha)
|
|
||||||
|
|
||||||
cherryPickedCommits := slices.Filter(commitsList, func(commit *models.Commit) bool {
|
|
||||||
return commitSet.Includes(commit.Sha)
|
|
||||||
})
|
|
||||||
|
|
||||||
self.getData().CherryPickedCommits = slices.Map(cherryPickedCommits, func(commit *models.Commit) *models.Commit {
|
|
||||||
return &models.Commit{Name: commit.Name, Sha: commit.Sha}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// you can only copy from one context at a time, because the order and position of commits matter
|
// you can only copy from one context at a time, because the order and position of commits matter
|
||||||
func (self *CherryPickHelper) resetIfNecessary(context types.Context) error {
|
func (self *CherryPickHelper) resetIfNecessary(context types.Context) error {
|
||||||
oldContextKey := types.ContextKey(self.getData().ContextKey)
|
oldContextKey := types.ContextKey(self.getData().ContextKey)
|
||||||
@ -142,9 +106,9 @@ func (self *CherryPickHelper) resetIfNecessary(context types.Context) error {
|
|||||||
|
|
||||||
func (self *CherryPickHelper) rerender() error {
|
func (self *CherryPickHelper) rerender() error {
|
||||||
for _, context := range []types.Context{
|
for _, context := range []types.Context{
|
||||||
self.contexts.LocalCommits,
|
self.c.Contexts().LocalCommits,
|
||||||
self.contexts.ReflogCommits,
|
self.c.Contexts().ReflogCommits,
|
||||||
self.contexts.SubCommits,
|
self.c.Contexts().SubCommits,
|
||||||
} {
|
} {
|
||||||
if err := self.c.PostRefreshUpdate(context); err != nil {
|
if err := self.c.PostRefreshUpdate(context); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -3,7 +3,6 @@ package helpers
|
|||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -12,38 +11,29 @@ type ICommitsHelper interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type CommitsHelper struct {
|
type CommitsHelper struct {
|
||||||
c *types.HelperCommon
|
c *HelperCommon
|
||||||
|
|
||||||
model *types.Model
|
|
||||||
contexts *context.ContextTree
|
|
||||||
getCommitSummary func() string
|
getCommitSummary func() string
|
||||||
setCommitSummary func(string)
|
setCommitSummary func(string)
|
||||||
getCommitDescription func() string
|
getCommitDescription func() string
|
||||||
setCommitDescription func(string)
|
setCommitDescription func(string)
|
||||||
renderCommitLength func()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ ICommitsHelper = &CommitsHelper{}
|
var _ ICommitsHelper = &CommitsHelper{}
|
||||||
|
|
||||||
func NewCommitsHelper(
|
func NewCommitsHelper(
|
||||||
c *types.HelperCommon,
|
c *HelperCommon,
|
||||||
model *types.Model,
|
|
||||||
contexts *context.ContextTree,
|
|
||||||
getCommitSummary func() string,
|
getCommitSummary func() string,
|
||||||
setCommitSummary func(string),
|
setCommitSummary func(string),
|
||||||
getCommitDescription func() string,
|
getCommitDescription func() string,
|
||||||
setCommitDescription func(string),
|
setCommitDescription func(string),
|
||||||
renderCommitLength func(),
|
|
||||||
) *CommitsHelper {
|
) *CommitsHelper {
|
||||||
return &CommitsHelper{
|
return &CommitsHelper{
|
||||||
c: c,
|
c: c,
|
||||||
model: model,
|
|
||||||
contexts: contexts,
|
|
||||||
getCommitSummary: getCommitSummary,
|
getCommitSummary: getCommitSummary,
|
||||||
setCommitSummary: setCommitSummary,
|
setCommitSummary: setCommitSummary,
|
||||||
getCommitDescription: getCommitDescription,
|
getCommitDescription: getCommitDescription,
|
||||||
setCommitDescription: setCommitDescription,
|
setCommitDescription: setCommitDescription,
|
||||||
renderCommitLength: renderCommitLength,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +52,7 @@ func (self *CommitsHelper) SetMessageAndDescriptionInView(message string) {
|
|||||||
|
|
||||||
self.setCommitSummary(summary)
|
self.setCommitSummary(summary)
|
||||||
self.setCommitDescription(description)
|
self.setCommitDescription(description)
|
||||||
self.renderCommitLength()
|
self.c.Contexts().CommitMessage.RenderCommitLength()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitsHelper) JoinCommitMessageAndDescription() string {
|
func (self *CommitsHelper) JoinCommitMessageAndDescription() string {
|
||||||
@ -78,7 +68,7 @@ func (self *CommitsHelper) UpdateCommitPanelView(message string) {
|
|||||||
self.SetMessageAndDescriptionInView(message)
|
self.SetMessageAndDescriptionInView(message)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
message = self.contexts.CommitMessage.GetPreservedMessage()
|
message = self.c.Contexts().CommitMessage.GetPreservedMessage()
|
||||||
if message != "" {
|
if message != "" {
|
||||||
self.SetMessageAndDescriptionInView(message)
|
self.SetMessageAndDescriptionInView(message)
|
||||||
} else {
|
} else {
|
||||||
@ -95,7 +85,7 @@ type OpenCommitMessagePanelOpts struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitsHelper) OpenCommitMessagePanel(opts *OpenCommitMessagePanelOpts) error {
|
func (self *CommitsHelper) OpenCommitMessagePanel(opts *OpenCommitMessagePanelOpts) error {
|
||||||
self.contexts.CommitMessage.SetPanelState(
|
self.c.Contexts().CommitMessage.SetPanelState(
|
||||||
opts.CommitIndex,
|
opts.CommitIndex,
|
||||||
opts.Title,
|
opts.Title,
|
||||||
opts.PreserveMessage,
|
opts.PreserveMessage,
|
||||||
@ -109,8 +99,8 @@ func (self *CommitsHelper) OpenCommitMessagePanel(opts *OpenCommitMessagePanelOp
|
|||||||
|
|
||||||
func (self *CommitsHelper) OnCommitSuccess() {
|
func (self *CommitsHelper) OnCommitSuccess() {
|
||||||
// if we have a preserved message we want to clear it on success
|
// if we have a preserved message we want to clear it on success
|
||||||
if self.contexts.CommitMessage.GetPreserveMessage() {
|
if self.c.Contexts().CommitMessage.GetPreserveMessage() {
|
||||||
self.contexts.CommitMessage.SetPreservedMessage("")
|
self.c.Contexts().CommitMessage.SetPreservedMessage("")
|
||||||
}
|
}
|
||||||
self.SetMessageAndDescriptionInView("")
|
self.SetMessageAndDescriptionInView("")
|
||||||
}
|
}
|
||||||
@ -122,7 +112,7 @@ func (self *CommitsHelper) HandleCommitConfirm() error {
|
|||||||
return self.c.ErrorMsg(self.c.Tr.CommitWithoutMessageErr)
|
return self.c.ErrorMsg(self.c.Tr.CommitWithoutMessageErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := self.contexts.CommitMessage.OnConfirm(fullMessage)
|
err := self.c.Contexts().CommitMessage.OnConfirm(fullMessage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -131,15 +121,15 @@ func (self *CommitsHelper) HandleCommitConfirm() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitsHelper) CloseCommitMessagePanel() error {
|
func (self *CommitsHelper) CloseCommitMessagePanel() error {
|
||||||
if self.contexts.CommitMessage.GetPreserveMessage() {
|
if self.c.Contexts().CommitMessage.GetPreserveMessage() {
|
||||||
message := self.JoinCommitMessageAndDescription()
|
message := self.JoinCommitMessageAndDescription()
|
||||||
|
|
||||||
self.contexts.CommitMessage.SetPreservedMessage(message)
|
self.c.Contexts().CommitMessage.SetPreservedMessage(message)
|
||||||
} else {
|
} else {
|
||||||
self.SetMessageAndDescriptionInView("")
|
self.SetMessageAndDescriptionInView("")
|
||||||
}
|
}
|
||||||
|
|
||||||
self.contexts.CommitMessage.SetHistoryMessage("")
|
self.c.Contexts().CommitMessage.SetHistoryMessage("")
|
||||||
|
|
||||||
return self.PopCommitMessageContexts()
|
return self.PopCommitMessageContexts()
|
||||||
}
|
}
|
||||||
@ -160,7 +150,7 @@ func (self *CommitsHelper) pushCommitMessageContexts() error {
|
|||||||
|
|
||||||
func (self *CommitsHelper) commitMessageContexts() []types.Context {
|
func (self *CommitsHelper) commitMessageContexts() []types.Context {
|
||||||
return []types.Context{
|
return []types.Context{
|
||||||
self.contexts.CommitDescription,
|
self.c.Contexts().CommitDescription,
|
||||||
self.contexts.CommitMessage,
|
self.c.Contexts().CommitMessage,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
349
pkg/gui/controllers/helpers/confirmation_helper.go
Normal file
349
pkg/gui/controllers/helpers/confirmation_helper.go
Normal file
@ -0,0 +1,349 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
goContext "context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/gocui"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||||
|
"github.com/mattn/go-runewidth"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ConfirmationHelper struct {
|
||||||
|
c *HelperCommon
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConfirmationHelper(c *HelperCommon) *ConfirmationHelper {
|
||||||
|
return &ConfirmationHelper{
|
||||||
|
c: c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This file is for the rendering of confirmation panels along with setting and handling associated
|
||||||
|
// keybindings.
|
||||||
|
|
||||||
|
func (self *ConfirmationHelper) wrappedConfirmationFunction(cancel goContext.CancelFunc, function func() error) func() error {
|
||||||
|
return func() error {
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
if err := self.c.PopContext(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if function != nil {
|
||||||
|
if err := function(); err != nil {
|
||||||
|
return self.c.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ConfirmationHelper) wrappedPromptConfirmationFunction(cancel goContext.CancelFunc, function func(string) error, getResponse func() string) func() error {
|
||||||
|
return self.wrappedConfirmationFunction(cancel, func() error {
|
||||||
|
return function(getResponse())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ConfirmationHelper) DeactivateConfirmationPrompt() {
|
||||||
|
self.c.Mutexes().PopupMutex.Lock()
|
||||||
|
self.c.State().GetRepoState().SetCurrentPopupOpts(nil)
|
||||||
|
self.c.Mutexes().PopupMutex.Unlock()
|
||||||
|
|
||||||
|
self.c.Views().Confirmation.Visible = false
|
||||||
|
self.c.Views().Suggestions.Visible = false
|
||||||
|
|
||||||
|
self.clearConfirmationViewKeyBindings()
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMessageHeight(wrap bool, message string, width int) int {
|
||||||
|
lines := strings.Split(message, "\n")
|
||||||
|
lineCount := 0
|
||||||
|
// if we need to wrap, calculate height to fit content within view's width
|
||||||
|
if wrap {
|
||||||
|
for _, line := range lines {
|
||||||
|
lineCount += runewidth.StringWidth(line)/width + 1
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lineCount = len(lines)
|
||||||
|
}
|
||||||
|
return lineCount
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ConfirmationHelper) getPopupPanelDimensions(wrap bool, prompt string) (int, int, int, int) {
|
||||||
|
panelWidth := self.getPopupPanelWidth()
|
||||||
|
panelHeight := getMessageHeight(wrap, prompt, panelWidth)
|
||||||
|
return self.getPopupPanelDimensionsAux(panelWidth, panelHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ConfirmationHelper) getPopupPanelDimensionsForContentHeight(panelWidth, contentHeight int) (int, int, int, int) {
|
||||||
|
return self.getPopupPanelDimensionsAux(panelWidth, contentHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ConfirmationHelper) getPopupPanelDimensionsAux(panelWidth int, panelHeight int) (int, int, int, int) {
|
||||||
|
width, height := self.c.GocuiGui().Size()
|
||||||
|
if panelHeight > height*3/4 {
|
||||||
|
panelHeight = height * 3 / 4
|
||||||
|
}
|
||||||
|
return width/2 - panelWidth/2,
|
||||||
|
height/2 - panelHeight/2 - panelHeight%2 - 1,
|
||||||
|
width/2 + panelWidth/2,
|
||||||
|
height/2 + panelHeight/2
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ConfirmationHelper) getPopupPanelWidth() int {
|
||||||
|
width, _ := self.c.GocuiGui().Size()
|
||||||
|
// we want a minimum width up to a point, then we do it based on ratio.
|
||||||
|
panelWidth := 4 * width / 7
|
||||||
|
minWidth := 80
|
||||||
|
if panelWidth < minWidth {
|
||||||
|
if width-2 < minWidth {
|
||||||
|
panelWidth = width - 2
|
||||||
|
} else {
|
||||||
|
panelWidth = minWidth
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return panelWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ConfirmationHelper) prepareConfirmationPanel(
|
||||||
|
ctx goContext.Context,
|
||||||
|
opts types.ConfirmOpts,
|
||||||
|
) error {
|
||||||
|
self.c.Views().Confirmation.HasLoader = opts.HasLoader
|
||||||
|
if opts.HasLoader {
|
||||||
|
self.c.GocuiGui().StartTicking(ctx)
|
||||||
|
}
|
||||||
|
self.c.Views().Confirmation.Title = opts.Title
|
||||||
|
// for now we do not support wrapping in our editor
|
||||||
|
self.c.Views().Confirmation.Wrap = !opts.Editable
|
||||||
|
self.c.Views().Confirmation.FgColor = theme.GocuiDefaultTextColor
|
||||||
|
self.c.Views().Confirmation.Mask = runeForMask(opts.Mask)
|
||||||
|
_ = self.c.Views().Confirmation.SetOrigin(0, 0)
|
||||||
|
|
||||||
|
suggestionsContext := self.c.Contexts().Suggestions
|
||||||
|
suggestionsContext.State.FindSuggestions = opts.FindSuggestionsFunc
|
||||||
|
if opts.FindSuggestionsFunc != nil {
|
||||||
|
suggestionsView := self.c.Views().Suggestions
|
||||||
|
suggestionsView.Wrap = false
|
||||||
|
suggestionsView.FgColor = theme.GocuiDefaultTextColor
|
||||||
|
suggestionsContext.SetSuggestions(opts.FindSuggestionsFunc(""))
|
||||||
|
suggestionsView.Visible = true
|
||||||
|
suggestionsView.Title = fmt.Sprintf(self.c.Tr.SuggestionsTitle, self.c.UserConfig.Keybinding.Universal.TogglePanel)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.ResizeConfirmationPanel()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func runeForMask(mask bool) rune {
|
||||||
|
if mask {
|
||||||
|
return '*'
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ConfirmationHelper) CreatePopupPanel(ctx goContext.Context, opts types.CreatePopupPanelOpts) error {
|
||||||
|
self.c.Mutexes().PopupMutex.Lock()
|
||||||
|
defer self.c.Mutexes().PopupMutex.Unlock()
|
||||||
|
|
||||||
|
ctx, cancel := goContext.WithCancel(ctx)
|
||||||
|
|
||||||
|
// we don't allow interruptions of non-loader popups in case we get stuck somehow
|
||||||
|
// e.g. a credentials popup never gets its required user input so a process hangs
|
||||||
|
// forever.
|
||||||
|
// The proper solution is to have a queue of popup options
|
||||||
|
currentPopupOpts := self.c.State().GetRepoState().GetCurrentPopupOpts()
|
||||||
|
if currentPopupOpts != nil && !currentPopupOpts.HasLoader {
|
||||||
|
self.c.Log.Error("ignoring create popup panel because a popup panel is already open")
|
||||||
|
cancel()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove any previous keybindings
|
||||||
|
self.clearConfirmationViewKeyBindings()
|
||||||
|
|
||||||
|
err := self.prepareConfirmationPanel(
|
||||||
|
ctx,
|
||||||
|
types.ConfirmOpts{
|
||||||
|
Title: opts.Title,
|
||||||
|
Prompt: opts.Prompt,
|
||||||
|
HasLoader: opts.HasLoader,
|
||||||
|
FindSuggestionsFunc: opts.FindSuggestionsFunc,
|
||||||
|
Editable: opts.Editable,
|
||||||
|
Mask: opts.Mask,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
cancel()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
confirmationView := self.c.Views().Confirmation
|
||||||
|
confirmationView.Editable = opts.Editable
|
||||||
|
|
||||||
|
if opts.Editable {
|
||||||
|
textArea := confirmationView.TextArea
|
||||||
|
textArea.Clear()
|
||||||
|
textArea.TypeString(opts.Prompt)
|
||||||
|
self.ResizeConfirmationPanel()
|
||||||
|
confirmationView.RenderTextArea()
|
||||||
|
} else {
|
||||||
|
self.c.ResetViewOrigin(confirmationView)
|
||||||
|
self.c.SetViewContent(confirmationView, style.AttrBold.Sprint(opts.Prompt))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := self.setKeyBindings(cancel, opts); err != nil {
|
||||||
|
cancel()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
self.c.State().GetRepoState().SetCurrentPopupOpts(&opts)
|
||||||
|
|
||||||
|
return self.c.PushContext(self.c.Contexts().Confirmation)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ConfirmationHelper) setKeyBindings(cancel goContext.CancelFunc, opts types.CreatePopupPanelOpts) error {
|
||||||
|
var onConfirm func() error
|
||||||
|
if opts.HandleConfirmPrompt != nil {
|
||||||
|
onConfirm = self.wrappedPromptConfirmationFunction(cancel, opts.HandleConfirmPrompt, func() string { return self.c.Views().Confirmation.TextArea.GetContent() })
|
||||||
|
} else {
|
||||||
|
onConfirm = self.wrappedConfirmationFunction(cancel, opts.HandleConfirm)
|
||||||
|
}
|
||||||
|
|
||||||
|
onSuggestionConfirm := self.wrappedPromptConfirmationFunction(
|
||||||
|
cancel,
|
||||||
|
opts.HandleConfirmPrompt,
|
||||||
|
self.getSelectedSuggestionValue,
|
||||||
|
)
|
||||||
|
|
||||||
|
onClose := self.wrappedConfirmationFunction(cancel, opts.HandleClose)
|
||||||
|
|
||||||
|
self.c.Contexts().Confirmation.State.OnConfirm = onConfirm
|
||||||
|
self.c.Contexts().Confirmation.State.OnClose = onClose
|
||||||
|
self.c.Contexts().Suggestions.State.OnConfirm = onSuggestionConfirm
|
||||||
|
self.c.Contexts().Suggestions.State.OnClose = onClose
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ConfirmationHelper) clearConfirmationViewKeyBindings() {
|
||||||
|
noop := func() error { return nil }
|
||||||
|
self.c.Contexts().Confirmation.State.OnConfirm = noop
|
||||||
|
self.c.Contexts().Confirmation.State.OnClose = noop
|
||||||
|
self.c.Contexts().Suggestions.State.OnConfirm = noop
|
||||||
|
self.c.Contexts().Suggestions.State.OnClose = noop
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ConfirmationHelper) getSelectedSuggestionValue() string {
|
||||||
|
selectedSuggestion := self.c.Contexts().Suggestions.GetSelected()
|
||||||
|
|
||||||
|
if selectedSuggestion != nil {
|
||||||
|
return selectedSuggestion.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ConfirmationHelper) ResizeConfirmationPanel() {
|
||||||
|
suggestionsViewHeight := 0
|
||||||
|
if self.c.Views().Suggestions.Visible {
|
||||||
|
suggestionsViewHeight = 11
|
||||||
|
}
|
||||||
|
panelWidth := self.getPopupPanelWidth()
|
||||||
|
prompt := self.c.Views().Confirmation.Buffer()
|
||||||
|
wrap := true
|
||||||
|
if self.c.Views().Confirmation.Editable {
|
||||||
|
prompt = self.c.Views().Confirmation.TextArea.GetContent()
|
||||||
|
wrap = false
|
||||||
|
}
|
||||||
|
panelHeight := getMessageHeight(wrap, prompt, panelWidth) + suggestionsViewHeight
|
||||||
|
x0, y0, x1, y1 := self.getPopupPanelDimensionsAux(panelWidth, panelHeight)
|
||||||
|
confirmationViewBottom := y1 - suggestionsViewHeight
|
||||||
|
_, _ = self.c.GocuiGui().SetView(self.c.Views().Confirmation.Name(), x0, y0, x1, confirmationViewBottom, 0)
|
||||||
|
|
||||||
|
suggestionsViewTop := confirmationViewBottom + 1
|
||||||
|
_, _ = self.c.GocuiGui().SetView(self.c.Views().Suggestions.Name(), x0, suggestionsViewTop, x1, suggestionsViewTop+suggestionsViewHeight, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ConfirmationHelper) ResizeCurrentPopupPanel() error {
|
||||||
|
c := self.c.CurrentContext()
|
||||||
|
|
||||||
|
switch c {
|
||||||
|
case self.c.Contexts().Menu:
|
||||||
|
self.resizeMenu()
|
||||||
|
case self.c.Contexts().Confirmation, self.c.Contexts().Suggestions:
|
||||||
|
self.resizeConfirmationPanel()
|
||||||
|
case self.c.Contexts().CommitMessage, self.c.Contexts().CommitDescription:
|
||||||
|
self.ResizeCommitMessagePanels()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ConfirmationHelper) ResizePopupPanel(v *gocui.View, content string) error {
|
||||||
|
x0, y0, x1, y1 := self.getPopupPanelDimensions(v.Wrap, content)
|
||||||
|
_, err := self.c.GocuiGui().SetView(v.Name(), x0, y0, x1, y1, 0)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ConfirmationHelper) resizeMenu() {
|
||||||
|
itemCount := self.c.Contexts().Menu.GetList().Len()
|
||||||
|
offset := 3
|
||||||
|
panelWidth := self.getPopupPanelWidth()
|
||||||
|
x0, y0, x1, y1 := self.getPopupPanelDimensionsForContentHeight(panelWidth, itemCount+offset)
|
||||||
|
menuBottom := y1 - offset
|
||||||
|
_, _ = self.c.GocuiGui().SetView(self.c.Views().Menu.Name(), x0, y0, x1, menuBottom, 0)
|
||||||
|
|
||||||
|
tooltipTop := menuBottom + 1
|
||||||
|
tooltipHeight := getMessageHeight(true, self.c.Contexts().Menu.GetSelected().Tooltip, panelWidth) + 2 // plus 2 for the frame
|
||||||
|
_, _ = self.c.GocuiGui().SetView(self.c.Views().Tooltip.Name(), x0, tooltipTop, x1, tooltipTop+tooltipHeight-1, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ConfirmationHelper) resizeConfirmationPanel() {
|
||||||
|
suggestionsViewHeight := 0
|
||||||
|
if self.c.Views().Suggestions.Visible {
|
||||||
|
suggestionsViewHeight = 11
|
||||||
|
}
|
||||||
|
panelWidth := self.getPopupPanelWidth()
|
||||||
|
prompt := self.c.Views().Confirmation.Buffer()
|
||||||
|
wrap := true
|
||||||
|
if self.c.Views().Confirmation.Editable {
|
||||||
|
prompt = self.c.Views().Confirmation.TextArea.GetContent()
|
||||||
|
wrap = false
|
||||||
|
}
|
||||||
|
panelHeight := getMessageHeight(wrap, prompt, panelWidth) + suggestionsViewHeight
|
||||||
|
x0, y0, x1, y1 := self.getPopupPanelDimensionsAux(panelWidth, panelHeight)
|
||||||
|
confirmationViewBottom := y1 - suggestionsViewHeight
|
||||||
|
_, _ = self.c.GocuiGui().SetView(self.c.Views().Confirmation.Name(), x0, y0, x1, confirmationViewBottom, 0)
|
||||||
|
|
||||||
|
suggestionsViewTop := confirmationViewBottom + 1
|
||||||
|
_, _ = self.c.GocuiGui().SetView(self.c.Views().Suggestions.Name(), x0, suggestionsViewTop, x1, suggestionsViewTop+suggestionsViewHeight, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ConfirmationHelper) ResizeCommitMessagePanels() {
|
||||||
|
panelWidth := self.getPopupPanelWidth()
|
||||||
|
content := self.c.Views().CommitDescription.TextArea.GetContent()
|
||||||
|
summaryViewHeight := 3
|
||||||
|
panelHeight := getMessageHeight(false, content, panelWidth)
|
||||||
|
minHeight := 7
|
||||||
|
if panelHeight < minHeight {
|
||||||
|
panelHeight = minHeight
|
||||||
|
}
|
||||||
|
x0, y0, x1, y1 := self.getPopupPanelDimensionsAux(panelWidth, panelHeight)
|
||||||
|
|
||||||
|
_, _ = self.c.GocuiGui().SetView(self.c.Views().CommitMessage.Name(), x0, y0, x1, y0+summaryViewHeight-1, 0)
|
||||||
|
_, _ = self.c.GocuiGui().SetView(self.c.Views().CommitDescription.Name(), x0, y0+summaryViewHeight, x1, y1+summaryViewHeight, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ConfirmationHelper) IsPopupPanel(viewName string) bool {
|
||||||
|
return viewName == "commitMessage" || viewName == "confirmation" || viewName == "menu"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ConfirmationHelper) IsPopupPanelFocused() bool {
|
||||||
|
return self.IsPopupPanel(self.c.CurrentContext().GetViewName())
|
||||||
|
}
|
@ -8,11 +8,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type CredentialsHelper struct {
|
type CredentialsHelper struct {
|
||||||
c *types.HelperCommon
|
c *HelperCommon
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCredentialsHelper(
|
func NewCredentialsHelper(
|
||||||
c *types.HelperCommon,
|
c *HelperCommon,
|
||||||
) *CredentialsHelper {
|
) *CredentialsHelper {
|
||||||
return &CredentialsHelper{
|
return &CredentialsHelper{
|
||||||
c: c,
|
c: c,
|
||||||
|
114
pkg/gui/controllers/helpers/diff_helper.go
Normal file
114
pkg/gui/controllers/helpers/diff_helper.go
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/modes/diffing"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
|
"github.com/samber/lo"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DiffHelper struct {
|
||||||
|
c *HelperCommon
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDiffHelper(c *HelperCommon) *DiffHelper {
|
||||||
|
return &DiffHelper{
|
||||||
|
c: c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *DiffHelper) DiffStr() string {
|
||||||
|
output := self.c.Modes().Diffing.Ref
|
||||||
|
|
||||||
|
right := self.currentDiffTerminal()
|
||||||
|
if right != "" {
|
||||||
|
output += " " + right
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.c.Modes().Diffing.Reverse {
|
||||||
|
output += " -R"
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.c.State().GetIgnoreWhitespaceInDiffView() {
|
||||||
|
output += " --ignore-all-space"
|
||||||
|
}
|
||||||
|
|
||||||
|
file := self.currentlySelectedFilename()
|
||||||
|
if file != "" {
|
||||||
|
output += " -- " + file
|
||||||
|
} else if self.c.Modes().Filtering.Active() {
|
||||||
|
output += " -- " + self.c.Modes().Filtering.GetPath()
|
||||||
|
}
|
||||||
|
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *DiffHelper) ExitDiffMode() error {
|
||||||
|
self.c.Modes().Diffing = diffing.New()
|
||||||
|
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *DiffHelper) RenderDiff() error {
|
||||||
|
cmdObj := self.c.OS().Cmd.New(
|
||||||
|
fmt.Sprintf("git diff --submodule --no-ext-diff --color %s", self.DiffStr()),
|
||||||
|
)
|
||||||
|
task := types.NewRunPtyTask(cmdObj.GetCmd())
|
||||||
|
|
||||||
|
return self.c.RenderToMainViews(types.RefreshMainOpts{
|
||||||
|
Pair: self.c.MainViewPairs().Normal,
|
||||||
|
Main: &types.ViewUpdateOpts{
|
||||||
|
Title: "Diff",
|
||||||
|
Task: task,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// CurrentDiffTerminals returns the current diff terminals of the currently selected item.
|
||||||
|
// in the case of a branch it returns both the branch and it's upstream name,
|
||||||
|
// which becomes an option when you bring up the diff menu, but when you're just
|
||||||
|
// flicking through branches it will be using the local branch name.
|
||||||
|
func (self *DiffHelper) CurrentDiffTerminals() []string {
|
||||||
|
c := self.c.CurrentSideContext()
|
||||||
|
|
||||||
|
if c.GetKey() == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v := c.(type) {
|
||||||
|
case types.DiffableContext:
|
||||||
|
return v.GetDiffTerminals()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *DiffHelper) currentDiffTerminal() string {
|
||||||
|
names := self.CurrentDiffTerminals()
|
||||||
|
if len(names) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return names[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *DiffHelper) currentlySelectedFilename() string {
|
||||||
|
currentContext := self.c.CurrentContext()
|
||||||
|
|
||||||
|
switch currentContext := currentContext.(type) {
|
||||||
|
case types.IListContext:
|
||||||
|
if lo.Contains([]types.ContextKey{context.FILES_CONTEXT_KEY, context.COMMIT_FILES_CONTEXT_KEY}, currentContext.GetKey()) {
|
||||||
|
return currentContext.GetSelectedItemId()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *DiffHelper) WithDiffModeCheck(f func() error) error {
|
||||||
|
if self.c.Modes().Diffing.Active() {
|
||||||
|
return self.RenderDiff()
|
||||||
|
}
|
||||||
|
|
||||||
|
return f()
|
||||||
|
}
|
@ -1,11 +1,5 @@
|
|||||||
package helpers
|
package helpers
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
type IFilesHelper interface {
|
type IFilesHelper interface {
|
||||||
EditFile(filename string) error
|
EditFile(filename string) error
|
||||||
EditFileAtLine(filename string, lineNumber int) error
|
EditFileAtLine(filename string, lineNumber int) error
|
||||||
@ -13,37 +7,29 @@ type IFilesHelper interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type FilesHelper struct {
|
type FilesHelper struct {
|
||||||
c *types.HelperCommon
|
c *HelperCommon
|
||||||
git *commands.GitCommand
|
|
||||||
os *oscommands.OSCommand
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFilesHelper(
|
func NewFilesHelper(c *HelperCommon) *FilesHelper {
|
||||||
c *types.HelperCommon,
|
|
||||||
git *commands.GitCommand,
|
|
||||||
os *oscommands.OSCommand,
|
|
||||||
) *FilesHelper {
|
|
||||||
return &FilesHelper{
|
return &FilesHelper{
|
||||||
c: c,
|
c: c,
|
||||||
git: git,
|
|
||||||
os: os,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ IFilesHelper = &FilesHelper{}
|
var _ IFilesHelper = &FilesHelper{}
|
||||||
|
|
||||||
func (self *FilesHelper) EditFile(filename string) error {
|
func (self *FilesHelper) EditFile(filename string) error {
|
||||||
cmdStr, editInTerminal := self.git.File.GetEditCmdStr(filename)
|
cmdStr, editInTerminal := self.c.Git().File.GetEditCmdStr(filename)
|
||||||
return self.callEditor(cmdStr, editInTerminal)
|
return self.callEditor(cmdStr, editInTerminal)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *FilesHelper) EditFileAtLine(filename string, lineNumber int) error {
|
func (self *FilesHelper) EditFileAtLine(filename string, lineNumber int) error {
|
||||||
cmdStr, editInTerminal := self.git.File.GetEditAtLineCmdStr(filename, lineNumber)
|
cmdStr, editInTerminal := self.c.Git().File.GetEditAtLineCmdStr(filename, lineNumber)
|
||||||
return self.callEditor(cmdStr, editInTerminal)
|
return self.callEditor(cmdStr, editInTerminal)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *FilesHelper) EditFileAtLineAndWait(filename string, lineNumber int) error {
|
func (self *FilesHelper) EditFileAtLineAndWait(filename string, lineNumber int) error {
|
||||||
cmdStr := self.git.File.GetEditAtLineAndWaitCmdStr(filename, lineNumber)
|
cmdStr := self.c.Git().File.GetEditAtLineAndWaitCmdStr(filename, lineNumber)
|
||||||
|
|
||||||
// Always suspend, regardless of the value of the editInTerminal config,
|
// Always suspend, regardless of the value of the editInTerminal config,
|
||||||
// since we want to prevent interacting with the UI until the editor
|
// since we want to prevent interacting with the UI until the editor
|
||||||
@ -54,16 +40,16 @@ func (self *FilesHelper) EditFileAtLineAndWait(filename string, lineNumber int)
|
|||||||
func (self *FilesHelper) callEditor(cmdStr string, editInTerminal bool) error {
|
func (self *FilesHelper) callEditor(cmdStr string, editInTerminal bool) error {
|
||||||
if editInTerminal {
|
if editInTerminal {
|
||||||
return self.c.RunSubprocessAndRefresh(
|
return self.c.RunSubprocessAndRefresh(
|
||||||
self.os.Cmd.NewShell(cmdStr),
|
self.c.OS().Cmd.NewShell(cmdStr),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.os.Cmd.NewShell(cmdStr).Run()
|
return self.c.OS().Cmd.NewShell(cmdStr).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *FilesHelper) OpenFile(filename string) error {
|
func (self *FilesHelper) OpenFile(filename string) error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.OpenFile)
|
self.c.LogAction(self.c.Tr.Actions.OpenFile)
|
||||||
if err := self.os.OpenFile(filename); err != nil {
|
if err := self.c.OS().OpenFile(filename); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -3,26 +3,17 @@ package helpers
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type GpgHelper struct {
|
type GpgHelper struct {
|
||||||
c *types.HelperCommon
|
c *HelperCommon
|
||||||
os *oscommands.OSCommand
|
|
||||||
git *commands.GitCommand
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGpgHelper(
|
func NewGpgHelper(c *HelperCommon) *GpgHelper {
|
||||||
c *types.HelperCommon,
|
|
||||||
os *oscommands.OSCommand,
|
|
||||||
git *commands.GitCommand,
|
|
||||||
) *GpgHelper {
|
|
||||||
return &GpgHelper{
|
return &GpgHelper{
|
||||||
c: c,
|
c: c,
|
||||||
os: os,
|
|
||||||
git: git,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,9 +23,9 @@ func NewGpgHelper(
|
|||||||
// we don't need to see a loading status if we're in a subprocess.
|
// we don't need to see a loading status if we're in a subprocess.
|
||||||
// TODO: we shouldn't need to use a shell here, but looks like that NewShell function contains some windows specific quoting stuff. We should centralise that.
|
// TODO: we shouldn't need to use a shell here, but looks like that NewShell function contains some windows specific quoting stuff. We should centralise that.
|
||||||
func (self *GpgHelper) WithGpgHandling(cmdObj oscommands.ICmdObj, waitingStatus string, onSuccess func() error) error {
|
func (self *GpgHelper) WithGpgHandling(cmdObj oscommands.ICmdObj, waitingStatus string, onSuccess func() error) error {
|
||||||
useSubprocess := self.git.Config.UsingGpg()
|
useSubprocess := self.c.Git().Config.UsingGpg()
|
||||||
if useSubprocess {
|
if useSubprocess {
|
||||||
success, err := self.c.RunSubprocess(self.os.Cmd.NewShell(cmdObj.ToString()))
|
success, err := self.c.RunSubprocess(self.c.OS().Cmd.NewShell(cmdObj.ToString()))
|
||||||
if success && onSuccess != nil {
|
if success && onSuccess != nil {
|
||||||
if err := onSuccess(); err != nil {
|
if err := onSuccess(); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -51,7 +42,7 @@ func (self *GpgHelper) WithGpgHandling(cmdObj oscommands.ICmdObj, waitingStatus
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *GpgHelper) runAndStream(cmdObj oscommands.ICmdObj, waitingStatus string, onSuccess func() error) error {
|
func (self *GpgHelper) runAndStream(cmdObj oscommands.ICmdObj, waitingStatus string, onSuccess func() error) error {
|
||||||
cmdObj = self.os.Cmd.NewShell(cmdObj.ToString())
|
cmdObj = self.c.OS().Cmd.NewShell(cmdObj.ToString())
|
||||||
|
|
||||||
return self.c.WithWaitingStatus(waitingStatus, func() error {
|
return self.c.WithWaitingStatus(waitingStatus, func() error {
|
||||||
if err := cmdObj.StreamOutput().Run(); err != nil {
|
if err := cmdObj.StreamOutput().Run(); err != nil {
|
||||||
|
@ -1,5 +1,21 @@
|
|||||||
package helpers
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/common"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HelperCommon struct {
|
||||||
|
*common.Common
|
||||||
|
types.IGuiCommon
|
||||||
|
IGetContexts
|
||||||
|
}
|
||||||
|
|
||||||
|
type IGetContexts interface {
|
||||||
|
Contexts() *context.ContextTree
|
||||||
|
}
|
||||||
|
|
||||||
type Helpers struct {
|
type Helpers struct {
|
||||||
Refs *RefsHelper
|
Refs *RefsHelper
|
||||||
Bisect *BisectHelper
|
Bisect *BisectHelper
|
||||||
@ -12,28 +28,55 @@ type Helpers struct {
|
|||||||
CherryPick *CherryPickHelper
|
CherryPick *CherryPickHelper
|
||||||
Host *HostHelper
|
Host *HostHelper
|
||||||
PatchBuilding *PatchBuildingHelper
|
PatchBuilding *PatchBuildingHelper
|
||||||
|
Staging *StagingHelper
|
||||||
GPG *GpgHelper
|
GPG *GpgHelper
|
||||||
Upstream *UpstreamHelper
|
Upstream *UpstreamHelper
|
||||||
AmendHelper *AmendHelper
|
AmendHelper *AmendHelper
|
||||||
Commits *CommitsHelper
|
Commits *CommitsHelper
|
||||||
|
Snake *SnakeHelper
|
||||||
|
// lives in context package because our contexts need it to render to main
|
||||||
|
Diff *DiffHelper
|
||||||
|
Repos *ReposHelper
|
||||||
|
RecordDirectory *RecordDirectoryHelper
|
||||||
|
Update *UpdateHelper
|
||||||
|
Window *WindowHelper
|
||||||
|
View *ViewHelper
|
||||||
|
Refresh *RefreshHelper
|
||||||
|
Confirmation *ConfirmationHelper
|
||||||
|
Mode *ModeHelper
|
||||||
|
AppStatus *AppStatusHelper
|
||||||
|
WindowArrangement *WindowArrangementHelper
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStubHelpers() *Helpers {
|
func NewStubHelpers() *Helpers {
|
||||||
return &Helpers{
|
return &Helpers{
|
||||||
Refs: &RefsHelper{},
|
Refs: &RefsHelper{},
|
||||||
Bisect: &BisectHelper{},
|
Bisect: &BisectHelper{},
|
||||||
Suggestions: &SuggestionsHelper{},
|
Suggestions: &SuggestionsHelper{},
|
||||||
Files: &FilesHelper{},
|
Files: &FilesHelper{},
|
||||||
WorkingTree: &WorkingTreeHelper{},
|
WorkingTree: &WorkingTreeHelper{},
|
||||||
Tags: &TagsHelper{},
|
Tags: &TagsHelper{},
|
||||||
MergeAndRebase: &MergeAndRebaseHelper{},
|
MergeAndRebase: &MergeAndRebaseHelper{},
|
||||||
MergeConflicts: &MergeConflictsHelper{},
|
MergeConflicts: &MergeConflictsHelper{},
|
||||||
CherryPick: &CherryPickHelper{},
|
CherryPick: &CherryPickHelper{},
|
||||||
Host: &HostHelper{},
|
Host: &HostHelper{},
|
||||||
PatchBuilding: &PatchBuildingHelper{},
|
PatchBuilding: &PatchBuildingHelper{},
|
||||||
GPG: &GpgHelper{},
|
Staging: &StagingHelper{},
|
||||||
Upstream: &UpstreamHelper{},
|
GPG: &GpgHelper{},
|
||||||
AmendHelper: &AmendHelper{},
|
Upstream: &UpstreamHelper{},
|
||||||
Commits: &CommitsHelper{},
|
AmendHelper: &AmendHelper{},
|
||||||
|
Commits: &CommitsHelper{},
|
||||||
|
Snake: &SnakeHelper{},
|
||||||
|
Diff: &DiffHelper{},
|
||||||
|
Repos: &ReposHelper{},
|
||||||
|
RecordDirectory: &RecordDirectoryHelper{},
|
||||||
|
Update: &UpdateHelper{},
|
||||||
|
Window: &WindowHelper{},
|
||||||
|
View: &ViewHelper{},
|
||||||
|
Refresh: &RefreshHelper{},
|
||||||
|
Confirmation: &ConfirmationHelper{},
|
||||||
|
Mode: &ModeHelper{},
|
||||||
|
AppStatus: &AppStatusHelper{},
|
||||||
|
WindowArrangement: &WindowArrangementHelper{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
package helpers
|
package helpers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/hosting_service"
|
"github.com/jesseduffield/lazygit/pkg/commands/hosting_service"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// this helper just wraps our hosting_service package
|
// this helper just wraps our hosting_service package
|
||||||
@ -14,17 +12,14 @@ type IHostHelper interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type HostHelper struct {
|
type HostHelper struct {
|
||||||
c *types.HelperCommon
|
c *HelperCommon
|
||||||
git *commands.GitCommand
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHostHelper(
|
func NewHostHelper(
|
||||||
c *types.HelperCommon,
|
c *HelperCommon,
|
||||||
git *commands.GitCommand,
|
|
||||||
) *HostHelper {
|
) *HostHelper {
|
||||||
return &HostHelper{
|
return &HostHelper{
|
||||||
c: c,
|
c: c,
|
||||||
git: git,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,7 +35,7 @@ func (self *HostHelper) GetCommitURL(commitSha string) (string, error) {
|
|||||||
// from one invocation to the next. Note however that we're currently caching config
|
// from one invocation to the next. Note however that we're currently caching config
|
||||||
// results so we might want to invalidate the cache here if it becomes a problem.
|
// results so we might want to invalidate the cache here if it becomes a problem.
|
||||||
func (self *HostHelper) getHostingServiceMgr() *hosting_service.HostingServiceMgr {
|
func (self *HostHelper) getHostingServiceMgr() *hosting_service.HostingServiceMgr {
|
||||||
remoteUrl := self.git.Config.GetRemoteURL()
|
remoteUrl := self.c.Git().Config.GetRemoteURL()
|
||||||
configServices := self.c.UserConfig.Services
|
configServices := self.c.UserConfig.Services
|
||||||
return hosting_service.NewHostingServiceMgr(self.c.Log, self.c.Tr, remoteUrl, configServices)
|
return hosting_service.NewHostingServiceMgr(self.c.Log, self.c.Tr, remoteUrl, configServices)
|
||||||
}
|
}
|
||||||
|
@ -5,31 +5,23 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/jesseduffield/generics/slices"
|
"github.com/jesseduffield/generics/slices"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MergeAndRebaseHelper struct {
|
type MergeAndRebaseHelper struct {
|
||||||
c *types.HelperCommon
|
c *HelperCommon
|
||||||
contexts *context.ContextTree
|
|
||||||
git *commands.GitCommand
|
|
||||||
refsHelper *RefsHelper
|
refsHelper *RefsHelper
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMergeAndRebaseHelper(
|
func NewMergeAndRebaseHelper(
|
||||||
c *types.HelperCommon,
|
c *HelperCommon,
|
||||||
contexts *context.ContextTree,
|
|
||||||
git *commands.GitCommand,
|
|
||||||
refsHelper *RefsHelper,
|
refsHelper *RefsHelper,
|
||||||
) *MergeAndRebaseHelper {
|
) *MergeAndRebaseHelper {
|
||||||
return &MergeAndRebaseHelper{
|
return &MergeAndRebaseHelper{
|
||||||
c: c,
|
c: c,
|
||||||
contexts: contexts,
|
|
||||||
git: git,
|
|
||||||
refsHelper: refsHelper,
|
refsHelper: refsHelper,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -53,7 +45,7 @@ func (self *MergeAndRebaseHelper) CreateRebaseOptionsMenu() error {
|
|||||||
{option: REBASE_OPTION_ABORT, key: 'a'},
|
{option: REBASE_OPTION_ABORT, key: 'a'},
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.git.Status.WorkingTreeState() == enums.REBASE_MODE_REBASING {
|
if self.c.Git().Status.WorkingTreeState() == enums.REBASE_MODE_REBASING {
|
||||||
options = append(options, optionAndKey{
|
options = append(options, optionAndKey{
|
||||||
option: REBASE_OPTION_SKIP, key: 's',
|
option: REBASE_OPTION_SKIP, key: 's',
|
||||||
})
|
})
|
||||||
@ -70,7 +62,7 @@ func (self *MergeAndRebaseHelper) CreateRebaseOptionsMenu() error {
|
|||||||
})
|
})
|
||||||
|
|
||||||
var title string
|
var title string
|
||||||
if self.git.Status.WorkingTreeState() == enums.REBASE_MODE_MERGING {
|
if self.c.Git().Status.WorkingTreeState() == enums.REBASE_MODE_MERGING {
|
||||||
title = self.c.Tr.MergeOptionsTitle
|
title = self.c.Tr.MergeOptionsTitle
|
||||||
} else {
|
} else {
|
||||||
title = self.c.Tr.RebaseOptionsTitle
|
title = self.c.Tr.RebaseOptionsTitle
|
||||||
@ -80,7 +72,7 @@ func (self *MergeAndRebaseHelper) CreateRebaseOptionsMenu() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *MergeAndRebaseHelper) genericMergeCommand(command string) error {
|
func (self *MergeAndRebaseHelper) genericMergeCommand(command string) error {
|
||||||
status := self.git.Status.WorkingTreeState()
|
status := self.c.Git().Status.WorkingTreeState()
|
||||||
|
|
||||||
if status != enums.REBASE_MODE_MERGING && status != enums.REBASE_MODE_REBASING {
|
if status != enums.REBASE_MODE_MERGING && status != enums.REBASE_MODE_REBASING {
|
||||||
return self.c.ErrorMsg(self.c.Tr.NotMergingOrRebasing)
|
return self.c.ErrorMsg(self.c.Tr.NotMergingOrRebasing)
|
||||||
@ -104,10 +96,10 @@ func (self *MergeAndRebaseHelper) genericMergeCommand(command string) error {
|
|||||||
if status == enums.REBASE_MODE_MERGING && command != REBASE_OPTION_ABORT && self.c.UserConfig.Git.Merging.ManualCommit {
|
if status == enums.REBASE_MODE_MERGING && command != REBASE_OPTION_ABORT && self.c.UserConfig.Git.Merging.ManualCommit {
|
||||||
// TODO: see if we should be calling more of the code from self.Git.Rebase.GenericMergeOrRebaseAction
|
// TODO: see if we should be calling more of the code from self.Git.Rebase.GenericMergeOrRebaseAction
|
||||||
return self.c.RunSubprocessAndRefresh(
|
return self.c.RunSubprocessAndRefresh(
|
||||||
self.git.Rebase.GenericMergeOrRebaseActionCmdObj(commandType, command),
|
self.c.Git().Rebase.GenericMergeOrRebaseActionCmdObj(commandType, command),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
result := self.git.Rebase.GenericMergeOrRebaseAction(commandType, command)
|
result := self.c.Git().Rebase.GenericMergeOrRebaseAction(commandType, command)
|
||||||
if err := self.CheckMergeOrRebase(result); err != nil {
|
if err := self.CheckMergeOrRebase(result); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -150,7 +142,7 @@ func (self *MergeAndRebaseHelper) CheckMergeOrRebase(result error) error {
|
|||||||
Title: self.c.Tr.FoundConflictsTitle,
|
Title: self.c.Tr.FoundConflictsTitle,
|
||||||
Prompt: self.c.Tr.FoundConflicts,
|
Prompt: self.c.Tr.FoundConflicts,
|
||||||
HandleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
return self.c.PushContext(self.contexts.Files)
|
return self.c.PushContext(self.c.Contexts().Files)
|
||||||
},
|
},
|
||||||
HandleClose: func() error {
|
HandleClose: func() error {
|
||||||
return self.genericMergeCommand(REBASE_OPTION_ABORT)
|
return self.genericMergeCommand(REBASE_OPTION_ABORT)
|
||||||
@ -174,7 +166,7 @@ func (self *MergeAndRebaseHelper) AbortMergeOrRebaseWithConfirm() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *MergeAndRebaseHelper) workingTreeStateNoun() string {
|
func (self *MergeAndRebaseHelper) workingTreeStateNoun() string {
|
||||||
workingTreeState := self.git.Status.WorkingTreeState()
|
workingTreeState := self.c.Git().Status.WorkingTreeState()
|
||||||
switch workingTreeState {
|
switch workingTreeState {
|
||||||
case enums.REBASE_MODE_NONE:
|
case enums.REBASE_MODE_NONE:
|
||||||
return ""
|
return ""
|
||||||
@ -207,7 +199,7 @@ func (self *MergeAndRebaseHelper) RebaseOntoRef(ref string) error {
|
|||||||
Key: 's',
|
Key: 's',
|
||||||
OnPress: func() error {
|
OnPress: func() error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.RebaseBranch)
|
self.c.LogAction(self.c.Tr.Actions.RebaseBranch)
|
||||||
err := self.git.Rebase.RebaseBranch(ref)
|
err := self.c.Git().Rebase.RebaseBranch(ref)
|
||||||
return self.CheckMergeOrRebase(err)
|
return self.CheckMergeOrRebase(err)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -217,11 +209,11 @@ func (self *MergeAndRebaseHelper) RebaseOntoRef(ref string) error {
|
|||||||
Tooltip: self.c.Tr.InteractiveRebaseTooltip,
|
Tooltip: self.c.Tr.InteractiveRebaseTooltip,
|
||||||
OnPress: func() error {
|
OnPress: func() error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.RebaseBranch)
|
self.c.LogAction(self.c.Tr.Actions.RebaseBranch)
|
||||||
err := self.git.Rebase.EditRebase(ref)
|
err := self.c.Git().Rebase.EditRebase(ref)
|
||||||
if err = self.CheckMergeOrRebase(err); err != nil {
|
if err = self.CheckMergeOrRebase(err); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return self.c.PushContext(self.contexts.LocalCommits)
|
return self.c.PushContext(self.c.Contexts().LocalCommits)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -241,7 +233,7 @@ func (self *MergeAndRebaseHelper) RebaseOntoRef(ref string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *MergeAndRebaseHelper) MergeRefIntoCheckedOutBranch(refName string) error {
|
func (self *MergeAndRebaseHelper) MergeRefIntoCheckedOutBranch(refName string) error {
|
||||||
if self.git.Branch.IsHeadDetached() {
|
if self.c.Git().Branch.IsHeadDetached() {
|
||||||
return self.c.ErrorMsg("Cannot merge branch in detached head state. You might have checked out a commit directly or a remote branch, in which case you should checkout the local branch you want to be on")
|
return self.c.ErrorMsg("Cannot merge branch in detached head state. You might have checked out a commit directly or a remote branch, in which case you should checkout the local branch you want to be on")
|
||||||
}
|
}
|
||||||
checkedOutBranchName := self.refsHelper.GetCheckedOutRef().Name
|
checkedOutBranchName := self.refsHelper.GetCheckedOutRef().Name
|
||||||
@ -261,7 +253,7 @@ func (self *MergeAndRebaseHelper) MergeRefIntoCheckedOutBranch(refName string) e
|
|||||||
Prompt: prompt,
|
Prompt: prompt,
|
||||||
HandleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.Merge)
|
self.c.LogAction(self.c.Tr.Actions.Merge)
|
||||||
err := self.git.Branch.Merge(refName, git_commands.MergeOpts{})
|
err := self.c.Git().Branch.Merge(refName, git_commands.MergeOpts{})
|
||||||
return self.CheckMergeOrRebase(err)
|
return self.CheckMergeOrRebase(err)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -1,41 +1,19 @@
|
|||||||
package helpers
|
package helpers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/keybindings"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MergeConflictsHelper struct {
|
type MergeConflictsHelper struct {
|
||||||
c *types.HelperCommon
|
c *HelperCommon
|
||||||
contexts *context.ContextTree
|
|
||||||
git *commands.GitCommand
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMergeConflictsHelper(
|
func NewMergeConflictsHelper(
|
||||||
c *types.HelperCommon,
|
c *HelperCommon,
|
||||||
contexts *context.ContextTree,
|
|
||||||
git *commands.GitCommand,
|
|
||||||
) *MergeConflictsHelper {
|
) *MergeConflictsHelper {
|
||||||
return &MergeConflictsHelper{
|
return &MergeConflictsHelper{
|
||||||
c: c,
|
c: c,
|
||||||
contexts: contexts,
|
|
||||||
git: git,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *MergeConflictsHelper) GetMergingOptions() map[string]string {
|
|
||||||
keybindingConfig := self.c.UserConfig.Keybinding
|
|
||||||
|
|
||||||
return map[string]string{
|
|
||||||
fmt.Sprintf("%s %s", keybindings.Label(keybindingConfig.Universal.PrevItem), keybindings.Label(keybindingConfig.Universal.NextItem)): self.c.Tr.LcSelectHunk,
|
|
||||||
fmt.Sprintf("%s %s", keybindings.Label(keybindingConfig.Universal.PrevBlock), keybindings.Label(keybindingConfig.Universal.NextBlock)): self.c.Tr.LcNavigateConflicts,
|
|
||||||
keybindings.Label(keybindingConfig.Universal.Select): self.c.Tr.LcPickHunk,
|
|
||||||
keybindings.Label(keybindingConfig.Main.PickBothHunks): self.c.Tr.LcPickAllHunks,
|
|
||||||
keybindings.Label(keybindingConfig.Universal.Undo): self.c.Tr.LcUndo,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +25,7 @@ func (self *MergeConflictsHelper) SetMergeState(path string) (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *MergeConflictsHelper) setMergeStateWithoutLock(path string) (bool, error) {
|
func (self *MergeConflictsHelper) setMergeStateWithoutLock(path string) (bool, error) {
|
||||||
content, err := self.git.File.Cat(path)
|
content, err := self.c.Git().File.Cat(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
@ -78,7 +56,7 @@ func (self *MergeConflictsHelper) EscapeMerge() error {
|
|||||||
|
|
||||||
// doing this in separate UI thread so that we're not still holding the lock by the time refresh the file
|
// doing this in separate UI thread so that we're not still holding the lock by the time refresh the file
|
||||||
self.c.OnUIThread(func() error {
|
self.c.OnUIThread(func() error {
|
||||||
return self.c.PushContext(self.contexts.Files)
|
return self.c.PushContext(self.c.Contexts().Files)
|
||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -107,9 +85,48 @@ func (self *MergeConflictsHelper) SwitchToMerge(path string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.c.PushContext(self.contexts.MergeConflicts)
|
return self.c.PushContext(self.c.Contexts().MergeConflicts)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *MergeConflictsHelper) context() *context.MergeConflictsContext {
|
func (self *MergeConflictsHelper) context() *context.MergeConflictsContext {
|
||||||
return self.contexts.MergeConflicts
|
return self.c.Contexts().MergeConflicts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *MergeConflictsHelper) Render(isFocused bool) error {
|
||||||
|
content := self.context().GetContentToRender(isFocused)
|
||||||
|
|
||||||
|
var task types.UpdateTask
|
||||||
|
if self.context().IsUserScrolling() {
|
||||||
|
task = types.NewRenderStringWithoutScrollTask(content)
|
||||||
|
} else {
|
||||||
|
originY := self.context().GetOriginY()
|
||||||
|
task = types.NewRenderStringWithScrollTask(content, 0, originY)
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.c.RenderToMainViews(types.RefreshMainOpts{
|
||||||
|
Pair: self.c.MainViewPairs().MergeConflicts,
|
||||||
|
Main: &types.ViewUpdateOpts{
|
||||||
|
Task: task,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *MergeConflictsHelper) RefreshMergeState() error {
|
||||||
|
self.c.Contexts().MergeConflicts.GetMutex().Lock()
|
||||||
|
defer self.c.Contexts().MergeConflicts.GetMutex().Unlock()
|
||||||
|
|
||||||
|
if self.c.CurrentContext().GetKey() != context.MERGE_CONFLICTS_CONTEXT_KEY {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
hasConflicts, err := self.SetConflictsAndRender(self.c.Contexts().MergeConflicts.GetState().GetPath(), true)
|
||||||
|
if err != nil {
|
||||||
|
return self.c.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasConflicts {
|
||||||
|
return self.EscapeMerge()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
159
pkg/gui/controllers/helpers/mode_helper.go
Normal file
159
pkg/gui/controllers/helpers/mode_helper.go
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/generics/slices"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ModeHelper struct {
|
||||||
|
c *HelperCommon
|
||||||
|
|
||||||
|
diffHelper *DiffHelper
|
||||||
|
patchBuildingHelper *PatchBuildingHelper
|
||||||
|
cherryPickHelper *CherryPickHelper
|
||||||
|
mergeAndRebaseHelper *MergeAndRebaseHelper
|
||||||
|
bisectHelper *BisectHelper
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewModeHelper(
|
||||||
|
c *HelperCommon,
|
||||||
|
diffHelper *DiffHelper,
|
||||||
|
patchBuildingHelper *PatchBuildingHelper,
|
||||||
|
cherryPickHelper *CherryPickHelper,
|
||||||
|
mergeAndRebaseHelper *MergeAndRebaseHelper,
|
||||||
|
bisectHelper *BisectHelper,
|
||||||
|
) *ModeHelper {
|
||||||
|
return &ModeHelper{
|
||||||
|
c: c,
|
||||||
|
diffHelper: diffHelper,
|
||||||
|
patchBuildingHelper: patchBuildingHelper,
|
||||||
|
cherryPickHelper: cherryPickHelper,
|
||||||
|
mergeAndRebaseHelper: mergeAndRebaseHelper,
|
||||||
|
bisectHelper: bisectHelper,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ModeStatus struct {
|
||||||
|
IsActive func() bool
|
||||||
|
Description func() string
|
||||||
|
Reset func() error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ModeHelper) Statuses() []ModeStatus {
|
||||||
|
return []ModeStatus{
|
||||||
|
{
|
||||||
|
IsActive: self.c.Modes().Diffing.Active,
|
||||||
|
Description: func() string {
|
||||||
|
return self.withResetButton(
|
||||||
|
fmt.Sprintf(
|
||||||
|
"%s %s",
|
||||||
|
self.c.Tr.LcShowingGitDiff,
|
||||||
|
"git diff "+self.diffHelper.DiffStr(),
|
||||||
|
),
|
||||||
|
style.FgMagenta,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
Reset: self.diffHelper.ExitDiffMode,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
IsActive: self.c.Git().Patch.PatchBuilder.Active,
|
||||||
|
Description: func() string {
|
||||||
|
return self.withResetButton(self.c.Tr.LcBuildingPatch, style.FgYellow.SetBold())
|
||||||
|
},
|
||||||
|
Reset: self.patchBuildingHelper.Reset,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
IsActive: self.c.Modes().Filtering.Active,
|
||||||
|
Description: func() string {
|
||||||
|
return self.withResetButton(
|
||||||
|
fmt.Sprintf(
|
||||||
|
"%s '%s'",
|
||||||
|
self.c.Tr.LcFilteringBy,
|
||||||
|
self.c.Modes().Filtering.GetPath(),
|
||||||
|
),
|
||||||
|
style.FgRed,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
Reset: self.ExitFilterMode,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
IsActive: self.c.Modes().CherryPicking.Active,
|
||||||
|
Description: func() string {
|
||||||
|
copiedCount := len(self.c.Modes().CherryPicking.CherryPickedCommits)
|
||||||
|
text := self.c.Tr.LcCommitsCopied
|
||||||
|
if copiedCount == 1 {
|
||||||
|
text = self.c.Tr.LcCommitCopied
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.withResetButton(
|
||||||
|
fmt.Sprintf(
|
||||||
|
"%d %s",
|
||||||
|
copiedCount,
|
||||||
|
text,
|
||||||
|
),
|
||||||
|
style.FgCyan,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
Reset: self.cherryPickHelper.Reset,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
IsActive: func() bool {
|
||||||
|
return self.c.Git().Status.WorkingTreeState() != enums.REBASE_MODE_NONE
|
||||||
|
},
|
||||||
|
Description: func() string {
|
||||||
|
workingTreeState := self.c.Git().Status.WorkingTreeState()
|
||||||
|
return self.withResetButton(
|
||||||
|
presentation.FormatWorkingTreeState(workingTreeState), style.FgYellow,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
Reset: self.mergeAndRebaseHelper.AbortMergeOrRebaseWithConfirm,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
IsActive: func() bool {
|
||||||
|
return self.c.Model().BisectInfo.Started()
|
||||||
|
},
|
||||||
|
Description: func() string {
|
||||||
|
return self.withResetButton("bisecting", style.FgGreen)
|
||||||
|
},
|
||||||
|
Reset: self.bisectHelper.Reset,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ModeHelper) withResetButton(content string, textStyle style.TextStyle) string {
|
||||||
|
return textStyle.Sprintf(
|
||||||
|
"%s %s",
|
||||||
|
content,
|
||||||
|
style.AttrUnderline.Sprint(self.c.Tr.ResetInParentheses),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ModeHelper) GetActiveMode() (ModeStatus, bool) {
|
||||||
|
return slices.Find(self.Statuses(), func(mode ModeStatus) bool {
|
||||||
|
return mode.IsActive()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ModeHelper) IsAnyModeActive() bool {
|
||||||
|
return slices.Some(self.Statuses(), func(mode ModeStatus) bool {
|
||||||
|
return mode.IsActive()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ModeHelper) ExitFilterMode() error {
|
||||||
|
return self.ClearFiltering()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ModeHelper) ClearFiltering() error {
|
||||||
|
self.c.Modes().Filtering.Reset()
|
||||||
|
if self.c.State().GetRepoState().GetScreenMode() == types.SCREEN_HALF {
|
||||||
|
self.c.State().GetRepoState().SetScreenMode(types.SCREEN_NORMAL)
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.COMMITS}})
|
||||||
|
}
|
@ -1,9 +1,8 @@
|
|||||||
package helpers
|
package helpers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
"github.com/jesseduffield/lazygit/pkg/gui/patch_exploring"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -12,25 +11,19 @@ type IPatchBuildingHelper interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type PatchBuildingHelper struct {
|
type PatchBuildingHelper struct {
|
||||||
c *types.HelperCommon
|
c *HelperCommon
|
||||||
git *commands.GitCommand
|
|
||||||
contexts *context.ContextTree
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPatchBuildingHelper(
|
func NewPatchBuildingHelper(
|
||||||
c *types.HelperCommon,
|
c *HelperCommon,
|
||||||
git *commands.GitCommand,
|
|
||||||
contexts *context.ContextTree,
|
|
||||||
) *PatchBuildingHelper {
|
) *PatchBuildingHelper {
|
||||||
return &PatchBuildingHelper{
|
return &PatchBuildingHelper{
|
||||||
c: c,
|
c: c,
|
||||||
git: git,
|
|
||||||
contexts: contexts,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *PatchBuildingHelper) ValidateNormalWorkingTreeState() (bool, error) {
|
func (self *PatchBuildingHelper) ValidateNormalWorkingTreeState() (bool, error) {
|
||||||
if self.git.Status.WorkingTreeState() != enums.REBASE_MODE_NONE {
|
if self.c.Git().Status.WorkingTreeState() != enums.REBASE_MODE_NONE {
|
||||||
return false, self.c.ErrorMsg(self.c.Tr.CantPatchWhileRebasingError)
|
return false, self.c.ErrorMsg(self.c.Tr.CantPatchWhileRebasingError)
|
||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
@ -43,7 +36,7 @@ func (self *PatchBuildingHelper) Escape() error {
|
|||||||
|
|
||||||
// kills the custom patch and returns us back to the commit files panel if needed
|
// kills the custom patch and returns us back to the commit files panel if needed
|
||||||
func (self *PatchBuildingHelper) Reset() error {
|
func (self *PatchBuildingHelper) Reset() error {
|
||||||
self.git.Patch.PatchBuilder.Reset()
|
self.c.Git().Patch.PatchBuilder.Reset()
|
||||||
|
|
||||||
if self.c.CurrentStaticContext().GetKind() != types.SIDE_CONTEXT {
|
if self.c.CurrentStaticContext().GetKind() != types.SIDE_CONTEXT {
|
||||||
if err := self.Escape(); err != nil {
|
if err := self.Escape(); err != nil {
|
||||||
@ -60,3 +53,59 @@ func (self *PatchBuildingHelper) Reset() error {
|
|||||||
// refreshing the current context so that the secondary panel is hidden if necessary.
|
// refreshing the current context so that the secondary panel is hidden if necessary.
|
||||||
return self.c.PostRefreshUpdate(self.c.CurrentContext())
|
return self.c.PostRefreshUpdate(self.c.CurrentContext())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *PatchBuildingHelper) RefreshPatchBuildingPanel(opts types.OnFocusOpts) error {
|
||||||
|
selectedLineIdx := -1
|
||||||
|
if opts.ClickedWindowName == "main" {
|
||||||
|
selectedLineIdx = opts.ClickedViewLineIdx
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.c.Git().Patch.PatchBuilder.Active() {
|
||||||
|
return self.Escape()
|
||||||
|
}
|
||||||
|
|
||||||
|
// get diff from commit file that's currently selected
|
||||||
|
path := self.c.Contexts().CommitFiles.GetSelectedPath()
|
||||||
|
if path == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ref := self.c.Contexts().CommitFiles.CommitFileTreeViewModel.GetRef()
|
||||||
|
to := ref.RefName()
|
||||||
|
from, reverse := self.c.Modes().Diffing.GetFromAndReverseArgsForDiff(ref.ParentRefName())
|
||||||
|
diff, err := self.c.Git().WorkingTree.ShowFileDiff(from, to, reverse, path, true, self.c.State().GetIgnoreWhitespaceInDiffView())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
secondaryDiff := self.c.Git().Patch.PatchBuilder.RenderPatchForFile(path, false, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
context := self.c.Contexts().CustomPatchBuilder
|
||||||
|
|
||||||
|
oldState := context.GetState()
|
||||||
|
|
||||||
|
state := patch_exploring.NewState(diff, selectedLineIdx, oldState, self.c.Log)
|
||||||
|
context.SetState(state)
|
||||||
|
if state == nil {
|
||||||
|
return self.Escape()
|
||||||
|
}
|
||||||
|
|
||||||
|
mainContent := context.GetContentToRender(true)
|
||||||
|
|
||||||
|
self.c.Contexts().CustomPatchBuilder.FocusSelection()
|
||||||
|
|
||||||
|
return self.c.RenderToMainViews(types.RefreshMainOpts{
|
||||||
|
Pair: self.c.MainViewPairs().PatchBuilding,
|
||||||
|
Main: &types.ViewUpdateOpts{
|
||||||
|
Task: types.NewRenderStringWithoutScrollTask(mainContent),
|
||||||
|
Title: self.c.Tr.Patch,
|
||||||
|
},
|
||||||
|
Secondary: &types.ViewUpdateOpts{
|
||||||
|
Task: types.NewRenderStringWithoutScrollTask(secondaryDiff),
|
||||||
|
Title: self.c.Tr.CustomPatch,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
36
pkg/gui/controllers/helpers/record_directory_helper.go
Normal file
36
pkg/gui/controllers/helpers/record_directory_helper.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RecordDirectoryHelper struct {
|
||||||
|
c *HelperCommon
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRecordDirectoryHelper(c *HelperCommon) *RecordDirectoryHelper {
|
||||||
|
return &RecordDirectoryHelper{
|
||||||
|
c: c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// when a user runs lazygit with the LAZYGIT_NEW_DIR_FILE env variable defined
|
||||||
|
// we will write the current directory to that file on exit so that their
|
||||||
|
// shell can then change to that directory. That means you don't get kicked
|
||||||
|
// back to the directory that you started with.
|
||||||
|
func (self *RecordDirectoryHelper) RecordCurrentDirectory() error {
|
||||||
|
// determine current directory, set it in LAZYGIT_NEW_DIR_FILE
|
||||||
|
dirName, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return self.RecordDirectory(dirName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *RecordDirectoryHelper) RecordDirectory(dirName string) error {
|
||||||
|
newDirFilePath := os.Getenv("LAZYGIT_NEW_DIR_FILE")
|
||||||
|
if newDirFilePath == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return self.c.OS().CreateFileWithContent(newDirFilePath, dirName)
|
||||||
|
}
|
611
pkg/gui/controllers/helpers/refresh_helper.go
Normal file
611
pkg/gui/controllers/helpers/refresh_helper.go
Normal file
@ -0,0 +1,611 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/generics/set"
|
||||||
|
"github.com/jesseduffield/generics/slices"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/mergeconflicts"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RefreshHelper struct {
|
||||||
|
c *HelperCommon
|
||||||
|
refsHelper *RefsHelper
|
||||||
|
mergeAndRebaseHelper *MergeAndRebaseHelper
|
||||||
|
patchBuildingHelper *PatchBuildingHelper
|
||||||
|
stagingHelper *StagingHelper
|
||||||
|
mergeConflictsHelper *MergeConflictsHelper
|
||||||
|
fileWatcher types.IFileWatcher
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRefreshHelper(
|
||||||
|
c *HelperCommon,
|
||||||
|
refsHelper *RefsHelper,
|
||||||
|
mergeAndRebaseHelper *MergeAndRebaseHelper,
|
||||||
|
patchBuildingHelper *PatchBuildingHelper,
|
||||||
|
stagingHelper *StagingHelper,
|
||||||
|
mergeConflictsHelper *MergeConflictsHelper,
|
||||||
|
fileWatcher types.IFileWatcher,
|
||||||
|
) *RefreshHelper {
|
||||||
|
return &RefreshHelper{
|
||||||
|
c: c,
|
||||||
|
refsHelper: refsHelper,
|
||||||
|
mergeAndRebaseHelper: mergeAndRebaseHelper,
|
||||||
|
patchBuildingHelper: patchBuildingHelper,
|
||||||
|
stagingHelper: stagingHelper,
|
||||||
|
mergeConflictsHelper: mergeConflictsHelper,
|
||||||
|
fileWatcher: fileWatcher,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *RefreshHelper) Refresh(options types.RefreshOptions) error {
|
||||||
|
if options.Scope == nil {
|
||||||
|
self.c.Log.Infof(
|
||||||
|
"refreshing all scopes in %s mode",
|
||||||
|
getModeName(options.Mode),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
self.c.Log.Infof(
|
||||||
|
"refreshing the following scopes in %s mode: %s",
|
||||||
|
getModeName(options.Mode),
|
||||||
|
strings.Join(getScopeNames(options.Scope), ","),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
|
||||||
|
f := func() {
|
||||||
|
var scopeSet *set.Set[types.RefreshableView]
|
||||||
|
if len(options.Scope) == 0 {
|
||||||
|
// not refreshing staging/patch-building unless explicitly requested because we only need
|
||||||
|
// to refresh those while focused.
|
||||||
|
scopeSet = set.NewFromSlice([]types.RefreshableView{
|
||||||
|
types.COMMITS,
|
||||||
|
types.BRANCHES,
|
||||||
|
types.FILES,
|
||||||
|
types.STASH,
|
||||||
|
types.REFLOG,
|
||||||
|
types.TAGS,
|
||||||
|
types.REMOTES,
|
||||||
|
types.STATUS,
|
||||||
|
types.BISECT_INFO,
|
||||||
|
types.STAGING,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
scopeSet = set.NewFromSlice(options.Scope)
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh := func(f func()) {
|
||||||
|
wg.Add(1)
|
||||||
|
func() {
|
||||||
|
if options.Mode == types.ASYNC {
|
||||||
|
go utils.Safe(f)
|
||||||
|
} else {
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
if scopeSet.Includes(types.COMMITS) || scopeSet.Includes(types.BRANCHES) || scopeSet.Includes(types.REFLOG) || scopeSet.Includes(types.BISECT_INFO) {
|
||||||
|
refresh(self.refreshCommits)
|
||||||
|
} else if scopeSet.Includes(types.REBASE_COMMITS) {
|
||||||
|
// the above block handles rebase commits so we only need to call this one
|
||||||
|
// if we've asked specifically for rebase commits and not those other things
|
||||||
|
refresh(func() { _ = self.refreshRebaseCommits() })
|
||||||
|
}
|
||||||
|
|
||||||
|
if scopeSet.Includes(types.SUB_COMMITS) {
|
||||||
|
refresh(func() { _ = self.refreshSubCommitsWithLimit() })
|
||||||
|
}
|
||||||
|
|
||||||
|
// reason we're not doing this if the COMMITS type is included is that if the COMMITS type _is_ included we will refresh the commit files context anyway
|
||||||
|
if scopeSet.Includes(types.COMMIT_FILES) && !scopeSet.Includes(types.COMMITS) {
|
||||||
|
refresh(func() { _ = self.refreshCommitFilesContext() })
|
||||||
|
}
|
||||||
|
|
||||||
|
if scopeSet.Includes(types.FILES) || scopeSet.Includes(types.SUBMODULES) {
|
||||||
|
refresh(func() { _ = self.refreshFilesAndSubmodules() })
|
||||||
|
}
|
||||||
|
|
||||||
|
if scopeSet.Includes(types.STASH) {
|
||||||
|
refresh(func() { _ = self.refreshStashEntries() })
|
||||||
|
}
|
||||||
|
|
||||||
|
if scopeSet.Includes(types.TAGS) {
|
||||||
|
refresh(func() { _ = self.refreshTags() })
|
||||||
|
}
|
||||||
|
|
||||||
|
if scopeSet.Includes(types.REMOTES) {
|
||||||
|
refresh(func() { _ = self.refreshRemotes() })
|
||||||
|
}
|
||||||
|
|
||||||
|
if scopeSet.Includes(types.STAGING) {
|
||||||
|
refresh(func() { _ = self.stagingHelper.RefreshStagingPanel(types.OnFocusOpts{}) })
|
||||||
|
}
|
||||||
|
|
||||||
|
if scopeSet.Includes(types.PATCH_BUILDING) {
|
||||||
|
refresh(func() { _ = self.patchBuildingHelper.RefreshPatchBuildingPanel(types.OnFocusOpts{}) })
|
||||||
|
}
|
||||||
|
|
||||||
|
if scopeSet.Includes(types.MERGE_CONFLICTS) || scopeSet.Includes(types.FILES) {
|
||||||
|
refresh(func() { _ = self.mergeConflictsHelper.RefreshMergeState() })
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
self.refreshStatus()
|
||||||
|
|
||||||
|
if options.Then != nil {
|
||||||
|
options.Then()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.Mode == types.BLOCK_UI {
|
||||||
|
self.c.OnUIThread(func() error {
|
||||||
|
f()
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getScopeNames(scopes []types.RefreshableView) []string {
|
||||||
|
scopeNameMap := map[types.RefreshableView]string{
|
||||||
|
types.COMMITS: "commits",
|
||||||
|
types.BRANCHES: "branches",
|
||||||
|
types.FILES: "files",
|
||||||
|
types.SUBMODULES: "submodules",
|
||||||
|
types.SUB_COMMITS: "subCommits",
|
||||||
|
types.STASH: "stash",
|
||||||
|
types.REFLOG: "reflog",
|
||||||
|
types.TAGS: "tags",
|
||||||
|
types.REMOTES: "remotes",
|
||||||
|
types.STATUS: "status",
|
||||||
|
types.BISECT_INFO: "bisect",
|
||||||
|
types.STAGING: "staging",
|
||||||
|
types.MERGE_CONFLICTS: "mergeConflicts",
|
||||||
|
}
|
||||||
|
|
||||||
|
return slices.Map(scopes, func(scope types.RefreshableView) string {
|
||||||
|
return scopeNameMap[scope]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func getModeName(mode types.RefreshMode) string {
|
||||||
|
switch mode {
|
||||||
|
case types.SYNC:
|
||||||
|
return "sync"
|
||||||
|
case types.ASYNC:
|
||||||
|
return "async"
|
||||||
|
case types.BLOCK_UI:
|
||||||
|
return "block-ui"
|
||||||
|
default:
|
||||||
|
return "unknown mode"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// during startup, the bottleneck is fetching the reflog entries. We need these
|
||||||
|
// on startup to sort the branches by recency. So we have two phases: INITIAL, and COMPLETE.
|
||||||
|
// In the initial phase we don't get any reflog commits, but we asynchronously get them
|
||||||
|
// and refresh the branches after that
|
||||||
|
func (self *RefreshHelper) refreshReflogCommitsConsideringStartup() {
|
||||||
|
switch self.c.State().GetRepoState().GetStartupStage() {
|
||||||
|
case types.INITIAL:
|
||||||
|
go utils.Safe(func() {
|
||||||
|
_ = self.refreshReflogCommits()
|
||||||
|
self.refreshBranches()
|
||||||
|
self.c.State().GetRepoState().SetStartupStage(types.COMPLETE)
|
||||||
|
})
|
||||||
|
|
||||||
|
case types.COMPLETE:
|
||||||
|
_ = self.refreshReflogCommits()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// whenever we change commits, we should update branches because the upstream/downstream
|
||||||
|
// counts can change. Whenever we change branches we should probably also change commits
|
||||||
|
// e.g. in the case of switching branches.
|
||||||
|
func (self *RefreshHelper) refreshCommits() {
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
wg.Add(2)
|
||||||
|
|
||||||
|
go utils.Safe(func() {
|
||||||
|
self.refreshReflogCommitsConsideringStartup()
|
||||||
|
|
||||||
|
self.refreshBranches()
|
||||||
|
wg.Done()
|
||||||
|
})
|
||||||
|
|
||||||
|
go utils.Safe(func() {
|
||||||
|
_ = self.refreshCommitsWithLimit()
|
||||||
|
ctx, ok := self.c.Contexts().CommitFiles.GetParentContext()
|
||||||
|
if ok && ctx.GetKey() == context.LOCAL_COMMITS_CONTEXT_KEY {
|
||||||
|
// This makes sense when we've e.g. just amended a commit, meaning we get a new commit SHA at the same position.
|
||||||
|
// However if we've just added a brand new commit, it pushes the list down by one and so we would end up
|
||||||
|
// showing the contents of a different commit than the one we initially entered.
|
||||||
|
// Ideally we would know when to refresh the commit files context and when not to,
|
||||||
|
// or perhaps we could just pop that context off the stack whenever cycling windows.
|
||||||
|
// For now the awkwardness remains.
|
||||||
|
commit := self.c.Contexts().LocalCommits.GetSelected()
|
||||||
|
if commit != nil {
|
||||||
|
self.c.Contexts().CommitFiles.SetRef(commit)
|
||||||
|
self.c.Contexts().CommitFiles.SetTitleRef(commit.RefName())
|
||||||
|
_ = self.refreshCommitFilesContext()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wg.Done()
|
||||||
|
})
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *RefreshHelper) refreshCommitsWithLimit() error {
|
||||||
|
self.c.Mutexes().LocalCommitsMutex.Lock()
|
||||||
|
defer self.c.Mutexes().LocalCommitsMutex.Unlock()
|
||||||
|
|
||||||
|
commits, err := self.c.Git().Loaders.CommitLoader.GetCommits(
|
||||||
|
git_commands.GetCommitsOptions{
|
||||||
|
Limit: self.c.Contexts().LocalCommits.GetLimitCommits(),
|
||||||
|
FilterPath: self.c.Modes().Filtering.GetPath(),
|
||||||
|
IncludeRebaseCommits: true,
|
||||||
|
RefName: self.refForLog(),
|
||||||
|
All: self.c.Contexts().LocalCommits.GetShowWholeGitGraph(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
self.c.Model().Commits = commits
|
||||||
|
self.c.Model().WorkingTreeStateAtLastCommitRefresh = self.c.Git().Status.WorkingTreeState()
|
||||||
|
|
||||||
|
return self.c.PostRefreshUpdate(self.c.Contexts().LocalCommits)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *RefreshHelper) refreshSubCommitsWithLimit() error {
|
||||||
|
self.c.Mutexes().SubCommitsMutex.Lock()
|
||||||
|
defer self.c.Mutexes().SubCommitsMutex.Unlock()
|
||||||
|
|
||||||
|
commits, err := self.c.Git().Loaders.CommitLoader.GetCommits(
|
||||||
|
git_commands.GetCommitsOptions{
|
||||||
|
Limit: self.c.Contexts().SubCommits.GetLimitCommits(),
|
||||||
|
FilterPath: self.c.Modes().Filtering.GetPath(),
|
||||||
|
IncludeRebaseCommits: false,
|
||||||
|
RefName: self.c.Contexts().SubCommits.GetRef().FullRefName(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
self.c.Model().SubCommits = commits
|
||||||
|
|
||||||
|
return self.c.PostRefreshUpdate(self.c.Contexts().SubCommits)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *RefreshHelper) refreshCommitFilesContext() error {
|
||||||
|
ref := self.c.Contexts().CommitFiles.GetRef()
|
||||||
|
to := ref.RefName()
|
||||||
|
from, reverse := self.c.Modes().Diffing.GetFromAndReverseArgsForDiff(ref.ParentRefName())
|
||||||
|
|
||||||
|
files, err := self.c.Git().Loaders.CommitFileLoader.GetFilesInDiff(from, to, reverse)
|
||||||
|
if err != nil {
|
||||||
|
return self.c.Error(err)
|
||||||
|
}
|
||||||
|
self.c.Model().CommitFiles = files
|
||||||
|
self.c.Contexts().CommitFiles.CommitFileTreeViewModel.SetTree()
|
||||||
|
|
||||||
|
return self.c.PostRefreshUpdate(self.c.Contexts().CommitFiles)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *RefreshHelper) refreshRebaseCommits() error {
|
||||||
|
self.c.Mutexes().LocalCommitsMutex.Lock()
|
||||||
|
defer self.c.Mutexes().LocalCommitsMutex.Unlock()
|
||||||
|
|
||||||
|
updatedCommits, err := self.c.Git().Loaders.CommitLoader.MergeRebasingCommits(self.c.Model().Commits)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
self.c.Model().Commits = updatedCommits
|
||||||
|
self.c.Model().WorkingTreeStateAtLastCommitRefresh = self.c.Git().Status.WorkingTreeState()
|
||||||
|
|
||||||
|
return self.c.PostRefreshUpdate(self.c.Contexts().LocalCommits)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *RefreshHelper) refreshTags() error {
|
||||||
|
tags, err := self.c.Git().Loaders.TagLoader.GetTags()
|
||||||
|
if err != nil {
|
||||||
|
return self.c.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.c.Model().Tags = tags
|
||||||
|
|
||||||
|
return self.c.PostRefreshUpdate(self.c.Contexts().Tags)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *RefreshHelper) refreshStateSubmoduleConfigs() error {
|
||||||
|
configs, err := self.c.Git().Submodule.GetConfigs()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
self.c.Model().Submodules = configs
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// self.refreshStatus is called at the end of this because that's when we can
|
||||||
|
// be sure there is a State.Model.Branches array to pick the current branch from
|
||||||
|
func (self *RefreshHelper) refreshBranches() {
|
||||||
|
reflogCommits := self.c.Model().FilteredReflogCommits
|
||||||
|
if self.c.Modes().Filtering.Active() {
|
||||||
|
// in filter mode we filter our reflog commits to just those containing the path
|
||||||
|
// however we need all the reflog entries to populate the recencies of our branches
|
||||||
|
// which allows us to order them correctly. So if we're filtering we'll just
|
||||||
|
// manually load all the reflog commits here
|
||||||
|
var err error
|
||||||
|
reflogCommits, _, err = self.c.Git().Loaders.ReflogCommitLoader.GetReflogCommits(nil, "")
|
||||||
|
if err != nil {
|
||||||
|
self.c.Log.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
branches, err := self.c.Git().Loaders.BranchLoader.Load(reflogCommits)
|
||||||
|
if err != nil {
|
||||||
|
_ = self.c.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.c.Model().Branches = branches
|
||||||
|
|
||||||
|
if err := self.c.PostRefreshUpdate(self.c.Contexts().Branches); err != nil {
|
||||||
|
self.c.Log.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.refreshStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *RefreshHelper) refreshFilesAndSubmodules() error {
|
||||||
|
self.c.Mutexes().RefreshingFilesMutex.Lock()
|
||||||
|
self.c.State().SetIsRefreshingFiles(true)
|
||||||
|
defer func() {
|
||||||
|
self.c.State().SetIsRefreshingFiles(false)
|
||||||
|
self.c.Mutexes().RefreshingFilesMutex.Unlock()
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err := self.refreshStateSubmoduleConfigs(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := self.refreshStateFiles(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
self.c.OnUIThread(func() error {
|
||||||
|
if err := self.c.PostRefreshUpdate(self.c.Contexts().Submodules); err != nil {
|
||||||
|
self.c.Log.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := self.c.PostRefreshUpdate(self.c.Contexts().Files); err != nil {
|
||||||
|
self.c.Log.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *RefreshHelper) refreshStateFiles() error {
|
||||||
|
fileTreeViewModel := self.c.Contexts().Files.FileTreeViewModel
|
||||||
|
|
||||||
|
// If git thinks any of our files have inline merge conflicts, but they actually don't,
|
||||||
|
// we stage them.
|
||||||
|
// Note that if files with merge conflicts have both arisen and have been resolved
|
||||||
|
// between refreshes, we won't stage them here. This is super unlikely though,
|
||||||
|
// and this approach spares us from having to call `git status` twice in a row.
|
||||||
|
// Although this also means that at startup we won't be staging anything until
|
||||||
|
// we call git status again.
|
||||||
|
pathsToStage := []string{}
|
||||||
|
prevConflictFileCount := 0
|
||||||
|
for _, file := range self.c.Model().Files {
|
||||||
|
if file.HasMergeConflicts {
|
||||||
|
prevConflictFileCount++
|
||||||
|
}
|
||||||
|
if file.HasInlineMergeConflicts {
|
||||||
|
hasConflicts, err := mergeconflicts.FileHasConflictMarkers(file.Name)
|
||||||
|
if err != nil {
|
||||||
|
self.c.Log.Error(err)
|
||||||
|
} else if !hasConflicts {
|
||||||
|
pathsToStage = append(pathsToStage, file.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(pathsToStage) > 0 {
|
||||||
|
self.c.LogAction(self.c.Tr.Actions.StageResolvedFiles)
|
||||||
|
if err := self.c.Git().WorkingTree.StageFiles(pathsToStage); err != nil {
|
||||||
|
return self.c.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
files := self.c.Git().Loaders.FileLoader.
|
||||||
|
GetStatusFiles(git_commands.GetStatusFileOptions{})
|
||||||
|
|
||||||
|
conflictFileCount := 0
|
||||||
|
for _, file := range files {
|
||||||
|
if file.HasMergeConflicts {
|
||||||
|
conflictFileCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.c.Git().Status.WorkingTreeState() != enums.REBASE_MODE_NONE && conflictFileCount == 0 && prevConflictFileCount > 0 {
|
||||||
|
self.c.OnUIThread(func() error { return self.mergeAndRebaseHelper.PromptToContinueRebase() })
|
||||||
|
}
|
||||||
|
|
||||||
|
fileTreeViewModel.RWMutex.Lock()
|
||||||
|
|
||||||
|
// only taking over the filter if it hasn't already been set by the user.
|
||||||
|
// Though this does make it impossible for the user to actually say they want to display all if
|
||||||
|
// conflicts are currently being shown. Hmm. Worth it I reckon. If we need to add some
|
||||||
|
// extra state here to see if the user's set the filter themselves we can do that, but
|
||||||
|
// I'd prefer to maintain as little state as possible.
|
||||||
|
if conflictFileCount > 0 {
|
||||||
|
if fileTreeViewModel.GetFilter() == filetree.DisplayAll {
|
||||||
|
fileTreeViewModel.SetFilter(filetree.DisplayConflicted)
|
||||||
|
}
|
||||||
|
} else if fileTreeViewModel.GetFilter() == filetree.DisplayConflicted {
|
||||||
|
fileTreeViewModel.SetFilter(filetree.DisplayAll)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.c.Model().Files = files
|
||||||
|
fileTreeViewModel.SetTree()
|
||||||
|
fileTreeViewModel.RWMutex.Unlock()
|
||||||
|
|
||||||
|
if err := self.fileWatcher.AddFilesToFileWatcher(files); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// the reflogs panel is the only panel where we cache data, in that we only
|
||||||
|
// load entries that have been created since we last ran the call. This means
|
||||||
|
// we need to be more careful with how we use this, and to ensure we're emptying
|
||||||
|
// the reflogs array when changing contexts.
|
||||||
|
// This method also manages two things: ReflogCommits and FilteredReflogCommits.
|
||||||
|
// FilteredReflogCommits are rendered in the reflogs panel, and ReflogCommits
|
||||||
|
// are used by the branches panel to obtain recency values for sorting.
|
||||||
|
func (self *RefreshHelper) refreshReflogCommits() error {
|
||||||
|
// pulling state into its own variable incase it gets swapped out for another state
|
||||||
|
// and we get an out of bounds exception
|
||||||
|
model := self.c.Model()
|
||||||
|
var lastReflogCommit *models.Commit
|
||||||
|
if len(model.ReflogCommits) > 0 {
|
||||||
|
lastReflogCommit = model.ReflogCommits[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh := func(stateCommits *[]*models.Commit, filterPath string) error {
|
||||||
|
commits, onlyObtainedNewReflogCommits, err := self.c.Git().Loaders.ReflogCommitLoader.
|
||||||
|
GetReflogCommits(lastReflogCommit, filterPath)
|
||||||
|
if err != nil {
|
||||||
|
return self.c.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if onlyObtainedNewReflogCommits {
|
||||||
|
*stateCommits = append(commits, *stateCommits...)
|
||||||
|
} else {
|
||||||
|
*stateCommits = commits
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := refresh(&model.ReflogCommits, ""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.c.Modes().Filtering.Active() {
|
||||||
|
if err := refresh(&model.FilteredReflogCommits, self.c.Modes().Filtering.GetPath()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
model.FilteredReflogCommits = model.ReflogCommits
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.c.PostRefreshUpdate(self.c.Contexts().ReflogCommits)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *RefreshHelper) refreshRemotes() error {
|
||||||
|
prevSelectedRemote := self.c.Contexts().Remotes.GetSelected()
|
||||||
|
|
||||||
|
remotes, err := self.c.Git().Loaders.RemoteLoader.GetRemotes()
|
||||||
|
if err != nil {
|
||||||
|
return self.c.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.c.Model().Remotes = remotes
|
||||||
|
|
||||||
|
// we need to ensure our selected remote branches aren't now outdated
|
||||||
|
if prevSelectedRemote != nil && self.c.Model().RemoteBranches != nil {
|
||||||
|
// find remote now
|
||||||
|
for _, remote := range remotes {
|
||||||
|
if remote.Name == prevSelectedRemote.Name {
|
||||||
|
self.c.Model().RemoteBranches = remote.Branches
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := self.c.PostRefreshUpdate(self.c.Contexts().Remotes); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := self.c.PostRefreshUpdate(self.c.Contexts().RemoteBranches); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *RefreshHelper) refreshStashEntries() error {
|
||||||
|
self.c.Model().StashEntries = self.c.Git().Loaders.StashLoader.
|
||||||
|
GetStashEntries(self.c.Modes().Filtering.GetPath())
|
||||||
|
|
||||||
|
return self.c.PostRefreshUpdate(self.c.Contexts().Stash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// never call this on its own, it should only be called from within refreshCommits()
|
||||||
|
func (self *RefreshHelper) refreshStatus() {
|
||||||
|
self.c.Mutexes().RefreshingStatusMutex.Lock()
|
||||||
|
defer self.c.Mutexes().RefreshingStatusMutex.Unlock()
|
||||||
|
|
||||||
|
currentBranch := self.refsHelper.GetCheckedOutRef()
|
||||||
|
if currentBranch == nil {
|
||||||
|
// need to wait for branches to refresh
|
||||||
|
return
|
||||||
|
}
|
||||||
|
status := ""
|
||||||
|
|
||||||
|
if currentBranch.IsRealBranch() {
|
||||||
|
status += presentation.ColoredBranchStatus(currentBranch, self.c.Tr) + " "
|
||||||
|
}
|
||||||
|
|
||||||
|
workingTreeState := self.c.Git().Status.WorkingTreeState()
|
||||||
|
if workingTreeState != enums.REBASE_MODE_NONE {
|
||||||
|
status += style.FgYellow.Sprintf("(%s) ", presentation.FormatWorkingTreeState(workingTreeState))
|
||||||
|
}
|
||||||
|
|
||||||
|
name := presentation.GetBranchTextStyle(currentBranch.Name).Sprint(currentBranch.Name)
|
||||||
|
repoName := utils.GetCurrentRepoName()
|
||||||
|
status += fmt.Sprintf("%s → %s ", repoName, name)
|
||||||
|
|
||||||
|
self.c.SetViewContent(self.c.Views().Status, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *RefreshHelper) refForLog() string {
|
||||||
|
bisectInfo := self.c.Git().Bisect.GetInfo()
|
||||||
|
self.c.Model().BisectInfo = bisectInfo
|
||||||
|
|
||||||
|
if !bisectInfo.Started() {
|
||||||
|
return "HEAD"
|
||||||
|
}
|
||||||
|
|
||||||
|
// need to see if our bisect's current commit is reachable from our 'new' ref.
|
||||||
|
if bisectInfo.Bisecting() && !self.c.Git().Bisect.ReachableFromStart(bisectInfo) {
|
||||||
|
return bisectInfo.GetNewSha()
|
||||||
|
}
|
||||||
|
|
||||||
|
return bisectInfo.GetStartSha()
|
||||||
|
}
|
@ -5,10 +5,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/jesseduffield/generics/slices"
|
"github.com/jesseduffield/generics/slices"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
@ -23,23 +21,14 @@ type IRefsHelper interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type RefsHelper struct {
|
type RefsHelper struct {
|
||||||
c *types.HelperCommon
|
c *HelperCommon
|
||||||
git *commands.GitCommand
|
|
||||||
contexts *context.ContextTree
|
|
||||||
model *types.Model
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRefsHelper(
|
func NewRefsHelper(
|
||||||
c *types.HelperCommon,
|
c *HelperCommon,
|
||||||
git *commands.GitCommand,
|
|
||||||
contexts *context.ContextTree,
|
|
||||||
model *types.Model,
|
|
||||||
) *RefsHelper {
|
) *RefsHelper {
|
||||||
return &RefsHelper{
|
return &RefsHelper{
|
||||||
c: c,
|
c: c,
|
||||||
git: git,
|
|
||||||
contexts: contexts,
|
|
||||||
model: model,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,15 +43,15 @@ func (self *RefsHelper) CheckoutRef(ref string, options types.CheckoutRefOptions
|
|||||||
cmdOptions := git_commands.CheckoutOptions{Force: false, EnvVars: options.EnvVars}
|
cmdOptions := git_commands.CheckoutOptions{Force: false, EnvVars: options.EnvVars}
|
||||||
|
|
||||||
onSuccess := func() {
|
onSuccess := func() {
|
||||||
self.contexts.Branches.SetSelectedLineIdx(0)
|
self.c.Contexts().Branches.SetSelectedLineIdx(0)
|
||||||
self.contexts.ReflogCommits.SetSelectedLineIdx(0)
|
self.c.Contexts().ReflogCommits.SetSelectedLineIdx(0)
|
||||||
self.contexts.LocalCommits.SetSelectedLineIdx(0)
|
self.c.Contexts().LocalCommits.SetSelectedLineIdx(0)
|
||||||
// loading a heap of commits is slow so we limit them whenever doing a reset
|
// loading a heap of commits is slow so we limit them whenever doing a reset
|
||||||
self.contexts.LocalCommits.SetLimitCommits(true)
|
self.c.Contexts().LocalCommits.SetLimitCommits(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.c.WithWaitingStatus(waitingStatus, func() error {
|
return self.c.WithWaitingStatus(waitingStatus, func() error {
|
||||||
if err := self.git.Branch.Checkout(ref, cmdOptions); err != nil {
|
if err := self.c.Git().Branch.Checkout(ref, cmdOptions); err != nil {
|
||||||
// note, this will only work for english-language git commands. If we force git to use english, and the error isn't this one, then the user will receive an english command they may not understand. I'm not sure what the best solution to this is. Running the command once in english and a second time in the native language is one option
|
// note, this will only work for english-language git commands. If we force git to use english, and the error isn't this one, then the user will receive an english command they may not understand. I'm not sure what the best solution to this is. Running the command once in english and a second time in the native language is one option
|
||||||
|
|
||||||
if options.OnRefNotFound != nil && strings.Contains(err.Error(), "did not match any file(s) known to git") {
|
if options.OnRefNotFound != nil && strings.Contains(err.Error(), "did not match any file(s) known to git") {
|
||||||
@ -75,15 +64,15 @@ func (self *RefsHelper) CheckoutRef(ref string, options types.CheckoutRefOptions
|
|||||||
Title: self.c.Tr.AutoStashTitle,
|
Title: self.c.Tr.AutoStashTitle,
|
||||||
Prompt: self.c.Tr.AutoStashPrompt,
|
Prompt: self.c.Tr.AutoStashPrompt,
|
||||||
HandleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
if err := self.git.Stash.Save(self.c.Tr.StashPrefix + ref); err != nil {
|
if err := self.c.Git().Stash.Save(self.c.Tr.StashPrefix + ref); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
if err := self.git.Branch.Checkout(ref, cmdOptions); err != nil {
|
if err := self.c.Git().Branch.Checkout(ref, cmdOptions); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
onSuccess()
|
onSuccess()
|
||||||
if err := self.git.Stash.Pop(0); err != nil {
|
if err := self.c.Git().Stash.Pop(0); err != nil {
|
||||||
if err := self.c.Refresh(types.RefreshOptions{Mode: types.BLOCK_UI}); err != nil {
|
if err := self.c.Refresh(types.RefreshOptions{Mode: types.BLOCK_UI}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -105,22 +94,22 @@ func (self *RefsHelper) CheckoutRef(ref string, options types.CheckoutRefOptions
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *RefsHelper) GetCheckedOutRef() *models.Branch {
|
func (self *RefsHelper) GetCheckedOutRef() *models.Branch {
|
||||||
if len(self.model.Branches) == 0 {
|
if len(self.c.Model().Branches) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.model.Branches[0]
|
return self.c.Model().Branches[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *RefsHelper) ResetToRef(ref string, strength string, envVars []string) error {
|
func (self *RefsHelper) ResetToRef(ref string, strength string, envVars []string) error {
|
||||||
if err := self.git.Commit.ResetToCommit(ref, strength, envVars); err != nil {
|
if err := self.c.Git().Commit.ResetToCommit(ref, strength, envVars); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.contexts.LocalCommits.SetSelectedLineIdx(0)
|
self.c.Contexts().LocalCommits.SetSelectedLineIdx(0)
|
||||||
self.contexts.ReflogCommits.SetSelectedLineIdx(0)
|
self.c.Contexts().ReflogCommits.SetSelectedLineIdx(0)
|
||||||
// loading a heap of commits is slow so we limit them whenever doing a reset
|
// loading a heap of commits is slow so we limit them whenever doing a reset
|
||||||
self.contexts.LocalCommits.SetLimitCommits(true)
|
self.c.Contexts().LocalCommits.SetLimitCommits(true)
|
||||||
|
|
||||||
if err := self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.FILES, types.BRANCHES, types.REFLOG, types.COMMITS}}); err != nil {
|
if err := self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.FILES, types.BRANCHES, types.REFLOG, types.COMMITS}}); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -173,18 +162,18 @@ func (self *RefsHelper) NewBranch(from string, fromFormattedName string, suggest
|
|||||||
InitialContent: suggestedBranchName,
|
InitialContent: suggestedBranchName,
|
||||||
HandleConfirm: func(response string) error {
|
HandleConfirm: func(response string) error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.CreateBranch)
|
self.c.LogAction(self.c.Tr.Actions.CreateBranch)
|
||||||
if err := self.git.Branch.New(sanitizedBranchName(response), from); err != nil {
|
if err := self.c.Git().Branch.New(sanitizedBranchName(response), from); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.c.CurrentContext() != self.contexts.Branches {
|
if self.c.CurrentContext() != self.c.Contexts().Branches {
|
||||||
if err := self.c.PushContext(self.contexts.Branches); err != nil {
|
if err := self.c.PushContext(self.c.Contexts().Branches); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.contexts.LocalCommits.SetSelectedLineIdx(0)
|
self.c.Contexts().LocalCommits.SetSelectedLineIdx(0)
|
||||||
self.contexts.Branches.SetSelectedLineIdx(0)
|
self.c.Contexts().Branches.SetSelectedLineIdx(0)
|
||||||
|
|
||||||
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||||
},
|
},
|
||||||
|
175
pkg/gui/controllers/helpers/repos_helper.go
Normal file
175
pkg/gui/controllers/helpers/repos_helper.go
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/generics/slices"
|
||||||
|
appTypes "github.com/jesseduffield/lazygit/pkg/app/types"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/env"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/presentation/icons"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type onNewRepoFn func(startArgs appTypes.StartArgs, reuseState bool) error
|
||||||
|
|
||||||
|
// helps switch back and forth between repos
|
||||||
|
type ReposHelper struct {
|
||||||
|
c *HelperCommon
|
||||||
|
recordDirectoryHelper *RecordDirectoryHelper
|
||||||
|
onNewRepo onNewRepoFn
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRecentReposHelper(
|
||||||
|
c *HelperCommon,
|
||||||
|
recordDirectoryHelper *RecordDirectoryHelper,
|
||||||
|
onNewRepo onNewRepoFn,
|
||||||
|
) *ReposHelper {
|
||||||
|
return &ReposHelper{
|
||||||
|
c: c,
|
||||||
|
recordDirectoryHelper: recordDirectoryHelper,
|
||||||
|
onNewRepo: onNewRepo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ReposHelper) EnterSubmodule(submodule *models.SubmoduleConfig) error {
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
self.c.State().GetRepoPathStack().Push(wd)
|
||||||
|
|
||||||
|
return self.DispatchSwitchToRepo(submodule.Path, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ReposHelper) getCurrentBranch(path string) string {
|
||||||
|
readHeadFile := func(path string) (string, error) {
|
||||||
|
headFile, err := os.ReadFile(filepath.Join(path, "HEAD"))
|
||||||
|
if err == nil {
|
||||||
|
content := strings.TrimSpace(string(headFile))
|
||||||
|
refsPrefix := "ref: refs/heads/"
|
||||||
|
var branchDisplay string
|
||||||
|
if strings.HasPrefix(content, refsPrefix) {
|
||||||
|
// is a branch
|
||||||
|
branchDisplay = strings.TrimPrefix(content, refsPrefix)
|
||||||
|
} else {
|
||||||
|
// detached HEAD state, displaying short SHA
|
||||||
|
branchDisplay = utils.ShortSha(content)
|
||||||
|
}
|
||||||
|
return branchDisplay, nil
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
gitDirPath := filepath.Join(path, ".git")
|
||||||
|
|
||||||
|
if gitDir, err := os.Stat(gitDirPath); err == nil {
|
||||||
|
if gitDir.IsDir() {
|
||||||
|
// ordinary repo
|
||||||
|
if branch, err := readHeadFile(gitDirPath); err == nil {
|
||||||
|
return branch
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// worktree
|
||||||
|
if worktreeGitDir, err := os.ReadFile(gitDirPath); err == nil {
|
||||||
|
content := strings.TrimSpace(string(worktreeGitDir))
|
||||||
|
worktreePath := strings.TrimPrefix(content, "gitdir: ")
|
||||||
|
if branch, err := readHeadFile(worktreePath); err == nil {
|
||||||
|
return branch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.c.Tr.LcBranchUnknown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ReposHelper) CreateRecentReposMenu() error {
|
||||||
|
// we'll show an empty panel if there are no recent repos
|
||||||
|
recentRepoPaths := []string{}
|
||||||
|
if len(self.c.GetAppState().RecentRepos) > 0 {
|
||||||
|
// we skip the first one because we're currently in it
|
||||||
|
recentRepoPaths = self.c.GetAppState().RecentRepos[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
currentBranches := sync.Map{}
|
||||||
|
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
wg.Add(len(recentRepoPaths))
|
||||||
|
|
||||||
|
for _, path := range recentRepoPaths {
|
||||||
|
go func(path string) {
|
||||||
|
defer wg.Done()
|
||||||
|
currentBranches.Store(path, self.getCurrentBranch(path))
|
||||||
|
}(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
menuItems := slices.Map(recentRepoPaths, func(path string) *types.MenuItem {
|
||||||
|
branchName, _ := currentBranches.Load(path)
|
||||||
|
if icons.IsIconEnabled() {
|
||||||
|
branchName = icons.BRANCH_ICON + " " + fmt.Sprintf("%v", branchName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &types.MenuItem{
|
||||||
|
LabelColumns: []string{
|
||||||
|
filepath.Base(path),
|
||||||
|
style.FgCyan.Sprint(branchName),
|
||||||
|
style.FgMagenta.Sprint(path),
|
||||||
|
},
|
||||||
|
OnPress: func() error {
|
||||||
|
// if we were in a submodule, we want to forget about that stack of repos
|
||||||
|
// so that hitting escape in the new repo does nothing
|
||||||
|
self.c.State().GetRepoPathStack().Clear()
|
||||||
|
return self.DispatchSwitchToRepo(path, false)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return self.c.Menu(types.CreateMenuOptions{Title: self.c.Tr.RecentRepos, Items: menuItems})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ReposHelper) DispatchSwitchToRepo(path string, reuse bool) error {
|
||||||
|
env.UnsetGitDirEnvs()
|
||||||
|
originalPath, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.Chdir(path); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return self.c.ErrorMsg(self.c.Tr.ErrRepositoryMovedOrDeleted)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := commands.VerifyInGitRepo(self.c.OS()); err != nil {
|
||||||
|
if err := os.Chdir(originalPath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := self.recordDirectoryHelper.RecordCurrentDirectory(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// these two mutexes are used by our background goroutines (triggered via `self.goEvery`. We don't want to
|
||||||
|
// switch to a repo while one of these goroutines is in the process of updating something
|
||||||
|
self.c.Mutexes().SyncMutex.Lock()
|
||||||
|
defer self.c.Mutexes().SyncMutex.Unlock()
|
||||||
|
|
||||||
|
self.c.Mutexes().RefreshingFilesMutex.Lock()
|
||||||
|
defer self.c.Mutexes().RefreshingFilesMutex.Unlock()
|
||||||
|
|
||||||
|
return self.onNewRepo(appTypes.StartArgs{}, reuse)
|
||||||
|
}
|
75
pkg/gui/controllers/helpers/snake_helper.go
Normal file
75
pkg/gui/controllers/helpers/snake_helper.go
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/snake"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SnakeHelper struct {
|
||||||
|
c *HelperCommon
|
||||||
|
game *snake.Game
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSnakeHelper(c *HelperCommon) *SnakeHelper {
|
||||||
|
return &SnakeHelper{
|
||||||
|
c: c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *SnakeHelper) StartGame() {
|
||||||
|
view := self.c.Views().Snake
|
||||||
|
|
||||||
|
game := snake.NewGame(view.Width(), view.Height(), self.renderSnakeGame, self.c.LogAction)
|
||||||
|
self.game = game
|
||||||
|
game.Start()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *SnakeHelper) ExitGame() {
|
||||||
|
self.game.Exit()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *SnakeHelper) SetDirection(direction snake.Direction) {
|
||||||
|
self.game.SetDirection(direction)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *SnakeHelper) renderSnakeGame(cells [][]snake.CellType, alive bool) {
|
||||||
|
view := self.c.Views().Snake
|
||||||
|
|
||||||
|
if !alive {
|
||||||
|
_ = self.c.ErrorMsg(self.c.Tr.YouDied)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
output := self.drawSnakeGame(cells)
|
||||||
|
|
||||||
|
view.Clear()
|
||||||
|
fmt.Fprint(view, output)
|
||||||
|
self.c.Render()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *SnakeHelper) drawSnakeGame(cells [][]snake.CellType) string {
|
||||||
|
writer := &strings.Builder{}
|
||||||
|
|
||||||
|
for i, row := range cells {
|
||||||
|
for _, cell := range row {
|
||||||
|
switch cell {
|
||||||
|
case snake.None:
|
||||||
|
writer.WriteString(" ")
|
||||||
|
case snake.Snake:
|
||||||
|
writer.WriteString("█")
|
||||||
|
case snake.Food:
|
||||||
|
writer.WriteString(style.FgMagenta.Sprint("█"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if i < len(cells) {
|
||||||
|
writer.WriteString("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output := writer.String()
|
||||||
|
return output
|
||||||
|
}
|
122
pkg/gui/controllers/helpers/staging_helper.go
Normal file
122
pkg/gui/controllers/helpers/staging_helper.go
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/patch_exploring"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StagingHelper struct {
|
||||||
|
c *HelperCommon
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStagingHelper(
|
||||||
|
c *HelperCommon,
|
||||||
|
) *StagingHelper {
|
||||||
|
return &StagingHelper{
|
||||||
|
c: c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: used from outside this file
|
||||||
|
func (self *StagingHelper) RefreshStagingPanel(focusOpts types.OnFocusOpts) error {
|
||||||
|
secondaryFocused := self.secondaryStagingFocused()
|
||||||
|
mainFocused := self.mainStagingFocused()
|
||||||
|
|
||||||
|
// this method could be called when the staging panel is not being used,
|
||||||
|
// in which case we don't want to do anything.
|
||||||
|
if !mainFocused && !secondaryFocused {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
mainSelectedLineIdx := -1
|
||||||
|
secondarySelectedLineIdx := -1
|
||||||
|
if focusOpts.ClickedViewLineIdx > 0 {
|
||||||
|
if secondaryFocused {
|
||||||
|
secondarySelectedLineIdx = focusOpts.ClickedViewLineIdx
|
||||||
|
} else {
|
||||||
|
mainSelectedLineIdx = focusOpts.ClickedViewLineIdx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mainContext := self.c.Contexts().Staging
|
||||||
|
secondaryContext := self.c.Contexts().StagingSecondary
|
||||||
|
|
||||||
|
var file *models.File
|
||||||
|
node := self.c.Contexts().Files.GetSelected()
|
||||||
|
if node != nil {
|
||||||
|
file = node.File
|
||||||
|
}
|
||||||
|
|
||||||
|
if file == nil || (!file.HasUnstagedChanges && !file.HasStagedChanges) {
|
||||||
|
return self.handleStagingEscape()
|
||||||
|
}
|
||||||
|
|
||||||
|
mainDiff := self.c.Git().WorkingTree.WorktreeFileDiff(file, true, false, false)
|
||||||
|
secondaryDiff := self.c.Git().WorkingTree.WorktreeFileDiff(file, true, true, false)
|
||||||
|
|
||||||
|
// grabbing locks here and releasing before we finish the function
|
||||||
|
// because pushing say the secondary context could mean entering this function
|
||||||
|
// again, and we don't want to have a deadlock
|
||||||
|
mainContext.GetMutex().Lock()
|
||||||
|
secondaryContext.GetMutex().Lock()
|
||||||
|
|
||||||
|
mainContext.SetState(
|
||||||
|
patch_exploring.NewState(mainDiff, mainSelectedLineIdx, mainContext.GetState(), self.c.Log),
|
||||||
|
)
|
||||||
|
|
||||||
|
secondaryContext.SetState(
|
||||||
|
patch_exploring.NewState(secondaryDiff, secondarySelectedLineIdx, secondaryContext.GetState(), self.c.Log),
|
||||||
|
)
|
||||||
|
|
||||||
|
mainState := mainContext.GetState()
|
||||||
|
secondaryState := secondaryContext.GetState()
|
||||||
|
|
||||||
|
mainContent := mainContext.GetContentToRender(!secondaryFocused)
|
||||||
|
secondaryContent := secondaryContext.GetContentToRender(secondaryFocused)
|
||||||
|
|
||||||
|
mainContext.GetMutex().Unlock()
|
||||||
|
secondaryContext.GetMutex().Unlock()
|
||||||
|
|
||||||
|
if mainState == nil && secondaryState == nil {
|
||||||
|
return self.handleStagingEscape()
|
||||||
|
}
|
||||||
|
|
||||||
|
if mainState == nil && !secondaryFocused {
|
||||||
|
return self.c.PushContext(secondaryContext, focusOpts)
|
||||||
|
}
|
||||||
|
|
||||||
|
if secondaryState == nil && secondaryFocused {
|
||||||
|
return self.c.PushContext(mainContext, focusOpts)
|
||||||
|
}
|
||||||
|
|
||||||
|
if secondaryFocused {
|
||||||
|
self.c.Contexts().StagingSecondary.FocusSelection()
|
||||||
|
} else {
|
||||||
|
self.c.Contexts().Staging.FocusSelection()
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.c.RenderToMainViews(types.RefreshMainOpts{
|
||||||
|
Pair: self.c.MainViewPairs().Staging,
|
||||||
|
Main: &types.ViewUpdateOpts{
|
||||||
|
Task: types.NewRenderStringWithoutScrollTask(mainContent),
|
||||||
|
Title: self.c.Tr.UnstagedChanges,
|
||||||
|
},
|
||||||
|
Secondary: &types.ViewUpdateOpts{
|
||||||
|
Task: types.NewRenderStringWithoutScrollTask(secondaryContent),
|
||||||
|
Title: self.c.Tr.StagedChanges,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *StagingHelper) handleStagingEscape() error {
|
||||||
|
return self.c.PushContext(self.c.Contexts().Files)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *StagingHelper) secondaryStagingFocused() bool {
|
||||||
|
return self.c.CurrentStaticContext().GetKey() == self.c.Contexts().StagingSecondary.GetKey()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *StagingHelper) mainStagingFocused() bool {
|
||||||
|
return self.c.CurrentStaticContext().GetKey() == self.c.Contexts().Staging.GetKey()
|
||||||
|
}
|
@ -33,28 +33,21 @@ type ISuggestionsHelper interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SuggestionsHelper struct {
|
type SuggestionsHelper struct {
|
||||||
c *types.HelperCommon
|
c *HelperCommon
|
||||||
|
|
||||||
model *types.Model
|
|
||||||
refreshSuggestionsFn func()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ ISuggestionsHelper = &SuggestionsHelper{}
|
var _ ISuggestionsHelper = &SuggestionsHelper{}
|
||||||
|
|
||||||
func NewSuggestionsHelper(
|
func NewSuggestionsHelper(
|
||||||
c *types.HelperCommon,
|
c *HelperCommon,
|
||||||
model *types.Model,
|
|
||||||
refreshSuggestionsFn func(),
|
|
||||||
) *SuggestionsHelper {
|
) *SuggestionsHelper {
|
||||||
return &SuggestionsHelper{
|
return &SuggestionsHelper{
|
||||||
c: c,
|
c: c,
|
||||||
model: model,
|
|
||||||
refreshSuggestionsFn: refreshSuggestionsFn,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *SuggestionsHelper) getRemoteNames() []string {
|
func (self *SuggestionsHelper) getRemoteNames() []string {
|
||||||
return slices.Map(self.model.Remotes, func(remote *models.Remote) string {
|
return slices.Map(self.c.Model().Remotes, func(remote *models.Remote) string {
|
||||||
return remote.Name
|
return remote.Name
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -75,7 +68,7 @@ func (self *SuggestionsHelper) GetRemoteSuggestionsFunc() func(string) []*types.
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *SuggestionsHelper) getBranchNames() []string {
|
func (self *SuggestionsHelper) getBranchNames() []string {
|
||||||
return slices.Map(self.model.Branches, func(branch *models.Branch) string {
|
return slices.Map(self.c.Model().Branches, func(branch *models.Branch) string {
|
||||||
return branch.Name
|
return branch.Name
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -101,8 +94,8 @@ func (self *SuggestionsHelper) GetBranchNameSuggestionsFunc() func(string) []*ty
|
|||||||
}
|
}
|
||||||
|
|
||||||
// here we asynchronously fetch the latest set of paths in the repo and store in
|
// here we asynchronously fetch the latest set of paths in the repo and store in
|
||||||
// self.model.FilesTrie. On the main thread we'll be doing a fuzzy search via
|
// self.c.Model().FilesTrie. On the main thread we'll be doing a fuzzy search via
|
||||||
// self.model.FilesTrie. So if we've looked for a file previously, we'll start with
|
// self.c.Model().FilesTrie. So if we've looked for a file previously, we'll start with
|
||||||
// the old trie and eventually it'll be swapped out for the new one.
|
// the old trie and eventually it'll be swapped out for the new one.
|
||||||
// Notably, unlike other suggestion functions we're not showing all the options
|
// Notably, unlike other suggestion functions we're not showing all the options
|
||||||
// if nothing has been typed because there'll be too much to display efficiently
|
// if nothing has been typed because there'll be too much to display efficiently
|
||||||
@ -125,16 +118,16 @@ func (self *SuggestionsHelper) GetFilePathSuggestionsFunc() func(string) []*type
|
|||||||
})
|
})
|
||||||
|
|
||||||
// cache the trie for future use
|
// cache the trie for future use
|
||||||
self.model.FilesTrie = trie
|
self.c.Model().FilesTrie = trie
|
||||||
|
|
||||||
self.refreshSuggestionsFn()
|
self.c.Contexts().Suggestions.RefreshSuggestions()
|
||||||
|
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
|
||||||
return func(input string) []*types.Suggestion {
|
return func(input string) []*types.Suggestion {
|
||||||
matchingNames := []string{}
|
matchingNames := []string{}
|
||||||
_ = self.model.FilesTrie.VisitFuzzy(patricia.Prefix(input), true, func(prefix patricia.Prefix, item patricia.Item, skipped int) error {
|
_ = self.c.Model().FilesTrie.VisitFuzzy(patricia.Prefix(input), true, func(prefix patricia.Prefix, item patricia.Item, skipped int) error {
|
||||||
matchingNames = append(matchingNames, item.(string))
|
matchingNames = append(matchingNames, item.(string))
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@ -147,7 +140,7 @@ func (self *SuggestionsHelper) GetFilePathSuggestionsFunc() func(string) []*type
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *SuggestionsHelper) getRemoteBranchNames(separator string) []string {
|
func (self *SuggestionsHelper) getRemoteBranchNames(separator string) []string {
|
||||||
return slices.FlatMap(self.model.Remotes, func(remote *models.Remote) []string {
|
return slices.FlatMap(self.c.Model().Remotes, func(remote *models.Remote) []string {
|
||||||
return slices.Map(remote.Branches, func(branch *models.RemoteBranch) string {
|
return slices.Map(remote.Branches, func(branch *models.RemoteBranch) string {
|
||||||
return fmt.Sprintf("%s%s%s", remote.Name, separator, branch.Name)
|
return fmt.Sprintf("%s%s%s", remote.Name, separator, branch.Name)
|
||||||
})
|
})
|
||||||
@ -159,7 +152,7 @@ func (self *SuggestionsHelper) GetRemoteBranchesSuggestionsFunc(separator string
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *SuggestionsHelper) getTagNames() []string {
|
func (self *SuggestionsHelper) getTagNames() []string {
|
||||||
return slices.Map(self.model.Tags, func(tag *models.Tag) string {
|
return slices.Map(self.c.Model().Tags, func(tag *models.Tag) string {
|
||||||
return tag.Name
|
return tag.Name
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -176,7 +169,7 @@ func (self *SuggestionsHelper) GetRefsSuggestionsFunc() func(string) []*types.Su
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *SuggestionsHelper) GetAuthorsSuggestionsFunc() func(string) []*types.Suggestion {
|
func (self *SuggestionsHelper) GetAuthorsSuggestionsFunc() func(string) []*types.Suggestion {
|
||||||
authors := lo.Uniq(slices.Map(self.model.Commits, func(commit *models.Commit) string {
|
authors := lo.Uniq(slices.Map(self.c.Model().Commits, func(commit *models.Commit) string {
|
||||||
return fmt.Sprintf("%s <%s>", commit.AuthorName, commit.AuthorEmail)
|
return fmt.Sprintf("%s <%s>", commit.AuthorName, commit.AuthorEmail)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package helpers
|
package helpers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -10,14 +9,12 @@ import (
|
|||||||
// and the commits context.
|
// and the commits context.
|
||||||
|
|
||||||
type TagsHelper struct {
|
type TagsHelper struct {
|
||||||
c *types.HelperCommon
|
c *HelperCommon
|
||||||
git *commands.GitCommand
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTagsHelper(c *types.HelperCommon, git *commands.GitCommand) *TagsHelper {
|
func NewTagsHelper(c *HelperCommon) *TagsHelper {
|
||||||
return &TagsHelper{
|
return &TagsHelper{
|
||||||
c: c,
|
c: c,
|
||||||
git: git,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,7 +53,7 @@ func (self *TagsHelper) handleCreateAnnotatedTag(ref string, onCreate func()) er
|
|||||||
Title: self.c.Tr.TagMessageTitle,
|
Title: self.c.Tr.TagMessageTitle,
|
||||||
HandleConfirm: func(msg string) error {
|
HandleConfirm: func(msg string) error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.CreateAnnotatedTag)
|
self.c.LogAction(self.c.Tr.Actions.CreateAnnotatedTag)
|
||||||
if err := self.git.Tag.CreateAnnotated(tagName, ref, msg); err != nil {
|
if err := self.c.Git().Tag.CreateAnnotated(tagName, ref, msg); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
return self.afterTagCreate(onCreate)
|
return self.afterTagCreate(onCreate)
|
||||||
@ -71,7 +68,7 @@ func (self *TagsHelper) handleCreateLightweightTag(ref string, onCreate func())
|
|||||||
Title: self.c.Tr.TagNameTitle,
|
Title: self.c.Tr.TagNameTitle,
|
||||||
HandleConfirm: func(tagName string) error {
|
HandleConfirm: func(tagName string) error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.CreateLightweightTag)
|
self.c.LogAction(self.c.Tr.Actions.CreateLightweightTag)
|
||||||
if err := self.git.Tag.CreateLightweight(tagName, ref); err != nil {
|
if err := self.c.Git().Tag.CreateLightweight(tagName, ref); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
return self.afterTagCreate(onCreate)
|
return self.afterTagCreate(onCreate)
|
||||||
|
96
pkg/gui/controllers/helpers/update_helper.go
Normal file
96
pkg/gui/controllers/helpers/update_helper.go
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/updates"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UpdateHelper struct {
|
||||||
|
c *HelperCommon
|
||||||
|
updater *updates.Updater
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUpdateHelper(c *HelperCommon, updater *updates.Updater) *UpdateHelper {
|
||||||
|
return &UpdateHelper{
|
||||||
|
c: c,
|
||||||
|
updater: updater,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *UpdateHelper) CheckForUpdateInBackground() {
|
||||||
|
self.updater.CheckForNewUpdate(func(newVersion string, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
// ignoring the error for now so that I'm not annoying users
|
||||||
|
self.c.Log.Error(err.Error())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if newVersion == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if self.c.UserConfig.Update.Method == "background" {
|
||||||
|
self.startUpdating(newVersion)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return self.showUpdatePrompt(newVersion)
|
||||||
|
}, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *UpdateHelper) CheckForUpdateInForeground() error {
|
||||||
|
return self.c.WithWaitingStatus(self.c.Tr.CheckingForUpdates, func() error {
|
||||||
|
self.updater.CheckForNewUpdate(func(newVersion string, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return self.c.Error(err)
|
||||||
|
}
|
||||||
|
if newVersion == "" {
|
||||||
|
return self.c.ErrorMsg(self.c.Tr.FailedToRetrieveLatestVersionErr)
|
||||||
|
}
|
||||||
|
return self.showUpdatePrompt(newVersion)
|
||||||
|
}, true)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *UpdateHelper) startUpdating(newVersion string) {
|
||||||
|
_ = self.c.WithWaitingStatus(self.c.Tr.UpdateInProgressWaitingStatus, func() error {
|
||||||
|
self.c.State().SetUpdating(true)
|
||||||
|
err := self.updater.Update(newVersion)
|
||||||
|
return self.onUpdateFinish(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *UpdateHelper) onUpdateFinish(err error) error {
|
||||||
|
self.c.State().SetUpdating(false)
|
||||||
|
self.c.OnUIThread(func() error {
|
||||||
|
self.c.SetViewContent(self.c.Views().AppStatus, "")
|
||||||
|
if err != nil {
|
||||||
|
errMessage := utils.ResolvePlaceholderString(
|
||||||
|
self.c.Tr.UpdateFailedErr, map[string]string{
|
||||||
|
"errMessage": err.Error(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return self.c.ErrorMsg(errMessage)
|
||||||
|
}
|
||||||
|
return self.c.Alert(self.c.Tr.UpdateCompletedTitle, self.c.Tr.UpdateCompleted)
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *UpdateHelper) showUpdatePrompt(newVersion string) error {
|
||||||
|
message := utils.ResolvePlaceholderString(
|
||||||
|
self.c.Tr.UpdateAvailable, map[string]string{
|
||||||
|
"newVersion": newVersion,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
return self.c.Confirm(types.ConfirmOpts{
|
||||||
|
Title: self.c.Tr.UpdateAvailableTitle,
|
||||||
|
Prompt: message,
|
||||||
|
HandleConfirm: func() error {
|
||||||
|
self.startUpdating(newVersion)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
@ -9,8 +9,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type UpstreamHelper struct {
|
type UpstreamHelper struct {
|
||||||
c *types.HelperCommon
|
c *HelperCommon
|
||||||
model *types.Model
|
|
||||||
|
|
||||||
getRemoteBranchesSuggestionsFunc func(string) func(string) []*types.Suggestion
|
getRemoteBranchesSuggestionsFunc func(string) func(string) []*types.Suggestion
|
||||||
}
|
}
|
||||||
@ -25,13 +24,11 @@ type IUpstreamHelper interface {
|
|||||||
var _ IUpstreamHelper = &UpstreamHelper{}
|
var _ IUpstreamHelper = &UpstreamHelper{}
|
||||||
|
|
||||||
func NewUpstreamHelper(
|
func NewUpstreamHelper(
|
||||||
c *types.HelperCommon,
|
c *HelperCommon,
|
||||||
model *types.Model,
|
|
||||||
getRemoteBranchesSuggestionsFunc func(string) func(string) []*types.Suggestion,
|
getRemoteBranchesSuggestionsFunc func(string) func(string) []*types.Suggestion,
|
||||||
) *UpstreamHelper {
|
) *UpstreamHelper {
|
||||||
return &UpstreamHelper{
|
return &UpstreamHelper{
|
||||||
c: c,
|
c: c,
|
||||||
model: model,
|
|
||||||
getRemoteBranchesSuggestionsFunc: getRemoteBranchesSuggestionsFunc,
|
getRemoteBranchesSuggestionsFunc: getRemoteBranchesSuggestionsFunc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -70,7 +67,7 @@ func (self *UpstreamHelper) PromptForUpstreamWithoutInitialContent(_ *models.Bra
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *UpstreamHelper) GetSuggestedRemote() string {
|
func (self *UpstreamHelper) GetSuggestedRemote() string {
|
||||||
return getSuggestedRemote(self.model.Remotes)
|
return getSuggestedRemote(self.c.Model().Remotes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSuggestedRemote(remotes []*models.Remote) string {
|
func getSuggestedRemote(remotes []*models.Remote) string {
|
||||||
|
31
pkg/gui/controllers/helpers/view_helper.go
Normal file
31
pkg/gui/controllers/helpers/view_helper.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ViewHelper struct {
|
||||||
|
c *HelperCommon
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewViewHelper(c *HelperCommon, contexts *context.ContextTree) *ViewHelper {
|
||||||
|
return &ViewHelper{
|
||||||
|
c: c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ViewHelper) ContextForView(viewName string) (types.Context, bool) {
|
||||||
|
view, err := self.c.GocuiGui().View(viewName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, context := range self.c.Contexts().Flatten() {
|
||||||
|
if context.GetViewName() == view.Name() {
|
||||||
|
return context, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, false
|
||||||
|
}
|
@ -1,7 +1,8 @@
|
|||||||
package gui
|
package helpers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jesseduffield/lazycore/pkg/boxlayout"
|
"github.com/jesseduffield/lazycore/pkg/boxlayout"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/constants"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
@ -11,12 +12,33 @@ import (
|
|||||||
// In this file we use the boxlayout package, along with knowledge about the app's state,
|
// In this file we use the boxlayout package, along with knowledge about the app's state,
|
||||||
// to arrange the windows (i.e. panels) on the screen.
|
// to arrange the windows (i.e. panels) on the screen.
|
||||||
|
|
||||||
|
type WindowArrangementHelper struct {
|
||||||
|
c *HelperCommon
|
||||||
|
windowHelper *WindowHelper
|
||||||
|
modeHelper *ModeHelper
|
||||||
|
appStatusHelper *AppStatusHelper
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWindowArrangementHelper(
|
||||||
|
c *HelperCommon,
|
||||||
|
windowHelper *WindowHelper,
|
||||||
|
modeHelper *ModeHelper,
|
||||||
|
appStatusHelper *AppStatusHelper,
|
||||||
|
) *WindowArrangementHelper {
|
||||||
|
return &WindowArrangementHelper{
|
||||||
|
c: c,
|
||||||
|
windowHelper: windowHelper,
|
||||||
|
modeHelper: modeHelper,
|
||||||
|
appStatusHelper: appStatusHelper,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const INFO_SECTION_PADDING = " "
|
const INFO_SECTION_PADDING = " "
|
||||||
|
|
||||||
func (gui *Gui) getWindowDimensions(informationStr string, appStatus string) map[string]boxlayout.Dimensions {
|
func (self *WindowArrangementHelper) GetWindowDimensions(informationStr string, appStatus string) map[string]boxlayout.Dimensions {
|
||||||
width, height := gui.g.Size()
|
width, height := self.c.GocuiGui().Size()
|
||||||
|
|
||||||
sideSectionWeight, mainSectionWeight := gui.getMidSectionWeights()
|
sideSectionWeight, mainSectionWeight := self.getMidSectionWeights()
|
||||||
|
|
||||||
sidePanelsDirection := boxlayout.COLUMN
|
sidePanelsDirection := boxlayout.COLUMN
|
||||||
portraitMode := width <= 84 && height > 45
|
portraitMode := width <= 84 && height > 45
|
||||||
@ -25,13 +47,18 @@ func (gui *Gui) getWindowDimensions(informationStr string, appStatus string) map
|
|||||||
}
|
}
|
||||||
|
|
||||||
mainPanelsDirection := boxlayout.ROW
|
mainPanelsDirection := boxlayout.ROW
|
||||||
if gui.splitMainPanelSideBySide() {
|
if self.splitMainPanelSideBySide() {
|
||||||
mainPanelsDirection = boxlayout.COLUMN
|
mainPanelsDirection = boxlayout.COLUMN
|
||||||
}
|
}
|
||||||
|
|
||||||
extrasWindowSize := gui.getExtrasWindowSize(height)
|
extrasWindowSize := self.getExtrasWindowSize(height)
|
||||||
|
|
||||||
showInfoSection := gui.c.UserConfig.Gui.ShowBottomLine || gui.State.Searching.isSearching || gui.isAnyModeActive() || gui.statusManager.showStatus()
|
self.c.Modes().Filtering.Active()
|
||||||
|
|
||||||
|
showInfoSection := self.c.UserConfig.Gui.ShowBottomLine ||
|
||||||
|
self.c.State().GetRepoState().IsSearching() ||
|
||||||
|
self.modeHelper.IsAnyModeActive() ||
|
||||||
|
self.appStatusHelper.HasStatus()
|
||||||
infoSectionSize := 0
|
infoSectionSize := 0
|
||||||
if showInfoSection {
|
if showInfoSection {
|
||||||
infoSectionSize = 1
|
infoSectionSize = 1
|
||||||
@ -47,7 +74,7 @@ func (gui *Gui) getWindowDimensions(informationStr string, appStatus string) map
|
|||||||
{
|
{
|
||||||
Direction: boxlayout.ROW,
|
Direction: boxlayout.ROW,
|
||||||
Weight: sideSectionWeight,
|
Weight: sideSectionWeight,
|
||||||
ConditionalChildren: gui.sidePanelChildren,
|
ConditionalChildren: self.sidePanelChildren,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Direction: boxlayout.ROW,
|
Direction: boxlayout.ROW,
|
||||||
@ -55,7 +82,7 @@ func (gui *Gui) getWindowDimensions(informationStr string, appStatus string) map
|
|||||||
Children: []*boxlayout.Box{
|
Children: []*boxlayout.Box{
|
||||||
{
|
{
|
||||||
Direction: mainPanelsDirection,
|
Direction: mainPanelsDirection,
|
||||||
Children: gui.mainSectionChildren(),
|
Children: self.mainSectionChildren(),
|
||||||
Weight: 1,
|
Weight: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -69,7 +96,7 @@ func (gui *Gui) getWindowDimensions(informationStr string, appStatus string) map
|
|||||||
{
|
{
|
||||||
Direction: boxlayout.COLUMN,
|
Direction: boxlayout.COLUMN,
|
||||||
Size: infoSectionSize,
|
Size: infoSectionSize,
|
||||||
Children: gui.infoSectionChildren(informationStr, appStatus),
|
Children: self.infoSectionChildren(informationStr, appStatus),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -91,12 +118,12 @@ func MergeMaps[K comparable, V any](maps ...map[K]V) map[K]V {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) mainSectionChildren() []*boxlayout.Box {
|
func (self *WindowArrangementHelper) mainSectionChildren() []*boxlayout.Box {
|
||||||
currentWindow := gui.currentWindow()
|
currentWindow := self.windowHelper.CurrentWindow()
|
||||||
|
|
||||||
// if we're not in split mode we can just show the one main panel. Likewise if
|
// if we're not in split mode we can just show the one main panel. Likewise if
|
||||||
// the main panel is focused and we're in full-screen mode
|
// the main panel is focused and we're in full-screen mode
|
||||||
if !gui.isMainPanelSplit() || (gui.State.ScreenMode == SCREEN_FULL && currentWindow == "main") {
|
if !self.c.State().GetRepoState().GetSplitMainPanel() || (self.c.State().GetRepoState().GetScreenMode() == types.SCREEN_FULL && currentWindow == "main") {
|
||||||
return []*boxlayout.Box{
|
return []*boxlayout.Box{
|
||||||
{
|
{
|
||||||
Window: "main",
|
Window: "main",
|
||||||
@ -117,27 +144,29 @@ func (gui *Gui) mainSectionChildren() []*boxlayout.Box {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) getMidSectionWeights() (int, int) {
|
func (self *WindowArrangementHelper) getMidSectionWeights() (int, int) {
|
||||||
currentWindow := gui.currentWindow()
|
currentWindow := self.windowHelper.CurrentWindow()
|
||||||
|
|
||||||
// we originally specified this as a ratio i.e. .20 would correspond to a weight of 1 against 4
|
// we originally specified this as a ratio i.e. .20 would correspond to a weight of 1 against 4
|
||||||
sidePanelWidthRatio := gui.c.UserConfig.Gui.SidePanelWidth
|
sidePanelWidthRatio := self.c.UserConfig.Gui.SidePanelWidth
|
||||||
// we could make this better by creating ratios like 2:3 rather than always 1:something
|
// we could make this better by creating ratios like 2:3 rather than always 1:something
|
||||||
mainSectionWeight := int(1/sidePanelWidthRatio) - 1
|
mainSectionWeight := int(1/sidePanelWidthRatio) - 1
|
||||||
sideSectionWeight := 1
|
sideSectionWeight := 1
|
||||||
|
|
||||||
if gui.splitMainPanelSideBySide() {
|
if self.splitMainPanelSideBySide() {
|
||||||
mainSectionWeight = 5 // need to shrink side panel to make way for main panels if side-by-side
|
mainSectionWeight = 5 // need to shrink side panel to make way for main panels if side-by-side
|
||||||
}
|
}
|
||||||
|
|
||||||
|
screenMode := self.c.State().GetRepoState().GetScreenMode()
|
||||||
|
|
||||||
if currentWindow == "main" {
|
if currentWindow == "main" {
|
||||||
if gui.State.ScreenMode == SCREEN_HALF || gui.State.ScreenMode == SCREEN_FULL {
|
if screenMode == types.SCREEN_HALF || screenMode == types.SCREEN_FULL {
|
||||||
sideSectionWeight = 0
|
sideSectionWeight = 0
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if gui.State.ScreenMode == SCREEN_HALF {
|
if screenMode == types.SCREEN_HALF {
|
||||||
mainSectionWeight = 1
|
mainSectionWeight = 1
|
||||||
} else if gui.State.ScreenMode == SCREEN_FULL {
|
} else if screenMode == types.SCREEN_FULL {
|
||||||
mainSectionWeight = 0
|
mainSectionWeight = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -145,12 +174,12 @@ func (gui *Gui) getMidSectionWeights() (int, int) {
|
|||||||
return sideSectionWeight, mainSectionWeight
|
return sideSectionWeight, mainSectionWeight
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) infoSectionChildren(informationStr string, appStatus string) []*boxlayout.Box {
|
func (self *WindowArrangementHelper) infoSectionChildren(informationStr string, appStatus string) []*boxlayout.Box {
|
||||||
if gui.State.Searching.isSearching {
|
if self.c.State().GetRepoState().IsSearching() {
|
||||||
return []*boxlayout.Box{
|
return []*boxlayout.Box{
|
||||||
{
|
{
|
||||||
Window: "searchPrefix",
|
Window: "searchPrefix",
|
||||||
Size: runewidth.StringWidth(SEARCH_PREFIX),
|
Size: runewidth.StringWidth(constants.SEARCH_PREFIX),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Window: "search",
|
Window: "search",
|
||||||
@ -162,7 +191,7 @@ func (gui *Gui) infoSectionChildren(informationStr string, appStatus string) []*
|
|||||||
appStatusBox := &boxlayout.Box{Window: "appStatus"}
|
appStatusBox := &boxlayout.Box{Window: "appStatus"}
|
||||||
optionsBox := &boxlayout.Box{Window: "options"}
|
optionsBox := &boxlayout.Box{Window: "options"}
|
||||||
|
|
||||||
if !gui.c.UserConfig.Gui.ShowBottomLine {
|
if !self.c.UserConfig.Gui.ShowBottomLine {
|
||||||
optionsBox.Weight = 0
|
optionsBox.Weight = 0
|
||||||
appStatusBox.Weight = 1
|
appStatusBox.Weight = 1
|
||||||
} else {
|
} else {
|
||||||
@ -172,7 +201,7 @@ func (gui *Gui) infoSectionChildren(informationStr string, appStatus string) []*
|
|||||||
|
|
||||||
result := []*boxlayout.Box{appStatusBox, optionsBox}
|
result := []*boxlayout.Box{appStatusBox, optionsBox}
|
||||||
|
|
||||||
if gui.c.UserConfig.Gui.ShowBottomLine || gui.isAnyModeActive() {
|
if self.c.UserConfig.Gui.ShowBottomLine || self.modeHelper.IsAnyModeActive() {
|
||||||
result = append(result, &boxlayout.Box{
|
result = append(result, &boxlayout.Box{
|
||||||
Window: "information",
|
Window: "information",
|
||||||
// unlike appStatus, informationStr has various colors so we need to decolorise before taking the length
|
// unlike appStatus, informationStr has various colors so we need to decolorise before taking the length
|
||||||
@ -183,13 +212,13 @@ func (gui *Gui) infoSectionChildren(informationStr string, appStatus string) []*
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) splitMainPanelSideBySide() bool {
|
func (self *WindowArrangementHelper) splitMainPanelSideBySide() bool {
|
||||||
if !gui.isMainPanelSplit() {
|
if !self.c.State().GetRepoState().GetSplitMainPanel() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
mainPanelSplitMode := gui.c.UserConfig.Gui.MainPanelSplitMode
|
mainPanelSplitMode := self.c.UserConfig.Gui.MainPanelSplitMode
|
||||||
width, height := gui.g.Size()
|
width, height := self.c.GocuiGui().Size()
|
||||||
|
|
||||||
switch mainPanelSplitMode {
|
switch mainPanelSplitMode {
|
||||||
case "vertical":
|
case "vertical":
|
||||||
@ -205,18 +234,18 @@ func (gui *Gui) splitMainPanelSideBySide() bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) getExtrasWindowSize(screenHeight int) int {
|
func (self *WindowArrangementHelper) getExtrasWindowSize(screenHeight int) int {
|
||||||
if !gui.ShowExtrasWindow {
|
if !self.c.State().GetShowExtrasWindow() {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
var baseSize int
|
var baseSize int
|
||||||
if gui.currentStaticContext().GetKey() == context.COMMAND_LOG_CONTEXT_KEY {
|
if self.c.CurrentStaticContext().GetKey() == context.COMMAND_LOG_CONTEXT_KEY {
|
||||||
baseSize = 1000 // my way of saying 'fill the available space'
|
baseSize = 1000 // my way of saying 'fill the available space'
|
||||||
} else if screenHeight < 40 {
|
} else if screenHeight < 40 {
|
||||||
baseSize = 1
|
baseSize = 1
|
||||||
} else {
|
} else {
|
||||||
baseSize = gui.c.UserConfig.Gui.CommandLogSize
|
baseSize = self.c.UserConfig.Gui.CommandLogSize
|
||||||
}
|
}
|
||||||
|
|
||||||
frameSize := 2
|
frameSize := 2
|
||||||
@ -227,17 +256,15 @@ func (gui *Gui) getExtrasWindowSize(screenHeight int) int {
|
|||||||
// too much space, but if you access it it should take up some space. This is
|
// too much space, but if you access it it should take up some space. This is
|
||||||
// the default behaviour when accordion mode is NOT in effect. If it is in effect
|
// the default behaviour when accordion mode is NOT in effect. If it is in effect
|
||||||
// then when it's accessed it will have weight 2, not 1.
|
// then when it's accessed it will have weight 2, not 1.
|
||||||
func (gui *Gui) getDefaultStashWindowBox() *boxlayout.Box {
|
func (self *WindowArrangementHelper) getDefaultStashWindowBox() *boxlayout.Box {
|
||||||
gui.State.ContextManager.RLock()
|
|
||||||
defer gui.State.ContextManager.RUnlock()
|
|
||||||
|
|
||||||
box := &boxlayout.Box{Window: "stash"}
|
|
||||||
stashWindowAccessed := false
|
stashWindowAccessed := false
|
||||||
for _, context := range gui.State.ContextManager.ContextStack {
|
self.c.Context().ForEach(func(context types.Context) {
|
||||||
if context.GetWindowName() == "stash" {
|
if context.GetWindowName() == "stash" {
|
||||||
stashWindowAccessed = true
|
stashWindowAccessed = true
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
|
box := &boxlayout.Box{Window: "stash"}
|
||||||
// if the stash window is anywhere in our stack we should enlargen it
|
// if the stash window is anywhere in our stack we should enlargen it
|
||||||
if stashWindowAccessed {
|
if stashWindowAccessed {
|
||||||
box.Weight = 1
|
box.Weight = 1
|
||||||
@ -248,10 +275,11 @@ func (gui *Gui) getDefaultStashWindowBox() *boxlayout.Box {
|
|||||||
return box
|
return box
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) sidePanelChildren(width int, height int) []*boxlayout.Box {
|
func (self *WindowArrangementHelper) sidePanelChildren(width int, height int) []*boxlayout.Box {
|
||||||
currentWindow := gui.currentSideWindowName()
|
currentWindow := self.c.CurrentSideContext().GetWindowName()
|
||||||
|
|
||||||
if gui.State.ScreenMode == SCREEN_FULL || gui.State.ScreenMode == SCREEN_HALF {
|
screenMode := self.c.State().GetRepoState().GetScreenMode()
|
||||||
|
if screenMode == types.SCREEN_FULL || screenMode == types.SCREEN_HALF {
|
||||||
fullHeightBox := func(window string) *boxlayout.Box {
|
fullHeightBox := func(window string) *boxlayout.Box {
|
||||||
if window == currentWindow {
|
if window == currentWindow {
|
||||||
return &boxlayout.Box{
|
return &boxlayout.Box{
|
||||||
@ -274,7 +302,7 @@ func (gui *Gui) sidePanelChildren(width int, height int) []*boxlayout.Box {
|
|||||||
fullHeightBox("stash"),
|
fullHeightBox("stash"),
|
||||||
}
|
}
|
||||||
} else if height >= 28 {
|
} else if height >= 28 {
|
||||||
accordionMode := gui.c.UserConfig.Gui.ExpandFocusedSidePanel
|
accordionMode := self.c.UserConfig.Gui.ExpandFocusedSidePanel
|
||||||
accordionBox := func(defaultBox *boxlayout.Box) *boxlayout.Box {
|
accordionBox := func(defaultBox *boxlayout.Box) *boxlayout.Box {
|
||||||
if accordionMode && defaultBox.Window == currentWindow {
|
if accordionMode && defaultBox.Window == currentWindow {
|
||||||
return &boxlayout.Box{
|
return &boxlayout.Box{
|
||||||
@ -294,7 +322,7 @@ func (gui *Gui) sidePanelChildren(width int, height int) []*boxlayout.Box {
|
|||||||
accordionBox(&boxlayout.Box{Window: "files", Weight: 1}),
|
accordionBox(&boxlayout.Box{Window: "files", Weight: 1}),
|
||||||
accordionBox(&boxlayout.Box{Window: "branches", Weight: 1}),
|
accordionBox(&boxlayout.Box{Window: "branches", Weight: 1}),
|
||||||
accordionBox(&boxlayout.Box{Window: "commits", Weight: 1}),
|
accordionBox(&boxlayout.Box{Window: "commits", Weight: 1}),
|
||||||
accordionBox(gui.getDefaultStashWindowBox()),
|
accordionBox(self.getDefaultStashWindowBox()),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
squashedHeight := 1
|
squashedHeight := 1
|
||||||
@ -325,24 +353,3 @@ func (gui *Gui) sidePanelChildren(width int, height int) []*boxlayout.Box {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) getCyclableWindows() []string {
|
|
||||||
return []string{"status", "files", "branches", "commits", "stash"}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) currentSideWindowName() string {
|
|
||||||
// there is always one and only one cyclable context in the context stack. We'll look from top to bottom
|
|
||||||
gui.State.ContextManager.RLock()
|
|
||||||
defer gui.State.ContextManager.RUnlock()
|
|
||||||
|
|
||||||
for idx := range gui.State.ContextManager.ContextStack {
|
|
||||||
reversedIdx := len(gui.State.ContextManager.ContextStack) - 1 - idx
|
|
||||||
context := gui.State.ContextManager.ContextStack[reversedIdx]
|
|
||||||
|
|
||||||
if context.GetKind() == types.SIDE_CONTEXT {
|
|
||||||
return context.GetWindowName()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "files" // default
|
|
||||||
}
|
|
139
pkg/gui/controllers/helpers/window_helper.go
Normal file
139
pkg/gui/controllers/helpers/window_helper.go
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/gocui"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
|
"github.com/samber/lo"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WindowHelper struct {
|
||||||
|
c *HelperCommon
|
||||||
|
viewHelper *ViewHelper
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWindowHelper(c *HelperCommon, viewHelper *ViewHelper) *WindowHelper {
|
||||||
|
return &WindowHelper{
|
||||||
|
c: c,
|
||||||
|
viewHelper: viewHelper,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A window refers to a place on the screen which can hold one or more views.
|
||||||
|
// A view is a box that renders content, and within a window only one view will
|
||||||
|
// appear at a time. When a view appears within a window, it occupies the whole
|
||||||
|
// space. Right now most windows are 1:1 with views, except for commitFiles which
|
||||||
|
// is a view that moves between windows
|
||||||
|
|
||||||
|
func (self *WindowHelper) GetViewNameForWindow(window string) string {
|
||||||
|
viewName, ok := self.windowViewNameMap().Get(window)
|
||||||
|
if !ok {
|
||||||
|
panic(fmt.Sprintf("Viewname not found for window: %s", window))
|
||||||
|
}
|
||||||
|
|
||||||
|
return viewName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *WindowHelper) GetContextForWindow(window string) types.Context {
|
||||||
|
viewName := self.GetViewNameForWindow(window)
|
||||||
|
|
||||||
|
context, ok := self.viewHelper.ContextForView(viewName)
|
||||||
|
if !ok {
|
||||||
|
panic("TODO: fix this")
|
||||||
|
}
|
||||||
|
|
||||||
|
return context
|
||||||
|
}
|
||||||
|
|
||||||
|
// for now all we actually care about is the context's view so we're storing that
|
||||||
|
func (self *WindowHelper) SetWindowContext(c types.Context) {
|
||||||
|
if c.IsTransient() {
|
||||||
|
self.resetWindowContext(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.windowViewNameMap().Set(c.GetWindowName(), c.GetViewName())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *WindowHelper) windowViewNameMap() *utils.ThreadSafeMap[string, string] {
|
||||||
|
return self.c.State().GetRepoState().GetWindowViewNameMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *WindowHelper) CurrentWindow() string {
|
||||||
|
return self.c.CurrentContext().GetWindowName()
|
||||||
|
}
|
||||||
|
|
||||||
|
// assumes the context's windowName has been set to the new window if necessary
|
||||||
|
func (self *WindowHelper) resetWindowContext(c types.Context) {
|
||||||
|
for _, windowName := range self.windowViewNameMap().Keys() {
|
||||||
|
viewName, ok := self.windowViewNameMap().Get(windowName)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if viewName == c.GetViewName() && windowName != c.GetWindowName() {
|
||||||
|
for _, context := range self.c.Contexts().Flatten() {
|
||||||
|
if context.GetKey() != c.GetKey() && context.GetWindowName() == windowName {
|
||||||
|
self.windowViewNameMap().Set(windowName, context.GetViewName())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// moves given context's view to the top of the window
|
||||||
|
func (self *WindowHelper) MoveToTopOfWindow(context types.Context) {
|
||||||
|
view := context.GetView()
|
||||||
|
if view == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
window := context.GetWindowName()
|
||||||
|
|
||||||
|
topView := self.TopViewInWindow(window)
|
||||||
|
|
||||||
|
if view.Name() != topView.Name() {
|
||||||
|
if err := self.c.GocuiGui().SetViewOnTopOf(view.Name(), topView.Name()); err != nil {
|
||||||
|
self.c.Log.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *WindowHelper) TopViewInWindow(windowName string) *gocui.View {
|
||||||
|
// now I need to find all views in that same window, via contexts. And I guess then I need to find the index of the highest view in that list.
|
||||||
|
viewNamesInWindow := self.viewNamesInWindow(windowName)
|
||||||
|
|
||||||
|
// The views list is ordered highest-last, so we're grabbing the last view of the window
|
||||||
|
var topView *gocui.View
|
||||||
|
for _, currentView := range self.c.GocuiGui().Views() {
|
||||||
|
if lo.Contains(viewNamesInWindow, currentView.Name()) {
|
||||||
|
topView = currentView
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return topView
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *WindowHelper) viewNamesInWindow(windowName string) []string {
|
||||||
|
result := []string{}
|
||||||
|
for _, context := range self.c.Contexts().Flatten() {
|
||||||
|
if context.GetWindowName() == windowName {
|
||||||
|
result = append(result, context.GetViewName())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *WindowHelper) WindowForView(viewName string) string {
|
||||||
|
context, ok := self.viewHelper.ContextForView(viewName)
|
||||||
|
if !ok {
|
||||||
|
panic("todo: deal with this")
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.GetWindowName()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *WindowHelper) SideWindows() []string {
|
||||||
|
return []string{"status", "files", "branches", "commits", "stash"}
|
||||||
|
}
|
@ -4,7 +4,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
"github.com/jesseduffield/lazygit/pkg/config"
|
"github.com/jesseduffield/lazygit/pkg/config"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||||
@ -20,40 +19,28 @@ type IWorkingTreeHelper interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type WorkingTreeHelper struct {
|
type WorkingTreeHelper struct {
|
||||||
c *types.HelperCommon
|
c *HelperCommon
|
||||||
git *commands.GitCommand
|
refHelper *RefsHelper
|
||||||
contexts *context.ContextTree
|
commitsHelper *CommitsHelper
|
||||||
refHelper *RefsHelper
|
gpgHelper *GpgHelper
|
||||||
model *types.Model
|
|
||||||
setCommitMessage func(message string)
|
|
||||||
commitsHelper *CommitsHelper
|
|
||||||
gpgHelper *GpgHelper
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWorkingTreeHelper(
|
func NewWorkingTreeHelper(
|
||||||
c *types.HelperCommon,
|
c *HelperCommon,
|
||||||
git *commands.GitCommand,
|
|
||||||
contexts *context.ContextTree,
|
|
||||||
refHelper *RefsHelper,
|
refHelper *RefsHelper,
|
||||||
model *types.Model,
|
|
||||||
setCommitMessage func(message string),
|
|
||||||
commitsHelper *CommitsHelper,
|
commitsHelper *CommitsHelper,
|
||||||
gpgHelper *GpgHelper,
|
gpgHelper *GpgHelper,
|
||||||
) *WorkingTreeHelper {
|
) *WorkingTreeHelper {
|
||||||
return &WorkingTreeHelper{
|
return &WorkingTreeHelper{
|
||||||
c: c,
|
c: c,
|
||||||
git: git,
|
refHelper: refHelper,
|
||||||
contexts: contexts,
|
commitsHelper: commitsHelper,
|
||||||
refHelper: refHelper,
|
gpgHelper: gpgHelper,
|
||||||
model: model,
|
|
||||||
setCommitMessage: setCommitMessage,
|
|
||||||
commitsHelper: commitsHelper,
|
|
||||||
gpgHelper: gpgHelper,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *WorkingTreeHelper) AnyStagedFiles() bool {
|
func (self *WorkingTreeHelper) AnyStagedFiles() bool {
|
||||||
for _, file := range self.model.Files {
|
for _, file := range self.c.Model().Files {
|
||||||
if file.HasStagedChanges {
|
if file.HasStagedChanges {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -62,7 +49,7 @@ func (self *WorkingTreeHelper) AnyStagedFiles() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *WorkingTreeHelper) AnyTrackedFiles() bool {
|
func (self *WorkingTreeHelper) AnyTrackedFiles() bool {
|
||||||
for _, file := range self.model.Files {
|
for _, file := range self.c.Model().Files {
|
||||||
if file.Tracked {
|
if file.Tracked {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -75,7 +62,7 @@ func (self *WorkingTreeHelper) IsWorkingTreeDirty() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *WorkingTreeHelper) FileForSubmodule(submodule *models.SubmoduleConfig) *models.File {
|
func (self *WorkingTreeHelper) FileForSubmodule(submodule *models.SubmoduleConfig) *models.File {
|
||||||
for _, file := range self.model.Files {
|
for _, file := range self.c.Model().Files {
|
||||||
if file.IsSubmodule([]*models.SubmoduleConfig{submodule}) {
|
if file.IsSubmodule([]*models.SubmoduleConfig{submodule}) {
|
||||||
return file
|
return file
|
||||||
}
|
}
|
||||||
@ -91,7 +78,7 @@ func (self *WorkingTreeHelper) OpenMergeTool() error {
|
|||||||
HandleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.OpenMergeTool)
|
self.c.LogAction(self.c.Tr.Actions.OpenMergeTool)
|
||||||
return self.c.RunSubprocessAndRefresh(
|
return self.c.RunSubprocessAndRefresh(
|
||||||
self.git.WorkingTree.OpenMergeToolCmdObj(),
|
self.c.Git().WorkingTree.OpenMergeToolCmdObj(),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -102,7 +89,7 @@ func (self *WorkingTreeHelper) HandleCommitPressWithMessage(initialMessage strin
|
|||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(self.model.Files) == 0 {
|
if len(self.c.Model().Files) == 0 {
|
||||||
return self.c.ErrorMsg(self.c.Tr.NoFilesStagedTitle)
|
return self.c.ErrorMsg(self.c.Tr.NoFilesStagedTitle)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,7 +109,7 @@ func (self *WorkingTreeHelper) HandleCommitPressWithMessage(initialMessage strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *WorkingTreeHelper) handleCommit(message string) error {
|
func (self *WorkingTreeHelper) handleCommit(message string) error {
|
||||||
cmdObj := self.git.Commit.CommitCmdObj(message)
|
cmdObj := self.c.Git().Commit.CommitCmdObj(message)
|
||||||
self.c.LogAction(self.c.Tr.Actions.Commit)
|
self.c.LogAction(self.c.Tr.Actions.Commit)
|
||||||
_ = self.commitsHelper.PopCommitMessageContexts()
|
_ = self.commitsHelper.PopCommitMessageContexts()
|
||||||
return self.gpgHelper.WithGpgHandling(cmdObj, self.c.Tr.CommittingStatus, func() error {
|
return self.gpgHelper.WithGpgHandling(cmdObj, self.c.Tr.CommittingStatus, func() error {
|
||||||
@ -134,7 +121,7 @@ func (self *WorkingTreeHelper) handleCommit(message string) error {
|
|||||||
// HandleCommitEditorPress - handle when the user wants to commit changes via
|
// HandleCommitEditorPress - handle when the user wants to commit changes via
|
||||||
// their editor rather than via the popup panel
|
// their editor rather than via the popup panel
|
||||||
func (self *WorkingTreeHelper) HandleCommitEditorPress() error {
|
func (self *WorkingTreeHelper) HandleCommitEditorPress() error {
|
||||||
if len(self.model.Files) == 0 {
|
if len(self.c.Model().Files) == 0 {
|
||||||
return self.c.ErrorMsg(self.c.Tr.NoFilesStagedTitle)
|
return self.c.ErrorMsg(self.c.Tr.NoFilesStagedTitle)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,7 +131,7 @@ func (self *WorkingTreeHelper) HandleCommitEditorPress() error {
|
|||||||
|
|
||||||
self.c.LogAction(self.c.Tr.Actions.Commit)
|
self.c.LogAction(self.c.Tr.Actions.Commit)
|
||||||
return self.c.RunSubprocessAndRefresh(
|
return self.c.RunSubprocessAndRefresh(
|
||||||
self.git.Commit.CommitEditorCmdObj(),
|
self.c.Git().Commit.CommitEditorCmdObj(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,7 +145,7 @@ func (self *WorkingTreeHelper) HandleWIPCommitPress() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *WorkingTreeHelper) HandleCommitPress() error {
|
func (self *WorkingTreeHelper) HandleCommitPress() error {
|
||||||
message := self.contexts.CommitMessage.GetPreservedMessage()
|
message := self.c.Contexts().CommitMessage.GetPreservedMessage()
|
||||||
|
|
||||||
if message != "" {
|
if message != "" {
|
||||||
commitPrefixConfig := self.commitPrefixConfigForRepo()
|
commitPrefixConfig := self.commitPrefixConfigForRepo()
|
||||||
@ -183,7 +170,7 @@ func (self *WorkingTreeHelper) PromptToStageAllAndRetry(retry func() error) erro
|
|||||||
Prompt: self.c.Tr.NoFilesStagedPrompt,
|
Prompt: self.c.Tr.NoFilesStagedPrompt,
|
||||||
HandleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.StageAllFiles)
|
self.c.LogAction(self.c.Tr.Actions.StageAllFiles)
|
||||||
if err := self.git.WorkingTree.StageAll(); err != nil {
|
if err := self.c.Git().WorkingTree.StageAll(); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
if err := self.syncRefresh(); err != nil {
|
if err := self.syncRefresh(); err != nil {
|
||||||
@ -204,7 +191,7 @@ func (self *WorkingTreeHelper) prepareFilesForCommit() error {
|
|||||||
noStagedFiles := !self.AnyStagedFiles()
|
noStagedFiles := !self.AnyStagedFiles()
|
||||||
if noStagedFiles && self.c.UserConfig.Gui.SkipNoStagedFilesWarning {
|
if noStagedFiles && self.c.UserConfig.Gui.SkipNoStagedFilesWarning {
|
||||||
self.c.LogAction(self.c.Tr.Actions.StageAllFiles)
|
self.c.LogAction(self.c.Tr.Actions.StageAllFiles)
|
||||||
err := self.git.WorkingTree.StageAll()
|
err := self.c.Git().WorkingTree.StageAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
53
pkg/gui/controllers/jump_to_side_window_controller.go
Normal file
53
pkg/gui/controllers/jump_to_side_window_controller.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/gocui"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
|
"github.com/samber/lo"
|
||||||
|
)
|
||||||
|
|
||||||
|
type JumpToSideWindowController struct {
|
||||||
|
baseController
|
||||||
|
c *ControllerCommon
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewJumpToSideWindowController(
|
||||||
|
common *ControllerCommon,
|
||||||
|
) *JumpToSideWindowController {
|
||||||
|
return &JumpToSideWindowController{
|
||||||
|
baseController: baseController{},
|
||||||
|
c: common,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *JumpToSideWindowController) Context() types.Context {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *JumpToSideWindowController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
|
||||||
|
windows := self.c.Helpers().Window.SideWindows()
|
||||||
|
|
||||||
|
if len(opts.Config.Universal.JumpToBlock) != len(windows) {
|
||||||
|
log.Fatal("Jump to block keybindings cannot be set. Exactly 5 keybindings must be supplied.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return lo.Map(windows, func(window string, index int) *types.Binding {
|
||||||
|
return &types.Binding{
|
||||||
|
ViewName: "",
|
||||||
|
// by default the keys are 1, 2, 3, etc
|
||||||
|
Key: opts.GetKey(opts.Config.Universal.JumpToBlock[index]),
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: self.goToSideWindow(window),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *JumpToSideWindowController) goToSideWindow(window string) func() error {
|
||||||
|
return func() error {
|
||||||
|
context := self.c.Helpers().Window.GetContextForWindow(window)
|
||||||
|
|
||||||
|
return self.c.PushContext(context)
|
||||||
|
}
|
||||||
|
}
|
@ -6,10 +6,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ListControllerFactory struct {
|
type ListControllerFactory struct {
|
||||||
c *types.HelperCommon
|
c *ControllerCommon
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewListControllerFactory(c *types.HelperCommon) *ListControllerFactory {
|
func NewListControllerFactory(c *ControllerCommon) *ListControllerFactory {
|
||||||
return &ListControllerFactory{
|
return &ListControllerFactory{
|
||||||
c: c,
|
c: c,
|
||||||
}
|
}
|
||||||
@ -25,7 +25,7 @@ func (self *ListControllerFactory) Create(context types.IListContext) *ListContr
|
|||||||
|
|
||||||
type ListController struct {
|
type ListController struct {
|
||||||
baseController
|
baseController
|
||||||
c *types.HelperCommon
|
c *ControllerCommon
|
||||||
|
|
||||||
context types.IListContext
|
context types.IListContext
|
||||||
}
|
}
|
||||||
|
@ -13,26 +13,30 @@ import (
|
|||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// after selecting the 200th commit, we'll load in all the rest
|
||||||
|
const COMMIT_THRESHOLD = 200
|
||||||
|
|
||||||
type (
|
type (
|
||||||
PullFilesFn func() error
|
PullFilesFn func() error
|
||||||
)
|
)
|
||||||
|
|
||||||
type LocalCommitsController struct {
|
type LocalCommitsController struct {
|
||||||
baseController
|
baseController
|
||||||
*controllerCommon
|
c *ControllerCommon
|
||||||
|
|
||||||
pullFiles PullFilesFn
|
pullFiles PullFilesFn
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ types.IController = &LocalCommitsController{}
|
var _ types.IController = &LocalCommitsController{}
|
||||||
|
|
||||||
func NewLocalCommitsController(
|
func NewLocalCommitsController(
|
||||||
common *controllerCommon,
|
common *ControllerCommon,
|
||||||
pullFiles PullFilesFn,
|
pullFiles PullFilesFn,
|
||||||
) *LocalCommitsController {
|
) *LocalCommitsController {
|
||||||
return &LocalCommitsController{
|
return &LocalCommitsController{
|
||||||
baseController: baseController{},
|
baseController: baseController{},
|
||||||
controllerCommon: common,
|
c: common,
|
||||||
pullFiles: pullFiles,
|
pullFiles: pullFiles,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,8 +154,52 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [
|
|||||||
return bindings
|
return bindings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *LocalCommitsController) GetOnRenderToMain() func() error {
|
||||||
|
return func() error {
|
||||||
|
return self.c.Helpers().Diff.WithDiffModeCheck(func() error {
|
||||||
|
var task types.UpdateTask
|
||||||
|
commit := self.context().GetSelected()
|
||||||
|
if commit == nil {
|
||||||
|
task = types.NewRenderStringTask(self.c.Tr.NoCommitsThisBranch)
|
||||||
|
} else if commit.Action == todo.UpdateRef {
|
||||||
|
task = types.NewRenderStringTask(
|
||||||
|
utils.ResolvePlaceholderString(
|
||||||
|
self.c.Tr.UpdateRefHere,
|
||||||
|
map[string]string{
|
||||||
|
"ref": commit.Name,
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
cmdObj := self.c.Git().Commit.ShowCmdObj(commit.Sha, self.c.Modes().Filtering.GetPath(), self.c.State().GetIgnoreWhitespaceInDiffView())
|
||||||
|
task = types.NewRunPtyTask(cmdObj.GetCmd())
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.c.RenderToMainViews(types.RefreshMainOpts{
|
||||||
|
Pair: self.c.MainViewPairs().Normal,
|
||||||
|
Main: &types.ViewUpdateOpts{
|
||||||
|
Title: "Patch",
|
||||||
|
Task: task,
|
||||||
|
},
|
||||||
|
Secondary: secondaryPatchPanelUpdateOpts(self.c),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func secondaryPatchPanelUpdateOpts(c *ControllerCommon) *types.ViewUpdateOpts {
|
||||||
|
if c.Git().Patch.PatchBuilder.Active() {
|
||||||
|
patch := c.Git().Patch.PatchBuilder.RenderAggregatedPatch(false)
|
||||||
|
|
||||||
|
return &types.ViewUpdateOpts{
|
||||||
|
Task: types.NewRenderStringWithoutScrollTask(patch),
|
||||||
|
Title: c.Tr.CustomPatch,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (self *LocalCommitsController) squashDown(commit *models.Commit) error {
|
func (self *LocalCommitsController) squashDown(commit *models.Commit) error {
|
||||||
if self.context().GetSelectedLineIdx() >= len(self.model.Commits)-1 {
|
if self.context().GetSelectedLineIdx() >= len(self.c.Model().Commits)-1 {
|
||||||
return self.c.ErrorMsg(self.c.Tr.CannotSquashOrFixupFirstCommit)
|
return self.c.ErrorMsg(self.c.Tr.CannotSquashOrFixupFirstCommit)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,7 +224,7 @@ func (self *LocalCommitsController) squashDown(commit *models.Commit) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *LocalCommitsController) fixup(commit *models.Commit) error {
|
func (self *LocalCommitsController) fixup(commit *models.Commit) error {
|
||||||
if self.context().GetSelectedLineIdx() >= len(self.model.Commits)-1 {
|
if self.context().GetSelectedLineIdx() >= len(self.c.Model().Commits)-1 {
|
||||||
return self.c.ErrorMsg(self.c.Tr.CannotSquashOrFixupFirstCommit)
|
return self.c.ErrorMsg(self.c.Tr.CannotSquashOrFixupFirstCommit)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,12 +257,12 @@ func (self *LocalCommitsController) reword(commit *models.Commit) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
commitMessage, err := self.git.Commit.GetCommitMessage(commit.Sha)
|
commitMessage, err := self.c.Git().Commit.GetCommitMessage(commit.Sha)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.helpers.Commits.OpenCommitMessagePanel(
|
return self.c.Helpers().Commits.OpenCommitMessagePanel(
|
||||||
&helpers.OpenCommitMessagePanelOpts{
|
&helpers.OpenCommitMessagePanelOpts{
|
||||||
CommitIndex: self.context().GetSelectedLineIdx(),
|
CommitIndex: self.context().GetSelectedLineIdx(),
|
||||||
InitialMessage: commitMessage,
|
InitialMessage: commitMessage,
|
||||||
@ -226,12 +274,12 @@ func (self *LocalCommitsController) reword(commit *models.Commit) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *LocalCommitsController) handleReword(message string) error {
|
func (self *LocalCommitsController) handleReword(message string) error {
|
||||||
err := self.git.Rebase.RewordCommit(self.model.Commits, self.contexts.LocalCommits.GetSelectedLineIdx(), message)
|
err := self.c.Git().Rebase.RewordCommit(self.c.Model().Commits, self.c.Contexts().LocalCommits.GetSelectedLineIdx(), message)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
self.helpers.Commits.OnCommitSuccess()
|
self.c.Helpers().Commits.OnCommitSuccess()
|
||||||
_ = self.helpers.Commits.PopCommitMessageContexts()
|
_ = self.c.Helpers().Commits.PopCommitMessageContexts()
|
||||||
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,11 +287,11 @@ func (self *LocalCommitsController) doRewordEditor() error {
|
|||||||
self.c.LogAction(self.c.Tr.Actions.RewordCommit)
|
self.c.LogAction(self.c.Tr.Actions.RewordCommit)
|
||||||
|
|
||||||
if self.isHeadCommit() {
|
if self.isHeadCommit() {
|
||||||
return self.c.RunSubprocessAndRefresh(self.os.Cmd.New("git commit --allow-empty --amend --only"))
|
return self.c.RunSubprocessAndRefresh(self.c.OS().Cmd.New("git commit --allow-empty --amend --only"))
|
||||||
}
|
}
|
||||||
|
|
||||||
subProcess, err := self.git.Rebase.RewordCommitInEditor(
|
subProcess, err := self.c.Git().Rebase.RewordCommitInEditor(
|
||||||
self.model.Commits, self.context().GetSelectedLineIdx(),
|
self.c.Model().Commits, self.context().GetSelectedLineIdx(),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
@ -307,8 +355,8 @@ func (self *LocalCommitsController) edit(commit *models.Commit) error {
|
|||||||
|
|
||||||
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func() error {
|
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func() error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.EditCommit)
|
self.c.LogAction(self.c.Tr.Actions.EditCommit)
|
||||||
err := self.git.Rebase.InteractiveRebaseBreakAfter(self.model.Commits, self.context().GetSelectedLineIdx())
|
err := self.c.Git().Rebase.InteractiveRebaseBreakAfter(self.c.Model().Commits, self.context().GetSelectedLineIdx())
|
||||||
return self.helpers.MergeAndRebase.CheckMergeOrRebase(err)
|
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,8 +375,8 @@ func (self *LocalCommitsController) pick(commit *models.Commit) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *LocalCommitsController) interactiveRebase(action string) error {
|
func (self *LocalCommitsController) interactiveRebase(action string) error {
|
||||||
err := self.git.Rebase.InteractiveRebase(self.model.Commits, self.context().GetSelectedLineIdx(), action)
|
err := self.c.Git().Rebase.InteractiveRebase(self.c.Model().Commits, self.context().GetSelectedLineIdx(), action)
|
||||||
return self.helpers.MergeAndRebase.CheckMergeOrRebase(err)
|
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleMidRebaseCommand sees if the selected commit is in fact a rebasing
|
// handleMidRebaseCommand sees if the selected commit is in fact a rebasing
|
||||||
@ -336,7 +384,7 @@ func (self *LocalCommitsController) interactiveRebase(action string) error {
|
|||||||
// begin a rebase. It then updates the todo file with that action
|
// begin a rebase. It then updates the todo file with that action
|
||||||
func (self *LocalCommitsController) handleMidRebaseCommand(action todo.TodoCommand, commit *models.Commit) (bool, error) {
|
func (self *LocalCommitsController) handleMidRebaseCommand(action todo.TodoCommand, commit *models.Commit) (bool, error) {
|
||||||
if !commit.IsTODO() {
|
if !commit.IsTODO() {
|
||||||
if self.git.Status.WorkingTreeState() != enums.REBASE_MODE_NONE {
|
if self.c.Git().Status.WorkingTreeState() != enums.REBASE_MODE_NONE {
|
||||||
// If we are in a rebase, the only action that is allowed for
|
// If we are in a rebase, the only action that is allowed for
|
||||||
// non-todo commits is rewording the current head commit
|
// non-todo commits is rewording the current head commit
|
||||||
if !(action == todo.Reword && self.isHeadCommit()) {
|
if !(action == todo.Reword && self.isHeadCommit()) {
|
||||||
@ -365,7 +413,7 @@ func (self *LocalCommitsController) handleMidRebaseCommand(action todo.TodoComma
|
|||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
|
|
||||||
if err := self.git.Rebase.EditRebaseTodo(commit, action); err != nil {
|
if err := self.c.Git().Rebase.EditRebaseTodo(commit, action); err != nil {
|
||||||
return false, self.c.Error(err)
|
return false, self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -376,7 +424,7 @@ func (self *LocalCommitsController) handleMidRebaseCommand(action todo.TodoComma
|
|||||||
|
|
||||||
func (self *LocalCommitsController) moveDown(commit *models.Commit) error {
|
func (self *LocalCommitsController) moveDown(commit *models.Commit) error {
|
||||||
index := self.context().GetSelectedLineIdx()
|
index := self.context().GetSelectedLineIdx()
|
||||||
commits := self.model.Commits
|
commits := self.c.Model().Commits
|
||||||
|
|
||||||
// can't move past the initial commit
|
// can't move past the initial commit
|
||||||
if index >= len(commits)-1 {
|
if index >= len(commits)-1 {
|
||||||
@ -393,7 +441,7 @@ func (self *LocalCommitsController) moveDown(commit *models.Commit) error {
|
|||||||
self.c.LogAction(self.c.Tr.Actions.MoveCommitDown)
|
self.c.LogAction(self.c.Tr.Actions.MoveCommitDown)
|
||||||
self.c.LogCommand(fmt.Sprintf("Moving commit %s down", commit.ShortSha()), false)
|
self.c.LogCommand(fmt.Sprintf("Moving commit %s down", commit.ShortSha()), false)
|
||||||
|
|
||||||
if err := self.git.Rebase.MoveTodoDown(commit); err != nil {
|
if err := self.c.Git().Rebase.MoveTodoDown(commit); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
self.context().MoveSelectedLine(1)
|
self.context().MoveSelectedLine(1)
|
||||||
@ -402,17 +450,17 @@ func (self *LocalCommitsController) moveDown(commit *models.Commit) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.git.Status.WorkingTreeState() != enums.REBASE_MODE_NONE {
|
if self.c.Git().Status.WorkingTreeState() != enums.REBASE_MODE_NONE {
|
||||||
return self.c.ErrorMsg(self.c.Tr.AlreadyRebasing)
|
return self.c.ErrorMsg(self.c.Tr.AlreadyRebasing)
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.c.WithWaitingStatus(self.c.Tr.MovingStatus, func() error {
|
return self.c.WithWaitingStatus(self.c.Tr.MovingStatus, func() error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.MoveCommitDown)
|
self.c.LogAction(self.c.Tr.Actions.MoveCommitDown)
|
||||||
err := self.git.Rebase.MoveCommitDown(self.model.Commits, index)
|
err := self.c.Git().Rebase.MoveCommitDown(self.c.Model().Commits, index)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
self.context().MoveSelectedLine(1)
|
self.context().MoveSelectedLine(1)
|
||||||
}
|
}
|
||||||
return self.helpers.MergeAndRebase.CheckMergeOrRebase(err)
|
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -431,7 +479,7 @@ func (self *LocalCommitsController) moveUp(commit *models.Commit) error {
|
|||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
|
|
||||||
if err := self.git.Rebase.MoveTodoUp(self.model.Commits[index]); err != nil {
|
if err := self.c.Git().Rebase.MoveTodoUp(self.c.Model().Commits[index]); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
self.context().MoveSelectedLine(-1)
|
self.context().MoveSelectedLine(-1)
|
||||||
@ -440,29 +488,29 @@ func (self *LocalCommitsController) moveUp(commit *models.Commit) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.git.Status.WorkingTreeState() != enums.REBASE_MODE_NONE {
|
if self.c.Git().Status.WorkingTreeState() != enums.REBASE_MODE_NONE {
|
||||||
return self.c.ErrorMsg(self.c.Tr.AlreadyRebasing)
|
return self.c.ErrorMsg(self.c.Tr.AlreadyRebasing)
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.c.WithWaitingStatus(self.c.Tr.MovingStatus, func() error {
|
return self.c.WithWaitingStatus(self.c.Tr.MovingStatus, func() error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.MoveCommitUp)
|
self.c.LogAction(self.c.Tr.Actions.MoveCommitUp)
|
||||||
err := self.git.Rebase.MoveCommitDown(self.model.Commits, index-1)
|
err := self.c.Git().Rebase.MoveCommitDown(self.c.Model().Commits, index-1)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
self.context().MoveSelectedLine(-1)
|
self.context().MoveSelectedLine(-1)
|
||||||
}
|
}
|
||||||
return self.helpers.MergeAndRebase.CheckMergeOrRebase(err)
|
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *LocalCommitsController) amendTo(commit *models.Commit) error {
|
func (self *LocalCommitsController) amendTo(commit *models.Commit) error {
|
||||||
if self.isHeadCommit() {
|
if self.isHeadCommit() {
|
||||||
if err := self.helpers.AmendHelper.AmendHead(); err != nil {
|
if err := self.c.Helpers().AmendHelper.AmendHead(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.git.Status.WorkingTreeState() != enums.REBASE_MODE_NONE {
|
if self.c.Git().Status.WorkingTreeState() != enums.REBASE_MODE_NONE {
|
||||||
return self.c.ErrorMsg(self.c.Tr.AlreadyRebasing)
|
return self.c.ErrorMsg(self.c.Tr.AlreadyRebasing)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -472,15 +520,15 @@ func (self *LocalCommitsController) amendTo(commit *models.Commit) error {
|
|||||||
HandleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
return self.c.WithWaitingStatus(self.c.Tr.AmendingStatus, func() error {
|
return self.c.WithWaitingStatus(self.c.Tr.AmendingStatus, func() error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.AmendCommit)
|
self.c.LogAction(self.c.Tr.Actions.AmendCommit)
|
||||||
err := self.git.Rebase.AmendTo(commit)
|
err := self.c.Git().Rebase.AmendTo(commit)
|
||||||
return self.helpers.MergeAndRebase.CheckMergeOrRebase(err)
|
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *LocalCommitsController) amendAttribute(commit *models.Commit) error {
|
func (self *LocalCommitsController) amendAttribute(commit *models.Commit) error {
|
||||||
if self.git.Status.WorkingTreeState() != enums.REBASE_MODE_NONE && !self.isHeadCommit() {
|
if self.c.Git().Status.WorkingTreeState() != enums.REBASE_MODE_NONE && !self.isHeadCommit() {
|
||||||
return self.c.ErrorMsg(self.c.Tr.AlreadyRebasing)
|
return self.c.ErrorMsg(self.c.Tr.AlreadyRebasing)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -506,7 +554,7 @@ func (self *LocalCommitsController) amendAttribute(commit *models.Commit) error
|
|||||||
func (self *LocalCommitsController) resetAuthor() error {
|
func (self *LocalCommitsController) resetAuthor() error {
|
||||||
return self.c.WithWaitingStatus(self.c.Tr.AmendingStatus, func() error {
|
return self.c.WithWaitingStatus(self.c.Tr.AmendingStatus, func() error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.ResetCommitAuthor)
|
self.c.LogAction(self.c.Tr.Actions.ResetCommitAuthor)
|
||||||
if err := self.git.Rebase.ResetCommitAuthor(self.model.Commits, self.context().GetSelectedLineIdx()); err != nil {
|
if err := self.c.Git().Rebase.ResetCommitAuthor(self.c.Model().Commits, self.context().GetSelectedLineIdx()); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -517,11 +565,11 @@ func (self *LocalCommitsController) resetAuthor() error {
|
|||||||
func (self *LocalCommitsController) setAuthor() error {
|
func (self *LocalCommitsController) setAuthor() error {
|
||||||
return self.c.Prompt(types.PromptOpts{
|
return self.c.Prompt(types.PromptOpts{
|
||||||
Title: self.c.Tr.SetAuthorPromptTitle,
|
Title: self.c.Tr.SetAuthorPromptTitle,
|
||||||
FindSuggestionsFunc: self.helpers.Suggestions.GetAuthorsSuggestionsFunc(),
|
FindSuggestionsFunc: self.c.Helpers().Suggestions.GetAuthorsSuggestionsFunc(),
|
||||||
HandleConfirm: func(value string) error {
|
HandleConfirm: func(value string) error {
|
||||||
return self.c.WithWaitingStatus(self.c.Tr.AmendingStatus, func() error {
|
return self.c.WithWaitingStatus(self.c.Tr.AmendingStatus, func() error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.SetCommitAuthor)
|
self.c.LogAction(self.c.Tr.Actions.SetCommitAuthor)
|
||||||
if err := self.git.Rebase.SetCommitAuthor(self.model.Commits, self.context().GetSelectedLineIdx(), value); err != nil {
|
if err := self.c.Git().Rebase.SetCommitAuthor(self.c.Model().Commits, self.context().GetSelectedLineIdx(), value); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -544,7 +592,7 @@ func (self *LocalCommitsController) revert(commit *models.Commit) error {
|
|||||||
}),
|
}),
|
||||||
HandleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.RevertCommit)
|
self.c.LogAction(self.c.Tr.Actions.RevertCommit)
|
||||||
if err := self.git.Commit.Revert(commit.Sha); err != nil {
|
if err := self.c.Git().Commit.Revert(commit.Sha); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
return self.afterRevertCommit()
|
return self.afterRevertCommit()
|
||||||
@ -557,7 +605,7 @@ func (self *LocalCommitsController) createRevertMergeCommitMenu(commit *models.C
|
|||||||
menuItems := make([]*types.MenuItem, len(commit.Parents))
|
menuItems := make([]*types.MenuItem, len(commit.Parents))
|
||||||
for i, parentSha := range commit.Parents {
|
for i, parentSha := range commit.Parents {
|
||||||
i := i
|
i := i
|
||||||
message, err := self.git.Commit.GetCommitMessageFirstLine(parentSha)
|
message, err := self.c.Git().Commit.GetCommitMessageFirstLine(parentSha)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
@ -567,7 +615,7 @@ func (self *LocalCommitsController) createRevertMergeCommitMenu(commit *models.C
|
|||||||
OnPress: func() error {
|
OnPress: func() error {
|
||||||
parentNumber := i + 1
|
parentNumber := i + 1
|
||||||
self.c.LogAction(self.c.Tr.Actions.RevertCommit)
|
self.c.LogAction(self.c.Tr.Actions.RevertCommit)
|
||||||
if err := self.git.Commit.RevertMerge(commit.Sha, parentNumber); err != nil {
|
if err := self.c.Git().Commit.RevertMerge(commit.Sha, parentNumber); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
return self.afterRevertCommit()
|
return self.afterRevertCommit()
|
||||||
@ -598,7 +646,7 @@ func (self *LocalCommitsController) createFixupCommit(commit *models.Commit) err
|
|||||||
Prompt: prompt,
|
Prompt: prompt,
|
||||||
HandleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.CreateFixupCommit)
|
self.c.LogAction(self.c.Tr.Actions.CreateFixupCommit)
|
||||||
if err := self.git.Commit.CreateFixupCommit(commit.Sha); err != nil {
|
if err := self.c.Git().Commit.CreateFixupCommit(commit.Sha); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -619,15 +667,15 @@ func (self *LocalCommitsController) squashAllAboveFixupCommits(commit *models.Co
|
|||||||
HandleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
return self.c.WithWaitingStatus(self.c.Tr.SquashingStatus, func() error {
|
return self.c.WithWaitingStatus(self.c.Tr.SquashingStatus, func() error {
|
||||||
self.c.LogAction(self.c.Tr.Actions.SquashAllAboveFixupCommits)
|
self.c.LogAction(self.c.Tr.Actions.SquashAllAboveFixupCommits)
|
||||||
err := self.git.Rebase.SquashAllAboveFixupCommits(commit)
|
err := self.c.Git().Rebase.SquashAllAboveFixupCommits(commit)
|
||||||
return self.helpers.MergeAndRebase.CheckMergeOrRebase(err)
|
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *LocalCommitsController) createTag(commit *models.Commit) error {
|
func (self *LocalCommitsController) createTag(commit *models.Commit) error {
|
||||||
return self.helpers.Tags.CreateTagMenu(commit.Sha, func() {})
|
return self.c.Helpers().Tags.CreateTagMenu(commit.Sha, func() {})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *LocalCommitsController) openSearch() error {
|
func (self *LocalCommitsController) openSearch() error {
|
||||||
@ -759,20 +807,36 @@ func (self *LocalCommitsController) checkSelected(callback func(*models.Commit)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *LocalCommitsController) GetOnFocus() func(types.OnFocusOpts) error {
|
||||||
|
return func(types.OnFocusOpts) error {
|
||||||
|
context := self.context()
|
||||||
|
if context.GetSelectedLineIdx() > COMMIT_THRESHOLD && context.GetLimitCommits() {
|
||||||
|
context.SetLimitCommits(false)
|
||||||
|
go utils.Safe(func() {
|
||||||
|
if err := self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.COMMITS}}); err != nil {
|
||||||
|
_ = self.c.Error(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (self *LocalCommitsController) Context() types.Context {
|
func (self *LocalCommitsController) Context() types.Context {
|
||||||
return self.context()
|
return self.context()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *LocalCommitsController) context() *context.LocalCommitsContext {
|
func (self *LocalCommitsController) context() *context.LocalCommitsContext {
|
||||||
return self.contexts.LocalCommits
|
return self.c.Contexts().LocalCommits
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *LocalCommitsController) paste() error {
|
func (self *LocalCommitsController) paste() error {
|
||||||
return self.helpers.CherryPick.Paste()
|
return self.c.Helpers().CherryPick.Paste()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *LocalCommitsController) isHeadCommit() bool {
|
func (self *LocalCommitsController) isHeadCommit() bool {
|
||||||
return models.IsHeadCommit(self.model.Commits, self.context().GetSelectedLineIdx())
|
return models.IsHeadCommit(self.c.Model().Commits, self.context().GetSelectedLineIdx())
|
||||||
}
|
}
|
||||||
|
|
||||||
func isChangeOfRebaseTodoAllowed(action todo.TodoCommand) bool {
|
func isChangeOfRebaseTodoAllowed(action todo.TodoCommand) bool {
|
||||||
|
@ -7,17 +7,17 @@ import (
|
|||||||
|
|
||||||
type MenuController struct {
|
type MenuController struct {
|
||||||
baseController
|
baseController
|
||||||
*controllerCommon
|
c *ControllerCommon
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ types.IController = &MenuController{}
|
var _ types.IController = &MenuController{}
|
||||||
|
|
||||||
func NewMenuController(
|
func NewMenuController(
|
||||||
common *controllerCommon,
|
common *ControllerCommon,
|
||||||
) *MenuController {
|
) *MenuController {
|
||||||
return &MenuController{
|
return &MenuController{
|
||||||
baseController: baseController{},
|
baseController: baseController{},
|
||||||
controllerCommon: common,
|
c: common,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,12 +28,16 @@ func (self *MenuController) GetKeybindings(opts types.KeybindingsOpts) []*types.
|
|||||||
Handler: self.press,
|
Handler: self.press,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: opts.GetKey(opts.Config.Universal.Confirm),
|
Key: opts.GetKey(opts.Config.Universal.Confirm),
|
||||||
Handler: self.press,
|
Handler: self.press,
|
||||||
|
Description: self.c.Tr.LcExecute,
|
||||||
|
Display: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: opts.GetKey(opts.Config.Universal.Return),
|
Key: opts.GetKey(opts.Config.Universal.Return),
|
||||||
Handler: self.close,
|
Handler: self.close,
|
||||||
|
Description: self.c.Tr.LcClose,
|
||||||
|
Display: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,6 +48,14 @@ func (self *MenuController) GetOnClick() func() error {
|
|||||||
return self.press
|
return self.press
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *MenuController) GetOnFocus() func(types.OnFocusOpts) error {
|
||||||
|
return func(types.OnFocusOpts) error {
|
||||||
|
selectedMenuItem := self.context().GetSelected()
|
||||||
|
self.c.Views().Tooltip.SetContent(selectedMenuItem.Tooltip)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (self *MenuController) press() error {
|
func (self *MenuController) press() error {
|
||||||
return self.context().OnMenuPress(self.context().GetSelected())
|
return self.context().OnMenuPress(self.context().GetSelected())
|
||||||
}
|
}
|
||||||
@ -57,5 +69,5 @@ func (self *MenuController) Context() types.Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *MenuController) context() *context.MenuContext {
|
func (self *MenuController) context() *context.MenuContext {
|
||||||
return self.contexts.Menu
|
return self.c.Contexts().Menu
|
||||||
}
|
}
|
||||||
|
@ -11,17 +11,17 @@ import (
|
|||||||
|
|
||||||
type MergeConflictsController struct {
|
type MergeConflictsController struct {
|
||||||
baseController
|
baseController
|
||||||
*controllerCommon
|
c *ControllerCommon
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ types.IController = &MergeConflictsController{}
|
var _ types.IController = &MergeConflictsController{}
|
||||||
|
|
||||||
func NewMergeConflictsController(
|
func NewMergeConflictsController(
|
||||||
common *controllerCommon,
|
common *ControllerCommon,
|
||||||
) *MergeConflictsController {
|
) *MergeConflictsController {
|
||||||
return &MergeConflictsController{
|
return &MergeConflictsController{
|
||||||
baseController: baseController{},
|
baseController: baseController{},
|
||||||
controllerCommon: common,
|
c: common,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,21 +41,25 @@ func (self *MergeConflictsController) GetKeybindings(opts types.KeybindingsOpts)
|
|||||||
Key: opts.GetKey(opts.Config.Universal.PrevBlock),
|
Key: opts.GetKey(opts.Config.Universal.PrevBlock),
|
||||||
Handler: self.withRenderAndFocus(self.PrevConflict),
|
Handler: self.withRenderAndFocus(self.PrevConflict),
|
||||||
Description: self.c.Tr.PrevConflict,
|
Description: self.c.Tr.PrevConflict,
|
||||||
|
Display: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: opts.GetKey(opts.Config.Universal.NextBlock),
|
Key: opts.GetKey(opts.Config.Universal.NextBlock),
|
||||||
Handler: self.withRenderAndFocus(self.NextConflict),
|
Handler: self.withRenderAndFocus(self.NextConflict),
|
||||||
Description: self.c.Tr.NextConflict,
|
Description: self.c.Tr.NextConflict,
|
||||||
|
Display: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: opts.GetKey(opts.Config.Universal.PrevItem),
|
Key: opts.GetKey(opts.Config.Universal.PrevItem),
|
||||||
Handler: self.withRenderAndFocus(self.PrevConflictHunk),
|
Handler: self.withRenderAndFocus(self.PrevConflictHunk),
|
||||||
Description: self.c.Tr.SelectPrevHunk,
|
Description: self.c.Tr.SelectPrevHunk,
|
||||||
|
Display: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: opts.GetKey(opts.Config.Universal.NextItem),
|
Key: opts.GetKey(opts.Config.Universal.NextItem),
|
||||||
Handler: self.withRenderAndFocus(self.NextConflictHunk),
|
Handler: self.withRenderAndFocus(self.NextConflictHunk),
|
||||||
Description: self.c.Tr.SelectNextHunk,
|
Description: self.c.Tr.SelectNextHunk,
|
||||||
|
Display: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: opts.GetKey(opts.Config.Universal.PrevBlockAlt),
|
Key: opts.GetKey(opts.Config.Universal.PrevBlockAlt),
|
||||||
@ -89,21 +93,24 @@ func (self *MergeConflictsController) GetKeybindings(opts types.KeybindingsOpts)
|
|||||||
Key: opts.GetKey(opts.Config.Universal.Undo),
|
Key: opts.GetKey(opts.Config.Universal.Undo),
|
||||||
Handler: self.withRenderAndFocus(self.HandleUndo),
|
Handler: self.withRenderAndFocus(self.HandleUndo),
|
||||||
Description: self.c.Tr.LcUndo,
|
Description: self.c.Tr.LcUndo,
|
||||||
|
Display: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: opts.GetKey(opts.Config.Files.OpenMergeTool),
|
Key: opts.GetKey(opts.Config.Files.OpenMergeTool),
|
||||||
Handler: self.helpers.WorkingTree.OpenMergeTool,
|
Handler: self.c.Helpers().WorkingTree.OpenMergeTool,
|
||||||
Description: self.c.Tr.LcOpenMergeTool,
|
Description: self.c.Tr.LcOpenMergeTool,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: opts.GetKey(opts.Config.Universal.Select),
|
Key: opts.GetKey(opts.Config.Universal.Select),
|
||||||
Handler: self.withRenderAndFocus(self.HandlePickHunk),
|
Handler: self.withRenderAndFocus(self.HandlePickHunk),
|
||||||
Description: self.c.Tr.PickHunk,
|
Description: self.c.Tr.PickHunk,
|
||||||
|
Display: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: opts.GetKey(opts.Config.Main.PickBothHunks),
|
Key: opts.GetKey(opts.Config.Main.PickBothHunks),
|
||||||
Handler: self.withRenderAndFocus(self.HandlePickAllHunks),
|
Handler: self.withRenderAndFocus(self.HandlePickAllHunks),
|
||||||
Description: self.c.Tr.PickAllHunks,
|
Description: self.c.Tr.PickAllHunks,
|
||||||
|
Display: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: opts.GetKey(opts.Config.Universal.Return),
|
Key: opts.GetKey(opts.Config.Universal.Return),
|
||||||
@ -134,6 +141,24 @@ func (self *MergeConflictsController) GetMouseKeybindings(opts types.Keybindings
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *MergeConflictsController) GetOnFocus() func(types.OnFocusOpts) error {
|
||||||
|
return func(types.OnFocusOpts) error {
|
||||||
|
self.c.Views().MergeConflicts.Wrap = false
|
||||||
|
|
||||||
|
return self.c.Helpers().MergeConflicts.Render(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *MergeConflictsController) GetOnFocusLost() func(types.OnFocusLostOpts) error {
|
||||||
|
return func(types.OnFocusLostOpts) error {
|
||||||
|
self.context().SetUserScrolling(false)
|
||||||
|
self.context().GetState().ResetConflictSelection()
|
||||||
|
self.c.Views().MergeConflicts.Wrap = true
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (self *MergeConflictsController) HandleScrollUp() error {
|
func (self *MergeConflictsController) HandleScrollUp() error {
|
||||||
self.context().SetUserScrolling(true)
|
self.context().SetUserScrolling(true)
|
||||||
self.context().GetViewTrait().ScrollUp(self.c.UserConfig.Gui.ScrollHeight)
|
self.context().GetViewTrait().ScrollUp(self.c.UserConfig.Gui.ScrollHeight)
|
||||||
@ -153,7 +178,7 @@ func (self *MergeConflictsController) Context() types.Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *MergeConflictsController) context() *context.MergeConflictsContext {
|
func (self *MergeConflictsController) context() *context.MergeConflictsContext {
|
||||||
return self.contexts.MergeConflicts
|
return self.c.Contexts().MergeConflicts
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *MergeConflictsController) Escape() error {
|
func (self *MergeConflictsController) Escape() error {
|
||||||
@ -162,11 +187,11 @@ func (self *MergeConflictsController) Escape() error {
|
|||||||
|
|
||||||
func (self *MergeConflictsController) HandleEditFile() error {
|
func (self *MergeConflictsController) HandleEditFile() error {
|
||||||
lineNumber := self.context().GetState().GetSelectedLine()
|
lineNumber := self.context().GetState().GetSelectedLine()
|
||||||
return self.helpers.Files.EditFileAtLine(self.context().GetState().GetPath(), lineNumber)
|
return self.c.Helpers().Files.EditFileAtLine(self.context().GetState().GetPath(), lineNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *MergeConflictsController) HandleOpenFile() error {
|
func (self *MergeConflictsController) HandleOpenFile() error {
|
||||||
return self.helpers.Files.OpenFile(self.context().GetState().GetPath())
|
return self.c.Helpers().Files.OpenFile(self.context().GetState().GetPath())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *MergeConflictsController) HandleScrollLeft() error {
|
func (self *MergeConflictsController) HandleScrollLeft() error {
|
||||||
|
@ -1,23 +1,52 @@
|
|||||||
package gui
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/jesseduffield/generics/slices"
|
"github.com/jesseduffield/generics/slices"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/keybindings"
|
"github.com/jesseduffield/lazygit/pkg/gui/keybindings"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (gui *Gui) getBindings(context types.Context) []*types.Binding {
|
type OptionsMenuAction struct {
|
||||||
|
c *ControllerCommon
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *OptionsMenuAction) Call() error {
|
||||||
|
ctx := self.c.CurrentContext()
|
||||||
|
// Don't show menu while displaying popup.
|
||||||
|
if ctx.GetKind() == types.PERSISTENT_POPUP || ctx.GetKind() == types.TEMPORARY_POPUP {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
bindings := self.getBindings(ctx)
|
||||||
|
|
||||||
|
menuItems := slices.Map(bindings, func(binding *types.Binding) *types.MenuItem {
|
||||||
|
return &types.MenuItem{
|
||||||
|
OpensMenu: binding.OpensMenu,
|
||||||
|
Label: binding.Description,
|
||||||
|
OnPress: func() error {
|
||||||
|
if binding.Handler == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return binding.Handler()
|
||||||
|
},
|
||||||
|
Key: binding.Key,
|
||||||
|
Tooltip: binding.Tooltip,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return self.c.Menu(types.CreateMenuOptions{
|
||||||
|
Title: self.c.Tr.MenuTitle,
|
||||||
|
Items: menuItems,
|
||||||
|
HideCancel: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *OptionsMenuAction) getBindings(context types.Context) []*types.Binding {
|
||||||
var bindingsGlobal, bindingsPanel, bindingsNavigation []*types.Binding
|
var bindingsGlobal, bindingsPanel, bindingsNavigation []*types.Binding
|
||||||
|
|
||||||
bindings, _ := gui.GetInitialKeybindings()
|
bindings, _ := self.c.GetInitialKeybindingsWithCustomCommands()
|
||||||
customBindings, err := gui.CustomCommandsClient.GetCustomCommandKeybindings()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
bindings = append(customBindings, bindings...)
|
|
||||||
|
|
||||||
for _, binding := range bindings {
|
for _, binding := range bindings {
|
||||||
if keybindings.LabelFromKey(binding.Key) != "" && binding.Description != "" {
|
if keybindings.LabelFromKey(binding.Key) != "" && binding.Description != "" {
|
||||||
@ -48,35 +77,3 @@ func uniqueBindings(bindings []*types.Binding) []*types.Binding {
|
|||||||
return binding.Description
|
return binding.Description
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleCreateOptionsMenu() error {
|
|
||||||
ctx := gui.currentContext()
|
|
||||||
// Don't show menu while displaying popup.
|
|
||||||
if ctx.GetKind() == types.PERSISTENT_POPUP || ctx.GetKind() == types.TEMPORARY_POPUP {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
bindings := gui.getBindings(ctx)
|
|
||||||
|
|
||||||
menuItems := slices.Map(bindings, func(binding *types.Binding) *types.MenuItem {
|
|
||||||
return &types.MenuItem{
|
|
||||||
OpensMenu: binding.OpensMenu,
|
|
||||||
Label: binding.Description,
|
|
||||||
OnPress: func() error {
|
|
||||||
if binding.Handler == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return binding.Handler()
|
|
||||||
},
|
|
||||||
Key: binding.Key,
|
|
||||||
Tooltip: binding.Tooltip,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return gui.c.Menu(types.CreateMenuOptions{
|
|
||||||
Title: gui.c.Tr.MenuTitle,
|
|
||||||
Items: menuItems,
|
|
||||||
HideCancel: true,
|
|
||||||
})
|
|
||||||
}
|
|
@ -8,17 +8,17 @@ import (
|
|||||||
|
|
||||||
type PatchBuildingController struct {
|
type PatchBuildingController struct {
|
||||||
baseController
|
baseController
|
||||||
*controllerCommon
|
c *ControllerCommon
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ types.IController = &PatchBuildingController{}
|
var _ types.IController = &PatchBuildingController{}
|
||||||
|
|
||||||
func NewPatchBuildingController(
|
func NewPatchBuildingController(
|
||||||
common *controllerCommon,
|
common *ControllerCommon,
|
||||||
) *PatchBuildingController {
|
) *PatchBuildingController {
|
||||||
return &PatchBuildingController{
|
return &PatchBuildingController{
|
||||||
baseController: baseController{},
|
baseController: baseController{},
|
||||||
controllerCommon: common,
|
c: common,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,42 +48,63 @@ func (self *PatchBuildingController) GetKeybindings(opts types.KeybindingsOpts)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *PatchBuildingController) Context() types.Context {
|
func (self *PatchBuildingController) Context() types.Context {
|
||||||
return self.contexts.CustomPatchBuilder
|
return self.c.Contexts().CustomPatchBuilder
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *PatchBuildingController) context() types.IPatchExplorerContext {
|
func (self *PatchBuildingController) context() types.IPatchExplorerContext {
|
||||||
return self.contexts.CustomPatchBuilder
|
return self.c.Contexts().CustomPatchBuilder
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *PatchBuildingController) GetMouseKeybindings(opts types.KeybindingsOpts) []*gocui.ViewMouseBinding {
|
func (self *PatchBuildingController) GetMouseKeybindings(opts types.KeybindingsOpts) []*gocui.ViewMouseBinding {
|
||||||
return []*gocui.ViewMouseBinding{}
|
return []*gocui.ViewMouseBinding{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *PatchBuildingController) GetOnFocus() func(types.OnFocusOpts) error {
|
||||||
|
return func(opts types.OnFocusOpts) error {
|
||||||
|
// no need to change wrap on the secondary view because it can't be interacted with
|
||||||
|
self.c.Views().PatchBuilding.Wrap = false
|
||||||
|
|
||||||
|
return self.c.Helpers().PatchBuilding.RefreshPatchBuildingPanel(opts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *PatchBuildingController) GetOnFocusLost() func(types.OnFocusLostOpts) error {
|
||||||
|
return func(opts types.OnFocusLostOpts) error {
|
||||||
|
self.c.Views().PatchBuilding.Wrap = true
|
||||||
|
|
||||||
|
if self.c.Git().Patch.PatchBuilder.IsEmpty() {
|
||||||
|
self.c.Git().Patch.PatchBuilder.Reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (self *PatchBuildingController) OpenFile() error {
|
func (self *PatchBuildingController) OpenFile() error {
|
||||||
self.context().GetMutex().Lock()
|
self.context().GetMutex().Lock()
|
||||||
defer self.context().GetMutex().Unlock()
|
defer self.context().GetMutex().Unlock()
|
||||||
|
|
||||||
path := self.contexts.CommitFiles.GetSelectedPath()
|
path := self.c.Contexts().CommitFiles.GetSelectedPath()
|
||||||
|
|
||||||
if path == "" {
|
if path == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.helpers.Files.OpenFile(path)
|
return self.c.Helpers().Files.OpenFile(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *PatchBuildingController) EditFile() error {
|
func (self *PatchBuildingController) EditFile() error {
|
||||||
self.context().GetMutex().Lock()
|
self.context().GetMutex().Lock()
|
||||||
defer self.context().GetMutex().Unlock()
|
defer self.context().GetMutex().Unlock()
|
||||||
|
|
||||||
path := self.contexts.CommitFiles.GetSelectedPath()
|
path := self.c.Contexts().CommitFiles.GetSelectedPath()
|
||||||
|
|
||||||
if path == "" {
|
if path == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
lineNumber := self.context().GetState().CurrentLineNumber()
|
lineNumber := self.context().GetState().CurrentLineNumber()
|
||||||
return self.helpers.Files.EditFileAtLine(path, lineNumber)
|
return self.c.Helpers().Files.EditFileAtLine(path, lineNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *PatchBuildingController) ToggleSelectionAndRefresh() error {
|
func (self *PatchBuildingController) ToggleSelectionAndRefresh() error {
|
||||||
@ -100,21 +121,21 @@ func (self *PatchBuildingController) toggleSelection() error {
|
|||||||
self.context().GetMutex().Lock()
|
self.context().GetMutex().Lock()
|
||||||
defer self.context().GetMutex().Unlock()
|
defer self.context().GetMutex().Unlock()
|
||||||
|
|
||||||
toggleFunc := self.git.Patch.PatchBuilder.AddFileLineRange
|
toggleFunc := self.c.Git().Patch.PatchBuilder.AddFileLineRange
|
||||||
filename := self.contexts.CommitFiles.GetSelectedPath()
|
filename := self.c.Contexts().CommitFiles.GetSelectedPath()
|
||||||
if filename == "" {
|
if filename == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
state := self.context().GetState()
|
state := self.context().GetState()
|
||||||
|
|
||||||
includedLineIndices, err := self.git.Patch.PatchBuilder.GetFileIncLineIndices(filename)
|
includedLineIndices, err := self.c.Git().Patch.PatchBuilder.GetFileIncLineIndices(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
currentLineIsStaged := lo.Contains(includedLineIndices, state.GetSelectedLineIdx())
|
currentLineIsStaged := lo.Contains(includedLineIndices, state.GetSelectedLineIdx())
|
||||||
if currentLineIsStaged {
|
if currentLineIsStaged {
|
||||||
toggleFunc = self.git.Patch.PatchBuilder.RemoveFileLineRange
|
toggleFunc = self.c.Git().Patch.PatchBuilder.RemoveFileLineRange
|
||||||
}
|
}
|
||||||
|
|
||||||
// add range of lines to those set for the file
|
// add range of lines to those set for the file
|
||||||
@ -133,5 +154,5 @@ func (self *PatchBuildingController) toggleSelection() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *PatchBuildingController) Escape() error {
|
func (self *PatchBuildingController) Escape() error {
|
||||||
return self.helpers.PatchBuilding.Escape()
|
return self.c.Helpers().PatchBuilding.Escape()
|
||||||
}
|
}
|
||||||
|
@ -6,26 +6,26 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type PatchExplorerControllerFactory struct {
|
type PatchExplorerControllerFactory struct {
|
||||||
*controllerCommon
|
c *ControllerCommon
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPatchExplorerControllerFactory(c *controllerCommon) *PatchExplorerControllerFactory {
|
func NewPatchExplorerControllerFactory(c *ControllerCommon) *PatchExplorerControllerFactory {
|
||||||
return &PatchExplorerControllerFactory{
|
return &PatchExplorerControllerFactory{
|
||||||
controllerCommon: c,
|
c: c,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *PatchExplorerControllerFactory) Create(context types.IPatchExplorerContext) *PatchExplorerController {
|
func (self *PatchExplorerControllerFactory) Create(context types.IPatchExplorerContext) *PatchExplorerController {
|
||||||
return &PatchExplorerController{
|
return &PatchExplorerController{
|
||||||
baseController: baseController{},
|
baseController: baseController{},
|
||||||
controllerCommon: self.controllerCommon,
|
c: self.c,
|
||||||
context: context,
|
context: context,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type PatchExplorerController struct {
|
type PatchExplorerController struct {
|
||||||
baseController
|
baseController
|
||||||
*controllerCommon
|
c *ControllerCommon
|
||||||
|
|
||||||
context types.IPatchExplorerContext
|
context types.IPatchExplorerContext
|
||||||
}
|
}
|
||||||
@ -254,7 +254,7 @@ func (self *PatchExplorerController) CopySelectedToClipboard() error {
|
|||||||
selected := self.context.GetState().PlainRenderSelected()
|
selected := self.context.GetState().PlainRenderSelected()
|
||||||
|
|
||||||
self.c.LogAction(self.c.Tr.Actions.CopySelectedTextToClipboard)
|
self.c.LogAction(self.c.Tr.Actions.CopySelectedTextToClipboard)
|
||||||
if err := self.os.CopyToClipboard(selected); err != nil {
|
if err := self.c.OS().CopyToClipboard(selected); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
75
pkg/gui/controllers/quit_actions.go
Normal file
75
pkg/gui/controllers/quit_actions.go
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jesseduffield/gocui"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type QuitActions struct {
|
||||||
|
c *ControllerCommon
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *QuitActions) Quit() error {
|
||||||
|
self.c.State().SetRetainOriginalDir(false)
|
||||||
|
return self.quitAux()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *QuitActions) QuitWithoutChangingDirectory() error {
|
||||||
|
self.c.State().SetRetainOriginalDir(true)
|
||||||
|
return self.quitAux()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *QuitActions) quitAux() error {
|
||||||
|
if self.c.State().GetUpdating() {
|
||||||
|
return self.confirmQuitDuringUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.c.UserConfig.ConfirmOnQuit {
|
||||||
|
return self.c.Confirm(types.ConfirmOpts{
|
||||||
|
Title: "",
|
||||||
|
Prompt: self.c.Tr.ConfirmQuit,
|
||||||
|
HandleConfirm: func() error {
|
||||||
|
return gocui.ErrQuit
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return gocui.ErrQuit
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *QuitActions) confirmQuitDuringUpdate() error {
|
||||||
|
return self.c.Confirm(types.ConfirmOpts{
|
||||||
|
Title: self.c.Tr.ConfirmQuitDuringUpdateTitle,
|
||||||
|
Prompt: self.c.Tr.ConfirmQuitDuringUpdate,
|
||||||
|
HandleConfirm: func() error {
|
||||||
|
return gocui.ErrQuit
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *QuitActions) Escape() error {
|
||||||
|
currentContext := self.c.CurrentContext()
|
||||||
|
|
||||||
|
parentContext, hasParent := currentContext.GetParentContext()
|
||||||
|
if hasParent && currentContext != nil && parentContext != nil {
|
||||||
|
// TODO: think about whether this should be marked as a return rather than adding to the stack
|
||||||
|
return self.c.PushContext(parentContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, mode := range self.c.Helpers().Mode.Statuses() {
|
||||||
|
if mode.IsActive() {
|
||||||
|
return mode.Reset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repoPathStack := self.c.State().GetRepoPathStack()
|
||||||
|
if !repoPathStack.IsEmpty() {
|
||||||
|
return self.c.Helpers().Repos.DispatchSwitchToRepo(repoPathStack.Pop(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.c.UserConfig.QuitOnTopLevelReturn {
|
||||||
|
return self.Quit()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user