mirror of
https://github.com/jesseduffield/lazygit.git
synced 2024-11-28 09:08:41 +02:00
refactor to only have one context per view
This commit is contained in:
parent
6dfef08efc
commit
524bf83a4a
@ -274,7 +274,7 @@ os:
|
||||
|
||||
Lazygit will log an error if none of these options are set.
|
||||
|
||||
You can specify a line number you are currently at when in the line-by-line mode.
|
||||
You can specify the current line number when you're in the patch explorer.
|
||||
|
||||
```yaml
|
||||
os:
|
||||
|
@ -30,13 +30,13 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## List Panel Navigation
|
||||
|
||||
<pre>
|
||||
<kbd>.</kbd>: next page
|
||||
<kbd>,</kbd>: previous page
|
||||
<kbd><</kbd>: scroll to top
|
||||
<kbd>></kbd>: scroll to bottom
|
||||
<kbd>/</kbd>: start search
|
||||
<kbd>H</kbd>: scroll left
|
||||
<kbd>L</kbd>: scroll right
|
||||
<kbd>,</kbd>: previous page
|
||||
<kbd>.</kbd>: next page
|
||||
<kbd><</kbd>: scroll to top
|
||||
<kbd>/</kbd>: start search
|
||||
<kbd>></kbd>: scroll to bottom
|
||||
<kbd>]</kbd>: next tab
|
||||
<kbd>[</kbd>: previous tab
|
||||
</pre>
|
||||
@ -163,36 +163,33 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## Main Panel (Patch Building)
|
||||
|
||||
<pre>
|
||||
<kbd>esc</kbd>: exit line-by-line mode
|
||||
<kbd>o</kbd>: open file
|
||||
<kbd>▲</kbd>: select previous line
|
||||
<kbd>▼</kbd>: select next line
|
||||
<kbd>◄</kbd>: select previous hunk
|
||||
<kbd>►</kbd>: select next hunk
|
||||
<kbd>ctrl+o</kbd>: copy the selected text to the clipboard
|
||||
<kbd>space</kbd>: add/remove line(s) to patch
|
||||
<kbd>v</kbd>: toggle drag select
|
||||
<kbd>V</kbd>: toggle drag select
|
||||
<kbd>a</kbd>: toggle select hunk
|
||||
<kbd>ctrl+o</kbd>: copy the selected text to the clipboard
|
||||
<kbd>o</kbd>: open file
|
||||
<kbd>e</kbd>: edit file
|
||||
<kbd>space</kbd>: add/remove line(s) to patch
|
||||
<kbd>esc</kbd>: exit custom patch builder
|
||||
</pre>
|
||||
|
||||
## Main Panel (Staging)
|
||||
|
||||
<pre>
|
||||
<kbd>esc</kbd>: return to files panel
|
||||
<kbd>space</kbd>: toggle line staged / unstaged
|
||||
<kbd>d</kbd>: delete change (git reset)
|
||||
<kbd>tab</kbd>: switch to other panel
|
||||
<kbd>o</kbd>: open file
|
||||
<kbd>▲</kbd>: select previous line
|
||||
<kbd>▼</kbd>: select next line
|
||||
<kbd>◄</kbd>: select previous hunk
|
||||
<kbd>►</kbd>: select next hunk
|
||||
<kbd>ctrl+o</kbd>: copy the selected text to the clipboard
|
||||
<kbd>e</kbd>: edit file
|
||||
<kbd>v</kbd>: toggle drag select
|
||||
<kbd>V</kbd>: toggle drag select
|
||||
<kbd>a</kbd>: toggle select hunk
|
||||
<kbd>ctrl+o</kbd>: copy the selected text to the clipboard
|
||||
<kbd>o</kbd>: open file
|
||||
<kbd>e</kbd>: edit file
|
||||
<kbd>esc</kbd>: return to files panel
|
||||
<kbd>tab</kbd>: switch to other panel (staged/unstaged changes)
|
||||
<kbd>space</kbd>: toggle line staged / unstaged
|
||||
<kbd>d</kbd>: delete change (git reset)
|
||||
<kbd>E</kbd>: edit hunk
|
||||
</pre>
|
||||
|
||||
|
@ -30,13 +30,13 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## 一覧パネルの操作
|
||||
|
||||
<pre>
|
||||
<kbd>.</kbd>: 次のページ
|
||||
<kbd>,</kbd>: 前のページ
|
||||
<kbd><</kbd>: 最上部までスクロール
|
||||
<kbd>></kbd>: 最下部までスクロール
|
||||
<kbd>/</kbd>: 検索を開始
|
||||
<kbd>H</kbd>: 左スクロール
|
||||
<kbd>L</kbd>: 右スクロール
|
||||
<kbd>,</kbd>: 前のページ
|
||||
<kbd>.</kbd>: 次のページ
|
||||
<kbd><</kbd>: 最上部までスクロール
|
||||
<kbd>/</kbd>: 検索を開始
|
||||
<kbd>></kbd>: 最下部までスクロール
|
||||
<kbd>]</kbd>: 次のタブ
|
||||
<kbd>[</kbd>: 前のタブ
|
||||
</pre>
|
||||
@ -222,36 +222,33 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## メインパネル (Patch Building)
|
||||
|
||||
<pre>
|
||||
<kbd>esc</kbd>: line-by-lineモードを終了
|
||||
<kbd>o</kbd>: ファイルを開く
|
||||
<kbd>▲</kbd>: 前の行を選択
|
||||
<kbd>▼</kbd>: 次の行を選択
|
||||
<kbd>◄</kbd>: 前のhunkを選択
|
||||
<kbd>►</kbd>: 次のhunkを選択
|
||||
<kbd>ctrl+o</kbd>: 選択されたテキストをクリップボードにコピー
|
||||
<kbd>space</kbd>: 行をパッチに追加/削除
|
||||
<kbd>v</kbd>: 範囲選択を切り替え
|
||||
<kbd>V</kbd>: 範囲選択を切り替え
|
||||
<kbd>a</kbd>: hunk選択を切り替え
|
||||
<kbd>ctrl+o</kbd>: 選択されたテキストをクリップボードにコピー
|
||||
<kbd>o</kbd>: ファイルを開く
|
||||
<kbd>e</kbd>: ファイルを編集
|
||||
<kbd>space</kbd>: 行をパッチに追加/削除
|
||||
<kbd>esc</kbd>: exit custom patch builder
|
||||
</pre>
|
||||
|
||||
## メインパネル (Staging)
|
||||
|
||||
<pre>
|
||||
<kbd>esc</kbd>: ファイル一覧に戻る
|
||||
<kbd>space</kbd>: 選択行をステージ/アンステージ
|
||||
<kbd>d</kbd>: 変更を削除 (git reset)
|
||||
<kbd>tab</kbd>: パネルを切り替え
|
||||
<kbd>o</kbd>: ファイルを開く
|
||||
<kbd>▲</kbd>: 前の行を選択
|
||||
<kbd>▼</kbd>: 次の行を選択
|
||||
<kbd>◄</kbd>: 前のhunkを選択
|
||||
<kbd>►</kbd>: 次のhunkを選択
|
||||
<kbd>ctrl+o</kbd>: 選択されたテキストをクリップボードにコピー
|
||||
<kbd>e</kbd>: ファイルを編集
|
||||
<kbd>v</kbd>: 範囲選択を切り替え
|
||||
<kbd>V</kbd>: 範囲選択を切り替え
|
||||
<kbd>a</kbd>: hunk選択を切り替え
|
||||
<kbd>ctrl+o</kbd>: 選択されたテキストをクリップボードにコピー
|
||||
<kbd>o</kbd>: ファイルを開く
|
||||
<kbd>e</kbd>: ファイルを編集
|
||||
<kbd>esc</kbd>: ファイル一覧に戻る
|
||||
<kbd>tab</kbd>: パネルを切り替え
|
||||
<kbd>space</kbd>: 選択行をステージ/アンステージ
|
||||
<kbd>d</kbd>: 変更を削除 (git reset)
|
||||
<kbd>E</kbd>: edit hunk
|
||||
</pre>
|
||||
|
||||
|
@ -30,13 +30,13 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## List Panel Navigation
|
||||
|
||||
<pre>
|
||||
<kbd>.</kbd>: 다음 페이지
|
||||
<kbd>,</kbd>: 이전 페이지
|
||||
<kbd><</kbd>: 맨 위로 스크롤
|
||||
<kbd>></kbd>: 맨 아래로 스크롤
|
||||
<kbd>/</kbd>: 검색 시작
|
||||
<kbd>H</kbd>: 우 스크롤
|
||||
<kbd>L</kbd>: 좌 스크롤
|
||||
<kbd>,</kbd>: 이전 페이지
|
||||
<kbd>.</kbd>: 다음 페이지
|
||||
<kbd><</kbd>: 맨 위로 스크롤
|
||||
<kbd>/</kbd>: 검색 시작
|
||||
<kbd>></kbd>: 맨 아래로 스크롤
|
||||
<kbd>]</kbd>: 이전 탭
|
||||
<kbd>[</kbd>: 다음 탭
|
||||
</pre>
|
||||
@ -107,36 +107,33 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## 메인 패널 (Patch Building)
|
||||
|
||||
<pre>
|
||||
<kbd>esc</kbd>: line-by-line 모드 종료
|
||||
<kbd>o</kbd>: 파일 닫기
|
||||
<kbd>▲</kbd>: 이전 줄 선택
|
||||
<kbd>▼</kbd>: 다음 줄 선택
|
||||
<kbd>◄</kbd>: 이전 hunk를 선택
|
||||
<kbd>►</kbd>: 다음 hunk를 선택
|
||||
<kbd>ctrl+o</kbd>: 선택한 텍스트를 클립보드에 복사
|
||||
<kbd>space</kbd>: line(s)을 패치에 추가/삭제
|
||||
<kbd>v</kbd>: 드래그 선택 전환
|
||||
<kbd>V</kbd>: 드래그 선택 전환
|
||||
<kbd>a</kbd>: toggle select hunk
|
||||
<kbd>ctrl+o</kbd>: 선택한 텍스트를 클립보드에 복사
|
||||
<kbd>o</kbd>: 파일 닫기
|
||||
<kbd>e</kbd>: 파일 편집
|
||||
<kbd>space</kbd>: line(s)을 패치에 추가/삭제
|
||||
<kbd>esc</kbd>: exit custom patch builder
|
||||
</pre>
|
||||
|
||||
## 메인 패널 (Staging)
|
||||
|
||||
<pre>
|
||||
<kbd>esc</kbd>: 파일 목록으로 돌아가기
|
||||
<kbd>space</kbd>: 선택한 행을 staged / unstaged
|
||||
<kbd>d</kbd>: 변경을 삭제 (git reset)
|
||||
<kbd>tab</kbd>: 패널 전환
|
||||
<kbd>o</kbd>: 파일 닫기
|
||||
<kbd>▲</kbd>: 이전 줄 선택
|
||||
<kbd>▼</kbd>: 다음 줄 선택
|
||||
<kbd>◄</kbd>: 이전 hunk를 선택
|
||||
<kbd>►</kbd>: 다음 hunk를 선택
|
||||
<kbd>ctrl+o</kbd>: 선택한 텍스트를 클립보드에 복사
|
||||
<kbd>e</kbd>: 파일 편집
|
||||
<kbd>v</kbd>: 드래그 선택 전환
|
||||
<kbd>V</kbd>: 드래그 선택 전환
|
||||
<kbd>a</kbd>: toggle select hunk
|
||||
<kbd>ctrl+o</kbd>: 선택한 텍스트를 클립보드에 복사
|
||||
<kbd>o</kbd>: 파일 닫기
|
||||
<kbd>e</kbd>: 파일 편집
|
||||
<kbd>esc</kbd>: 파일 목록으로 돌아가기
|
||||
<kbd>tab</kbd>: 패널 전환
|
||||
<kbd>space</kbd>: 선택한 행을 staged / unstaged
|
||||
<kbd>d</kbd>: 변경을 삭제 (git reset)
|
||||
<kbd>E</kbd>: edit hunk
|
||||
</pre>
|
||||
|
||||
|
@ -30,13 +30,13 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## Lijstpaneel Navigatie
|
||||
|
||||
<pre>
|
||||
<kbd>.</kbd>: volgende pagina
|
||||
<kbd>,</kbd>: vorige pagina
|
||||
<kbd><</kbd>: scroll naar boven
|
||||
<kbd>></kbd>: scroll naar beneden
|
||||
<kbd>/</kbd>: start met zoeken
|
||||
<kbd>H</kbd>: scroll left
|
||||
<kbd>L</kbd>: scroll right
|
||||
<kbd>,</kbd>: vorige pagina
|
||||
<kbd>.</kbd>: volgende pagina
|
||||
<kbd><</kbd>: scroll naar boven
|
||||
<kbd>/</kbd>: start met zoeken
|
||||
<kbd>></kbd>: scroll naar beneden
|
||||
<kbd>]</kbd>: volgende tabblad
|
||||
<kbd>[</kbd>: vorige tabblad
|
||||
</pre>
|
||||
@ -163,17 +163,16 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## Patch Bouwen
|
||||
|
||||
<pre>
|
||||
<kbd>esc</kbd>: sluit lijn-bij-lijn modus
|
||||
<kbd>o</kbd>: open bestand
|
||||
<kbd>▲</kbd>: selecteer de vorige lijn
|
||||
<kbd>▼</kbd>: selecteer de volgende lijn
|
||||
<kbd>◄</kbd>: selecteer de vorige hunk
|
||||
<kbd>►</kbd>: selecteer de volgende hunk
|
||||
<kbd>ctrl+o</kbd>: copy the selected text to the clipboard
|
||||
<kbd>space</kbd>: voeg toe/verwijder lijn(en) in patch
|
||||
<kbd>v</kbd>: toggle drag selecteer
|
||||
<kbd>V</kbd>: toggle drag selecteer
|
||||
<kbd>a</kbd>: toggle selecteer hunk
|
||||
<kbd>ctrl+o</kbd>: copy the selected text to the clipboard
|
||||
<kbd>o</kbd>: open bestand
|
||||
<kbd>e</kbd>: verander bestand
|
||||
<kbd>space</kbd>: voeg toe/verwijder lijn(en) in patch
|
||||
<kbd>esc</kbd>: sluit lijn-bij-lijn modus
|
||||
</pre>
|
||||
|
||||
## Reflog
|
||||
@ -217,20 +216,18 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## Staging
|
||||
|
||||
<pre>
|
||||
<kbd>esc</kbd>: ga terug naar het bestanden paneel
|
||||
<kbd>space</kbd>: toggle lijnen staged / unstaged
|
||||
<kbd>d</kbd>: verwijdert change (git reset)
|
||||
<kbd>tab</kbd>: ga naar een ander paneel
|
||||
<kbd>o</kbd>: open bestand
|
||||
<kbd>▲</kbd>: selecteer de vorige lijn
|
||||
<kbd>▼</kbd>: selecteer de volgende lijn
|
||||
<kbd>◄</kbd>: selecteer de vorige hunk
|
||||
<kbd>►</kbd>: selecteer de volgende hunk
|
||||
<kbd>ctrl+o</kbd>: copy the selected text to the clipboard
|
||||
<kbd>e</kbd>: verander bestand
|
||||
<kbd>v</kbd>: toggle drag selecteer
|
||||
<kbd>V</kbd>: toggle drag selecteer
|
||||
<kbd>a</kbd>: toggle selecteer hunk
|
||||
<kbd>ctrl+o</kbd>: copy the selected text to the clipboard
|
||||
<kbd>o</kbd>: open bestand
|
||||
<kbd>e</kbd>: verander bestand
|
||||
<kbd>esc</kbd>: ga terug naar het bestanden paneel
|
||||
<kbd>tab</kbd>: ga naar een ander paneel
|
||||
<kbd>space</kbd>: toggle lijnen staged / unstaged
|
||||
<kbd>d</kbd>: verwijdert change (git reset)
|
||||
<kbd>E</kbd>: edit hunk
|
||||
</pre>
|
||||
|
||||
|
@ -30,13 +30,13 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## List Panel Navigation
|
||||
|
||||
<pre>
|
||||
<kbd>.</kbd>: next page
|
||||
<kbd>,</kbd>: previous page
|
||||
<kbd><</kbd>: scroll to top
|
||||
<kbd>></kbd>: scroll to bottom
|
||||
<kbd>/</kbd>: start search
|
||||
<kbd>H</kbd>: scroll left
|
||||
<kbd>L</kbd>: scroll right
|
||||
<kbd>,</kbd>: previous page
|
||||
<kbd>.</kbd>: next page
|
||||
<kbd><</kbd>: scroll to top
|
||||
<kbd>/</kbd>: start search
|
||||
<kbd>></kbd>: scroll to bottom
|
||||
<kbd>]</kbd>: next tab
|
||||
<kbd>[</kbd>: previous tab
|
||||
</pre>
|
||||
@ -99,17 +99,16 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## Main Panel (Patch Building)
|
||||
|
||||
<pre>
|
||||
<kbd>esc</kbd>: wyście z trybu "linia po linii"
|
||||
<kbd>o</kbd>: otwórz plik
|
||||
<kbd>▲</kbd>: poprzednia linia
|
||||
<kbd>▼</kbd>: następna linia
|
||||
<kbd>◄</kbd>: poprzedni kawałek
|
||||
<kbd>►</kbd>: następny kawałek
|
||||
<kbd>ctrl+o</kbd>: copy the selected text to the clipboard
|
||||
<kbd>space</kbd>: add/remove line(s) to patch
|
||||
<kbd>v</kbd>: toggle drag select
|
||||
<kbd>V</kbd>: toggle drag select
|
||||
<kbd>a</kbd>: toggle select hunk
|
||||
<kbd>ctrl+o</kbd>: copy the selected text to the clipboard
|
||||
<kbd>o</kbd>: otwórz plik
|
||||
<kbd>e</kbd>: edytuj plik
|
||||
<kbd>space</kbd>: add/remove line(s) to patch
|
||||
<kbd>esc</kbd>: wyście z trybu "linia po linii"
|
||||
</pre>
|
||||
|
||||
## Pliki
|
||||
@ -156,20 +155,18 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## Poczekalnia
|
||||
|
||||
<pre>
|
||||
<kbd>esc</kbd>: wróć do panelu plików
|
||||
<kbd>space</kbd>: toggle line staged / unstaged
|
||||
<kbd>d</kbd>: delete change (git reset)
|
||||
<kbd>tab</kbd>: switch to other panel
|
||||
<kbd>o</kbd>: otwórz plik
|
||||
<kbd>▲</kbd>: poprzednia linia
|
||||
<kbd>▼</kbd>: następna linia
|
||||
<kbd>◄</kbd>: poprzedni kawałek
|
||||
<kbd>►</kbd>: następny kawałek
|
||||
<kbd>ctrl+o</kbd>: copy the selected text to the clipboard
|
||||
<kbd>e</kbd>: edytuj plik
|
||||
<kbd>v</kbd>: toggle drag select
|
||||
<kbd>V</kbd>: toggle drag select
|
||||
<kbd>a</kbd>: toggle select hunk
|
||||
<kbd>ctrl+o</kbd>: copy the selected text to the clipboard
|
||||
<kbd>o</kbd>: otwórz plik
|
||||
<kbd>e</kbd>: edytuj plik
|
||||
<kbd>esc</kbd>: wróć do panelu plików
|
||||
<kbd>tab</kbd>: switch to other panel (staged/unstaged changes)
|
||||
<kbd>space</kbd>: toggle line staged / unstaged
|
||||
<kbd>d</kbd>: delete change (git reset)
|
||||
<kbd>E</kbd>: edit hunk
|
||||
</pre>
|
||||
|
||||
|
@ -30,13 +30,13 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## 列表面板导航
|
||||
|
||||
<pre>
|
||||
<kbd>.</kbd>: 下一页
|
||||
<kbd>,</kbd>: 上一页
|
||||
<kbd><</kbd>: 滚动到顶部
|
||||
<kbd>></kbd>: 滚动到底部
|
||||
<kbd>/</kbd>: 开始搜索
|
||||
<kbd>H</kbd>: 向左滚动
|
||||
<kbd>L</kbd>: 向右滚动
|
||||
<kbd>,</kbd>: 上一页
|
||||
<kbd>.</kbd>: 下一页
|
||||
<kbd><</kbd>: 滚动到顶部
|
||||
<kbd>/</kbd>: 开始搜索
|
||||
<kbd>></kbd>: 滚动到底部
|
||||
<kbd>]</kbd>: 下一个标签
|
||||
<kbd>[</kbd>: 上一个标签
|
||||
</pre>
|
||||
@ -183,17 +183,16 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## 构建补丁中
|
||||
|
||||
<pre>
|
||||
<kbd>esc</kbd>: 退出逐行模式
|
||||
<kbd>o</kbd>: 打开文件
|
||||
<kbd>▲</kbd>: 选择上一行
|
||||
<kbd>▼</kbd>: 选择下一行
|
||||
<kbd>◄</kbd>: 选择上一个区块
|
||||
<kbd>►</kbd>: 选择下一个区块
|
||||
<kbd>ctrl+o</kbd>: 将选中文本复制到剪贴板
|
||||
<kbd>space</kbd>: 添加/移除 行到补丁
|
||||
<kbd>v</kbd>: 切换拖动选择
|
||||
<kbd>V</kbd>: 切换拖动选择
|
||||
<kbd>a</kbd>: 切换选择区块
|
||||
<kbd>ctrl+o</kbd>: 将选中文本复制到剪贴板
|
||||
<kbd>o</kbd>: 打开文件
|
||||
<kbd>e</kbd>: 编辑文件
|
||||
<kbd>space</kbd>: 添加/移除 行到补丁
|
||||
<kbd>esc</kbd>: 退出逐行模式
|
||||
</pre>
|
||||
|
||||
## 标签页面
|
||||
@ -226,20 +225,18 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## 正在暂存
|
||||
|
||||
<pre>
|
||||
<kbd>esc</kbd>: 返回文件面板
|
||||
<kbd>space</kbd>: 切换行暂存状态
|
||||
<kbd>d</kbd>: 取消变更 (git reset)
|
||||
<kbd>tab</kbd>: 切换到其他面板
|
||||
<kbd>o</kbd>: 打开文件
|
||||
<kbd>▲</kbd>: 选择上一行
|
||||
<kbd>▼</kbd>: 选择下一行
|
||||
<kbd>◄</kbd>: 选择上一个区块
|
||||
<kbd>►</kbd>: 选择下一个区块
|
||||
<kbd>ctrl+o</kbd>: 将选中文本复制到剪贴板
|
||||
<kbd>e</kbd>: 编辑文件
|
||||
<kbd>v</kbd>: 切换拖动选择
|
||||
<kbd>V</kbd>: 切换拖动选择
|
||||
<kbd>a</kbd>: 切换选择区块
|
||||
<kbd>ctrl+o</kbd>: 将选中文本复制到剪贴板
|
||||
<kbd>o</kbd>: 打开文件
|
||||
<kbd>e</kbd>: 编辑文件
|
||||
<kbd>esc</kbd>: 返回文件面板
|
||||
<kbd>tab</kbd>: 切换到其他面板
|
||||
<kbd>space</kbd>: 切换行暂存状态
|
||||
<kbd>d</kbd>: 取消变更 (git reset)
|
||||
<kbd>E</kbd>: edit hunk
|
||||
</pre>
|
||||
|
||||
|
10
go.mod
10
go.mod
@ -17,7 +17,7 @@ require (
|
||||
github.com/integrii/flaggy v1.4.0
|
||||
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68
|
||||
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20220723050330-1f853fadb335
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20220806032055-dfd3eb22e18a
|
||||
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10
|
||||
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e
|
||||
github.com/jesseduffield/yaml v2.1.0+incompatible
|
||||
@ -42,7 +42,7 @@ require (
|
||||
github.com/emirpasic/gods v1.12.0 // indirect
|
||||
github.com/fatih/color v1.9.0 // indirect
|
||||
github.com/gdamore/encoding v1.0.0 // indirect
|
||||
github.com/gdamore/tcell/v2 v2.5.1 // indirect
|
||||
github.com/gdamore/tcell/v2 v2.5.2 // indirect
|
||||
github.com/go-git/gcfg v1.5.0 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.0.0 // indirect
|
||||
github.com/go-logfmt/logfmt v0.5.0 // indirect
|
||||
@ -58,14 +58,14 @@ require (
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/onsi/ginkgo v1.10.3 // indirect
|
||||
github.com/onsi/gomega v1.7.1 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/rivo/uniseg v0.3.4 // indirect
|
||||
github.com/sergi/go-diff v1.1.0 // indirect
|
||||
github.com/xanzy/ssh-agent v0.2.1 // indirect
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 // indirect
|
||||
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8 // indirect
|
||||
golang.org/x/net v0.0.0-20201002202402-0a1ea396d57c // indirect
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect
|
||||
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 // indirect
|
||||
golang.org/x/sys v0.0.0-20220804214406-8e32c043e418 // indirect
|
||||
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
|
19
go.sum
19
go.sum
@ -35,8 +35,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
|
||||
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
|
||||
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
||||
github.com/gdamore/tcell/v2 v2.4.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU=
|
||||
github.com/gdamore/tcell/v2 v2.5.1 h1:zc3LPdpK184lBW7syF2a5C6MV827KmErk9jGVnmsl/I=
|
||||
github.com/gdamore/tcell/v2 v2.5.1/go.mod h1:wSkrPaXoiIWZqW/g7Px4xc79di6FTcpB8tvaKJ6uGBo=
|
||||
github.com/gdamore/tcell/v2 v2.5.2 h1:tKzG29kO9p2V++3oBY2W9zUjYu7IK1MENFeY/BzJSVY=
|
||||
github.com/gdamore/tcell/v2 v2.5.2/go.mod h1:wSkrPaXoiIWZqW/g7Px4xc79di6FTcpB8tvaKJ6uGBo=
|
||||
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
|
||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
|
||||
@ -72,8 +72,8 @@ github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68 h1:EQP2Tv8T
|
||||
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68/go.mod h1:+LLj9/WUPAP8LqCchs7P+7X0R98HiFujVFANdNaxhGk=
|
||||
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4 h1:GOQrmaE8i+KEdB8NzAegKYd4tPn/inM0I1uo0NXFerg=
|
||||
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4/go.mod h1:nGNEErzf+NRznT+N2SWqmHnDnF9aLgANB1CUNEan09o=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20220723050330-1f853fadb335 h1:36XGBaxzg5umrZO99Ir7Y7zpJPlOzOq8rDZUuTUrCvI=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20220723050330-1f853fadb335/go.mod h1:znJuCDnF2Ph40YZSlBwdX/4GEofnIoWLGdT4mK5zRAU=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20220806032055-dfd3eb22e18a h1:k+lnojvZ6FoSzOIsSVVBlB9v3EZ+L5Qn/GS5PEzyTAA=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20220806032055-dfd3eb22e18a/go.mod h1:znJuCDnF2Ph40YZSlBwdX/4GEofnIoWLGdT4mK5zRAU=
|
||||
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10 h1:jmpr7KpX2+2GRiE91zTgfq49QvgiqB0nbmlwZ8UnOx0=
|
||||
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10/go.mod h1:aA97kHeNA+sj2Hbki0pvLslmE4CbDyhBeSSTUUnOuVo=
|
||||
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e h1:uw/oo+kg7t/oeMs6sqlAwr85ND/9cpO3up3VxphxY0U=
|
||||
@ -131,8 +131,9 @@ github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.3.4 h1:3Z3Eu6FGHZWSfNKJTOUiPatWwfc7DzJRU04jFUqJODw=
|
||||
github.com/rivo/uniseg v0.3.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI=
|
||||
github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
|
||||
github.com/samber/lo v1.10.1 h1:0D3h7i0U3hRAbaCeQ82DLe67n0A7Bbl0/cEoWqFGp+U=
|
||||
@ -189,11 +190,11 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220318055525-2edf467146b5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220804214406-8e32c043e418 h1:9vYwv7OjYaky/tlAeD7C4oC9EsPTlaFl1H2jS++V+ME=
|
||||
golang.org/x/sys v0.0.0-20220804214406-8e32c043e418/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 h1:EH1Deb8WZJ0xc0WK//leUHXcX9aLE5SymusoTmMZye8=
|
||||
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc=
|
||||
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
|
@ -21,7 +21,6 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||
"github.com/jesseduffield/lazygit/pkg/integration"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
@ -105,10 +104,9 @@ func localisedTitle(tr *i18n.TranslationSet, str string) string {
|
||||
"commits": tr.CommitsTitle,
|
||||
"confirmation": tr.ConfirmationTitle,
|
||||
"information": tr.InformationTitle,
|
||||
"main": tr.MainTitle,
|
||||
"main": tr.NormalTitle,
|
||||
"patchBuilding": tr.PatchBuildingTitle,
|
||||
"merging": tr.MergingTitle,
|
||||
"normal": tr.NormalTitle,
|
||||
"staging": tr.StagingTitle,
|
||||
"menu": tr.MenuTitle,
|
||||
"search": tr.SearchTitle,
|
||||
@ -127,12 +125,17 @@ func localisedTitle(tr *i18n.TranslationSet, str string) string {
|
||||
}
|
||||
|
||||
func getBindingSections(bindings []*types.Binding, tr *i18n.TranslationSet) []*bindingSection {
|
||||
excludedViews := []string{"stagingSecondary", "patchBuildingSecondary"}
|
||||
bindingsToDisplay := slices.Filter(bindings, func(binding *types.Binding) bool {
|
||||
return binding.Description != "" || binding.Alternative != ""
|
||||
if lo.Contains(excludedViews, binding.ViewName) {
|
||||
return false
|
||||
}
|
||||
|
||||
return (binding.Description != "" || binding.Alternative != "")
|
||||
})
|
||||
|
||||
bindingsByHeader := utils.MuiltiGroupBy(bindingsToDisplay, func(binding *types.Binding) []header {
|
||||
return getHeaders(binding, tr)
|
||||
bindingsByHeader := lo.GroupBy(bindingsToDisplay, func(binding *types.Binding) header {
|
||||
return getHeader(binding, tr)
|
||||
})
|
||||
|
||||
bindingGroups := maps.MapToSlice(
|
||||
@ -164,24 +167,16 @@ func getBindingSections(bindings []*types.Binding, tr *i18n.TranslationSet) []*b
|
||||
})
|
||||
}
|
||||
|
||||
// a binding may belong to multiple headers if it is applicable to multiple contexts,
|
||||
// for example the copy-to-clipboard binding.
|
||||
func getHeaders(binding *types.Binding, tr *i18n.TranslationSet) []header {
|
||||
func getHeader(binding *types.Binding, tr *i18n.TranslationSet) header {
|
||||
if binding.Tag == "navigation" {
|
||||
return []header{{priority: 2, title: localisedTitle(tr, "navigation")}}
|
||||
return header{priority: 2, title: localisedTitle(tr, "navigation")}
|
||||
}
|
||||
|
||||
if binding.ViewName == "" {
|
||||
return []header{{priority: 3, title: localisedTitle(tr, "global")}}
|
||||
return header{priority: 3, title: localisedTitle(tr, "global")}
|
||||
}
|
||||
|
||||
if len(binding.Contexts) == 0 {
|
||||
return []header{}
|
||||
}
|
||||
|
||||
return slices.Map(binding.Contexts, func(context string) header {
|
||||
return header{priority: 1, title: localisedTitle(tr, context)}
|
||||
})
|
||||
return header{priority: 1, title: localisedTitle(tr, binding.ViewName)}
|
||||
}
|
||||
|
||||
func formatSections(tr *i18n.TranslationSet, bindingSections []*bindingSection) string {
|
||||
|
@ -26,7 +26,6 @@ func TestGetBindingSections(t *testing.T) {
|
||||
bindings: []*types.Binding{
|
||||
{
|
||||
ViewName: "files",
|
||||
Contexts: []string{"files"},
|
||||
Description: "stage file",
|
||||
},
|
||||
},
|
||||
@ -36,7 +35,6 @@ func TestGetBindingSections(t *testing.T) {
|
||||
bindings: []*types.Binding{
|
||||
{
|
||||
ViewName: "files",
|
||||
Contexts: []string{"files"},
|
||||
Description: "stage file",
|
||||
},
|
||||
},
|
||||
@ -69,17 +67,14 @@ func TestGetBindingSections(t *testing.T) {
|
||||
{
|
||||
ViewName: "files",
|
||||
Description: "stage file",
|
||||
Contexts: []string{"files"},
|
||||
},
|
||||
{
|
||||
ViewName: "files",
|
||||
Description: "unstage file",
|
||||
Contexts: []string{"files"},
|
||||
},
|
||||
{
|
||||
ViewName: "files",
|
||||
ViewName: "submodules",
|
||||
Description: "drop submodule",
|
||||
Contexts: []string{"submodules"},
|
||||
},
|
||||
},
|
||||
expected: []*bindingSection{
|
||||
@ -89,12 +84,10 @@ func TestGetBindingSections(t *testing.T) {
|
||||
{
|
||||
ViewName: "files",
|
||||
Description: "stage file",
|
||||
Contexts: []string{"files"},
|
||||
},
|
||||
{
|
||||
ViewName: "files",
|
||||
Description: "unstage file",
|
||||
Contexts: []string{"files"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -102,9 +95,8 @@ func TestGetBindingSections(t *testing.T) {
|
||||
title: "Submodules",
|
||||
bindings: []*types.Binding{
|
||||
{
|
||||
ViewName: "files",
|
||||
ViewName: "submodules",
|
||||
Description: "drop submodule",
|
||||
Contexts: []string{"submodules"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -115,23 +107,19 @@ func TestGetBindingSections(t *testing.T) {
|
||||
bindings: []*types.Binding{
|
||||
{
|
||||
ViewName: "files",
|
||||
Contexts: []string{"files"},
|
||||
Description: "stage file",
|
||||
},
|
||||
{
|
||||
ViewName: "files",
|
||||
Contexts: []string{"files"},
|
||||
Description: "unstage file",
|
||||
},
|
||||
{
|
||||
ViewName: "files",
|
||||
Contexts: []string{"files"},
|
||||
Description: "scroll",
|
||||
Tag: "navigation",
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{"commits"},
|
||||
Description: "revert commit",
|
||||
},
|
||||
},
|
||||
@ -141,7 +129,6 @@ func TestGetBindingSections(t *testing.T) {
|
||||
bindings: []*types.Binding{
|
||||
{
|
||||
ViewName: "files",
|
||||
Contexts: []string{"files"},
|
||||
Description: "scroll",
|
||||
Tag: "navigation",
|
||||
},
|
||||
@ -152,7 +139,6 @@ func TestGetBindingSections(t *testing.T) {
|
||||
bindings: []*types.Binding{
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{"commits"},
|
||||
Description: "revert commit",
|
||||
},
|
||||
},
|
||||
@ -162,12 +148,10 @@ func TestGetBindingSections(t *testing.T) {
|
||||
bindings: []*types.Binding{
|
||||
{
|
||||
ViewName: "files",
|
||||
Contexts: []string{"files"},
|
||||
Description: "stage file",
|
||||
},
|
||||
{
|
||||
ViewName: "files",
|
||||
Contexts: []string{"files"},
|
||||
Description: "unstage file",
|
||||
},
|
||||
},
|
||||
@ -179,34 +163,28 @@ func TestGetBindingSections(t *testing.T) {
|
||||
bindings: []*types.Binding{
|
||||
{
|
||||
ViewName: "files",
|
||||
Contexts: []string{"files"},
|
||||
Description: "stage file",
|
||||
},
|
||||
{
|
||||
ViewName: "files",
|
||||
Contexts: []string{"files"},
|
||||
Description: "unstage file",
|
||||
},
|
||||
{
|
||||
ViewName: "files",
|
||||
Contexts: []string{"files"},
|
||||
Description: "scroll",
|
||||
Tag: "navigation",
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{"commits"},
|
||||
Description: "revert commit",
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{"commits"},
|
||||
Description: "scroll",
|
||||
Tag: "navigation",
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{"commits"},
|
||||
Description: "page up",
|
||||
Tag: "navigation",
|
||||
},
|
||||
@ -217,13 +195,11 @@ func TestGetBindingSections(t *testing.T) {
|
||||
bindings: []*types.Binding{
|
||||
{
|
||||
ViewName: "files",
|
||||
Contexts: []string{"files"},
|
||||
Description: "scroll",
|
||||
Tag: "navigation",
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{"commits"},
|
||||
Description: "page up",
|
||||
Tag: "navigation",
|
||||
},
|
||||
@ -234,7 +210,6 @@ func TestGetBindingSections(t *testing.T) {
|
||||
bindings: []*types.Binding{
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{"commits"},
|
||||
Description: "revert commit",
|
||||
},
|
||||
},
|
||||
@ -244,12 +219,10 @@ func TestGetBindingSections(t *testing.T) {
|
||||
bindings: []*types.Binding{
|
||||
{
|
||||
ViewName: "files",
|
||||
Contexts: []string{"files"},
|
||||
Description: "stage file",
|
||||
},
|
||||
{
|
||||
ViewName: "files",
|
||||
Contexts: []string{"files"},
|
||||
Description: "unstage file",
|
||||
},
|
||||
},
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
|
||||
// this takes something like:
|
||||
// * (HEAD detached at 264fc6f5)
|
||||
// remotes
|
||||
// remotes
|
||||
// and returns '264fc6f5' as the second match
|
||||
const CurrentBranchNameRegex = `(?m)^\*.*?([^ ]*?)\)?$`
|
||||
|
||||
|
@ -190,7 +190,7 @@ func (p *PatchManager) RenderPatchForFile(filename string, plain bool, reverse b
|
||||
parser := NewPatchParser(p.Log, patch)
|
||||
|
||||
// not passing included lines because we don't want to see them in the secondary panel
|
||||
return parser.Render(-1, -1, nil)
|
||||
return parser.Render(false, -1, -1, nil)
|
||||
}
|
||||
|
||||
func (p *PatchManager) renderEachFilePatch(plain bool) []string {
|
||||
|
@ -183,7 +183,7 @@ func parsePatch(patch string) ([]int, []int, []*PatchLine) {
|
||||
}
|
||||
|
||||
// Render returns the coloured string of the diff with any selected lines highlighted
|
||||
func (p *PatchParser) Render(firstLineIndex int, lastLineIndex int, incLineIndices []int) string {
|
||||
func (p *PatchParser) Render(isFocused bool, firstLineIndex int, lastLineIndex int, incLineIndices []int) string {
|
||||
contentToDisplay := slices.Some(p.PatchLines, func(line *PatchLine) bool {
|
||||
return line.Content != ""
|
||||
})
|
||||
@ -192,7 +192,7 @@ func (p *PatchParser) Render(firstLineIndex int, lastLineIndex int, incLineIndic
|
||||
}
|
||||
|
||||
renderedLines := slices.MapWithIndex(p.PatchLines, func(patchLine *PatchLine, index int) string {
|
||||
selected := index >= firstLineIndex && index <= lastLineIndex
|
||||
selected := isFocused && index >= firstLineIndex && index <= lastLineIndex
|
||||
included := lo.Contains(incLineIndices, index)
|
||||
return patchLine.render(selected, included)
|
||||
})
|
||||
@ -202,12 +202,18 @@ func (p *PatchParser) Render(firstLineIndex int, lastLineIndex int, incLineIndic
|
||||
return result
|
||||
}
|
||||
|
||||
// PlainRenderLines returns the non-coloured string of diff part from firstLineIndex to
|
||||
// lastLineIndex
|
||||
func (p *PatchParser) PlainRenderLines(firstLineIndex, lastLineIndex int) string {
|
||||
linesToCopy := p.PatchLines[firstLineIndex : lastLineIndex+1]
|
||||
func (p *PatchParser) RenderPlain() string {
|
||||
return renderLinesPlain(p.PatchLines)
|
||||
}
|
||||
|
||||
renderedLines := slices.Map(linesToCopy, func(line *PatchLine) string {
|
||||
// RenderLinesPlain returns the non-coloured string of diff part from firstLineIndex to
|
||||
// lastLineIndex
|
||||
func (p *PatchParser) RenderLinesPlain(firstLineIndex, lastLineIndex int) string {
|
||||
return renderLinesPlain(p.PatchLines[firstLineIndex : lastLineIndex+1])
|
||||
}
|
||||
|
||||
func renderLinesPlain(lines []*PatchLine) string {
|
||||
renderedLines := slices.Map(lines, func(line *PatchLine) string {
|
||||
return line.Content
|
||||
})
|
||||
|
||||
|
@ -105,20 +105,13 @@ func (gui *Gui) mainSectionChildren() []*boxlayout.Box {
|
||||
}
|
||||
}
|
||||
|
||||
main := "main"
|
||||
secondary := "secondary"
|
||||
if gui.secondaryViewFocused() {
|
||||
// when you think you've focused the secondary view, we've actually just swapped them around in the layout
|
||||
main, secondary = secondary, main
|
||||
}
|
||||
|
||||
return []*boxlayout.Box{
|
||||
{
|
||||
Window: main,
|
||||
Window: "main",
|
||||
Weight: 1,
|
||||
},
|
||||
{
|
||||
Window: secondary,
|
||||
Window: "secondary",
|
||||
Weight: 1,
|
||||
},
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ func (gui *Gui) branchesRenderToMain() error {
|
||||
}
|
||||
|
||||
return gui.refreshMainViews(refreshMainOpts{
|
||||
pair: gui.normalMainContextPair(),
|
||||
main: &viewUpdateOpts{
|
||||
title: gui.c.Tr.LogTitle,
|
||||
task: task,
|
||||
|
@ -12,10 +12,10 @@ import (
|
||||
)
|
||||
|
||||
// our UI command log looks like this:
|
||||
// Stage File
|
||||
// git add -- 'filename'
|
||||
// Unstage File
|
||||
// git reset HEAD 'filename'
|
||||
// Stage File:
|
||||
// git add -- 'filename'
|
||||
// Unstage File:
|
||||
// git reset HEAD 'filename'
|
||||
//
|
||||
// The 'Stage File' and 'Unstage File' lines are actions i.e they group up a set
|
||||
// of command logs (typically there's only one command under an action but there may be more).
|
||||
|
@ -2,14 +2,9 @@ package gui
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/controllers"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
// TODO: do we need this?
|
||||
func (gui *Gui) onCommitFileFocus() error {
|
||||
gui.escapeLineByLinePanel()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) commitFilesRenderToMain() error {
|
||||
node := gui.State.Contexts.CommitFiles.GetSelected()
|
||||
if node == nil {
|
||||
@ -23,16 +18,16 @@ func (gui *Gui) commitFilesRenderToMain() error {
|
||||
cmdObj := gui.git.WorkingTree.ShowFileDiffCmdObj(from, to, reverse, node.GetPath(), false)
|
||||
task := NewRunPtyTask(cmdObj.GetCmd())
|
||||
|
||||
mainContext := gui.State.Contexts.Normal
|
||||
pair := gui.normalMainContextPair()
|
||||
if node.File != nil {
|
||||
mainContext = gui.State.Contexts.PatchBuilding
|
||||
pair = gui.patchBuildingMainContextPair()
|
||||
}
|
||||
|
||||
return gui.refreshMainViews(refreshMainOpts{
|
||||
pair: pair,
|
||||
main: &viewUpdateOpts{
|
||||
title: "Patch",
|
||||
task: task,
|
||||
context: mainContext,
|
||||
title: gui.Tr.Patch,
|
||||
task: task,
|
||||
},
|
||||
secondary: gui.secondaryPatchPanelUpdateOpts(),
|
||||
})
|
||||
@ -46,33 +41,11 @@ func (gui *Gui) SwitchToCommitFilesContext(opts controllers.SwitchToCommitFilesC
|
||||
gui.State.Contexts.CommitFiles.SetParentContext(opts.Context)
|
||||
gui.State.Contexts.CommitFiles.SetWindowName(opts.Context.GetWindowName())
|
||||
|
||||
if err := gui.refreshCommitFilesContext(); err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
func (gui *Gui) refreshCommitFilesContext() error {
|
||||
ref := gui.State.Contexts.CommitFiles.GetRef()
|
||||
to := ref.RefName()
|
||||
from, reverse := gui.State.Modes.Diffing.GetFromAndReverseArgsForDiff(ref.ParentRefName())
|
||||
|
||||
files, err := gui.git.Loaders.CommitFiles.GetFilesInDiff(from, to, reverse)
|
||||
if err != nil {
|
||||
return gui.c.Error(err)
|
||||
}
|
||||
gui.State.Model.CommitFiles = files
|
||||
gui.State.Contexts.CommitFiles.CommitFileTreeViewModel.SetTree()
|
||||
|
||||
return gui.c.PostRefreshUpdate(gui.State.Contexts.CommitFiles)
|
||||
}
|
||||
|
||||
func (gui *Gui) getSelectedCommitFileName() string {
|
||||
node := gui.State.Contexts.CommitFiles.GetSelected()
|
||||
if node == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return node.Path
|
||||
}
|
||||
|
@ -25,8 +25,6 @@ func (gui *Gui) onCommitFocus() error {
|
||||
})
|
||||
}
|
||||
|
||||
gui.escapeLineByLinePanel()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -41,6 +39,7 @@ func (gui *Gui) branchCommitsRenderToMain() error {
|
||||
}
|
||||
|
||||
return gui.refreshMainViews(refreshMainOpts{
|
||||
pair: gui.normalMainContextPair(),
|
||||
main: &viewUpdateOpts{
|
||||
title: "Patch",
|
||||
task: task,
|
||||
@ -49,6 +48,19 @@ func (gui *Gui) branchCommitsRenderToMain() error {
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) secondaryPatchPanelUpdateOpts() *viewUpdateOpts {
|
||||
if gui.git.Patch.PatchManager.Active() {
|
||||
patch := gui.git.Patch.PatchManager.RenderAggregatedPatchColored(false)
|
||||
|
||||
return &viewUpdateOpts{
|
||||
task: NewRenderStringWithoutScrollTask(patch),
|
||||
title: gui.Tr.CustomPatch,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) refForLog() string {
|
||||
bisectInfo := gui.git.Bisect.GetInfo()
|
||||
gui.State.Model.BisectInfo = bisectInfo
|
||||
|
@ -5,7 +5,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||
@ -221,25 +220,21 @@ func (gui *Gui) setKeyBindings(opts types.CreatePopupPanelOpts) error {
|
||||
bindings := []*types.Binding{
|
||||
{
|
||||
ViewName: "confirmation",
|
||||
Contexts: []string{string(context.CONFIRMATION_CONTEXT_KEY)},
|
||||
Key: gui.getKey(keybindingConfig.Universal.Confirm),
|
||||
Handler: onConfirm,
|
||||
},
|
||||
{
|
||||
ViewName: "confirmation",
|
||||
Contexts: []string{string(context.CONFIRMATION_CONTEXT_KEY)},
|
||||
Key: gui.getKey(keybindingConfig.Universal.ConfirmAlt1),
|
||||
Handler: onConfirm,
|
||||
},
|
||||
{
|
||||
ViewName: "confirmation",
|
||||
Contexts: []string{string(context.CONFIRMATION_CONTEXT_KEY)},
|
||||
Key: gui.getKey(keybindingConfig.Universal.Return),
|
||||
Handler: gui.wrappedConfirmationFunction(opts.HandleClose),
|
||||
},
|
||||
{
|
||||
ViewName: "confirmation",
|
||||
Contexts: []string{string(context.CONFIRMATION_CONTEXT_KEY)},
|
||||
Key: gui.getKey(keybindingConfig.Universal.TogglePanel),
|
||||
Handler: func() error {
|
||||
if len(gui.State.Suggestions) > 0 {
|
||||
@ -250,25 +245,21 @@ func (gui *Gui) setKeyBindings(opts types.CreatePopupPanelOpts) error {
|
||||
},
|
||||
{
|
||||
ViewName: "suggestions",
|
||||
Contexts: []string{string(context.SUGGESTIONS_CONTEXT_KEY)},
|
||||
Key: gui.getKey(keybindingConfig.Universal.Confirm),
|
||||
Handler: onSuggestionConfirm,
|
||||
},
|
||||
{
|
||||
ViewName: "suggestions",
|
||||
Contexts: []string{string(context.SUGGESTIONS_CONTEXT_KEY)},
|
||||
Key: gui.getKey(keybindingConfig.Universal.ConfirmAlt1),
|
||||
Handler: onSuggestionConfirm,
|
||||
},
|
||||
{
|
||||
ViewName: "suggestions",
|
||||
Contexts: []string{string(context.SUGGESTIONS_CONTEXT_KEY)},
|
||||
Key: gui.getKey(keybindingConfig.Universal.Return),
|
||||
Handler: gui.wrappedConfirmationFunction(opts.HandleClose),
|
||||
},
|
||||
{
|
||||
ViewName: "suggestions",
|
||||
Contexts: []string{string(context.SUGGESTIONS_CONTEXT_KEY)},
|
||||
Key: gui.getKey(keybindingConfig.Universal.TogglePanel),
|
||||
Handler: func() error { return gui.replaceContext(gui.State.Contexts.Confirmation) },
|
||||
},
|
||||
|
@ -1,8 +1,6 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
@ -28,24 +26,6 @@ func (gui *Gui) popupViewNames() []string {
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) currentContextKeyIgnoringPopups() types.ContextKey {
|
||||
gui.State.ContextManager.RLock()
|
||||
defer gui.State.ContextManager.RUnlock()
|
||||
|
||||
stack := gui.State.ContextManager.ContextStack
|
||||
|
||||
for i := range stack {
|
||||
reversedIndex := len(stack) - 1 - i
|
||||
context := stack[reversedIndex]
|
||||
kind := stack[reversedIndex].GetKind()
|
||||
if kind != types.TEMPORARY_POPUP && kind != types.PERSISTENT_POPUP {
|
||||
return context.GetKey()
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// use replaceContext when you don't want to return to the original context upon
|
||||
// hitting escape: you want to go that context's parent instead.
|
||||
func (gui *Gui) replaceContext(c types.Context) error {
|
||||
@ -64,31 +44,43 @@ func (gui *Gui) replaceContext(c types.Context) error {
|
||||
|
||||
defer gui.State.ContextManager.Unlock()
|
||||
|
||||
return gui.activateContext(c)
|
||||
return gui.activateContext(c, types.OnFocusOpts{})
|
||||
}
|
||||
|
||||
func (gui *Gui) pushContext(c types.Context, opts ...types.OnFocusOpts) error {
|
||||
// using triple dot but you should only ever pass one of these opt structs
|
||||
if len(opts) > 1 {
|
||||
return errors.New("cannot pass multiple opts to pushContext")
|
||||
}
|
||||
|
||||
func (gui *Gui) pushContext(c types.Context, opts types.OnFocusOpts) error {
|
||||
if !c.IsFocusable() {
|
||||
return nil
|
||||
}
|
||||
|
||||
contextsToDeactivate := gui.pushToContextStack(c)
|
||||
|
||||
for _, contextToDeactivate := range contextsToDeactivate {
|
||||
if err := gui.deactivateContext(contextToDeactivate, types.OnFocusLostOpts{NewContextKey: c.GetKey()}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return gui.activateContext(c, opts)
|
||||
}
|
||||
|
||||
// Adjusts the context stack based on the context that's being pushed and returns contexts to deactivate
|
||||
func (gui *Gui) pushToContextStack(c types.Context) []types.Context {
|
||||
contextsToDeactivate := []types.Context{}
|
||||
|
||||
gui.State.ContextManager.Lock()
|
||||
defer gui.State.ContextManager.Unlock()
|
||||
|
||||
if len(gui.State.ContextManager.ContextStack) == 0 {
|
||||
gui.State.ContextManager.ContextStack = append(gui.State.ContextManager.ContextStack, c)
|
||||
} else if c.GetKind() == types.SIDE_CONTEXT {
|
||||
// if we are switching to a side context, remove all other contexts in the stack
|
||||
contextsToDeactivate = gui.State.ContextManager.ContextStack
|
||||
gui.State.ContextManager.ContextStack = []types.Context{c}
|
||||
} else if c.GetKind() == types.MAIN_CONTEXT {
|
||||
// if we're switching to a main context, remove all other main contexts in the stack
|
||||
for _, stackContext := range gui.State.ContextManager.ContextStack {
|
||||
if stackContext.GetKey() != c.GetKey() {
|
||||
if err := gui.deactivateContext(stackContext); err != nil {
|
||||
gui.State.ContextManager.Unlock()
|
||||
return err
|
||||
}
|
||||
if stackContext.GetKind() == types.MAIN_CONTEXT {
|
||||
contextsToDeactivate = append(contextsToDeactivate, stackContext)
|
||||
}
|
||||
}
|
||||
gui.State.ContextManager.ContextStack = []types.Context{c}
|
||||
@ -101,12 +93,11 @@ func (gui *Gui) pushContext(c types.Context, opts ...types.OnFocusOpts) error {
|
||||
// escape back to previous temporary popups, but because we're currently reusing
|
||||
// views for this, you might not be able to get back to where you previously were.
|
||||
// The exception is when going to the search context e.g. for searching a menu.
|
||||
if topContext.GetKind() == types.TEMPORARY_POPUP && c.GetKey() != context.SEARCH_CONTEXT_KEY {
|
||||
if err := gui.deactivateContext(topContext); err != nil {
|
||||
gui.State.ContextManager.Unlock()
|
||||
return err
|
||||
}
|
||||
if (topContext.GetKind() == types.TEMPORARY_POPUP && c.GetKey() != context.SEARCH_CONTEXT_KEY) ||
|
||||
// we only ever want one main context on the stack at a time.
|
||||
(topContext.GetKind() == types.MAIN_CONTEXT && c.GetKind() == types.MAIN_CONTEXT) {
|
||||
|
||||
contextsToDeactivate = append(contextsToDeactivate, topContext)
|
||||
_, gui.State.ContextManager.ContextStack = slices.Pop(gui.State.ContextManager.ContextStack)
|
||||
}
|
||||
|
||||
@ -114,16 +105,7 @@ func (gui *Gui) pushContext(c types.Context, opts ...types.OnFocusOpts) error {
|
||||
}
|
||||
}
|
||||
|
||||
gui.State.ContextManager.Unlock()
|
||||
|
||||
return gui.activateContext(c, opts...)
|
||||
}
|
||||
|
||||
// pushContextWithView is to be used when you don't know which context you
|
||||
// want to switch to: you only know the view that you want to switch to. It will
|
||||
// look up the context currently active for that view and switch to that context
|
||||
func (gui *Gui) pushContextWithView(viewName string) error {
|
||||
return gui.c.PushContext(gui.State.ViewContextMap.Get(viewName))
|
||||
return contextsToDeactivate
|
||||
}
|
||||
|
||||
func (gui *Gui) popContext() error {
|
||||
@ -140,18 +122,16 @@ func (gui *Gui) popContext() error {
|
||||
|
||||
newContext := gui.State.ContextManager.ContextStack[len(gui.State.ContextManager.ContextStack)-1]
|
||||
|
||||
gui.g.SetCurrentContext(string(newContext.GetKey()))
|
||||
|
||||
gui.State.ContextManager.Unlock()
|
||||
|
||||
if err := gui.deactivateContext(currentContext); err != nil {
|
||||
if err := gui.deactivateContext(currentContext, types.OnFocusLostOpts{NewContextKey: newContext.GetKey()}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return gui.activateContext(newContext)
|
||||
return gui.activateContext(newContext, types.OnFocusOpts{})
|
||||
}
|
||||
|
||||
func (gui *Gui) deactivateContext(c types.Context) error {
|
||||
func (gui *Gui) deactivateContext(c types.Context, opts types.OnFocusLostOpts) error {
|
||||
view, _ := gui.g.View(c.GetViewName())
|
||||
|
||||
if view != nil && view.IsSearching() {
|
||||
@ -167,7 +147,7 @@ func (gui *Gui) deactivateContext(c types.Context) error {
|
||||
view.Visible = false
|
||||
}
|
||||
|
||||
if err := c.HandleFocusLost(); err != nil {
|
||||
if err := c.HandleFocusLost(opts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -178,16 +158,12 @@ func (gui *Gui) deactivateContext(c types.Context) 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 gui.State.ViewContextMap.Get(c.GetViewName()).GetKey() != c.GetKey() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := c.HandleRender(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if gui.currentViewName() == c.GetViewName() {
|
||||
if err := c.HandleFocus(); err != nil {
|
||||
if err := c.HandleFocus(types.OnFocusOpts{}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -195,29 +171,16 @@ func (gui *Gui) postRefreshUpdate(c types.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) activateContext(c types.Context, opts ...types.OnFocusOpts) error {
|
||||
func (gui *Gui) activateContext(c types.Context, opts types.OnFocusOpts) error {
|
||||
viewName := c.GetViewName()
|
||||
v, err := gui.g.View(viewName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
originalViewContext := gui.State.ViewContextMap.Get(viewName)
|
||||
var originalViewContextKey types.ContextKey = ""
|
||||
if originalViewContext != nil {
|
||||
originalViewContextKey = originalViewContext.GetKey()
|
||||
}
|
||||
|
||||
gui.setWindowContext(c)
|
||||
gui.setViewTabForContext(c)
|
||||
|
||||
if viewName == "main" {
|
||||
gui.changeMainViewsContext(c)
|
||||
} else {
|
||||
gui.changeMainViewsContext(gui.State.Contexts.Normal)
|
||||
}
|
||||
|
||||
gui.g.SetCurrentContext(string(c.GetKey()))
|
||||
gui.moveToTopOfWindow(c)
|
||||
if _, err := gui.g.SetCurrentView(viewName); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -229,15 +192,6 @@ func (gui *Gui) activateContext(c types.Context, opts ...types.OnFocusOpts) erro
|
||||
|
||||
v.Visible = true
|
||||
|
||||
// if the new context's view was previously displaying another context, render the new context
|
||||
if originalViewContextKey != c.GetKey() {
|
||||
if err := c.HandleRender(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
gui.ViewContextMapSet(viewName, c)
|
||||
|
||||
gui.g.Cursor = v.Editable
|
||||
|
||||
// render the options available for the current context at the bottom of the screen
|
||||
@ -247,7 +201,7 @@ func (gui *Gui) activateContext(c types.Context, opts ...types.OnFocusOpts) erro
|
||||
}
|
||||
gui.renderOptionsMap(optionsMap)
|
||||
|
||||
if err := c.HandleFocus(opts...); err != nil {
|
||||
if err := c.HandleFocus(opts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -266,16 +220,6 @@ func (gui *Gui) renderOptionsMap(optionsMap map[string]string) {
|
||||
_ = gui.renderString(gui.Views.Options, gui.optionsMapToString(optionsMap))
|
||||
}
|
||||
|
||||
// also setting context on view for now. We'll need to pick one of these two approaches to stick with.
|
||||
func (gui *Gui) ViewContextMapSet(viewName string, c types.Context) {
|
||||
gui.State.ViewContextMap.Set(viewName, c)
|
||||
view, err := gui.g.View(viewName)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
view.Context = string(c.GetKey())
|
||||
}
|
||||
|
||||
// // currently unused
|
||||
// func (gui *Gui) renderContextStack() string {
|
||||
// result := ""
|
||||
@ -339,6 +283,10 @@ func (gui *Gui) currentStaticContext() types.Context {
|
||||
gui.State.ContextManager.RLock()
|
||||
defer gui.State.ContextManager.RUnlock()
|
||||
|
||||
return gui.currentStaticContextWithoutLock()
|
||||
}
|
||||
|
||||
func (gui *Gui) currentStaticContextWithoutLock() types.Context {
|
||||
stack := gui.State.ContextManager.ContextStack
|
||||
|
||||
if len(stack) == 0 {
|
||||
@ -400,57 +348,14 @@ func (gui *Gui) TransientContexts() []types.Context {
|
||||
})
|
||||
}
|
||||
|
||||
// changeContext is a helper function for when we want to change a 'main' context
|
||||
// which currently just means a context that affects both the main and secondary views
|
||||
// other views can have their context changed directly but this function helps
|
||||
// keep the main and secondary views in sync
|
||||
func (gui *Gui) changeMainViewsContext(c types.Context) {
|
||||
if gui.State.MainContext == c.GetKey() {
|
||||
return
|
||||
}
|
||||
|
||||
switch c.GetKey() {
|
||||
case context.MAIN_NORMAL_CONTEXT_KEY, context.MAIN_PATCH_BUILDING_CONTEXT_KEY, context.MAIN_STAGING_CONTEXT_KEY, context.MAIN_MERGING_CONTEXT_KEY:
|
||||
gui.ViewContextMapSet(gui.Views.Main.Name(), c)
|
||||
gui.ViewContextMapSet(gui.Views.Secondary.Name(), c)
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown context for main: %s", c.GetKey()))
|
||||
}
|
||||
|
||||
gui.State.MainContext = c.GetKey()
|
||||
}
|
||||
|
||||
func (gui *Gui) viewTabNames(viewName string) []string {
|
||||
tabContexts := gui.State.ViewTabContextMap[viewName]
|
||||
|
||||
return slices.Map(tabContexts, func(tabContext context.TabContext) string {
|
||||
return tabContext.Tab
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) setViewTabForContext(c types.Context) {
|
||||
// search for the context in our map and if we find it, set the tab for the corresponding view
|
||||
tabContexts, ok := gui.State.ViewTabContextMap[c.GetViewName()]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
for tabIndex, tabContext := range tabContexts {
|
||||
if tabContext.Context.GetKey() == c.GetKey() {
|
||||
// get the view, set the tab
|
||||
v, err := gui.g.View(c.GetViewName())
|
||||
if err != nil {
|
||||
gui.c.Log.Error(err)
|
||||
return
|
||||
}
|
||||
v.TabIndex = tabIndex
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) rerenderView(view *gocui.View) error {
|
||||
return gui.State.ViewContextMap.Get(view.Name()).HandleRender()
|
||||
context, ok := gui.contextForView(view.Name())
|
||||
if !ok {
|
||||
gui.Log.Errorf("no context found for view %s", view.Name())
|
||||
return nil
|
||||
}
|
||||
|
||||
return context.HandleRender()
|
||||
}
|
||||
|
||||
func (gui *Gui) getSideContextSelectedItemId() string {
|
||||
@ -462,11 +367,6 @@ func (gui *Gui) getSideContextSelectedItemId() string {
|
||||
return currentSideContext.GetSelectedItemId()
|
||||
}
|
||||
|
||||
func (gui *Gui) isContextVisible(c types.Context) bool {
|
||||
return gui.State.WindowViewNameMap[c.GetWindowName()] == c.GetViewName() &&
|
||||
gui.State.ViewContextMap.Get(c.GetViewName()).GetKey() == c.GetKey()
|
||||
}
|
||||
|
||||
// currently unused
|
||||
// func (gui *Gui) getCurrentSideView() *gocui.View {
|
||||
// currentSideContext := gui.currentSideContext()
|
||||
|
@ -8,7 +8,8 @@ import (
|
||||
type BaseContext struct {
|
||||
kind types.ContextKind
|
||||
key types.ContextKey
|
||||
ViewName string
|
||||
view *gocui.View
|
||||
viewTrait types.IViewTrait
|
||||
windowName string
|
||||
onGetOptionsMap func() map[string]string
|
||||
|
||||
@ -16,8 +17,9 @@ type BaseContext struct {
|
||||
mouseKeybindingsFns []types.MouseKeybindingsFn
|
||||
onClickFn func() error
|
||||
|
||||
focusable bool
|
||||
transient bool
|
||||
focusable bool
|
||||
transient bool
|
||||
hasControlledBounds bool
|
||||
|
||||
*ParentContextMgr
|
||||
}
|
||||
@ -25,26 +27,33 @@ type BaseContext struct {
|
||||
var _ types.IBaseContext = &BaseContext{}
|
||||
|
||||
type NewBaseContextOpts struct {
|
||||
Kind types.ContextKind
|
||||
Key types.ContextKey
|
||||
ViewName string
|
||||
WindowName string
|
||||
Focusable bool
|
||||
Transient bool
|
||||
Kind types.ContextKind
|
||||
Key types.ContextKey
|
||||
View *gocui.View
|
||||
WindowName string
|
||||
Focusable bool
|
||||
Transient bool
|
||||
HasUncontrolledBounds bool // negating for the sake of making false the default
|
||||
|
||||
OnGetOptionsMap func() map[string]string
|
||||
}
|
||||
|
||||
func NewBaseContext(opts NewBaseContextOpts) *BaseContext {
|
||||
viewTrait := NewViewTrait(opts.View)
|
||||
|
||||
hasControlledBounds := !opts.HasUncontrolledBounds
|
||||
|
||||
return &BaseContext{
|
||||
kind: opts.Kind,
|
||||
key: opts.Key,
|
||||
ViewName: opts.ViewName,
|
||||
windowName: opts.WindowName,
|
||||
onGetOptionsMap: opts.OnGetOptionsMap,
|
||||
focusable: opts.Focusable,
|
||||
transient: opts.Transient,
|
||||
ParentContextMgr: &ParentContextMgr{},
|
||||
kind: opts.Kind,
|
||||
key: opts.Key,
|
||||
view: opts.View,
|
||||
windowName: opts.WindowName,
|
||||
onGetOptionsMap: opts.OnGetOptionsMap,
|
||||
focusable: opts.Focusable,
|
||||
transient: opts.Transient,
|
||||
hasControlledBounds: hasControlledBounds,
|
||||
ParentContextMgr: &ParentContextMgr{},
|
||||
viewTrait: viewTrait,
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,7 +73,20 @@ func (self *BaseContext) GetWindowName() string {
|
||||
}
|
||||
|
||||
func (self *BaseContext) GetViewName() string {
|
||||
return self.ViewName
|
||||
// for the sake of the global context which has no view
|
||||
if self.view == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return self.view.Name()
|
||||
}
|
||||
|
||||
func (self *BaseContext) GetView() *gocui.View {
|
||||
return self.view
|
||||
}
|
||||
|
||||
func (self *BaseContext) GetViewTrait() types.IViewTrait {
|
||||
return self.viewTrait
|
||||
}
|
||||
|
||||
func (self *BaseContext) GetKind() types.ContextKind {
|
||||
@ -123,6 +145,10 @@ func (self *BaseContext) IsTransient() bool {
|
||||
return self.transient
|
||||
}
|
||||
|
||||
func (self *BaseContext) HasControlledBounds() bool {
|
||||
return self.hasControlledBounds
|
||||
}
|
||||
|
||||
func (self *BaseContext) Title() string {
|
||||
return ""
|
||||
}
|
||||
|
@ -18,9 +18,9 @@ func NewBranchesContext(
|
||||
view *gocui.View,
|
||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
||||
|
||||
onFocus func(...types.OnFocusOpts) error,
|
||||
onRenderToMain func(...types.OnFocusOpts) error,
|
||||
onFocusLost func() error,
|
||||
onFocus func(types.OnFocusOpts) error,
|
||||
onRenderToMain func() error,
|
||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
||||
|
||||
c *types.HelperCommon,
|
||||
) *BranchesContext {
|
||||
@ -30,7 +30,7 @@ func NewBranchesContext(
|
||||
BasicViewModel: viewModel,
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
ViewName: "branches",
|
||||
View: view,
|
||||
WindowName: "branches",
|
||||
Key: LOCAL_BRANCHES_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
@ -41,7 +41,6 @@ func NewBranchesContext(
|
||||
OnRenderToMain: onRenderToMain,
|
||||
}),
|
||||
list: viewModel,
|
||||
viewTrait: NewViewTrait(view),
|
||||
getDisplayStrings: getDisplayStrings,
|
||||
c: c,
|
||||
},
|
||||
|
@ -20,9 +20,9 @@ func NewCommitFilesContext(
|
||||
view *gocui.View,
|
||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
||||
|
||||
onFocus func(...types.OnFocusOpts) error,
|
||||
onRenderToMain func(...types.OnFocusOpts) error,
|
||||
onFocusLost func() error,
|
||||
onFocus func(types.OnFocusOpts) error,
|
||||
onRenderToMain func() error,
|
||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
||||
|
||||
c *types.HelperCommon,
|
||||
) *CommitFilesContext {
|
||||
@ -34,7 +34,7 @@ func NewCommitFilesContext(
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(
|
||||
NewBaseContext(NewBaseContextOpts{
|
||||
ViewName: "commitFiles",
|
||||
View: view,
|
||||
WindowName: "commits",
|
||||
Key: COMMIT_FILES_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
@ -47,7 +47,6 @@ func NewCommitFilesContext(
|
||||
OnRenderToMain: onRenderToMain,
|
||||
}),
|
||||
list: viewModel,
|
||||
viewTrait: NewViewTrait(view),
|
||||
getDisplayStrings: getDisplayStrings,
|
||||
c: c,
|
||||
},
|
||||
|
@ -1,39 +1,48 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
const (
|
||||
GLOBAL_CONTEXT_KEY types.ContextKey = "global"
|
||||
STATUS_CONTEXT_KEY types.ContextKey = "status"
|
||||
FILES_CONTEXT_KEY types.ContextKey = "files"
|
||||
LOCAL_BRANCHES_CONTEXT_KEY types.ContextKey = "localBranches"
|
||||
REMOTES_CONTEXT_KEY types.ContextKey = "remotes"
|
||||
REMOTE_BRANCHES_CONTEXT_KEY types.ContextKey = "remoteBranches"
|
||||
TAGS_CONTEXT_KEY types.ContextKey = "tags"
|
||||
LOCAL_COMMITS_CONTEXT_KEY types.ContextKey = "commits"
|
||||
REFLOG_COMMITS_CONTEXT_KEY types.ContextKey = "reflogCommits"
|
||||
SUB_COMMITS_CONTEXT_KEY types.ContextKey = "subCommits"
|
||||
COMMIT_FILES_CONTEXT_KEY types.ContextKey = "commitFiles"
|
||||
STASH_CONTEXT_KEY types.ContextKey = "stash"
|
||||
MAIN_NORMAL_CONTEXT_KEY types.ContextKey = "normal"
|
||||
MAIN_MERGING_CONTEXT_KEY types.ContextKey = "merging"
|
||||
MAIN_PATCH_BUILDING_CONTEXT_KEY types.ContextKey = "patchBuilding"
|
||||
MAIN_STAGING_CONTEXT_KEY types.ContextKey = "staging"
|
||||
MENU_CONTEXT_KEY types.ContextKey = "menu"
|
||||
CONFIRMATION_CONTEXT_KEY types.ContextKey = "confirmation"
|
||||
SEARCH_CONTEXT_KEY types.ContextKey = "search"
|
||||
COMMIT_MESSAGE_CONTEXT_KEY types.ContextKey = "commitMessage"
|
||||
SUBMODULES_CONTEXT_KEY types.ContextKey = "submodules"
|
||||
SUGGESTIONS_CONTEXT_KEY types.ContextKey = "suggestions"
|
||||
COMMAND_LOG_CONTEXT_KEY types.ContextKey = "cmdLog"
|
||||
GLOBAL_CONTEXT_KEY types.ContextKey = "global"
|
||||
STATUS_CONTEXT_KEY types.ContextKey = "status"
|
||||
FILES_CONTEXT_KEY types.ContextKey = "files"
|
||||
LOCAL_BRANCHES_CONTEXT_KEY types.ContextKey = "localBranches"
|
||||
REMOTES_CONTEXT_KEY types.ContextKey = "remotes"
|
||||
REMOTE_BRANCHES_CONTEXT_KEY types.ContextKey = "remoteBranches"
|
||||
TAGS_CONTEXT_KEY types.ContextKey = "tags"
|
||||
LOCAL_COMMITS_CONTEXT_KEY types.ContextKey = "commits"
|
||||
REFLOG_COMMITS_CONTEXT_KEY types.ContextKey = "reflogCommits"
|
||||
SUB_COMMITS_CONTEXT_KEY types.ContextKey = "subCommits"
|
||||
COMMIT_FILES_CONTEXT_KEY types.ContextKey = "commitFiles"
|
||||
STASH_CONTEXT_KEY types.ContextKey = "stash"
|
||||
NORMAL_MAIN_CONTEXT_KEY types.ContextKey = "normal"
|
||||
NORMAL_SECONDARY_CONTEXT_KEY types.ContextKey = "normalSecondary"
|
||||
STAGING_MAIN_CONTEXT_KEY types.ContextKey = "staging"
|
||||
STAGING_SECONDARY_CONTEXT_KEY types.ContextKey = "stagingSecondary"
|
||||
PATCH_BUILDING_MAIN_CONTEXT_KEY types.ContextKey = "patchBuilding"
|
||||
PATCH_BUILDING_SECONDARY_CONTEXT_KEY types.ContextKey = "patchBuildingSecondary"
|
||||
MERGING_MAIN_CONTEXT_KEY types.ContextKey = "merging"
|
||||
|
||||
// these shouldn't really be needed for anything but I'm giving them unique keys nonetheless
|
||||
OPTIONS_CONTEXT_KEY types.ContextKey = "options"
|
||||
APP_STATUS_CONTEXT_KEY types.ContextKey = "appStatus"
|
||||
SEARCH_PREFIX_CONTEXT_KEY types.ContextKey = "searchPrefix"
|
||||
INFORMATION_CONTEXT_KEY types.ContextKey = "information"
|
||||
LIMIT_CONTEXT_KEY types.ContextKey = "limit"
|
||||
|
||||
MENU_CONTEXT_KEY types.ContextKey = "menu"
|
||||
CONFIRMATION_CONTEXT_KEY types.ContextKey = "confirmation"
|
||||
SEARCH_CONTEXT_KEY types.ContextKey = "search"
|
||||
COMMIT_MESSAGE_CONTEXT_KEY types.ContextKey = "commitMessage"
|
||||
SUBMODULES_CONTEXT_KEY types.ContextKey = "submodules"
|
||||
SUGGESTIONS_CONTEXT_KEY types.ContextKey = "suggestions"
|
||||
COMMAND_LOG_CONTEXT_KEY types.ContextKey = "cmdLog"
|
||||
)
|
||||
|
||||
var AllContextKeys = []types.ContextKey{
|
||||
GLOBAL_CONTEXT_KEY, // not focusable
|
||||
GLOBAL_CONTEXT_KEY,
|
||||
STATUS_CONTEXT_KEY,
|
||||
FILES_CONTEXT_KEY,
|
||||
LOCAL_BRANCHES_CONTEXT_KEY,
|
||||
@ -45,10 +54,14 @@ var AllContextKeys = []types.ContextKey{
|
||||
SUB_COMMITS_CONTEXT_KEY,
|
||||
COMMIT_FILES_CONTEXT_KEY,
|
||||
STASH_CONTEXT_KEY,
|
||||
MAIN_NORMAL_CONTEXT_KEY, // not focusable
|
||||
MAIN_MERGING_CONTEXT_KEY,
|
||||
MAIN_PATCH_BUILDING_CONTEXT_KEY,
|
||||
MAIN_STAGING_CONTEXT_KEY, // not focusable for secondary view
|
||||
NORMAL_MAIN_CONTEXT_KEY,
|
||||
NORMAL_SECONDARY_CONTEXT_KEY,
|
||||
STAGING_MAIN_CONTEXT_KEY,
|
||||
STAGING_SECONDARY_CONTEXT_KEY,
|
||||
PATCH_BUILDING_MAIN_CONTEXT_KEY,
|
||||
PATCH_BUILDING_SECONDARY_CONTEXT_KEY,
|
||||
MERGING_MAIN_CONTEXT_KEY,
|
||||
|
||||
MENU_CONTEXT_KEY,
|
||||
CONFIRMATION_CONTEXT_KEY,
|
||||
SEARCH_CONTEXT_KEY,
|
||||
@ -59,87 +72,81 @@ var AllContextKeys = []types.ContextKey{
|
||||
}
|
||||
|
||||
type ContextTree struct {
|
||||
Global types.Context
|
||||
Status types.Context
|
||||
Files *WorkingTreeContext
|
||||
Menu *MenuContext
|
||||
Branches *BranchesContext
|
||||
Tags *TagsContext
|
||||
LocalCommits *LocalCommitsContext
|
||||
CommitFiles *CommitFilesContext
|
||||
Remotes *RemotesContext
|
||||
Submodules *SubmodulesContext
|
||||
RemoteBranches *RemoteBranchesContext
|
||||
ReflogCommits *ReflogCommitsContext
|
||||
SubCommits *SubCommitsContext
|
||||
Stash *StashContext
|
||||
Suggestions *SuggestionsContext
|
||||
Normal types.Context
|
||||
Staging types.Context
|
||||
PatchBuilding types.Context
|
||||
Merging types.Context
|
||||
Confirmation types.Context
|
||||
CommitMessage types.Context
|
||||
Search types.Context
|
||||
CommandLog types.Context
|
||||
Global types.Context
|
||||
Status types.Context
|
||||
Files *WorkingTreeContext
|
||||
Menu *MenuContext
|
||||
Branches *BranchesContext
|
||||
Tags *TagsContext
|
||||
LocalCommits *LocalCommitsContext
|
||||
CommitFiles *CommitFilesContext
|
||||
Remotes *RemotesContext
|
||||
Submodules *SubmodulesContext
|
||||
RemoteBranches *RemoteBranchesContext
|
||||
ReflogCommits *ReflogCommitsContext
|
||||
SubCommits *SubCommitsContext
|
||||
Stash *StashContext
|
||||
Suggestions *SuggestionsContext
|
||||
Normal types.Context
|
||||
NormalSecondary types.Context
|
||||
Staging *PatchExplorerContext
|
||||
StagingSecondary *PatchExplorerContext
|
||||
CustomPatchBuilder *PatchExplorerContext
|
||||
CustomPatchBuilderSecondary types.Context
|
||||
Merging types.Context
|
||||
Confirmation types.Context
|
||||
CommitMessage types.Context
|
||||
CommandLog types.Context
|
||||
|
||||
// display contexts
|
||||
AppStatus types.Context
|
||||
Options types.Context
|
||||
SearchPrefix types.Context
|
||||
Search types.Context
|
||||
Information types.Context
|
||||
Limit types.Context
|
||||
}
|
||||
|
||||
// the order of this decides which context is initially at the top of its window
|
||||
func (self *ContextTree) Flatten() []types.Context {
|
||||
return []types.Context{
|
||||
self.Global,
|
||||
self.Status,
|
||||
self.Files,
|
||||
self.Submodules,
|
||||
self.Branches,
|
||||
self.Files,
|
||||
self.SubCommits,
|
||||
self.Remotes,
|
||||
self.RemoteBranches,
|
||||
self.Tags,
|
||||
self.LocalCommits,
|
||||
self.Branches,
|
||||
self.CommitFiles,
|
||||
self.ReflogCommits,
|
||||
self.LocalCommits,
|
||||
self.Stash,
|
||||
self.Menu,
|
||||
self.Confirmation,
|
||||
self.CommitMessage,
|
||||
self.Normal,
|
||||
self.Staging,
|
||||
|
||||
self.Merging,
|
||||
self.PatchBuilding,
|
||||
self.SubCommits,
|
||||
self.StagingSecondary,
|
||||
self.Staging,
|
||||
self.CustomPatchBuilderSecondary,
|
||||
self.CustomPatchBuilder,
|
||||
self.NormalSecondary,
|
||||
self.Normal,
|
||||
|
||||
self.Suggestions,
|
||||
self.CommandLog,
|
||||
self.AppStatus,
|
||||
self.Options,
|
||||
self.SearchPrefix,
|
||||
self.Search,
|
||||
self.Information,
|
||||
self.Limit,
|
||||
}
|
||||
}
|
||||
|
||||
type ViewContextMap struct {
|
||||
content map[string]types.Context
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
func NewViewContextMap() *ViewContextMap {
|
||||
return &ViewContextMap{content: map[string]types.Context{}}
|
||||
}
|
||||
|
||||
func (self *ViewContextMap) Get(viewName string) types.Context {
|
||||
self.RLock()
|
||||
defer self.RUnlock()
|
||||
|
||||
return self.content[viewName]
|
||||
}
|
||||
|
||||
func (self *ViewContextMap) Set(viewName string, context types.Context) {
|
||||
self.Lock()
|
||||
defer self.Unlock()
|
||||
self.content[viewName] = context
|
||||
}
|
||||
|
||||
func (self *ViewContextMap) Entries() map[string]types.Context {
|
||||
self.Lock()
|
||||
defer self.Unlock()
|
||||
return self.content
|
||||
}
|
||||
|
||||
type TabContext struct {
|
||||
Tab string
|
||||
Context types.Context
|
||||
type TabView struct {
|
||||
Tab string
|
||||
ViewName string
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ type ListContextTrait struct {
|
||||
|
||||
c *types.HelperCommon
|
||||
list types.IList
|
||||
viewTrait *ViewTrait
|
||||
getDisplayStrings func(startIdx int, length int) [][]string
|
||||
}
|
||||
|
||||
@ -20,43 +19,39 @@ func (self *ListContextTrait) GetList() types.IList {
|
||||
return self.list
|
||||
}
|
||||
|
||||
func (self *ListContextTrait) GetViewTrait() types.IViewTrait {
|
||||
return self.viewTrait
|
||||
}
|
||||
|
||||
func (self *ListContextTrait) FocusLine() {
|
||||
// we need a way of knowing whether we've rendered to the view yet.
|
||||
self.viewTrait.FocusPoint(self.list.GetSelectedLineIdx())
|
||||
self.GetViewTrait().FocusPoint(self.list.GetSelectedLineIdx())
|
||||
self.setFooter()
|
||||
}
|
||||
|
||||
func (self *ListContextTrait) setFooter() {
|
||||
self.viewTrait.SetFooter(formatListFooter(self.list.GetSelectedLineIdx(), self.list.Len()))
|
||||
self.GetViewTrait().SetFooter(formatListFooter(self.list.GetSelectedLineIdx(), self.list.Len()))
|
||||
}
|
||||
|
||||
func formatListFooter(selectedLineIdx int, length int) string {
|
||||
return fmt.Sprintf("%d of %d", selectedLineIdx+1, length)
|
||||
}
|
||||
|
||||
func (self *ListContextTrait) HandleFocus(opts ...types.OnFocusOpts) error {
|
||||
func (self *ListContextTrait) HandleFocus(opts types.OnFocusOpts) error {
|
||||
self.FocusLine()
|
||||
|
||||
self.viewTrait.SetHighlight(self.list.Len() > 0)
|
||||
self.GetViewTrait().SetHighlight(self.list.Len() > 0)
|
||||
|
||||
return self.Context.HandleFocus(opts...)
|
||||
return self.Context.HandleFocus(opts)
|
||||
}
|
||||
|
||||
func (self *ListContextTrait) HandleFocusLost() error {
|
||||
self.viewTrait.SetOriginX(0)
|
||||
func (self *ListContextTrait) HandleFocusLost(opts types.OnFocusLostOpts) error {
|
||||
self.GetViewTrait().SetOriginX(0)
|
||||
|
||||
return self.Context.HandleFocusLost()
|
||||
return self.Context.HandleFocusLost(opts)
|
||||
}
|
||||
|
||||
// OnFocus assumes that the content of the context has already been rendered to the view. OnRender is the function which actually renders the content to the view
|
||||
func (self *ListContextTrait) HandleRender() error {
|
||||
self.list.RefreshSelectedIdx()
|
||||
content := utils.RenderDisplayStrings(self.getDisplayStrings(0, self.list.Len()))
|
||||
self.viewTrait.SetContent(content)
|
||||
self.GetViewTrait().SetContent(content)
|
||||
self.c.Render()
|
||||
self.setFooter()
|
||||
|
||||
@ -65,5 +60,5 @@ func (self *ListContextTrait) HandleRender() error {
|
||||
|
||||
func (self *ListContextTrait) OnSearchSelect(selectedLineIdx int) error {
|
||||
self.GetList().SetSelectedLineIdx(selectedLineIdx)
|
||||
return self.HandleFocus()
|
||||
return self.HandleFocus(types.OnFocusOpts{})
|
||||
}
|
||||
|
@ -18,9 +18,9 @@ func NewLocalCommitsContext(
|
||||
view *gocui.View,
|
||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
||||
|
||||
onFocus func(...types.OnFocusOpts) error,
|
||||
onRenderToMain func(...types.OnFocusOpts) error,
|
||||
onFocusLost func() error,
|
||||
onFocus func(types.OnFocusOpts) error,
|
||||
onRenderToMain func() error,
|
||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
||||
|
||||
c *types.HelperCommon,
|
||||
) *LocalCommitsContext {
|
||||
@ -31,7 +31,7 @@ func NewLocalCommitsContext(
|
||||
ViewportListContextTrait: &ViewportListContextTrait{
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
ViewName: "commits",
|
||||
View: view,
|
||||
WindowName: "commits",
|
||||
Key: LOCAL_COMMITS_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
@ -42,7 +42,6 @@ func NewLocalCommitsContext(
|
||||
OnRenderToMain: onRenderToMain,
|
||||
}),
|
||||
list: viewModel,
|
||||
viewTrait: NewViewTrait(view),
|
||||
getDisplayStrings: getDisplayStrings,
|
||||
c: c,
|
||||
},
|
||||
|
@ -24,7 +24,7 @@ func NewMenuContext(
|
||||
) *MenuContext {
|
||||
viewModel := NewMenuViewModel()
|
||||
|
||||
onFocus := func(...types.OnFocusOpts) error {
|
||||
onFocus := func(types.OnFocusOpts) error {
|
||||
selectedMenuItem := viewModel.GetSelected()
|
||||
renderToDescriptionView(selectedMenuItem.Tooltip)
|
||||
return nil
|
||||
@ -34,17 +34,18 @@ func NewMenuContext(
|
||||
MenuViewModel: viewModel,
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
ViewName: "menu",
|
||||
Key: "menu",
|
||||
Kind: types.TEMPORARY_POPUP,
|
||||
OnGetOptionsMap: getOptionsMap,
|
||||
Focusable: true,
|
||||
View: view,
|
||||
WindowName: "menu",
|
||||
Key: "menu",
|
||||
Kind: types.TEMPORARY_POPUP,
|
||||
OnGetOptionsMap: getOptionsMap,
|
||||
Focusable: true,
|
||||
HasUncontrolledBounds: true,
|
||||
}), ContextCallbackOpts{
|
||||
OnFocus: onFocus,
|
||||
}),
|
||||
getDisplayStrings: viewModel.GetDisplayStrings,
|
||||
list: viewModel,
|
||||
viewTrait: NewViewTrait(view),
|
||||
c: c,
|
||||
},
|
||||
}
|
||||
|
134
pkg/gui/context/patch_explorer_context.go
Normal file
134
pkg/gui/context/patch_explorer_context.go
Normal file
@ -0,0 +1,134 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/patch_exploring"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
type PatchExplorerContext struct {
|
||||
*SimpleContext
|
||||
|
||||
state *patch_exploring.State
|
||||
viewTrait *ViewTrait
|
||||
getIncludedLineIndices func() []int
|
||||
c *types.HelperCommon
|
||||
mutex *sync.Mutex
|
||||
}
|
||||
|
||||
var _ types.IPatchExplorerContext = (*PatchExplorerContext)(nil)
|
||||
|
||||
func NewPatchExplorerContext(
|
||||
view *gocui.View,
|
||||
windowName string,
|
||||
key types.ContextKey,
|
||||
|
||||
onFocus func(types.OnFocusOpts) error,
|
||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
||||
getIncludedLineIndices func() []int,
|
||||
|
||||
c *types.HelperCommon,
|
||||
) *PatchExplorerContext {
|
||||
return &PatchExplorerContext{
|
||||
state: nil,
|
||||
viewTrait: NewViewTrait(view),
|
||||
c: c,
|
||||
mutex: &sync.Mutex{},
|
||||
getIncludedLineIndices: getIncludedLineIndices,
|
||||
SimpleContext: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
View: view,
|
||||
WindowName: windowName,
|
||||
Key: key,
|
||||
Kind: types.MAIN_CONTEXT,
|
||||
Focusable: true,
|
||||
}), ContextCallbackOpts{
|
||||
OnFocus: onFocus,
|
||||
OnFocusLost: onFocusLost,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
func (self *PatchExplorerContext) GetState() *patch_exploring.State {
|
||||
return self.state
|
||||
}
|
||||
|
||||
func (self *PatchExplorerContext) SetState(state *patch_exploring.State) {
|
||||
self.state = state
|
||||
}
|
||||
|
||||
func (self *PatchExplorerContext) GetViewTrait() types.IViewTrait {
|
||||
return self.viewTrait
|
||||
}
|
||||
|
||||
func (self *PatchExplorerContext) GetIncludedLineIndices() []int {
|
||||
return self.getIncludedLineIndices()
|
||||
}
|
||||
|
||||
func (self *PatchExplorerContext) RenderAndFocus(isFocused bool) error {
|
||||
self.GetView().SetContent(self.GetContentToRender(isFocused))
|
||||
|
||||
if err := self.focusSelection(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.c.Render()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *PatchExplorerContext) Render(isFocused bool) error {
|
||||
self.GetView().SetContent(self.GetContentToRender(isFocused))
|
||||
|
||||
self.c.Render()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *PatchExplorerContext) Focus() error {
|
||||
if err := self.focusSelection(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.c.Render()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *PatchExplorerContext) focusSelection() error {
|
||||
view := self.GetView()
|
||||
state := self.GetState()
|
||||
_, viewHeight := view.Size()
|
||||
bufferHeight := viewHeight - 1
|
||||
_, origin := view.Origin()
|
||||
|
||||
selectedLineIdx := state.GetSelectedLineIdx()
|
||||
|
||||
newOrigin := state.CalculateOrigin(origin, bufferHeight)
|
||||
|
||||
if err := view.SetOriginY(newOrigin); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return view.SetCursor(0, selectedLineIdx-newOrigin)
|
||||
}
|
||||
|
||||
func (self *PatchExplorerContext) GetContentToRender(isFocused bool) string {
|
||||
if self.GetState() == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return self.GetState().RenderForLineIndices(isFocused, self.GetIncludedLineIndices())
|
||||
}
|
||||
|
||||
func (self *PatchExplorerContext) NavigateTo(isFocused bool, selectedLineIdx int) error {
|
||||
self.GetState().SetLineSelectMode()
|
||||
self.GetState().SelectLine(selectedLineIdx)
|
||||
|
||||
return self.RenderAndFocus(isFocused)
|
||||
}
|
||||
|
||||
func (self *PatchExplorerContext) GetMutex() *sync.Mutex {
|
||||
return self.mutex
|
||||
}
|
@ -18,9 +18,9 @@ func NewReflogCommitsContext(
|
||||
view *gocui.View,
|
||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
||||
|
||||
onFocus func(...types.OnFocusOpts) error,
|
||||
onRenderToMain func(...types.OnFocusOpts) error,
|
||||
onFocusLost func() error,
|
||||
onFocus func(types.OnFocusOpts) error,
|
||||
onRenderToMain func() error,
|
||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
||||
|
||||
c *types.HelperCommon,
|
||||
) *ReflogCommitsContext {
|
||||
@ -30,7 +30,7 @@ func NewReflogCommitsContext(
|
||||
BasicViewModel: viewModel,
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
ViewName: "commits",
|
||||
View: view,
|
||||
WindowName: "commits",
|
||||
Key: REFLOG_COMMITS_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
@ -41,7 +41,6 @@ func NewReflogCommitsContext(
|
||||
OnRenderToMain: onRenderToMain,
|
||||
}),
|
||||
list: viewModel,
|
||||
viewTrait: NewViewTrait(view),
|
||||
getDisplayStrings: getDisplayStrings,
|
||||
c: c,
|
||||
},
|
||||
|
@ -19,9 +19,9 @@ func NewRemoteBranchesContext(
|
||||
view *gocui.View,
|
||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
||||
|
||||
onFocus func(...types.OnFocusOpts) error,
|
||||
onRenderToMain func(...types.OnFocusOpts) error,
|
||||
onFocusLost func() error,
|
||||
onFocus func(types.OnFocusOpts) error,
|
||||
onRenderToMain func() error,
|
||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
||||
|
||||
c *types.HelperCommon,
|
||||
) *RemoteBranchesContext {
|
||||
@ -32,7 +32,7 @@ func NewRemoteBranchesContext(
|
||||
DynamicTitleBuilder: NewDynamicTitleBuilder(c.Tr.RemoteBranchesDynamicTitle),
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
ViewName: "remoteBranches",
|
||||
View: view,
|
||||
WindowName: "branches",
|
||||
Key: REMOTE_BRANCHES_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
@ -44,7 +44,6 @@ func NewRemoteBranchesContext(
|
||||
OnRenderToMain: onRenderToMain,
|
||||
}),
|
||||
list: viewModel,
|
||||
viewTrait: NewViewTrait(view),
|
||||
getDisplayStrings: getDisplayStrings,
|
||||
c: c,
|
||||
},
|
||||
|
@ -18,9 +18,9 @@ func NewRemotesContext(
|
||||
view *gocui.View,
|
||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
||||
|
||||
onFocus func(...types.OnFocusOpts) error,
|
||||
onRenderToMain func(...types.OnFocusOpts) error,
|
||||
onFocusLost func() error,
|
||||
onFocus func(types.OnFocusOpts) error,
|
||||
onRenderToMain func() error,
|
||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
||||
|
||||
c *types.HelperCommon,
|
||||
) *RemotesContext {
|
||||
@ -30,7 +30,7 @@ func NewRemotesContext(
|
||||
BasicViewModel: viewModel,
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
ViewName: "branches",
|
||||
View: view,
|
||||
WindowName: "branches",
|
||||
Key: REMOTES_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
@ -41,7 +41,6 @@ func NewRemotesContext(
|
||||
OnRenderToMain: onRenderToMain,
|
||||
}),
|
||||
list: viewModel,
|
||||
viewTrait: NewViewTrait(view),
|
||||
getDisplayStrings: getDisplayStrings,
|
||||
c: c,
|
||||
},
|
||||
|
@ -1,25 +1,25 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
type SimpleContext struct {
|
||||
OnFocus func(opts ...types.OnFocusOpts) error
|
||||
OnFocusLost func() error
|
||||
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(opts ...types.OnFocusOpts) error
|
||||
OnRenderToMain func() error
|
||||
|
||||
*BaseContext
|
||||
}
|
||||
|
||||
type ContextCallbackOpts struct {
|
||||
OnFocus func(opts ...types.OnFocusOpts) error
|
||||
OnFocusLost func() error
|
||||
OnRender func() error
|
||||
// this is for pushing some content to the main view
|
||||
OnRenderToMain func(opts ...types.OnFocusOpts) error
|
||||
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 {
|
||||
@ -34,15 +34,30 @@ func NewSimpleContext(baseContext *BaseContext, opts ContextCallbackOpts) *Simpl
|
||||
|
||||
var _ types.Context = &SimpleContext{}
|
||||
|
||||
func (self *SimpleContext) HandleFocus(opts ...types.OnFocusOpts) error {
|
||||
// A Display context only renders a view. It has no keybindings and is not focusable.
|
||||
func NewDisplayContext(key types.ContextKey, view *gocui.View, windowName string) types.Context {
|
||||
return NewSimpleContext(
|
||||
NewBaseContext(NewBaseContextOpts{
|
||||
Kind: types.DISPLAY_CONTEXT,
|
||||
Key: key,
|
||||
View: view,
|
||||
WindowName: windowName,
|
||||
Focusable: false,
|
||||
Transient: false,
|
||||
}),
|
||||
ContextCallbackOpts{},
|
||||
)
|
||||
}
|
||||
|
||||
func (self *SimpleContext) HandleFocus(opts types.OnFocusOpts) error {
|
||||
if self.OnFocus != nil {
|
||||
if err := self.OnFocus(opts...); err != nil {
|
||||
if err := self.OnFocus(opts); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if self.OnRenderToMain != nil {
|
||||
if err := self.OnRenderToMain(opts...); err != nil {
|
||||
if err := self.OnRenderToMain(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -50,9 +65,9 @@ func (self *SimpleContext) HandleFocus(opts ...types.OnFocusOpts) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SimpleContext) HandleFocusLost() error {
|
||||
func (self *SimpleContext) HandleFocusLost(opts types.OnFocusLostOpts) error {
|
||||
if self.OnFocusLost != nil {
|
||||
return self.OnFocusLost()
|
||||
return self.OnFocusLost(opts)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -18,9 +18,9 @@ func NewStashContext(
|
||||
view *gocui.View,
|
||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
||||
|
||||
onFocus func(...types.OnFocusOpts) error,
|
||||
onRenderToMain func(...types.OnFocusOpts) error,
|
||||
onFocusLost func() error,
|
||||
onFocus func(types.OnFocusOpts) error,
|
||||
onRenderToMain func() error,
|
||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
||||
|
||||
c *types.HelperCommon,
|
||||
) *StashContext {
|
||||
@ -30,7 +30,7 @@ func NewStashContext(
|
||||
BasicViewModel: viewModel,
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
ViewName: "stash",
|
||||
View: view,
|
||||
WindowName: "stash",
|
||||
Key: STASH_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
@ -41,7 +41,6 @@ func NewStashContext(
|
||||
OnRenderToMain: onRenderToMain,
|
||||
}),
|
||||
list: viewModel,
|
||||
viewTrait: NewViewTrait(view),
|
||||
getDisplayStrings: getDisplayStrings,
|
||||
c: c,
|
||||
},
|
||||
|
@ -22,9 +22,9 @@ func NewSubCommitsContext(
|
||||
view *gocui.View,
|
||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
||||
|
||||
onFocus func(...types.OnFocusOpts) error,
|
||||
onRenderToMain func(...types.OnFocusOpts) error,
|
||||
onFocusLost func() error,
|
||||
onFocus func(types.OnFocusOpts) error,
|
||||
onRenderToMain func() error,
|
||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
||||
|
||||
c *types.HelperCommon,
|
||||
) *SubCommitsContext {
|
||||
@ -39,7 +39,7 @@ func NewSubCommitsContext(
|
||||
ViewportListContextTrait: &ViewportListContextTrait{
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
ViewName: "subCommits",
|
||||
View: view,
|
||||
WindowName: "branches",
|
||||
Key: SUB_COMMITS_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
@ -51,7 +51,6 @@ func NewSubCommitsContext(
|
||||
OnRenderToMain: onRenderToMain,
|
||||
}),
|
||||
list: viewModel,
|
||||
viewTrait: NewViewTrait(view),
|
||||
getDisplayStrings: getDisplayStrings,
|
||||
c: c,
|
||||
},
|
||||
|
@ -18,9 +18,9 @@ func NewSubmodulesContext(
|
||||
view *gocui.View,
|
||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
||||
|
||||
onFocus func(...types.OnFocusOpts) error,
|
||||
onRenderToMain func(...types.OnFocusOpts) error,
|
||||
onFocusLost func() error,
|
||||
onFocus func(types.OnFocusOpts) error,
|
||||
onRenderToMain func() error,
|
||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
||||
|
||||
c *types.HelperCommon,
|
||||
) *SubmodulesContext {
|
||||
@ -30,7 +30,7 @@ func NewSubmodulesContext(
|
||||
BasicViewModel: viewModel,
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
ViewName: "files",
|
||||
View: view,
|
||||
WindowName: "files",
|
||||
Key: SUBMODULES_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
@ -41,7 +41,6 @@ func NewSubmodulesContext(
|
||||
OnRenderToMain: onRenderToMain,
|
||||
}),
|
||||
list: viewModel,
|
||||
viewTrait: NewViewTrait(view),
|
||||
getDisplayStrings: getDisplayStrings,
|
||||
c: c,
|
||||
},
|
||||
|
@ -17,9 +17,9 @@ func NewSuggestionsContext(
|
||||
view *gocui.View,
|
||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
||||
|
||||
onFocus func(...types.OnFocusOpts) error,
|
||||
onRenderToMain func(...types.OnFocusOpts) error,
|
||||
onFocusLost func() error,
|
||||
onFocus func(types.OnFocusOpts) error,
|
||||
onRenderToMain func() error,
|
||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
||||
|
||||
c *types.HelperCommon,
|
||||
) *SuggestionsContext {
|
||||
@ -29,7 +29,7 @@ func NewSuggestionsContext(
|
||||
BasicViewModel: viewModel,
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
ViewName: "suggestions",
|
||||
View: view,
|
||||
WindowName: "suggestions",
|
||||
Key: SUGGESTIONS_CONTEXT_KEY,
|
||||
Kind: types.PERSISTENT_POPUP,
|
||||
@ -40,7 +40,6 @@ func NewSuggestionsContext(
|
||||
OnRenderToMain: onRenderToMain,
|
||||
}),
|
||||
list: viewModel,
|
||||
viewTrait: NewViewTrait(view),
|
||||
getDisplayStrings: getDisplayStrings,
|
||||
c: c,
|
||||
},
|
||||
|
@ -18,9 +18,9 @@ func NewTagsContext(
|
||||
view *gocui.View,
|
||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
||||
|
||||
onFocus func(...types.OnFocusOpts) error,
|
||||
onRenderToMain func(...types.OnFocusOpts) error,
|
||||
onFocusLost func() error,
|
||||
onFocus func(types.OnFocusOpts) error,
|
||||
onRenderToMain func() error,
|
||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
||||
|
||||
c *types.HelperCommon,
|
||||
) *TagsContext {
|
||||
@ -30,7 +30,7 @@ func NewTagsContext(
|
||||
BasicViewModel: viewModel,
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
ViewName: "branches",
|
||||
View: view,
|
||||
WindowName: "branches",
|
||||
Key: TAGS_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
@ -41,7 +41,6 @@ func NewTagsContext(
|
||||
OnRenderToMain: onRenderToMain,
|
||||
}),
|
||||
list: viewModel,
|
||||
viewTrait: NewViewTrait(view),
|
||||
getDisplayStrings: getDisplayStrings,
|
||||
c: c,
|
||||
},
|
||||
|
@ -61,12 +61,12 @@ func (self *ViewTrait) horizontalScrollAmount() int {
|
||||
return self.view.InnerWidth() / HORIZONTAL_SCROLL_FACTOR
|
||||
}
|
||||
|
||||
func (self *ViewTrait) ScrollUp() {
|
||||
self.view.ScrollUp(1)
|
||||
func (self *ViewTrait) ScrollUp(value int) {
|
||||
self.view.ScrollUp(value)
|
||||
}
|
||||
|
||||
func (self *ViewTrait) ScrollDown() {
|
||||
self.view.ScrollDown(1)
|
||||
func (self *ViewTrait) ScrollDown(value int) {
|
||||
self.view.ScrollDown(value)
|
||||
}
|
||||
|
||||
// this returns the amount we'll scroll if we want to scroll by a page.
|
||||
|
@ -19,9 +19,9 @@ func NewWorkingTreeContext(
|
||||
view *gocui.View,
|
||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
||||
|
||||
onFocus func(...types.OnFocusOpts) error,
|
||||
onRenderToMain func(...types.OnFocusOpts) error,
|
||||
onFocusLost func() error,
|
||||
onFocus func(types.OnFocusOpts) error,
|
||||
onRenderToMain func() error,
|
||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
||||
|
||||
c *types.HelperCommon,
|
||||
) *WorkingTreeContext {
|
||||
@ -31,7 +31,7 @@ func NewWorkingTreeContext(
|
||||
FileTreeViewModel: viewModel,
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
ViewName: "files",
|
||||
View: view,
|
||||
WindowName: "files",
|
||||
Key: FILES_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
@ -42,7 +42,6 @@ func NewWorkingTreeContext(
|
||||
OnRenderToMain: onRenderToMain,
|
||||
}),
|
||||
list: viewModel,
|
||||
viewTrait: NewViewTrait(view),
|
||||
getDisplayStrings: getDisplayStrings,
|
||||
c: c,
|
||||
},
|
||||
|
@ -9,26 +9,27 @@ func (gui *Gui) contextTree() *context.ContextTree {
|
||||
return &context.ContextTree{
|
||||
Global: context.NewSimpleContext(
|
||||
context.NewBaseContext(context.NewBaseContextOpts{
|
||||
Kind: types.GLOBAL_CONTEXT,
|
||||
ViewName: "",
|
||||
WindowName: "",
|
||||
Key: context.GLOBAL_CONTEXT_KEY,
|
||||
Focusable: false,
|
||||
Kind: types.GLOBAL_CONTEXT,
|
||||
View: nil,
|
||||
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: OnFocusWrapper(gui.statusRenderToMain),
|
||||
OnRenderToMain: gui.statusRenderToMain,
|
||||
},
|
||||
),
|
||||
Status: context.NewSimpleContext(
|
||||
context.NewBaseContext(context.NewBaseContextOpts{
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
ViewName: "status",
|
||||
View: gui.Views.Status,
|
||||
WindowName: "status",
|
||||
Key: context.STATUS_CONTEXT_KEY,
|
||||
Focusable: true,
|
||||
}),
|
||||
context.ContextCallbackOpts{
|
||||
OnRenderToMain: OnFocusWrapper(gui.statusRenderToMain),
|
||||
OnRenderToMain: gui.statusRenderToMain,
|
||||
},
|
||||
),
|
||||
Files: gui.filesListContext(),
|
||||
@ -47,84 +48,150 @@ func (gui *Gui) contextTree() *context.ContextTree {
|
||||
Normal: context.NewSimpleContext(
|
||||
context.NewBaseContext(context.NewBaseContextOpts{
|
||||
Kind: types.MAIN_CONTEXT,
|
||||
ViewName: "main",
|
||||
View: gui.Views.Main,
|
||||
WindowName: "main",
|
||||
Key: context.MAIN_NORMAL_CONTEXT_KEY,
|
||||
Key: context.NORMAL_MAIN_CONTEXT_KEY,
|
||||
Focusable: false,
|
||||
}),
|
||||
context.ContextCallbackOpts{
|
||||
OnFocus: func(opts ...types.OnFocusOpts) error {
|
||||
OnFocus: func(opts types.OnFocusOpts) error {
|
||||
return nil // TODO: should we do something here? We should allow for scrolling the panel
|
||||
},
|
||||
},
|
||||
),
|
||||
Staging: context.NewSimpleContext(
|
||||
NormalSecondary: context.NewSimpleContext(
|
||||
context.NewBaseContext(context.NewBaseContextOpts{
|
||||
Kind: types.MAIN_CONTEXT,
|
||||
ViewName: "main",
|
||||
WindowName: "main",
|
||||
Key: context.MAIN_STAGING_CONTEXT_KEY,
|
||||
Focusable: true,
|
||||
View: gui.Views.Secondary,
|
||||
WindowName: "secondary",
|
||||
Key: context.NORMAL_SECONDARY_CONTEXT_KEY,
|
||||
Focusable: false,
|
||||
}),
|
||||
context.ContextCallbackOpts{
|
||||
OnFocus: func(opts ...types.OnFocusOpts) error {
|
||||
forceSecondaryFocused := false
|
||||
selectedLineIdx := -1
|
||||
if len(opts) > 0 && opts[0].ClickedViewName != "" {
|
||||
if opts[0].ClickedViewName == "main" || opts[0].ClickedViewName == "secondary" {
|
||||
selectedLineIdx = opts[0].ClickedViewLineIdx
|
||||
}
|
||||
if opts[0].ClickedViewName == "secondary" {
|
||||
forceSecondaryFocused = true
|
||||
}
|
||||
}
|
||||
return gui.onStagingFocus(forceSecondaryFocused, selectedLineIdx)
|
||||
},
|
||||
},
|
||||
context.ContextCallbackOpts{},
|
||||
),
|
||||
PatchBuilding: context.NewSimpleContext(
|
||||
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.PatchManager.IsEmpty() {
|
||||
gui.git.Patch.PatchManager.Reset()
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
func() []int {
|
||||
filename := gui.State.Contexts.CommitFiles.GetSelectedPath()
|
||||
includedLineIndices, err := gui.git.Patch.PatchManager.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,
|
||||
ViewName: "main",
|
||||
WindowName: "main",
|
||||
Key: context.MAIN_PATCH_BUILDING_CONTEXT_KEY,
|
||||
Focusable: true,
|
||||
View: gui.Views.PatchBuildingSecondary,
|
||||
WindowName: "secondary",
|
||||
Key: context.PATCH_BUILDING_SECONDARY_CONTEXT_KEY,
|
||||
Focusable: false,
|
||||
}),
|
||||
context.ContextCallbackOpts{
|
||||
OnFocus: func(opts ...types.OnFocusOpts) error {
|
||||
selectedLineIdx := -1
|
||||
if len(opts) > 0 && (opts[0].ClickedViewName == "main" || opts[0].ClickedViewName == "secondary") {
|
||||
selectedLineIdx = opts[0].ClickedViewLineIdx
|
||||
}
|
||||
|
||||
return gui.onPatchBuildingFocus(selectedLineIdx)
|
||||
},
|
||||
},
|
||||
context.ContextCallbackOpts{},
|
||||
),
|
||||
Merging: context.NewSimpleContext(
|
||||
context.NewBaseContext(context.NewBaseContextOpts{
|
||||
Kind: types.MAIN_CONTEXT,
|
||||
ViewName: "main",
|
||||
View: gui.Views.Merging,
|
||||
WindowName: "main",
|
||||
Key: context.MAIN_MERGING_CONTEXT_KEY,
|
||||
Key: context.MERGING_MAIN_CONTEXT_KEY,
|
||||
OnGetOptionsMap: gui.getMergingOptions,
|
||||
Focusable: true,
|
||||
}),
|
||||
context.ContextCallbackOpts{
|
||||
OnFocus: OnFocusWrapper(func() error { return gui.renderConflictsWithLock(true) }),
|
||||
OnFocus: OnFocusWrapper(func() error {
|
||||
gui.Views.Merging.Wrap = false
|
||||
|
||||
return gui.renderConflictsWithLock(true)
|
||||
}),
|
||||
OnFocusLost: func(types.OnFocusLostOpts) error {
|
||||
gui.Views.Merging.Wrap = true
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
),
|
||||
Confirmation: context.NewSimpleContext(
|
||||
context.NewBaseContext(context.NewBaseContextOpts{
|
||||
Kind: types.TEMPORARY_POPUP,
|
||||
ViewName: "confirmation",
|
||||
WindowName: "confirmation",
|
||||
Key: context.CONFIRMATION_CONTEXT_KEY,
|
||||
Focusable: true,
|
||||
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() error {
|
||||
OnFocusLost: func(types.OnFocusLostOpts) error {
|
||||
gui.deactivateConfirmationPrompt()
|
||||
return nil
|
||||
},
|
||||
@ -132,11 +199,12 @@ func (gui *Gui) contextTree() *context.ContextTree {
|
||||
),
|
||||
CommitMessage: context.NewSimpleContext(
|
||||
context.NewBaseContext(context.NewBaseContextOpts{
|
||||
Kind: types.PERSISTENT_POPUP,
|
||||
ViewName: "commitMessage",
|
||||
WindowName: "commitMessage",
|
||||
Key: context.COMMIT_MESSAGE_CONTEXT_KEY,
|
||||
Focusable: true,
|
||||
Kind: types.PERSISTENT_POPUP,
|
||||
View: gui.Views.CommitMessage,
|
||||
WindowName: "commitMessage",
|
||||
Key: context.COMMIT_MESSAGE_CONTEXT_KEY,
|
||||
Focusable: true,
|
||||
HasUncontrolledBounds: true,
|
||||
}),
|
||||
context.ContextCallbackOpts{
|
||||
OnFocus: OnFocusWrapper(gui.handleCommitMessageFocused),
|
||||
@ -145,7 +213,7 @@ func (gui *Gui) contextTree() *context.ContextTree {
|
||||
Search: context.NewSimpleContext(
|
||||
context.NewBaseContext(context.NewBaseContextOpts{
|
||||
Kind: types.PERSISTENT_POPUP,
|
||||
ViewName: "search",
|
||||
View: gui.Views.Search,
|
||||
WindowName: "search",
|
||||
Key: context.SEARCH_CONTEXT_KEY,
|
||||
Focusable: true,
|
||||
@ -155,26 +223,39 @@ func (gui *Gui) contextTree() *context.ContextTree {
|
||||
CommandLog: context.NewSimpleContext(
|
||||
context.NewBaseContext(context.NewBaseContextOpts{
|
||||
Kind: types.EXTRAS_CONTEXT,
|
||||
ViewName: "extras",
|
||||
View: gui.Views.Extras,
|
||||
WindowName: "extras",
|
||||
Key: context.COMMAND_LOG_CONTEXT_KEY,
|
||||
OnGetOptionsMap: gui.getMergingOptions,
|
||||
Focusable: true,
|
||||
}),
|
||||
context.ContextCallbackOpts{
|
||||
OnFocusLost: func() error {
|
||||
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"),
|
||||
}
|
||||
}
|
||||
|
||||
// using this wrapper for when an onFocus function doesn't care about any potential
|
||||
// props that could be passed
|
||||
func OnFocusWrapper(f func() error) func(opts ...types.OnFocusOpts) error {
|
||||
return func(opts ...types.OnFocusOpts) error {
|
||||
func OnFocusWrapper(f func() error) func(opts types.OnFocusOpts) error {
|
||||
return func(opts types.OnFocusOpts) error {
|
||||
return f()
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) getPatchExplorerContexts() []types.IPatchExplorerContext {
|
||||
return []types.IPatchExplorerContext{
|
||||
gui.State.Contexts.Staging,
|
||||
gui.State.Contexts.StagingSecondary,
|
||||
gui.State.Contexts.CustomPatchBuilder,
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ func (gui *Gui) resetControllers() {
|
||||
gui.helpers = &helpers.Helpers{
|
||||
Refs: refsHelper,
|
||||
Host: helpers.NewHostHelper(helperCommon, gui.git),
|
||||
PatchBuilding: helpers.NewPatchBuildingHelper(helperCommon, gui.git),
|
||||
PatchBuilding: helpers.NewPatchBuildingHelper(helperCommon, gui.git, gui.State.Contexts),
|
||||
Bisect: helpers.NewBisectHelper(helperCommon, gui.git),
|
||||
Suggestions: suggestionsHelper,
|
||||
Files: helpers.NewFilesHelper(helperCommon, gui.git, osCommand),
|
||||
@ -120,11 +120,18 @@ func (gui *Gui) resetControllers() {
|
||||
)
|
||||
undoController := controllers.NewUndoController(common)
|
||||
globalController := controllers.NewGlobalController(common)
|
||||
contextLinesController := controllers.NewContextLinesController(common)
|
||||
verticalScrollControllerFactory := controllers.NewVerticalScrollControllerFactory(common)
|
||||
|
||||
branchesController := controllers.NewBranchesController(common)
|
||||
gitFlowController := controllers.NewGitFlowController(common)
|
||||
filesRemoveController := controllers.NewFilesRemoveController(common)
|
||||
stashController := controllers.NewStashController(common)
|
||||
commitFilesController := controllers.NewCommitFilesController(common)
|
||||
patchExplorerControllerFactory := controllers.NewPatchExplorerControllerFactory(common)
|
||||
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)
|
||||
patchBuildingController := controllers.NewPatchBuildingController(common)
|
||||
|
||||
setSubCommits := func(commits []*models.Commit) { gui.State.Model.SubCommits = commits }
|
||||
|
||||
@ -157,19 +164,87 @@ func (gui *Gui) resetControllers() {
|
||||
controllers.AttachControllers(context, controllers.NewBasicCommitsController(common, context))
|
||||
}
|
||||
|
||||
controllers.AttachControllers(gui.State.Contexts.Files, filesController, filesRemoveController)
|
||||
controllers.AttachControllers(gui.State.Contexts.Tags, tagsController)
|
||||
controllers.AttachControllers(gui.State.Contexts.Submodules, submodulesController)
|
||||
controllers.AttachControllers(gui.State.Contexts.LocalCommits, localCommitsController, bisectController)
|
||||
controllers.AttachControllers(gui.State.Contexts.Branches, branchesController, gitFlowController)
|
||||
controllers.AttachControllers(gui.State.Contexts.LocalCommits, localCommitsController, bisectController)
|
||||
controllers.AttachControllers(gui.State.Contexts.CommitFiles, commitFilesController)
|
||||
controllers.AttachControllers(gui.State.Contexts.Remotes, remotesController)
|
||||
controllers.AttachControllers(gui.State.Contexts.Stash, stashController)
|
||||
controllers.AttachControllers(gui.State.Contexts.Menu, menuController)
|
||||
controllers.AttachControllers(gui.State.Contexts.CommitMessage, commitMessageController)
|
||||
controllers.AttachControllers(gui.State.Contexts.RemoteBranches, remoteBranchesController)
|
||||
controllers.AttachControllers(gui.State.Contexts.Global, syncController, undoController, globalController)
|
||||
// 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,
|
||||
stagingController,
|
||||
patchExplorerControllerFactory.Create(gui.State.Contexts.Staging),
|
||||
verticalScrollControllerFactory.Create(gui.State.Contexts.Staging),
|
||||
)
|
||||
|
||||
controllers.AttachControllers(gui.State.Contexts.StagingSecondary,
|
||||
stagingSecondaryController,
|
||||
patchExplorerControllerFactory.Create(gui.State.Contexts.StagingSecondary),
|
||||
verticalScrollControllerFactory.Create(gui.State.Contexts.StagingSecondary),
|
||||
)
|
||||
|
||||
controllers.AttachControllers(gui.State.Contexts.CustomPatchBuilder,
|
||||
patchBuildingController,
|
||||
patchExplorerControllerFactory.Create(gui.State.Contexts.CustomPatchBuilder),
|
||||
verticalScrollControllerFactory.Create(gui.State.Contexts.CustomPatchBuilder),
|
||||
)
|
||||
|
||||
controllers.AttachControllers(gui.State.Contexts.CustomPatchBuilderSecondary,
|
||||
verticalScrollControllerFactory.Create(gui.State.Contexts.CustomPatchBuilder),
|
||||
)
|
||||
|
||||
controllers.AttachControllers(gui.State.Contexts.Files,
|
||||
filesController,
|
||||
filesRemoveController,
|
||||
)
|
||||
|
||||
controllers.AttachControllers(gui.State.Contexts.Tags,
|
||||
tagsController,
|
||||
)
|
||||
|
||||
controllers.AttachControllers(gui.State.Contexts.Submodules,
|
||||
submodulesController,
|
||||
)
|
||||
|
||||
controllers.AttachControllers(gui.State.Contexts.LocalCommits,
|
||||
localCommitsController,
|
||||
bisectController,
|
||||
)
|
||||
|
||||
controllers.AttachControllers(gui.State.Contexts.Branches,
|
||||
branchesController,
|
||||
gitFlowController,
|
||||
)
|
||||
|
||||
controllers.AttachControllers(gui.State.Contexts.LocalCommits,
|
||||
localCommitsController,
|
||||
bisectController,
|
||||
)
|
||||
|
||||
controllers.AttachControllers(gui.State.Contexts.CommitFiles,
|
||||
commitFilesController,
|
||||
)
|
||||
|
||||
controllers.AttachControllers(gui.State.Contexts.Remotes,
|
||||
remotesController,
|
||||
)
|
||||
|
||||
controllers.AttachControllers(gui.State.Contexts.Stash,
|
||||
stashController,
|
||||
)
|
||||
|
||||
controllers.AttachControllers(gui.State.Contexts.Menu,
|
||||
menuController,
|
||||
)
|
||||
|
||||
controllers.AttachControllers(gui.State.Contexts.CommitMessage,
|
||||
commitMessageController,
|
||||
)
|
||||
|
||||
controllers.AttachControllers(gui.State.Contexts.RemoteBranches,
|
||||
remoteBranchesController,
|
||||
)
|
||||
|
||||
controllers.AttachControllers(gui.State.Contexts.Global,
|
||||
syncController,
|
||||
undoController,
|
||||
globalController,
|
||||
contextLinesController,
|
||||
)
|
||||
|
||||
// this must come last so that we've got our click handlers defined against the context
|
||||
listControllerFactory := controllers.NewListControllerFactory(gui.c)
|
||||
|
@ -222,7 +222,7 @@ func (self *BisectController) selectCurrentBisectCommit() {
|
||||
for i, commit := range self.model.Commits {
|
||||
if commit.Sha == info.GetCurrentSha() {
|
||||
self.context().SetSelectedLineIdx(i)
|
||||
_ = self.context().HandleFocus()
|
||||
_ = self.context().HandleFocus(types.OnFocusOpts{})
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -75,10 +75,10 @@ func (self *CommitFilesController) GetKeybindings(opts types.KeybindingsOpts) []
|
||||
func (self *CommitFilesController) GetMouseKeybindings(opts types.KeybindingsOpts) []*gocui.ViewMouseBinding {
|
||||
return []*gocui.ViewMouseBinding{
|
||||
{
|
||||
ViewName: "main",
|
||||
ViewName: "patchBuilding",
|
||||
Key: gocui.MouseLeft,
|
||||
Handler: self.onClickMain,
|
||||
FromContext: string(self.context().GetKey()),
|
||||
FocusedView: self.context().GetViewName(),
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -107,7 +107,7 @@ func (self *CommitFilesController) onClickMain(opts gocui.ViewMouseBindingOpts)
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
return self.enterCommitFile(node, types.OnFocusOpts{ClickedViewName: "main", ClickedViewLineIdx: opts.Y})
|
||||
return self.enterCommitFile(node, types.OnFocusOpts{ClickedWindowName: "main", ClickedViewLineIdx: opts.Y})
|
||||
}
|
||||
|
||||
func (self *CommitFilesController) checkout(node *filetree.CommitFileNode) error {
|
||||
@ -220,7 +220,7 @@ func (self *CommitFilesController) startPatchManager() error {
|
||||
}
|
||||
|
||||
func (self *CommitFilesController) enter(node *filetree.CommitFileNode) error {
|
||||
return self.enterCommitFile(node, types.OnFocusOpts{ClickedViewName: "", ClickedViewLineIdx: -1})
|
||||
return self.enterCommitFile(node, types.OnFocusOpts{ClickedWindowName: "", ClickedViewLineIdx: -1})
|
||||
}
|
||||
|
||||
func (self *CommitFilesController) enterCommitFile(node *filetree.CommitFileNode, opts types.OnFocusOpts) error {
|
||||
@ -235,7 +235,7 @@ func (self *CommitFilesController) enterCommitFile(node *filetree.CommitFileNode
|
||||
}
|
||||
}
|
||||
|
||||
return self.c.PushContext(self.contexts.PatchBuilding, opts)
|
||||
return self.c.PushContext(self.contexts.CustomPatchBuilder, opts)
|
||||
}
|
||||
|
||||
if self.git.Patch.PatchManager.Active() && self.git.Patch.PatchManager.To != self.context().GetRef().RefName() {
|
||||
|
116
pkg/gui/controllers/context_lines_controller.go
Normal file
116
pkg/gui/controllers/context_lines_controller.go
Normal file
@ -0,0 +1,116 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
// This controller lets you change the context size for diffs. The 'context' in 'context size' refers to the conventional meaning of the word 'context' in a diff, as opposed to lazygit's own idea of a 'context'.
|
||||
|
||||
var CONTEXT_KEYS_SHOWING_DIFFS = []types.ContextKey{
|
||||
context.FILES_CONTEXT_KEY,
|
||||
context.COMMIT_FILES_CONTEXT_KEY,
|
||||
context.STASH_CONTEXT_KEY,
|
||||
context.LOCAL_COMMITS_CONTEXT_KEY,
|
||||
context.SUB_COMMITS_CONTEXT_KEY,
|
||||
context.STAGING_MAIN_CONTEXT_KEY,
|
||||
context.STAGING_SECONDARY_CONTEXT_KEY,
|
||||
context.PATCH_BUILDING_MAIN_CONTEXT_KEY,
|
||||
context.PATCH_BUILDING_SECONDARY_CONTEXT_KEY,
|
||||
}
|
||||
|
||||
type ContextLinesController struct {
|
||||
baseController
|
||||
*controllerCommon
|
||||
}
|
||||
|
||||
var _ types.IController = &ContextLinesController{}
|
||||
|
||||
func NewContextLinesController(
|
||||
common *controllerCommon,
|
||||
) *ContextLinesController {
|
||||
return &ContextLinesController{
|
||||
baseController: baseController{},
|
||||
controllerCommon: common,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *ContextLinesController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
|
||||
bindings := []*types.Binding{
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.IncreaseContextInDiffView),
|
||||
Handler: self.Increase,
|
||||
Description: self.c.Tr.IncreaseContextInDiffView,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.DecreaseContextInDiffView),
|
||||
Handler: self.Decrease,
|
||||
Description: self.c.Tr.DecreaseContextInDiffView,
|
||||
},
|
||||
}
|
||||
|
||||
return bindings
|
||||
}
|
||||
|
||||
func (self *ContextLinesController) Context() types.Context {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ContextLinesController) Increase() error {
|
||||
if self.isShowingDiff() {
|
||||
if err := self.checkCanChangeContext(); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
self.c.UserConfig.Git.DiffContextSize = self.c.UserConfig.Git.DiffContextSize + 1
|
||||
return self.applyChange()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ContextLinesController) Decrease() error {
|
||||
old_size := self.c.UserConfig.Git.DiffContextSize
|
||||
|
||||
if self.isShowingDiff() && old_size > 1 {
|
||||
if err := self.checkCanChangeContext(); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
self.c.UserConfig.Git.DiffContextSize = old_size - 1
|
||||
return self.applyChange()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ContextLinesController) applyChange() error {
|
||||
currentContext := self.c.CurrentStaticContext()
|
||||
switch currentContext.GetKey() {
|
||||
// we make an exception for our staging and patch building contexts because they actually need to refresh their state afterwards.
|
||||
case context.PATCH_BUILDING_MAIN_CONTEXT_KEY:
|
||||
return self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.PATCH_BUILDING}})
|
||||
case context.STAGING_MAIN_CONTEXT_KEY, context.STAGING_SECONDARY_CONTEXT_KEY:
|
||||
return self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.STAGING}})
|
||||
default:
|
||||
return currentContext.HandleRenderToMain()
|
||||
}
|
||||
}
|
||||
|
||||
func (self *ContextLinesController) checkCanChangeContext() error {
|
||||
if self.git.Patch.PatchManager.Active() {
|
||||
return errors.New(self.c.Tr.CantChangeContextSizeError)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ContextLinesController) isShowingDiff() bool {
|
||||
return lo.Contains(
|
||||
CONTEXT_KEYS_SHOWING_DIFFS,
|
||||
self.c.CurrentStaticContext().GetKey(),
|
||||
)
|
||||
}
|
@ -152,13 +152,31 @@ func (self *FilesController) GetMouseKeybindings(opts types.KeybindingsOpts) []*
|
||||
ViewName: "main",
|
||||
Key: gocui.MouseLeft,
|
||||
Handler: self.onClickMain,
|
||||
FromContext: string(self.context().GetKey()),
|
||||
FocusedView: self.context().GetViewName(),
|
||||
},
|
||||
{
|
||||
ViewName: "patchBuilding",
|
||||
Key: gocui.MouseLeft,
|
||||
Handler: self.onClickMain,
|
||||
FocusedView: self.context().GetViewName(),
|
||||
},
|
||||
{
|
||||
ViewName: "merging",
|
||||
Key: gocui.MouseLeft,
|
||||
Handler: self.onClickMain,
|
||||
FocusedView: self.context().GetViewName(),
|
||||
},
|
||||
{
|
||||
ViewName: "secondary",
|
||||
Key: gocui.MouseLeft,
|
||||
Handler: self.onClickSecondary,
|
||||
FromContext: string(self.context().GetKey()),
|
||||
FocusedView: self.context().GetViewName(),
|
||||
},
|
||||
{
|
||||
ViewName: "patchBuildingSecondary",
|
||||
Key: gocui.MouseLeft,
|
||||
Handler: self.onClickSecondary,
|
||||
FocusedView: self.context().GetViewName(),
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -318,7 +336,7 @@ func (self *FilesController) press(node *filetree.FileNode) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return self.context().HandleFocus()
|
||||
return self.context().HandleFocus(types.OnFocusOpts{})
|
||||
}
|
||||
|
||||
func (self *FilesController) checkSelectedFileNode(callback func(*filetree.FileNode) error) func() error {
|
||||
@ -349,7 +367,7 @@ func (self *FilesController) getSelectedFile() *models.File {
|
||||
}
|
||||
|
||||
func (self *FilesController) enter() error {
|
||||
return self.EnterFile(types.OnFocusOpts{ClickedViewName: "", ClickedViewLineIdx: -1})
|
||||
return self.EnterFile(types.OnFocusOpts{ClickedWindowName: "", ClickedViewLineIdx: -1})
|
||||
}
|
||||
|
||||
func (self *FilesController) EnterFile(opts types.OnFocusOpts) error {
|
||||
@ -389,7 +407,7 @@ func (self *FilesController) toggleStagedAll() error {
|
||||
return err
|
||||
}
|
||||
|
||||
return self.context().HandleFocus()
|
||||
return self.context().HandleFocus(types.OnFocusOpts{})
|
||||
}
|
||||
|
||||
func (self *FilesController) toggleStagedAllWithLock() error {
|
||||
@ -828,11 +846,11 @@ func (self *FilesController) handleStashSave(stashFunc func(message string) erro
|
||||
}
|
||||
|
||||
func (self *FilesController) onClickMain(opts gocui.ViewMouseBindingOpts) error {
|
||||
return self.EnterFile(types.OnFocusOpts{ClickedViewName: "main", ClickedViewLineIdx: opts.Y})
|
||||
return self.EnterFile(types.OnFocusOpts{ClickedWindowName: "main", ClickedViewLineIdx: opts.Y})
|
||||
}
|
||||
|
||||
func (self *FilesController) onClickSecondary(opts gocui.ViewMouseBindingOpts) error {
|
||||
return self.EnterFile(types.OnFocusOpts{ClickedViewName: "secondary", ClickedViewLineIdx: opts.Y})
|
||||
return self.EnterFile(types.OnFocusOpts{ClickedWindowName: "secondary", ClickedViewLineIdx: opts.Y})
|
||||
}
|
||||
|
||||
func (self *FilesController) fetch() error {
|
||||
|
@ -3,6 +3,7 @@ package helpers
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
@ -11,17 +12,20 @@ type IPatchBuildingHelper interface {
|
||||
}
|
||||
|
||||
type PatchBuildingHelper struct {
|
||||
c *types.HelperCommon
|
||||
git *commands.GitCommand
|
||||
c *types.HelperCommon
|
||||
git *commands.GitCommand
|
||||
contexts *context.ContextTree
|
||||
}
|
||||
|
||||
func NewPatchBuildingHelper(
|
||||
c *types.HelperCommon,
|
||||
git *commands.GitCommand,
|
||||
contexts *context.ContextTree,
|
||||
) *PatchBuildingHelper {
|
||||
return &PatchBuildingHelper{
|
||||
c: c,
|
||||
git: git,
|
||||
c: c,
|
||||
git: git,
|
||||
contexts: contexts,
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,3 +35,28 @@ func (self *PatchBuildingHelper) ValidateNormalWorkingTreeState() (bool, error)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// takes us from the patch building panel back to the commit files panel
|
||||
func (self *PatchBuildingHelper) Escape() error {
|
||||
return self.c.PushContext(self.contexts.CommitFiles)
|
||||
}
|
||||
|
||||
// kills the custom patch and returns us back to the commit files panel if needed
|
||||
func (self *PatchBuildingHelper) Reset() error {
|
||||
self.git.Patch.PatchManager.Reset()
|
||||
|
||||
if self.c.CurrentStaticContext().GetKind() != types.SIDE_CONTEXT {
|
||||
if err := self.Escape(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := self.c.Refresh(types.RefreshOptions{
|
||||
Scope: []types.RefreshableView{types.COMMIT_FILES},
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// refreshing the current context so that the secondary panel is hidden if necessary.
|
||||
return self.c.PostRefreshUpdate(self.c.CurrentContext())
|
||||
}
|
||||
|
@ -51,22 +51,24 @@ func (self *ListController) HandleScrollRight() error {
|
||||
}
|
||||
|
||||
func (self *ListController) HandleScrollUp() error {
|
||||
self.context.GetViewTrait().ScrollUp()
|
||||
scrollHeight := self.c.UserConfig.Gui.ScrollHeight
|
||||
self.context.GetViewTrait().ScrollUp(scrollHeight)
|
||||
|
||||
// we only need to do a line change if our line has been pushed out of the viewport, because
|
||||
// at the moment much logic depends on the selected line always being visible
|
||||
if !self.isSelectedLineInViewPort() {
|
||||
return self.handleLineChange(-1)
|
||||
return self.handleLineChange(-scrollHeight)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ListController) HandleScrollDown() error {
|
||||
self.context.GetViewTrait().ScrollDown()
|
||||
scrollHeight := self.c.UserConfig.Gui.ScrollHeight
|
||||
self.context.GetViewTrait().ScrollDown(scrollHeight)
|
||||
|
||||
if !self.isSelectedLineInViewPort() {
|
||||
return self.handleLineChange(1)
|
||||
return self.handleLineChange(scrollHeight)
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -81,7 +83,7 @@ func (self *ListController) isSelectedLineInViewPort() bool {
|
||||
func (self *ListController) scrollHorizontal(scrollFunc func()) error {
|
||||
scrollFunc()
|
||||
|
||||
return self.context.HandleFocus()
|
||||
return self.context.HandleFocus(types.OnFocusOpts{})
|
||||
}
|
||||
|
||||
func (self *ListController) handleLineChange(change int) error {
|
||||
@ -96,7 +98,7 @@ func (self *ListController) handleLineChange(change int) error {
|
||||
// doing this check so that if we're holding the up key at the start of the list
|
||||
// we're not constantly re-rendering the main view.
|
||||
if before != after {
|
||||
return self.context.HandleFocus()
|
||||
return self.context.HandleFocus(types.OnFocusOpts{})
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -136,7 +138,7 @@ func (self *ListController) HandleClick(opts gocui.ViewMouseBindingOpts) error {
|
||||
if prevSelectedLineIdx == newSelectedLineIdx && alreadyFocused && self.context.GetOnClick() != nil {
|
||||
return self.context.GetOnClick()()
|
||||
}
|
||||
return self.context.HandleFocus()
|
||||
return self.context.HandleFocus(types.OnFocusOpts{})
|
||||
}
|
||||
|
||||
func (self *ListController) pushContextIfNotFocused() error {
|
||||
@ -182,22 +184,19 @@ func (self *ListController) GetKeybindings(opts types.KeybindingsOpts) []*types.
|
||||
func (self *ListController) GetMouseKeybindings(opts types.KeybindingsOpts) []*gocui.ViewMouseBinding {
|
||||
return []*gocui.ViewMouseBinding{
|
||||
{
|
||||
ViewName: self.context.GetViewName(),
|
||||
ToContext: string(self.context.GetKey()),
|
||||
Key: gocui.MouseWheelUp,
|
||||
Handler: func(gocui.ViewMouseBindingOpts) error { return self.HandleScrollUp() },
|
||||
ViewName: self.context.GetViewName(),
|
||||
Key: gocui.MouseWheelUp,
|
||||
Handler: func(gocui.ViewMouseBindingOpts) error { return self.HandleScrollUp() },
|
||||
},
|
||||
{
|
||||
ViewName: self.context.GetViewName(),
|
||||
ToContext: string(self.context.GetKey()),
|
||||
Key: gocui.MouseLeft,
|
||||
Handler: func(opts gocui.ViewMouseBindingOpts) error { return self.HandleClick(opts) },
|
||||
ViewName: self.context.GetViewName(),
|
||||
Key: gocui.MouseLeft,
|
||||
Handler: func(opts gocui.ViewMouseBindingOpts) error { return self.HandleClick(opts) },
|
||||
},
|
||||
{
|
||||
ViewName: self.context.GetViewName(),
|
||||
ToContext: string(self.context.GetKey()),
|
||||
Key: gocui.MouseWheelDown,
|
||||
Handler: func(gocui.ViewMouseBindingOpts) error { return self.HandleScrollDown() },
|
||||
ViewName: self.context.GetViewName(),
|
||||
Key: gocui.MouseWheelDown,
|
||||
Handler: func(gocui.ViewMouseBindingOpts) error { return self.HandleScrollDown() },
|
||||
},
|
||||
}
|
||||
}
|
||||
|
138
pkg/gui/controllers/patch_building_controller.go
Normal file
138
pkg/gui/controllers/patch_building_controller.go
Normal file
@ -0,0 +1,138 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type PatchBuildingController struct {
|
||||
baseController
|
||||
*controllerCommon
|
||||
}
|
||||
|
||||
var _ types.IController = &PatchBuildingController{}
|
||||
|
||||
func NewPatchBuildingController(
|
||||
common *controllerCommon,
|
||||
) *PatchBuildingController {
|
||||
return &PatchBuildingController{
|
||||
baseController: baseController{},
|
||||
controllerCommon: common,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *PatchBuildingController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
|
||||
return []*types.Binding{
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.OpenFile),
|
||||
Handler: self.OpenFile,
|
||||
Description: self.c.Tr.LcOpenFile,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.Edit),
|
||||
Handler: self.EditFile,
|
||||
Description: self.c.Tr.LcEditFile,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.Select),
|
||||
Handler: self.ToggleSelectionAndRefresh,
|
||||
Description: self.c.Tr.ToggleSelectionForPatch,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.Return),
|
||||
Handler: self.Escape,
|
||||
Description: self.c.Tr.ExitCustomPatchBuilder,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (self *PatchBuildingController) Context() types.Context {
|
||||
return self.contexts.CustomPatchBuilder
|
||||
}
|
||||
|
||||
func (self *PatchBuildingController) context() types.IPatchExplorerContext {
|
||||
return self.contexts.CustomPatchBuilder
|
||||
}
|
||||
|
||||
func (self *PatchBuildingController) GetMouseKeybindings(opts types.KeybindingsOpts) []*gocui.ViewMouseBinding {
|
||||
return []*gocui.ViewMouseBinding{}
|
||||
}
|
||||
|
||||
func (self *PatchBuildingController) OpenFile() error {
|
||||
self.context().GetMutex().Lock()
|
||||
defer self.context().GetMutex().Unlock()
|
||||
|
||||
path := self.contexts.CommitFiles.GetSelectedPath()
|
||||
|
||||
if path == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
lineNumber := self.context().GetState().CurrentLineNumber()
|
||||
return self.helpers.Files.OpenFileAtLine(path, lineNumber)
|
||||
}
|
||||
|
||||
func (self *PatchBuildingController) EditFile() error {
|
||||
self.context().GetMutex().Lock()
|
||||
defer self.context().GetMutex().Unlock()
|
||||
|
||||
path := self.contexts.CommitFiles.GetSelectedPath()
|
||||
|
||||
if path == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
lineNumber := self.context().GetState().CurrentLineNumber()
|
||||
return self.helpers.Files.EditFileAtLine(path, lineNumber)
|
||||
}
|
||||
|
||||
func (self *PatchBuildingController) ToggleSelectionAndRefresh() error {
|
||||
if err := self.toggleSelection(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return self.c.Refresh(types.RefreshOptions{
|
||||
Scope: []types.RefreshableView{types.PATCH_BUILDING, types.COMMIT_FILES},
|
||||
})
|
||||
}
|
||||
|
||||
func (self *PatchBuildingController) toggleSelection() error {
|
||||
self.context().GetMutex().Lock()
|
||||
defer self.context().GetMutex().Unlock()
|
||||
|
||||
toggleFunc := self.git.Patch.PatchManager.AddFileLineRange
|
||||
filename := self.contexts.CommitFiles.GetSelectedPath()
|
||||
if filename == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
state := self.context().GetState()
|
||||
|
||||
includedLineIndices, err := self.git.Patch.PatchManager.GetFileIncLineIndices(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
currentLineIsStaged := lo.Contains(includedLineIndices, state.GetSelectedLineIdx())
|
||||
if currentLineIsStaged {
|
||||
toggleFunc = self.git.Patch.PatchManager.RemoveFileLineRange
|
||||
}
|
||||
|
||||
// add range of lines to those set for the file
|
||||
firstLineIdx, lastLineIdx := state.SelectedRange()
|
||||
|
||||
if err := toggleFunc(filename, firstLineIdx, lastLineIdx); err != nil {
|
||||
// might actually want to return an error here
|
||||
self.c.Log.Error(err)
|
||||
}
|
||||
|
||||
if state.SelectingRange() {
|
||||
state.SetLineSelectMode()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *PatchBuildingController) Escape() error {
|
||||
return self.helpers.PatchBuilding.Escape()
|
||||
}
|
289
pkg/gui/controllers/patch_explorer_controller.go
Normal file
289
pkg/gui/controllers/patch_explorer_controller.go
Normal file
@ -0,0 +1,289 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
type PatchExplorerControllerFactory struct {
|
||||
*controllerCommon
|
||||
}
|
||||
|
||||
func NewPatchExplorerControllerFactory(c *controllerCommon) *PatchExplorerControllerFactory {
|
||||
return &PatchExplorerControllerFactory{
|
||||
controllerCommon: c,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *PatchExplorerControllerFactory) Create(context types.IPatchExplorerContext) *PatchExplorerController {
|
||||
return &PatchExplorerController{
|
||||
baseController: baseController{},
|
||||
controllerCommon: self.controllerCommon,
|
||||
context: context,
|
||||
}
|
||||
}
|
||||
|
||||
type PatchExplorerController struct {
|
||||
baseController
|
||||
*controllerCommon
|
||||
|
||||
context types.IPatchExplorerContext
|
||||
}
|
||||
|
||||
func (self *PatchExplorerController) Context() types.Context {
|
||||
return self.context
|
||||
}
|
||||
|
||||
func (self *PatchExplorerController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
|
||||
return []*types.Binding{
|
||||
{
|
||||
Tag: "navigation",
|
||||
Key: opts.GetKey(opts.Config.Universal.PrevItemAlt),
|
||||
Handler: self.withRenderAndFocus(self.HandlePrevLine),
|
||||
},
|
||||
{
|
||||
Tag: "navigation",
|
||||
Key: opts.GetKey(opts.Config.Universal.PrevItem),
|
||||
Handler: self.withRenderAndFocus(self.HandlePrevLine),
|
||||
},
|
||||
{
|
||||
Tag: "navigation",
|
||||
Key: opts.GetKey(opts.Config.Universal.NextItemAlt),
|
||||
Handler: self.withRenderAndFocus(self.HandleNextLine),
|
||||
},
|
||||
{
|
||||
Tag: "navigation",
|
||||
Key: opts.GetKey(opts.Config.Universal.NextItem),
|
||||
Handler: self.withRenderAndFocus(self.HandleNextLine),
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.PrevBlock),
|
||||
Handler: self.withRenderAndFocus(self.HandlePrevHunk),
|
||||
Description: self.c.Tr.PrevHunk,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.PrevBlockAlt),
|
||||
Handler: self.withRenderAndFocus(self.HandlePrevHunk),
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.NextBlock),
|
||||
Handler: self.withRenderAndFocus(self.HandleNextHunk),
|
||||
Description: self.c.Tr.NextHunk,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.NextBlockAlt),
|
||||
Handler: self.withRenderAndFocus(self.HandleNextHunk),
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Main.ToggleDragSelect),
|
||||
Handler: self.withRenderAndFocus(self.HandleToggleSelectRange),
|
||||
Description: self.c.Tr.ToggleDragSelect,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Main.ToggleDragSelectAlt),
|
||||
Handler: self.withRenderAndFocus(self.HandleToggleSelectRange),
|
||||
Description: self.c.Tr.ToggleDragSelect,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Main.ToggleSelectHunk),
|
||||
Handler: self.withRenderAndFocus(self.HandleToggleSelectHunk),
|
||||
Description: self.c.Tr.ToggleSelectHunk,
|
||||
},
|
||||
{
|
||||
Tag: "navigation",
|
||||
Key: opts.GetKey(opts.Config.Universal.PrevPage),
|
||||
Handler: self.withRenderAndFocus(self.HandlePrevPage),
|
||||
Description: self.c.Tr.LcPrevPage,
|
||||
},
|
||||
{
|
||||
Tag: "navigation",
|
||||
Key: opts.GetKey(opts.Config.Universal.NextPage),
|
||||
Handler: self.withRenderAndFocus(self.HandleNextPage),
|
||||
Description: self.c.Tr.LcNextPage,
|
||||
},
|
||||
{
|
||||
Tag: "navigation",
|
||||
Key: opts.GetKey(opts.Config.Universal.GotoTop),
|
||||
Handler: self.withRenderAndFocus(self.HandleGotoTop),
|
||||
Description: self.c.Tr.LcGotoTop,
|
||||
},
|
||||
{
|
||||
Tag: "navigation",
|
||||
Key: opts.GetKey(opts.Config.Universal.GotoBottom),
|
||||
Description: self.c.Tr.LcGotoBottom,
|
||||
Handler: self.withRenderAndFocus(self.HandleGotoBottom),
|
||||
},
|
||||
{
|
||||
Tag: "navigation",
|
||||
Key: opts.GetKey(opts.Config.Universal.ScrollLeft),
|
||||
Handler: self.withRenderAndFocus(self.HandleScrollLeft),
|
||||
},
|
||||
{
|
||||
Tag: "navigation",
|
||||
Key: opts.GetKey(opts.Config.Universal.ScrollRight),
|
||||
Handler: self.withRenderAndFocus(self.HandleScrollRight),
|
||||
},
|
||||
{
|
||||
Tag: "navigation",
|
||||
Key: opts.GetKey(opts.Config.Universal.StartSearch),
|
||||
Handler: func() error { self.c.OpenSearch(); return nil },
|
||||
Description: self.c.Tr.LcStartSearch,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.CopyToClipboard),
|
||||
Handler: self.withLock(self.CopySelectedToClipboard),
|
||||
Description: self.c.Tr.LcCopySelectedTexToClipboard,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (self *PatchExplorerController) GetMouseKeybindings(opts types.KeybindingsOpts) []*gocui.ViewMouseBinding {
|
||||
return []*gocui.ViewMouseBinding{
|
||||
{
|
||||
ViewName: self.context.GetViewName(),
|
||||
Key: gocui.MouseLeft,
|
||||
Handler: func(opts gocui.ViewMouseBindingOpts) error {
|
||||
if self.isFocused() {
|
||||
return self.withRenderAndFocus(self.HandleMouseDown)()
|
||||
}
|
||||
|
||||
return self.c.PushContext(self.context, types.OnFocusOpts{
|
||||
ClickedWindowName: self.context.GetWindowName(),
|
||||
ClickedViewLineIdx: opts.Y,
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
ViewName: self.context.GetViewName(),
|
||||
Key: gocui.MouseLeft,
|
||||
Modifier: gocui.ModMotion,
|
||||
Handler: func(gocui.ViewMouseBindingOpts) error {
|
||||
return self.withRenderAndFocus(self.HandleMouseDrag)()
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (self *PatchExplorerController) HandlePrevLine() error {
|
||||
self.context.GetState().CycleSelection(false)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *PatchExplorerController) HandleNextLine() error {
|
||||
self.context.GetState().CycleSelection(true)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *PatchExplorerController) HandlePrevHunk() error {
|
||||
self.context.GetState().CycleHunk(false)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *PatchExplorerController) HandleNextHunk() error {
|
||||
self.context.GetState().CycleHunk(true)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *PatchExplorerController) HandleToggleSelectRange() error {
|
||||
self.context.GetState().ToggleSelectRange()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *PatchExplorerController) HandleToggleSelectHunk() error {
|
||||
self.context.GetState().ToggleSelectHunk()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *PatchExplorerController) HandleScrollLeft() error {
|
||||
self.context.GetViewTrait().ScrollLeft()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *PatchExplorerController) HandleScrollRight() error {
|
||||
self.context.GetViewTrait().ScrollRight()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *PatchExplorerController) HandlePrevPage() error {
|
||||
self.context.GetState().SetLineSelectMode()
|
||||
self.context.GetState().AdjustSelectedLineIdx(-self.context.GetViewTrait().PageDelta())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *PatchExplorerController) HandleNextPage() error {
|
||||
self.context.GetState().SetLineSelectMode()
|
||||
self.context.GetState().AdjustSelectedLineIdx(self.context.GetViewTrait().PageDelta())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *PatchExplorerController) HandleGotoTop() error {
|
||||
self.context.GetState().SelectTop()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *PatchExplorerController) HandleGotoBottom() error {
|
||||
self.context.GetState().SelectBottom()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *PatchExplorerController) HandleMouseDown() error {
|
||||
self.context.GetState().SelectNewLineForRange(self.context.GetViewTrait().SelectedLineIdx())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *PatchExplorerController) HandleMouseDrag() error {
|
||||
self.context.GetState().SelectLine(self.context.GetViewTrait().SelectedLineIdx())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *PatchExplorerController) CopySelectedToClipboard() error {
|
||||
selected := self.context.GetState().PlainRenderSelected()
|
||||
|
||||
self.c.LogAction(self.c.Tr.Actions.CopySelectedTextToClipboard)
|
||||
if err := self.os.CopyToClipboard(selected); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *PatchExplorerController) isFocused() bool {
|
||||
return self.c.CurrentContext().GetKey() == self.context.GetKey()
|
||||
}
|
||||
|
||||
func (self *PatchExplorerController) withRenderAndFocus(f func() error) func() error {
|
||||
return self.withLock(func() error {
|
||||
if err := f(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return self.context.RenderAndFocus(self.isFocused())
|
||||
})
|
||||
}
|
||||
|
||||
func (self *PatchExplorerController) withLock(f func() error) func() error {
|
||||
return func() error {
|
||||
self.context.GetMutex().Lock()
|
||||
defer self.context.GetMutex().Unlock()
|
||||
|
||||
if self.context.GetState() == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return f()
|
||||
}
|
||||
}
|
70
pkg/gui/controllers/scroll_controller.go
Normal file
70
pkg/gui/controllers/scroll_controller.go
Normal file
@ -0,0 +1,70 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
// given we have no fields here, arguably we shouldn't even need this factory
|
||||
// struct, but we're maintaining consistency with the other files.
|
||||
type VerticalScrollControllerFactory struct {
|
||||
controllerCommon *controllerCommon
|
||||
}
|
||||
|
||||
func NewVerticalScrollControllerFactory(c *controllerCommon) *VerticalScrollControllerFactory {
|
||||
return &VerticalScrollControllerFactory{controllerCommon: c}
|
||||
}
|
||||
|
||||
func (self *VerticalScrollControllerFactory) Create(context types.Context) types.IController {
|
||||
return &VerticalScrollController{
|
||||
baseController: baseController{},
|
||||
controllerCommon: self.controllerCommon,
|
||||
context: context,
|
||||
}
|
||||
}
|
||||
|
||||
type VerticalScrollController struct {
|
||||
baseController
|
||||
*controllerCommon
|
||||
|
||||
context types.Context
|
||||
}
|
||||
|
||||
func (self *VerticalScrollController) Context() types.Context {
|
||||
return self.context
|
||||
}
|
||||
|
||||
func (self *VerticalScrollController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
|
||||
return []*types.Binding{}
|
||||
}
|
||||
|
||||
func (self *VerticalScrollController) GetMouseKeybindings(opts types.KeybindingsOpts) []*gocui.ViewMouseBinding {
|
||||
return []*gocui.ViewMouseBinding{
|
||||
{
|
||||
ViewName: self.context.GetViewName(),
|
||||
Key: gocui.MouseWheelUp,
|
||||
Handler: func(gocui.ViewMouseBindingOpts) error {
|
||||
return self.HandleScrollUp()
|
||||
},
|
||||
},
|
||||
{
|
||||
ViewName: self.context.GetViewName(),
|
||||
Key: gocui.MouseWheelDown,
|
||||
Handler: func(gocui.ViewMouseBindingOpts) error {
|
||||
return self.HandleScrollDown()
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (self *VerticalScrollController) HandleScrollUp() error {
|
||||
self.context.GetViewTrait().ScrollUp(self.c.UserConfig.Gui.ScrollHeight)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *VerticalScrollController) HandleScrollDown() error {
|
||||
self.context.GetViewTrait().ScrollDown(self.c.UserConfig.Gui.ScrollHeight)
|
||||
|
||||
return nil
|
||||
}
|
242
pkg/gui/controllers/staging_controller.go
Normal file
242
pkg/gui/controllers/staging_controller.go
Normal file
@ -0,0 +1,242 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/patch"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
type StagingController struct {
|
||||
baseController
|
||||
*controllerCommon
|
||||
|
||||
context types.IPatchExplorerContext
|
||||
otherContext types.IPatchExplorerContext
|
||||
|
||||
// if true, we're dealing with the secondary context i.e. dealing with staged file changes
|
||||
staged bool
|
||||
}
|
||||
|
||||
var _ types.IController = &StagingController{}
|
||||
|
||||
func NewStagingController(
|
||||
common *controllerCommon,
|
||||
context types.IPatchExplorerContext,
|
||||
otherContext types.IPatchExplorerContext,
|
||||
staged bool,
|
||||
) *StagingController {
|
||||
return &StagingController{
|
||||
baseController: baseController{},
|
||||
controllerCommon: common,
|
||||
context: context,
|
||||
otherContext: otherContext,
|
||||
staged: staged,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *StagingController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
|
||||
return []*types.Binding{
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.OpenFile),
|
||||
Handler: self.OpenFile,
|
||||
Description: self.c.Tr.LcOpenFile,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.Edit),
|
||||
Handler: self.EditFile,
|
||||
Description: self.c.Tr.LcEditFile,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.Return),
|
||||
Handler: self.Escape,
|
||||
Description: self.c.Tr.ReturnToFilesPanel,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.TogglePanel),
|
||||
Handler: self.TogglePanel,
|
||||
Description: self.c.Tr.ToggleStagingPanel,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.Select),
|
||||
Handler: self.ToggleStaged,
|
||||
Description: self.c.Tr.StageSelection,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.Remove),
|
||||
Handler: self.ResetSelection,
|
||||
Description: self.c.Tr.ResetSelection,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Main.EditSelectHunk),
|
||||
Handler: self.EditHunkAndRefresh,
|
||||
Description: self.c.Tr.EditHunk,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (self *StagingController) Context() types.Context {
|
||||
return self.context
|
||||
}
|
||||
|
||||
func (self *StagingController) GetMouseKeybindings(opts types.KeybindingsOpts) []*gocui.ViewMouseBinding {
|
||||
return []*gocui.ViewMouseBinding{}
|
||||
}
|
||||
|
||||
func (self *StagingController) OpenFile() error {
|
||||
self.context.GetMutex().Lock()
|
||||
defer self.context.GetMutex().Unlock()
|
||||
|
||||
path := self.FilePath()
|
||||
|
||||
if path == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
lineNumber := self.context.GetState().CurrentLineNumber()
|
||||
return self.helpers.Files.OpenFileAtLine(path, lineNumber)
|
||||
}
|
||||
|
||||
func (self *StagingController) EditFile() error {
|
||||
self.context.GetMutex().Lock()
|
||||
defer self.context.GetMutex().Unlock()
|
||||
|
||||
path := self.FilePath()
|
||||
|
||||
if path == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
lineNumber := self.context.GetState().CurrentLineNumber()
|
||||
return self.helpers.Files.EditFileAtLine(path, lineNumber)
|
||||
}
|
||||
|
||||
func (self *StagingController) Escape() error {
|
||||
return self.c.PushContext(self.contexts.Files)
|
||||
}
|
||||
|
||||
func (self *StagingController) TogglePanel() error {
|
||||
if self.otherContext.GetState() != nil {
|
||||
return self.c.PushContext(self.otherContext)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *StagingController) ToggleStaged() error {
|
||||
return self.applySelectionAndRefresh(self.staged)
|
||||
}
|
||||
|
||||
func (self *StagingController) ResetSelection() error {
|
||||
reset := func() error { return self.applySelectionAndRefresh(true) }
|
||||
|
||||
if !self.staged && !self.c.UserConfig.Gui.SkipUnstageLineWarning {
|
||||
return self.c.Confirm(types.ConfirmOpts{
|
||||
Title: self.c.Tr.UnstageLinesTitle,
|
||||
Prompt: self.c.Tr.UnstageLinesPrompt,
|
||||
HandleConfirm: reset,
|
||||
})
|
||||
}
|
||||
|
||||
return reset()
|
||||
}
|
||||
|
||||
func (self *StagingController) applySelectionAndRefresh(reverse bool) error {
|
||||
if err := self.applySelection(reverse); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.FILES, types.STAGING}})
|
||||
}
|
||||
|
||||
func (self *StagingController) applySelection(reverse bool) error {
|
||||
self.context.GetMutex().Lock()
|
||||
defer self.context.GetMutex().Unlock()
|
||||
|
||||
state := self.context.GetState()
|
||||
path := self.FilePath()
|
||||
if path == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
firstLineIdx, lastLineIdx := state.SelectedRange()
|
||||
patch := patch.ModifiedPatchForRange(self.c.Log, path, state.GetDiff(), firstLineIdx, lastLineIdx, reverse, false)
|
||||
|
||||
if patch == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// apply the patch then refresh this panel
|
||||
// create a new temp file with the patch, then call git apply with that patch
|
||||
applyFlags := []string{}
|
||||
if !reverse || self.staged {
|
||||
applyFlags = append(applyFlags, "cached")
|
||||
}
|
||||
self.c.LogAction(self.c.Tr.Actions.ApplyPatch)
|
||||
err := self.git.WorkingTree.ApplyPatch(patch, applyFlags...)
|
||||
if err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
if state.SelectingRange() {
|
||||
state.SetLineSelectMode()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *StagingController) EditHunkAndRefresh() error {
|
||||
if err := self.editHunk(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.FILES, types.STAGING}})
|
||||
}
|
||||
|
||||
func (self *StagingController) editHunk() error {
|
||||
self.context.GetMutex().Lock()
|
||||
defer self.context.GetMutex().Unlock()
|
||||
|
||||
state := self.context.GetState()
|
||||
path := self.FilePath()
|
||||
if path == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
hunk := state.CurrentHunk()
|
||||
patchText := patch.ModifiedPatchForRange(
|
||||
self.c.Log, path, state.GetDiff(), hunk.FirstLineIdx, hunk.LastLineIdx(), self.staged, false,
|
||||
)
|
||||
patchFilepath, err := self.git.WorkingTree.SaveTemporaryPatch(patchText)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lineOffset := 3
|
||||
lineIdxInHunk := state.GetSelectedLineIdx() - hunk.FirstLineIdx
|
||||
if err := self.helpers.Files.EditFileAtLine(patchFilepath, lineIdxInHunk+lineOffset); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
editedPatchText, err := self.git.File.Cat(patchFilepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.c.LogAction(self.c.Tr.Actions.ApplyPatch)
|
||||
|
||||
lineCount := strings.Count(editedPatchText, "\n") + 1
|
||||
newPatchText := patch.ModifiedPatchForRange(
|
||||
self.c.Log, path, editedPatchText, 0, lineCount, false, false,
|
||||
)
|
||||
if err := self.git.WorkingTree.ApplyPatch(newPatchText, "cached"); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *StagingController) FilePath() string {
|
||||
return self.contexts.Files.GetSelectedPath()
|
||||
}
|
@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
@ -16,7 +15,7 @@ func (gui *Gui) handleCreatePatchOptionsMenu() error {
|
||||
menuItems := []*types.MenuItem{
|
||||
{
|
||||
Label: "reset patch",
|
||||
OnPress: gui.handleResetPatch,
|
||||
OnPress: gui.helpers.PatchBuilding.Reset,
|
||||
Key: 'c',
|
||||
},
|
||||
{
|
||||
@ -89,9 +88,9 @@ func (gui *Gui) validateNormalWorkingTreeState() (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (gui *Gui) returnFocusFromLineByLinePanelIfNecessary() error {
|
||||
if gui.State.MainContext == context.MAIN_PATCH_BUILDING_CONTEXT_KEY {
|
||||
return gui.handleEscapePatchBuildingPanel()
|
||||
func (gui *Gui) returnFocusFromPatchExplorerIfNecessary() error {
|
||||
if gui.currentContext().GetKey() == gui.State.Contexts.CustomPatchBuilder.GetKey() {
|
||||
return gui.helpers.PatchBuilding.Escape()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -101,7 +100,7 @@ func (gui *Gui) handleDeletePatchFromCommit() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := gui.returnFocusFromLineByLinePanelIfNecessary(); err != nil {
|
||||
if err := gui.returnFocusFromPatchExplorerIfNecessary(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -118,7 +117,7 @@ func (gui *Gui) handleMovePatchToSelectedCommit() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := gui.returnFocusFromLineByLinePanelIfNecessary(); err != nil {
|
||||
if err := gui.returnFocusFromPatchExplorerIfNecessary(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -135,7 +134,7 @@ func (gui *Gui) handleMovePatchIntoWorkingTree() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := gui.returnFocusFromLineByLinePanelIfNecessary(); err != nil {
|
||||
if err := gui.returnFocusFromPatchExplorerIfNecessary(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -166,7 +165,7 @@ func (gui *Gui) handlePullPatchIntoNewCommit() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := gui.returnFocusFromLineByLinePanelIfNecessary(); err != nil {
|
||||
if err := gui.returnFocusFromPatchExplorerIfNecessary(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -179,7 +178,7 @@ func (gui *Gui) handlePullPatchIntoNewCommit() error {
|
||||
}
|
||||
|
||||
func (gui *Gui) handleApplyPatch(reverse bool) error {
|
||||
if err := gui.returnFocusFromLineByLinePanelIfNecessary(); err != nil {
|
||||
if err := gui.returnFocusFromPatchExplorerIfNecessary(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -193,18 +192,3 @@ func (gui *Gui) handleApplyPatch(reverse bool) error {
|
||||
}
|
||||
return gui.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleResetPatch() error {
|
||||
gui.git.Patch.PatchManager.Reset()
|
||||
if gui.currentContextKeyIgnoringPopups() == context.MAIN_PATCH_BUILDING_CONTEXT_KEY {
|
||||
if err := gui.c.PushContext(gui.State.Contexts.CommitFiles); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := gui.handleEscapePatchBuildingPanel(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return gui.refreshCommitFilesContext()
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
var CONTEXT_KEYS_SHOWING_DIFFS = []types.ContextKey{
|
||||
context.FILES_CONTEXT_KEY,
|
||||
context.COMMIT_FILES_CONTEXT_KEY,
|
||||
context.STASH_CONTEXT_KEY,
|
||||
context.LOCAL_COMMITS_CONTEXT_KEY,
|
||||
context.SUB_COMMITS_CONTEXT_KEY,
|
||||
context.MAIN_STAGING_CONTEXT_KEY,
|
||||
context.MAIN_PATCH_BUILDING_CONTEXT_KEY,
|
||||
}
|
||||
|
||||
func isShowingDiff(gui *Gui) bool {
|
||||
key := gui.currentStaticContext().GetKey()
|
||||
|
||||
for _, contextKey := range CONTEXT_KEYS_SHOWING_DIFFS {
|
||||
if key == contextKey {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (gui *Gui) IncreaseContextInDiffView() error {
|
||||
if isShowingDiff(gui) {
|
||||
if err := gui.CheckCanChangeContext(); err != nil {
|
||||
return gui.c.Error(err)
|
||||
}
|
||||
|
||||
gui.c.UserConfig.Git.DiffContextSize = gui.c.UserConfig.Git.DiffContextSize + 1
|
||||
return gui.handleDiffContextSizeChange()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) DecreaseContextInDiffView() error {
|
||||
old_size := gui.c.UserConfig.Git.DiffContextSize
|
||||
|
||||
if isShowingDiff(gui) && old_size > 1 {
|
||||
if err := gui.CheckCanChangeContext(); err != nil {
|
||||
return gui.c.Error(err)
|
||||
}
|
||||
|
||||
gui.c.UserConfig.Git.DiffContextSize = old_size - 1
|
||||
return gui.handleDiffContextSizeChange()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) handleDiffContextSizeChange() error {
|
||||
currentContext := gui.currentStaticContext()
|
||||
switch currentContext.GetKey() {
|
||||
// we make an exception for our staging and patch building contexts because they actually need to refresh their state afterwards.
|
||||
case context.MAIN_PATCH_BUILDING_CONTEXT_KEY:
|
||||
return gui.handleRefreshPatchBuildingPanel(-1)
|
||||
case context.MAIN_STAGING_CONTEXT_KEY:
|
||||
return gui.handleRefreshStagingPanel(false, -1)
|
||||
default:
|
||||
return currentContext.HandleRenderToMain()
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) CheckCanChangeContext() error {
|
||||
if gui.git.Patch.PatchManager.Active() {
|
||||
return errors.New(gui.c.Tr.CantChangeContextSizeError)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -21,6 +21,7 @@ func (gui *Gui) renderDiff() error {
|
||||
task := NewRunPtyTask(cmdObj.GetCmd())
|
||||
|
||||
return gui.refreshMainViews(refreshMainOpts{
|
||||
pair: gui.normalMainContextPair(),
|
||||
main: &viewUpdateOpts{
|
||||
title: "Diff",
|
||||
task: task,
|
||||
|
@ -34,8 +34,9 @@ func (gui *Gui) filesRenderToMain() error {
|
||||
|
||||
if node == nil {
|
||||
return gui.refreshMainViews(refreshMainOpts{
|
||||
pair: gui.normalMainContextPair(),
|
||||
main: &viewUpdateOpts{
|
||||
title: "",
|
||||
title: gui.c.Tr.DiffTitle,
|
||||
task: NewRenderStringTask(gui.c.Tr.NoChangedFiles),
|
||||
},
|
||||
})
|
||||
@ -53,31 +54,38 @@ func (gui *Gui) filesRenderToMain() error {
|
||||
|
||||
gui.resetMergeStateWithLock()
|
||||
|
||||
mainContext := gui.State.Contexts.Normal
|
||||
pair := gui.normalMainContextPair()
|
||||
if node.File != nil {
|
||||
mainContext = gui.State.Contexts.Staging
|
||||
pair = gui.stagingMainContextPair()
|
||||
}
|
||||
|
||||
split := gui.c.UserConfig.Gui.SplitDiff == "always" || (node.GetHasUnstagedChanges() && node.GetHasStagedChanges())
|
||||
mainShowsStaged := !split && node.GetHasStagedChanges()
|
||||
|
||||
cmdObj := gui.git.WorkingTree.WorktreeFileDiffCmdObj(node, false, mainShowsStaged, gui.IgnoreWhitespaceInDiffView)
|
||||
refreshOpts := refreshMainOpts{main: &viewUpdateOpts{
|
||||
title: gui.c.Tr.UnstagedChanges,
|
||||
task: NewRunPtyTask(cmdObj.GetCmd()),
|
||||
context: mainContext,
|
||||
}}
|
||||
title := gui.c.Tr.UnstagedChanges
|
||||
if mainShowsStaged {
|
||||
refreshOpts.main.title = gui.c.Tr.StagedChanges
|
||||
title = gui.c.Tr.StagedChanges
|
||||
}
|
||||
refreshOpts := refreshMainOpts{
|
||||
pair: pair,
|
||||
main: &viewUpdateOpts{
|
||||
task: NewRunPtyTask(cmdObj.GetCmd()),
|
||||
title: title,
|
||||
},
|
||||
}
|
||||
|
||||
if split {
|
||||
cmdObj := gui.git.WorkingTree.WorktreeFileDiffCmdObj(node, false, true, gui.IgnoreWhitespaceInDiffView)
|
||||
|
||||
title := gui.c.Tr.StagedChanges
|
||||
if mainShowsStaged {
|
||||
title = gui.c.Tr.UnstagedChanges
|
||||
}
|
||||
|
||||
refreshOpts.secondary = &viewUpdateOpts{
|
||||
title: gui.c.Tr.StagedChanges,
|
||||
task: NewRunPtyTask(cmdObj.GetCmd()),
|
||||
context: mainContext,
|
||||
title: title,
|
||||
task: NewRunPtyTask(cmdObj.GetCmd()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,6 +97,8 @@ func (gui *Gui) onFocusFile() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// test
|
||||
|
||||
func (gui *Gui) getSetTextareaTextFn(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
|
||||
@ -98,3 +108,5 @@ func (gui *Gui) getSetTextareaTextFn(getView func() *gocui.View) func(string) {
|
||||
view.RenderTextArea()
|
||||
}
|
||||
}
|
||||
|
||||
// test
|
||||
|
@ -15,8 +15,9 @@ const HORIZONTAL_SCROLL_FACTOR = 3
|
||||
// these views need to be re-rendered when the screen mode changes. The commits view,
|
||||
// for example, will show authorship information in half and full screen mode.
|
||||
func (gui *Gui) rerenderViewsWithScreenModeDependentContent() error {
|
||||
for _, view := range []*gocui.View{gui.Views.Branches, gui.Views.Commits} {
|
||||
if err := gui.rerenderView(view); err != nil {
|
||||
// for now we re-render all list views.
|
||||
for _, context := range gui.getListContexts() {
|
||||
if err := gui.rerenderView(context.GetView()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -24,7 +25,6 @@ func (gui *Gui) rerenderViewsWithScreenModeDependentContent() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: GENERICS
|
||||
func nextIntInCycle(sl []WindowMaximisation, current WindowMaximisation) WindowMaximisation {
|
||||
for i, val := range sl {
|
||||
if val == current {
|
||||
@ -37,7 +37,6 @@ func nextIntInCycle(sl []WindowMaximisation, current WindowMaximisation) WindowM
|
||||
return sl[0]
|
||||
}
|
||||
|
||||
// TODO: GENERICS
|
||||
func prevIntInCycle(sl []WindowMaximisation, current WindowMaximisation) WindowMaximisation {
|
||||
for i, val := range sl {
|
||||
if val == current {
|
||||
@ -80,7 +79,14 @@ func (gui *Gui) scrollUpMain() error {
|
||||
gui.State.Panels.Merging.UserVerticalScrolling = true
|
||||
}
|
||||
|
||||
gui.scrollUpView(gui.Views.Main)
|
||||
var view *gocui.View
|
||||
if gui.c.CurrentContext().GetWindowName() == "secondary" {
|
||||
view = gui.secondaryView()
|
||||
} else {
|
||||
view = gui.mainView()
|
||||
}
|
||||
|
||||
gui.scrollUpView(view)
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -90,19 +96,38 @@ func (gui *Gui) scrollDownMain() error {
|
||||
gui.State.Panels.Merging.UserVerticalScrolling = true
|
||||
}
|
||||
|
||||
gui.scrollDownView(gui.Views.Main)
|
||||
var view *gocui.View
|
||||
if gui.c.CurrentContext().GetWindowName() == "secondary" {
|
||||
view = gui.secondaryView()
|
||||
} else {
|
||||
view = gui.mainView()
|
||||
}
|
||||
|
||||
gui.scrollDownView(view)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) mainView() *gocui.View {
|
||||
viewName := gui.getViewNameForWindow("main")
|
||||
view, _ := gui.g.View(viewName)
|
||||
return view
|
||||
}
|
||||
|
||||
func (gui *Gui) secondaryView() *gocui.View {
|
||||
viewName := gui.getViewNameForWindow("secondary")
|
||||
view, _ := gui.g.View(viewName)
|
||||
return view
|
||||
}
|
||||
|
||||
func (gui *Gui) scrollLeftMain() error {
|
||||
gui.scrollLeft(gui.Views.Main)
|
||||
gui.scrollLeft(gui.mainView())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) scrollRightMain() error {
|
||||
gui.scrollRight(gui.Views.Main)
|
||||
gui.scrollRight(gui.mainView())
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -117,13 +142,15 @@ func (gui *Gui) scrollRight(view *gocui.View) {
|
||||
}
|
||||
|
||||
func (gui *Gui) scrollUpSecondary() error {
|
||||
gui.scrollUpView(gui.Views.Secondary)
|
||||
gui.scrollUpView(gui.secondaryView())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) scrollDownSecondary() error {
|
||||
gui.scrollDownView(gui.Views.Secondary)
|
||||
secondaryView := gui.secondaryView()
|
||||
|
||||
gui.scrollDownView(secondaryView)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/lbl"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/mergeconflicts"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/modes/cherrypicking"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/modes/diffing"
|
||||
@ -175,11 +174,8 @@ type GuiRepoState struct {
|
||||
Ptmx *os.File
|
||||
StartupStage StartupStage // Allows us to not load everything at once
|
||||
|
||||
MainContext types.ContextKey // used to keep the main and secondary views' contexts in sync
|
||||
ContextManager ContextManager
|
||||
Contexts *context.ContextTree
|
||||
ViewContextMap *context.ViewContextMap
|
||||
ViewTabContextMap map[string][]context.TabContext
|
||||
ContextManager ContextManager
|
||||
Contexts *context.ContextTree
|
||||
|
||||
// WindowViewNameMap is a mapping of windows to the current view of that window.
|
||||
// Some views move between windows for example the commitFiles view and when cycling through
|
||||
@ -200,14 +196,6 @@ type GuiRepoState struct {
|
||||
CurrentPopupOpts *types.CreatePopupPanelOpts
|
||||
}
|
||||
|
||||
// for now the staging panel state, unlike the other panel states, is going to be
|
||||
// non-mutative, so that we don't accidentally end up
|
||||
// with mismatches of data. We might change this in the future
|
||||
type LblPanelState struct {
|
||||
*lbl.State
|
||||
SecondaryFocused bool // this is for if we show the left or right panel
|
||||
}
|
||||
|
||||
type MergingPanelState struct {
|
||||
*mergeconflicts.State
|
||||
|
||||
@ -219,8 +207,7 @@ type MergingPanelState struct {
|
||||
// as we move things to the new context approach we're going to eventually
|
||||
// remove this struct altogether and store this state on the contexts.
|
||||
type panelStates struct {
|
||||
LineByLine *LblPanelState
|
||||
Merging *MergingPanelState
|
||||
Merging *MergingPanelState
|
||||
}
|
||||
|
||||
type searchingState struct {
|
||||
@ -284,7 +271,6 @@ func (gui *Gui) resetState(startArgs types.StartArgs, reuseState bool) {
|
||||
gui.State.CurrentPopupOpts = nil
|
||||
gui.Mutexes.PopupMutex.Unlock()
|
||||
|
||||
gui.syncViewContexts()
|
||||
return
|
||||
}
|
||||
} else {
|
||||
@ -297,10 +283,7 @@ func (gui *Gui) resetState(startArgs types.StartArgs, reuseState bool) {
|
||||
initialContext := initialContext(contextTree, startArgs)
|
||||
initialScreenMode := initialScreenMode(startArgs)
|
||||
|
||||
viewContextMap := context.NewViewContextMap()
|
||||
for viewName, context := range initialViewContextMapping(contextTree) {
|
||||
viewContextMap.Set(viewName, context)
|
||||
}
|
||||
initialWindowViewNameMap := gui.initialWindowViewNameMap(contextTree)
|
||||
|
||||
gui.State = &GuiRepoState{
|
||||
Model: &types.Model{
|
||||
@ -326,16 +309,13 @@ func (gui *Gui) resetState(startArgs types.StartArgs, reuseState bool) {
|
||||
CherryPicking: cherrypicking.New(),
|
||||
Diffing: diffing.New(),
|
||||
},
|
||||
ViewContextMap: viewContextMap,
|
||||
ViewTabContextMap: gui.initialViewTabContextMap(contextTree),
|
||||
ScreenMode: initialScreenMode,
|
||||
ScreenMode: initialScreenMode,
|
||||
// TODO: put contexts in the context manager
|
||||
ContextManager: NewContextManager(initialContext),
|
||||
Contexts: contextTree,
|
||||
ContextManager: NewContextManager(initialContext),
|
||||
Contexts: contextTree,
|
||||
WindowViewNameMap: initialWindowViewNameMap,
|
||||
}
|
||||
|
||||
gui.syncViewContexts()
|
||||
|
||||
gui.RepoStateMap[Repo(currentDir)] = gui.State
|
||||
}
|
||||
|
||||
@ -370,16 +350,6 @@ func initialContext(contextTree *context.ContextTree, startArgs types.StartArgs)
|
||||
return initialContext
|
||||
}
|
||||
|
||||
func (gui *Gui) syncViewContexts() {
|
||||
for viewName, context := range gui.State.ViewContextMap.Entries() {
|
||||
view, err := gui.g.View(viewName)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
view.Context = string(context.GetKey())
|
||||
}
|
||||
}
|
||||
|
||||
// for now the split view will always be on
|
||||
// NewGui builds a new gui handler
|
||||
func NewGui(
|
||||
@ -411,9 +381,9 @@ func NewGui(
|
||||
RefreshingStatusMutex: &sync.Mutex{},
|
||||
SyncMutex: &sync.Mutex{},
|
||||
LocalCommitsMutex: &sync.Mutex{},
|
||||
LineByLinePanelMutex: &sync.Mutex{},
|
||||
SubprocessMutex: &sync.Mutex{},
|
||||
PopupMutex: &sync.Mutex{},
|
||||
PtyMutex: &sync.Mutex{},
|
||||
},
|
||||
InitialDir: initialDir,
|
||||
}
|
||||
@ -482,40 +452,40 @@ func (gui *Gui) initGocui() (*gocui.Gui, error) {
|
||||
return g, nil
|
||||
}
|
||||
|
||||
func (gui *Gui) initialViewTabContextMap(contextTree *context.ContextTree) map[string][]context.TabContext {
|
||||
return map[string][]context.TabContext{
|
||||
func (gui *Gui) viewTabMap() map[string][]context.TabView {
|
||||
return map[string][]context.TabView{
|
||||
"branches": {
|
||||
{
|
||||
Tab: gui.c.Tr.LocalBranchesTitle,
|
||||
Context: contextTree.Branches,
|
||||
Tab: gui.c.Tr.LocalBranchesTitle,
|
||||
ViewName: "localBranches",
|
||||
},
|
||||
{
|
||||
Tab: gui.c.Tr.RemotesTitle,
|
||||
Context: contextTree.Remotes,
|
||||
Tab: gui.c.Tr.RemotesTitle,
|
||||
ViewName: "remotes",
|
||||
},
|
||||
{
|
||||
Tab: gui.c.Tr.TagsTitle,
|
||||
Context: contextTree.Tags,
|
||||
Tab: gui.c.Tr.TagsTitle,
|
||||
ViewName: "tags",
|
||||
},
|
||||
},
|
||||
"commits": {
|
||||
{
|
||||
Tab: gui.c.Tr.CommitsTitle,
|
||||
Context: contextTree.LocalCommits,
|
||||
Tab: gui.c.Tr.CommitsTitle,
|
||||
ViewName: "commits",
|
||||
},
|
||||
{
|
||||
Tab: gui.c.Tr.ReflogCommitsTitle,
|
||||
Context: contextTree.ReflogCommits,
|
||||
Tab: gui.c.Tr.ReflogCommitsTitle,
|
||||
ViewName: "reflogCommits",
|
||||
},
|
||||
},
|
||||
"files": {
|
||||
{
|
||||
Tab: gui.c.Tr.FilesTitle,
|
||||
Context: contextTree.Files,
|
||||
Tab: gui.c.Tr.FilesTitle,
|
||||
ViewName: "files",
|
||||
},
|
||||
{
|
||||
Tab: gui.c.Tr.SubmodulesTitle,
|
||||
Context: contextTree.Submodules,
|
||||
Tab: gui.c.Tr.SubmodulesTitle,
|
||||
ViewName: "submodules",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
@ -39,7 +41,17 @@ func (self *guiCommon) RunSubprocess(cmdObj oscommands.ICmdObj) (bool, error) {
|
||||
}
|
||||
|
||||
func (self *guiCommon) PushContext(context types.Context, opts ...types.OnFocusOpts) error {
|
||||
return self.gui.pushContext(context, opts...)
|
||||
singleOpts := types.OnFocusOpts{}
|
||||
if len(opts) > 0 {
|
||||
// using triple dot but you should only ever pass one of these opt structs
|
||||
if len(opts) > 1 {
|
||||
return errors.New("cannot pass multiple opts to pushContext")
|
||||
}
|
||||
|
||||
singleOpts = opts[0]
|
||||
}
|
||||
|
||||
return self.gui.pushContext(context, singleOpts)
|
||||
}
|
||||
|
||||
func (self *guiCommon) PopContext() error {
|
||||
@ -50,6 +62,14 @@ func (self *guiCommon) CurrentContext() types.Context {
|
||||
return self.gui.currentContext()
|
||||
}
|
||||
|
||||
func (self *guiCommon) CurrentStaticContext() types.Context {
|
||||
return self.gui.currentStaticContext()
|
||||
}
|
||||
|
||||
func (self *guiCommon) IsCurrentContext(c types.Context) bool {
|
||||
return self.CurrentContext().GetKey() == c.GetKey()
|
||||
}
|
||||
|
||||
func (self *guiCommon) GetAppState() *config.AppState {
|
||||
return self.gui.Config.GetAppState()
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import (
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/constants"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/keybindings"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
@ -46,6 +45,11 @@ func (gui *Gui) noPopupPanel(f func() error) func() error {
|
||||
|
||||
// only to be called from the cheatsheet generate script. This mutates the Gui struct.
|
||||
func (self *Gui) GetCheatsheetKeybindings() []*types.Binding {
|
||||
self.g = &gocui.Gui{}
|
||||
if err := self.createAllViews(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// need to instantiate views
|
||||
self.helpers = helpers.NewStubHelpers()
|
||||
self.State = &GuiRepoState{}
|
||||
self.State.Contexts = self.contextTree()
|
||||
@ -173,7 +177,6 @@ func (self *Gui) GetInitialKeybindings() ([]*types.Binding, []*gocui.ViewMouseBi
|
||||
},
|
||||
{
|
||||
ViewName: "status",
|
||||
Contexts: []string{string(context.STATUS_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Universal.Edit),
|
||||
Handler: self.handleEditConfig,
|
||||
Description: self.c.Tr.EditConfig,
|
||||
@ -192,70 +195,60 @@ func (self *Gui) GetInitialKeybindings() ([]*types.Binding, []*gocui.ViewMouseBi
|
||||
},
|
||||
{
|
||||
ViewName: "status",
|
||||
Contexts: []string{string(context.STATUS_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Universal.OpenFile),
|
||||
Handler: self.handleOpenConfig,
|
||||
Description: self.c.Tr.OpenConfig,
|
||||
},
|
||||
{
|
||||
ViewName: "status",
|
||||
Contexts: []string{string(context.STATUS_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Status.CheckForUpdate),
|
||||
Handler: self.handleCheckForUpdate,
|
||||
Description: self.c.Tr.LcCheckForUpdate,
|
||||
},
|
||||
{
|
||||
ViewName: "status",
|
||||
Contexts: []string{string(context.STATUS_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Status.RecentRepos),
|
||||
Handler: self.handleCreateRecentReposMenu,
|
||||
Description: self.c.Tr.SwitchRepo,
|
||||
},
|
||||
{
|
||||
ViewName: "status",
|
||||
Contexts: []string{string(context.STATUS_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Status.AllBranchesLogGraph),
|
||||
Handler: self.handleShowAllBranchLogs,
|
||||
Description: self.c.Tr.LcAllBranchesLogGraph,
|
||||
},
|
||||
{
|
||||
ViewName: "files",
|
||||
Contexts: []string{string(context.FILES_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Universal.CopyToClipboard),
|
||||
Handler: self.handleCopySelectedSideContextItemToClipboard,
|
||||
Description: self.c.Tr.LcCopyFileNameToClipboard,
|
||||
},
|
||||
{
|
||||
ViewName: "branches",
|
||||
Contexts: []string{string(context.LOCAL_BRANCHES_CONTEXT_KEY)},
|
||||
ViewName: "localBranches",
|
||||
Key: opts.GetKey(opts.Config.Universal.CopyToClipboard),
|
||||
Handler: self.handleCopySelectedSideContextItemToClipboard,
|
||||
Description: self.c.Tr.LcCopyBranchNameToClipboard,
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{string(context.LOCAL_COMMITS_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Universal.CopyToClipboard),
|
||||
Handler: self.handleCopySelectedSideContextItemToClipboard,
|
||||
Description: self.c.Tr.LcCopyCommitShaToClipboard,
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{string(context.LOCAL_COMMITS_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Commits.ResetCherryPick),
|
||||
Handler: self.helpers.CherryPick.Reset,
|
||||
Description: self.c.Tr.LcResetCherryPick,
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{string(context.REFLOG_COMMITS_CONTEXT_KEY)},
|
||||
ViewName: "reflogCommits",
|
||||
Key: opts.GetKey(opts.Config.Universal.CopyToClipboard),
|
||||
Handler: self.handleCopySelectedSideContextItemToClipboard,
|
||||
Description: self.c.Tr.LcCopyCommitShaToClipboard,
|
||||
},
|
||||
{
|
||||
ViewName: "subCommits",
|
||||
Contexts: []string{string(context.SUB_COMMITS_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Universal.CopyToClipboard),
|
||||
Handler: self.handleCopySelectedSideContextItemToClipboard,
|
||||
Description: self.c.Tr.LcCopyCommitShaToClipboard,
|
||||
@ -268,7 +261,6 @@ func (self *Gui) GetInitialKeybindings() ([]*types.Binding, []*gocui.ViewMouseBi
|
||||
},
|
||||
{
|
||||
ViewName: "commitFiles",
|
||||
Contexts: []string{string(context.COMMIT_FILES_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Universal.CopyToClipboard),
|
||||
Handler: self.handleCopySelectedSideContextItemToClipboard,
|
||||
Description: self.c.Tr.LcCopyCommitFileNameToClipboard,
|
||||
@ -315,7 +307,6 @@ func (self *Gui) GetInitialKeybindings() ([]*types.Binding, []*gocui.ViewMouseBi
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_NORMAL_CONTEXT_KEY)},
|
||||
Key: gocui.MouseWheelDown,
|
||||
Handler: self.scrollDownMain,
|
||||
Description: self.c.Tr.ScrollDown,
|
||||
@ -323,7 +314,6 @@ func (self *Gui) GetInitialKeybindings() ([]*types.Binding, []*gocui.ViewMouseBi
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_NORMAL_CONTEXT_KEY)},
|
||||
Key: gocui.MouseWheelUp,
|
||||
Handler: self.scrollUpMain,
|
||||
Description: self.c.Tr.ScrollUp,
|
||||
@ -331,370 +321,110 @@ func (self *Gui) GetInitialKeybindings() ([]*types.Binding, []*gocui.ViewMouseBi
|
||||
},
|
||||
{
|
||||
ViewName: "secondary",
|
||||
Contexts: []string{string(context.MAIN_STAGING_CONTEXT_KEY)},
|
||||
Key: gocui.MouseLeft,
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: self.handleTogglePanelClick,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_STAGING_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Universal.Return),
|
||||
Handler: self.handleStagingEscape,
|
||||
Description: self.c.Tr.ReturnToFilesPanel,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_STAGING_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Universal.Select),
|
||||
Handler: self.handleToggleStagedSelection,
|
||||
Description: self.c.Tr.StageSelection,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_STAGING_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Universal.Remove),
|
||||
Handler: self.handleResetSelection,
|
||||
Description: self.c.Tr.ResetSelection,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_STAGING_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Universal.TogglePanel),
|
||||
Handler: self.handleTogglePanel,
|
||||
Description: self.c.Tr.TogglePanel,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_PATCH_BUILDING_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Universal.Return),
|
||||
Handler: self.handleEscapePatchBuildingPanel,
|
||||
Description: self.c.Tr.ExitLineByLineMode,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_PATCH_BUILDING_CONTEXT_KEY), string(context.MAIN_STAGING_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Universal.OpenFile),
|
||||
Handler: self.handleOpenFileAtLine,
|
||||
Description: self.c.Tr.LcOpenFile,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_PATCH_BUILDING_CONTEXT_KEY), string(context.MAIN_STAGING_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Universal.PrevItem),
|
||||
Handler: self.handleSelectPrevLine,
|
||||
Description: self.c.Tr.PrevLine,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_PATCH_BUILDING_CONTEXT_KEY), string(context.MAIN_STAGING_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Universal.NextItem),
|
||||
Handler: self.handleSelectNextLine,
|
||||
Description: self.c.Tr.NextLine,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_PATCH_BUILDING_CONTEXT_KEY), string(context.MAIN_STAGING_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Universal.PrevItemAlt),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: self.handleSelectPrevLine,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_PATCH_BUILDING_CONTEXT_KEY), string(context.MAIN_STAGING_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Universal.NextItemAlt),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: self.handleSelectNextLine,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_PATCH_BUILDING_CONTEXT_KEY), string(context.MAIN_STAGING_CONTEXT_KEY)},
|
||||
Key: gocui.MouseWheelUp,
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: self.scrollUpMain,
|
||||
Handler: self.scrollUpSecondary,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_PATCH_BUILDING_CONTEXT_KEY), string(context.MAIN_STAGING_CONTEXT_KEY)},
|
||||
Key: gocui.MouseWheelDown,
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: self.scrollDownMain,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_PATCH_BUILDING_CONTEXT_KEY), string(context.MAIN_STAGING_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Universal.PrevBlock),
|
||||
Handler: self.handleSelectPrevHunk,
|
||||
Description: self.c.Tr.PrevHunk,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_PATCH_BUILDING_CONTEXT_KEY), string(context.MAIN_STAGING_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Universal.PrevBlockAlt),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: self.handleSelectPrevHunk,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_PATCH_BUILDING_CONTEXT_KEY), string(context.MAIN_STAGING_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Universal.NextBlock),
|
||||
Handler: self.handleSelectNextHunk,
|
||||
Description: self.c.Tr.NextHunk,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_PATCH_BUILDING_CONTEXT_KEY), string(context.MAIN_STAGING_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Universal.NextBlockAlt),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: self.handleSelectNextHunk,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_PATCH_BUILDING_CONTEXT_KEY), string(context.MAIN_STAGING_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Universal.CopyToClipboard),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: self.copySelectedToClipboard,
|
||||
Description: self.c.Tr.LcCopySelectedTexToClipboard,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_STAGING_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Universal.Edit),
|
||||
Handler: self.handleLineByLineEdit,
|
||||
Description: self.c.Tr.LcEditFile,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_STAGING_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Universal.OpenFile),
|
||||
Handler: self.HandleOpenFile,
|
||||
Description: self.c.Tr.LcOpenFile,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_PATCH_BUILDING_CONTEXT_KEY), string(context.MAIN_STAGING_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Universal.NextPage),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: self.handleLineByLineNextPage,
|
||||
Description: self.c.Tr.LcNextPage,
|
||||
Tag: "navigation",
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_PATCH_BUILDING_CONTEXT_KEY), string(context.MAIN_STAGING_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Universal.PrevPage),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: self.handleLineByLinePrevPage,
|
||||
Description: self.c.Tr.LcPrevPage,
|
||||
Tag: "navigation",
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_PATCH_BUILDING_CONTEXT_KEY), string(context.MAIN_STAGING_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Universal.GotoTop),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: self.handleLineByLineGotoTop,
|
||||
Description: self.c.Tr.LcGotoTop,
|
||||
Tag: "navigation",
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_PATCH_BUILDING_CONTEXT_KEY), string(context.MAIN_STAGING_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Universal.GotoBottom),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: self.handleLineByLineGotoBottom,
|
||||
Description: self.c.Tr.LcGotoBottom,
|
||||
Tag: "navigation",
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_PATCH_BUILDING_CONTEXT_KEY), string(context.MAIN_STAGING_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Universal.StartSearch),
|
||||
Handler: func() error { return self.handleOpenSearch("main") },
|
||||
Description: self.c.Tr.LcStartSearch,
|
||||
Tag: "navigation",
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_PATCH_BUILDING_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Universal.Select),
|
||||
Handler: self.handleToggleSelectionForPatch,
|
||||
Description: self.c.Tr.ToggleSelectionForPatch,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_PATCH_BUILDING_CONTEXT_KEY), string(context.MAIN_STAGING_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Main.ToggleDragSelect),
|
||||
Handler: self.handleToggleSelectRange,
|
||||
Description: self.c.Tr.ToggleDragSelect,
|
||||
},
|
||||
// Alias 'V' -> 'v'
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_PATCH_BUILDING_CONTEXT_KEY), string(context.MAIN_STAGING_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Main.ToggleDragSelectAlt),
|
||||
Handler: self.handleToggleSelectRange,
|
||||
Description: self.c.Tr.ToggleDragSelect,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_PATCH_BUILDING_CONTEXT_KEY), string(context.MAIN_STAGING_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Main.ToggleSelectHunk),
|
||||
Handler: self.handleToggleSelectHunk,
|
||||
Description: self.c.Tr.ToggleSelectHunk,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_STAGING_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Main.EditSelectHunk),
|
||||
Handler: self.handleEditHunk,
|
||||
Description: self.c.Tr.EditHunk,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_PATCH_BUILDING_CONTEXT_KEY), string(context.MAIN_STAGING_CONTEXT_KEY)},
|
||||
Key: gocui.MouseLeft,
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: self.handleLBLMouseDown,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_PATCH_BUILDING_CONTEXT_KEY), string(context.MAIN_STAGING_CONTEXT_KEY)},
|
||||
Key: gocui.MouseLeft,
|
||||
Modifier: gocui.ModMotion,
|
||||
Handler: self.handleMouseDrag,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_PATCH_BUILDING_CONTEXT_KEY), string(context.MAIN_STAGING_CONTEXT_KEY)},
|
||||
Key: gocui.MouseWheelUp,
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: self.scrollUpMain,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_PATCH_BUILDING_CONTEXT_KEY), string(context.MAIN_STAGING_CONTEXT_KEY)},
|
||||
Key: gocui.MouseWheelDown,
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: self.scrollDownMain,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_PATCH_BUILDING_CONTEXT_KEY), string(context.MAIN_STAGING_CONTEXT_KEY), string(context.MAIN_MERGING_CONTEXT_KEY)},
|
||||
ViewName: "merging",
|
||||
Key: opts.GetKey(opts.Config.Universal.ScrollLeft),
|
||||
Handler: self.scrollLeftMain,
|
||||
Description: self.c.Tr.LcScrollLeft,
|
||||
Tag: "navigation",
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_PATCH_BUILDING_CONTEXT_KEY), string(context.MAIN_STAGING_CONTEXT_KEY), string(context.MAIN_MERGING_CONTEXT_KEY)},
|
||||
ViewName: "merging",
|
||||
Key: opts.GetKey(opts.Config.Universal.ScrollRight),
|
||||
Handler: self.scrollRightMain,
|
||||
Description: self.c.Tr.LcScrollRight,
|
||||
Tag: "navigation",
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_MERGING_CONTEXT_KEY)},
|
||||
ViewName: "merging",
|
||||
Key: opts.GetKey(opts.Config.Universal.Return),
|
||||
Handler: self.handleEscapeMerge,
|
||||
Description: self.c.Tr.ReturnToFilesPanel,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_MERGING_CONTEXT_KEY)},
|
||||
ViewName: "merging",
|
||||
Key: opts.GetKey(opts.Config.Files.OpenMergeTool),
|
||||
Handler: self.helpers.WorkingTree.OpenMergeTool,
|
||||
Description: self.c.Tr.LcOpenMergeTool,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_MERGING_CONTEXT_KEY)},
|
||||
ViewName: "merging",
|
||||
Key: opts.GetKey(opts.Config.Universal.Select),
|
||||
Handler: self.handlePickHunk,
|
||||
Description: self.c.Tr.PickHunk,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_MERGING_CONTEXT_KEY)},
|
||||
ViewName: "merging",
|
||||
Key: opts.GetKey(opts.Config.Main.PickBothHunks),
|
||||
Handler: self.handlePickAllHunks,
|
||||
Description: self.c.Tr.PickAllHunks,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_MERGING_CONTEXT_KEY)},
|
||||
ViewName: "merging",
|
||||
Key: opts.GetKey(opts.Config.Universal.PrevBlock),
|
||||
Handler: self.handleSelectPrevConflict,
|
||||
Description: self.c.Tr.PrevConflict,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_MERGING_CONTEXT_KEY)},
|
||||
ViewName: "merging",
|
||||
Key: opts.GetKey(opts.Config.Universal.NextBlock),
|
||||
Handler: self.handleSelectNextConflict,
|
||||
Description: self.c.Tr.NextConflict,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_MERGING_CONTEXT_KEY)},
|
||||
ViewName: "merging",
|
||||
Key: opts.GetKey(opts.Config.Universal.PrevItem),
|
||||
Handler: self.handleSelectPrevConflictHunk,
|
||||
Description: self.c.Tr.SelectPrevHunk,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_MERGING_CONTEXT_KEY)},
|
||||
ViewName: "merging",
|
||||
Key: opts.GetKey(opts.Config.Universal.NextItem),
|
||||
Handler: self.handleSelectNextConflictHunk,
|
||||
Description: self.c.Tr.SelectNextHunk,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_MERGING_CONTEXT_KEY)},
|
||||
ViewName: "merging",
|
||||
Key: opts.GetKey(opts.Config.Universal.PrevBlockAlt),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: self.handleSelectPrevConflict,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_MERGING_CONTEXT_KEY)},
|
||||
ViewName: "merging",
|
||||
Key: opts.GetKey(opts.Config.Universal.NextBlockAlt),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: self.handleSelectNextConflict,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_MERGING_CONTEXT_KEY)},
|
||||
ViewName: "merging",
|
||||
Key: opts.GetKey(opts.Config.Universal.PrevItemAlt),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: self.handleSelectPrevConflictHunk,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_MERGING_CONTEXT_KEY)},
|
||||
ViewName: "merging",
|
||||
Key: opts.GetKey(opts.Config.Universal.NextItemAlt),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: self.handleSelectNextConflictHunk,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_MERGING_CONTEXT_KEY)},
|
||||
ViewName: "merging",
|
||||
Key: opts.GetKey(opts.Config.Universal.Edit),
|
||||
Handler: self.handleMergeConflictEditFileAtLine,
|
||||
Description: self.c.Tr.LcEditFile,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_MERGING_CONTEXT_KEY)},
|
||||
ViewName: "merging",
|
||||
Key: opts.GetKey(opts.Config.Universal.OpenFile),
|
||||
Handler: self.handleMergeConflictOpenFileAtLine,
|
||||
Description: self.c.Tr.LcOpenFile,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Contexts: []string{string(context.MAIN_MERGING_CONTEXT_KEY)},
|
||||
ViewName: "merging",
|
||||
Key: opts.GetKey(opts.Config.Universal.Undo),
|
||||
Handler: self.handleMergeConflictUndo,
|
||||
Description: self.c.Tr.LcUndo,
|
||||
@ -742,31 +472,17 @@ func (self *Gui) GetInitialKeybindings() ([]*types.Binding, []*gocui.ViewMouseBi
|
||||
Handler: self.scrollDownConfirmationPanel,
|
||||
},
|
||||
{
|
||||
ViewName: "files",
|
||||
Contexts: []string{string(context.SUBMODULES_CONTEXT_KEY)},
|
||||
ViewName: "submodules",
|
||||
Key: opts.GetKey(opts.Config.Universal.CopyToClipboard),
|
||||
Handler: self.handleCopySelectedSideContextItemToClipboard,
|
||||
Description: self.c.Tr.LcCopySubmoduleNameToClipboard,
|
||||
},
|
||||
{
|
||||
ViewName: "files",
|
||||
Contexts: []string{string(context.FILES_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Universal.ToggleWhitespaceInDiffView),
|
||||
Handler: self.toggleWhitespaceInDiffView,
|
||||
Description: self.c.Tr.ToggleWhitespaceInDiffView,
|
||||
},
|
||||
{
|
||||
ViewName: "",
|
||||
Key: opts.GetKey(opts.Config.Universal.IncreaseContextInDiffView),
|
||||
Handler: self.IncreaseContextInDiffView,
|
||||
Description: self.c.Tr.IncreaseContextInDiffView,
|
||||
},
|
||||
{
|
||||
ViewName: "",
|
||||
Key: opts.GetKey(opts.Config.Universal.DecreaseContextInDiffView),
|
||||
Handler: self.DecreaseContextInDiffView,
|
||||
Description: self.c.Tr.DecreaseContextInDiffView,
|
||||
},
|
||||
{
|
||||
ViewName: "extras",
|
||||
Key: gocui.MouseWheelUp,
|
||||
@ -777,17 +493,9 @@ func (self *Gui) GetInitialKeybindings() ([]*types.Binding, []*gocui.ViewMouseBi
|
||||
Key: gocui.MouseWheelDown,
|
||||
Handler: self.scrollDownExtra,
|
||||
},
|
||||
{
|
||||
ViewName: "extras",
|
||||
Key: opts.GetKey(opts.Config.Universal.ExtrasMenu),
|
||||
Handler: self.handleCreateExtrasMenuPanel,
|
||||
Description: self.c.Tr.LcOpenExtrasMenu,
|
||||
OpensMenu: true,
|
||||
},
|
||||
{
|
||||
ViewName: "extras",
|
||||
Tag: "navigation",
|
||||
Contexts: []string{string(context.COMMAND_LOG_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Universal.PrevItemAlt),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: self.scrollUpExtra,
|
||||
@ -795,7 +503,6 @@ func (self *Gui) GetInitialKeybindings() ([]*types.Binding, []*gocui.ViewMouseBi
|
||||
{
|
||||
ViewName: "extras",
|
||||
Tag: "navigation",
|
||||
Contexts: []string{string(context.COMMAND_LOG_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Universal.PrevItem),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: self.scrollUpExtra,
|
||||
@ -803,7 +510,6 @@ func (self *Gui) GetInitialKeybindings() ([]*types.Binding, []*gocui.ViewMouseBi
|
||||
{
|
||||
ViewName: "extras",
|
||||
Tag: "navigation",
|
||||
Contexts: []string{string(context.COMMAND_LOG_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Universal.NextItem),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: self.scrollDownExtra,
|
||||
@ -811,7 +517,6 @@ func (self *Gui) GetInitialKeybindings() ([]*types.Binding, []*gocui.ViewMouseBi
|
||||
{
|
||||
ViewName: "extras",
|
||||
Tag: "navigation",
|
||||
Contexts: []string{string(context.COMMAND_LOG_CONTEXT_KEY)},
|
||||
Key: opts.GetKey(opts.Config.Universal.NextItemAlt),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: self.scrollDownExtra,
|
||||
@ -828,12 +533,8 @@ func (self *Gui) GetInitialKeybindings() ([]*types.Binding, []*gocui.ViewMouseBi
|
||||
mouseKeybindings := []*gocui.ViewMouseBinding{}
|
||||
for _, c := range self.State.Contexts.Flatten() {
|
||||
viewName := c.GetViewName()
|
||||
contextKey := c.GetKey()
|
||||
for _, binding := range c.GetKeybindings(opts) {
|
||||
// TODO: move all mouse keybindings into the mouse keybindings approach below
|
||||
if !gocui.IsMouseKey(binding.Key) && contextKey != context.GLOBAL_CONTEXT_KEY {
|
||||
binding.Contexts = []string{string(contextKey)}
|
||||
}
|
||||
binding.ViewName = viewName
|
||||
bindings = append(bindings, binding)
|
||||
}
|
||||
@ -841,7 +542,7 @@ func (self *Gui) GetInitialKeybindings() ([]*types.Binding, []*gocui.ViewMouseBi
|
||||
mouseKeybindings = append(mouseKeybindings, c.GetMouseKeybindings(opts)...)
|
||||
}
|
||||
|
||||
for _, viewName := range []string{"status", "branches", "remoteBranches", "files", "commits", "commitFiles", "subCommits", "stash"} {
|
||||
for _, viewName := range []string{"status", "remotes", "tags", "localBranches", "remoteBranches", "files", "submodules", "reflogCommits", "commits", "commitFiles", "subCommits", "stash"} {
|
||||
bindings = append(bindings, []*types.Binding{
|
||||
{ViewName: viewName, Key: opts.GetKey(opts.Config.Universal.PrevBlock), Modifier: gocui.ModNone, Handler: self.previousSideWindow},
|
||||
{ViewName: viewName, Key: opts.GetKey(opts.Config.Universal.NextBlock), Modifier: gocui.ModNone, Handler: self.nextSideWindow},
|
||||
@ -868,24 +569,22 @@ func (self *Gui) GetInitialKeybindings() ([]*types.Binding, []*gocui.ViewMouseBi
|
||||
}
|
||||
}
|
||||
|
||||
for viewName := range self.initialViewTabContextMap(self.State.Contexts) {
|
||||
bindings = append(bindings, []*types.Binding{
|
||||
{
|
||||
ViewName: viewName,
|
||||
Key: opts.GetKey(opts.Config.Universal.NextTab),
|
||||
Handler: self.handleNextTab,
|
||||
Description: self.c.Tr.LcNextTab,
|
||||
Tag: "navigation",
|
||||
},
|
||||
{
|
||||
ViewName: viewName,
|
||||
Key: opts.GetKey(opts.Config.Universal.PrevTab),
|
||||
Handler: self.handlePrevTab,
|
||||
Description: self.c.Tr.LcPrevTab,
|
||||
Tag: "navigation",
|
||||
},
|
||||
}...)
|
||||
}
|
||||
bindings = append(bindings, []*types.Binding{
|
||||
{
|
||||
ViewName: "",
|
||||
Key: opts.GetKey(opts.Config.Universal.NextTab),
|
||||
Handler: self.handleNextTab,
|
||||
Description: self.c.Tr.LcNextTab,
|
||||
Tag: "navigation",
|
||||
},
|
||||
{
|
||||
ViewName: "",
|
||||
Key: opts.GetKey(opts.Config.Universal.PrevTab),
|
||||
Handler: self.handlePrevTab,
|
||||
Description: self.c.Tr.LcPrevTab,
|
||||
Tag: "navigation",
|
||||
},
|
||||
}...)
|
||||
|
||||
return bindings, mouseKeybindings
|
||||
}
|
||||
@ -914,12 +613,14 @@ func (gui *Gui) resetKeybindings() error {
|
||||
}
|
||||
}
|
||||
|
||||
for viewName := range gui.initialViewTabContextMap(gui.State.Contexts) {
|
||||
viewName := viewName
|
||||
tabClickCallback := func(tabIndex int) error { return gui.onViewTabClick(viewName, tabIndex) }
|
||||
for _, values := range gui.viewTabMap() {
|
||||
for _, value := range values {
|
||||
viewName := value.ViewName
|
||||
tabClickCallback := func(tabIndex int) error { return gui.onViewTabClick(gui.windowForView(viewName), tabIndex) }
|
||||
|
||||
if err := gui.g.SetTabClickBinding(viewName, tabClickCallback); err != nil {
|
||||
return err
|
||||
if err := gui.g.SetTabClickBinding(viewName, tabClickCallback); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -946,7 +647,7 @@ func (gui *Gui) SetKeybinding(binding *types.Binding) error {
|
||||
}
|
||||
}
|
||||
|
||||
return gui.g.SetKeybinding(binding.ViewName, binding.Contexts, binding.Key, binding.Modifier, gui.wrappedHandler(handler))
|
||||
return gui.g.SetKeybinding(binding.ViewName, binding.Key, binding.Modifier, gui.wrappedHandler(handler))
|
||||
}
|
||||
|
||||
// warning: mutates the binding
|
||||
|
@ -1,7 +1,9 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||
)
|
||||
|
||||
@ -41,26 +43,30 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
}
|
||||
}
|
||||
|
||||
setViewFromDimensions := func(viewName string, windowName string, frame bool) (*gocui.View, error) {
|
||||
// we assume that the view has already been created.
|
||||
setViewFromDimensions := func(viewName string, windowName string) (*gocui.View, error) {
|
||||
dimensionsObj, ok := viewDimensions[windowName]
|
||||
|
||||
view, err := g.View(viewName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !ok {
|
||||
// view not specified in dimensions object: so create the view and hide it
|
||||
// making the view take up the whole space in the background in case it needs
|
||||
// to render content as soon as it appears, because lazyloaded content (via a pty task)
|
||||
// cares about the size of the view.
|
||||
view, err := g.SetView(viewName, 0, 0, width, height, 0)
|
||||
if view != nil {
|
||||
view.Visible = false
|
||||
}
|
||||
_, err := g.SetView(viewName, 0, 0, width, height, 0)
|
||||
view.Visible = false
|
||||
return view, err
|
||||
}
|
||||
|
||||
frameOffset := 1
|
||||
if frame {
|
||||
if view.Frame {
|
||||
frameOffset = 0
|
||||
}
|
||||
view, err := g.SetView(
|
||||
_, err = g.SetView(
|
||||
viewName,
|
||||
dimensionsObj.X0-frameOffset,
|
||||
dimensionsObj.Y0-frameOffset,
|
||||
@ -68,16 +74,17 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
dimensionsObj.Y1+frameOffset,
|
||||
0,
|
||||
)
|
||||
if view != nil {
|
||||
view.Frame = frame
|
||||
view.Visible = true
|
||||
}
|
||||
view.Visible = true
|
||||
|
||||
return view, err
|
||||
}
|
||||
|
||||
for _, arg := range gui.controlledViews() {
|
||||
_, err := setViewFromDimensions(arg.viewName, arg.windowName, arg.frame)
|
||||
for _, context := range gui.State.Contexts.Flatten() {
|
||||
if !context.HasControlledBounds() {
|
||||
continue
|
||||
}
|
||||
|
||||
_, err := setViewFromDimensions(context.GetViewName(), context.GetWindowName())
|
||||
if err != nil && err.Error() != UNKNOWN_VIEW_ERROR_MSG {
|
||||
return err
|
||||
}
|
||||
@ -124,10 +131,6 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
continue
|
||||
}
|
||||
|
||||
if !gui.isContextVisible(listContext) {
|
||||
continue
|
||||
}
|
||||
|
||||
listContext.FocusLine()
|
||||
|
||||
view.SelBgColor = theme.GocuiSelectedLineBgColor
|
||||
@ -136,7 +139,16 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
view.SetOnSelectItem(gui.onSelectItemWrapper(listContext.OnSearchSelect))
|
||||
}
|
||||
|
||||
gui.Views.Main.SetOnSelectItem(gui.onSelectItemWrapper(gui.handlelineByLineNavigateTo))
|
||||
for _, context := range gui.getPatchExplorerContexts() {
|
||||
context := context
|
||||
context.GetView().SetOnSelectItem(gui.onSelectItemWrapper(
|
||||
func(selectedLineIdx int) error {
|
||||
context.GetMutex().Lock()
|
||||
defer context.GetMutex().Unlock()
|
||||
return context.NavigateTo(gui.c.IsCurrentContext(context), selectedLineIdx)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
mainViewWidth, mainViewHeight := gui.Views.Main.Size()
|
||||
if mainViewWidth != gui.PrevLayout.MainWidth || mainViewHeight != gui.PrevLayout.MainHeight {
|
||||
@ -188,11 +200,19 @@ func (gui *Gui) onInitialViewsCreation() error {
|
||||
gui.g.Mutexes.ViewsMutex.Lock()
|
||||
// add tabs to views
|
||||
for _, view := range gui.g.Views() {
|
||||
tabs := gui.viewTabNames(view.Name())
|
||||
if len(tabs) == 0 {
|
||||
continue
|
||||
// if the view is in our mapping, we'll set the tabs and the tab index
|
||||
for _, values := range gui.viewTabMap() {
|
||||
index := slices.IndexFunc(values, func(tabContext context.TabView) bool {
|
||||
return tabContext.ViewName == view.Name()
|
||||
})
|
||||
|
||||
if index != -1 {
|
||||
view.Tabs = slices.Map(values, func(tabContext context.TabView) string {
|
||||
return tabContext.Tab
|
||||
})
|
||||
view.TabIndex = index
|
||||
}
|
||||
}
|
||||
view.Tabs = tabs
|
||||
}
|
||||
gui.g.Mutexes.ViewsMutex.Unlock()
|
||||
|
||||
|
@ -1,275 +0,0 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/patch"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/lbl"
|
||||
)
|
||||
|
||||
// Currently there are two 'pseudo-panels' that make use of this 'pseudo-panel'.
|
||||
// One is the staging panel where we stage files line-by-line, the other is the
|
||||
// patch building panel where we add lines of an old commit's file to a patch.
|
||||
// This file contains the logic around selecting lines and displaying the diffs
|
||||
// staging_panel.go and patch_building_panel.go have functions specific to their
|
||||
// use cases
|
||||
|
||||
// returns whether the patch is empty so caller can escape if necessary
|
||||
// both diffs should be non-coloured because we'll parse them and colour them here
|
||||
func (gui *Gui) refreshLineByLinePanel(diff string, secondaryDiff string, secondaryFocused bool, selectedLineIdx int) (bool, error) {
|
||||
gui.splitMainPanel(true)
|
||||
|
||||
var oldState *lbl.State
|
||||
if gui.State.Panels.LineByLine != nil {
|
||||
oldState = gui.State.Panels.LineByLine.State
|
||||
}
|
||||
|
||||
state := lbl.NewState(diff, selectedLineIdx, oldState, gui.Log)
|
||||
if state == nil {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
gui.State.Panels.LineByLine = &LblPanelState{
|
||||
State: state,
|
||||
SecondaryFocused: secondaryFocused,
|
||||
}
|
||||
|
||||
if err := gui.refreshMainViewForLineByLine(gui.State.Panels.LineByLine); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if err := gui.focusSelection(gui.State.Panels.LineByLine); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
gui.Views.Secondary.Highlight = true
|
||||
gui.Views.Secondary.Wrap = false
|
||||
|
||||
secondaryPatchParser := patch.NewPatchParser(gui.Log, secondaryDiff)
|
||||
|
||||
gui.setViewContent(gui.Views.Secondary, secondaryPatchParser.Render(-1, -1, nil))
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (gui *Gui) handleSelectPrevLine() error {
|
||||
return gui.withLBLActiveCheck(func(state *LblPanelState) error {
|
||||
state.CycleSelection(false)
|
||||
|
||||
return gui.refreshAndFocusLblPanel(state)
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleSelectNextLine() error {
|
||||
return gui.withLBLActiveCheck(func(state *LblPanelState) error {
|
||||
state.CycleSelection(true)
|
||||
|
||||
return gui.refreshAndFocusLblPanel(state)
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleSelectPrevHunk() error {
|
||||
return gui.withLBLActiveCheck(func(state *LblPanelState) error {
|
||||
state.CycleHunk(false)
|
||||
|
||||
return gui.refreshAndFocusLblPanel(state)
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleSelectNextHunk() error {
|
||||
return gui.withLBLActiveCheck(func(state *LblPanelState) error {
|
||||
state.CycleHunk(true)
|
||||
|
||||
return gui.refreshAndFocusLblPanel(state)
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) copySelectedToClipboard() error {
|
||||
return gui.withLBLActiveCheck(func(state *LblPanelState) error {
|
||||
selected := state.PlainRenderSelected()
|
||||
|
||||
gui.c.LogAction(gui.c.Tr.Actions.CopySelectedTextToClipboard)
|
||||
if err := gui.os.CopyToClipboard(selected); err != nil {
|
||||
return gui.c.Error(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) refreshAndFocusLblPanel(state *LblPanelState) error {
|
||||
if err := gui.refreshMainViewForLineByLine(state); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return gui.focusSelection(state)
|
||||
}
|
||||
|
||||
func (gui *Gui) handleLBLMouseDown() error {
|
||||
return gui.withLBLActiveCheck(func(state *LblPanelState) error {
|
||||
state.SelectNewLineForRange(gui.Views.Main.SelectedLineIdx())
|
||||
|
||||
return gui.refreshAndFocusLblPanel(state)
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleMouseDrag() error {
|
||||
return gui.withLBLActiveCheck(func(state *LblPanelState) error {
|
||||
state.SelectLine(gui.Views.Main.SelectedLineIdx())
|
||||
|
||||
return gui.refreshAndFocusLblPanel(state)
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) refreshMainViewForLineByLine(state *LblPanelState) error {
|
||||
var includedLineIndices []int
|
||||
// I'd prefer not to have knowledge of contexts using this file but I'm not sure
|
||||
// how to get around this
|
||||
if gui.currentContext().GetKey() == gui.State.Contexts.PatchBuilding.GetKey() {
|
||||
filename := gui.getSelectedCommitFileName()
|
||||
var err error
|
||||
includedLineIndices, err = gui.git.Patch.PatchManager.GetFileIncLineIndices(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
colorDiff := state.RenderForLineIndices(includedLineIndices)
|
||||
|
||||
gui.Views.Main.Highlight = true
|
||||
gui.Views.Main.Wrap = false
|
||||
|
||||
gui.setViewContent(gui.Views.Main, colorDiff)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// focusSelection works out the best focus for the staging panel given the
|
||||
// selected line and size of the hunk
|
||||
func (gui *Gui) focusSelection(state *LblPanelState) error {
|
||||
stagingView := gui.Views.Main
|
||||
|
||||
_, viewHeight := stagingView.Size()
|
||||
bufferHeight := viewHeight - 1
|
||||
_, origin := stagingView.Origin()
|
||||
|
||||
selectedLineIdx := state.GetSelectedLineIdx()
|
||||
|
||||
newOrigin := state.CalculateOrigin(origin, bufferHeight)
|
||||
|
||||
if err := stagingView.SetOriginY(newOrigin); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return stagingView.SetCursor(0, selectedLineIdx-newOrigin)
|
||||
}
|
||||
|
||||
func (gui *Gui) handleToggleSelectRange() error {
|
||||
return gui.withLBLActiveCheck(func(state *LblPanelState) error {
|
||||
state.ToggleSelectRange()
|
||||
|
||||
return gui.refreshMainViewForLineByLine(state)
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleToggleSelectHunk() error {
|
||||
return gui.withLBLActiveCheck(func(state *LblPanelState) error {
|
||||
state.ToggleSelectHunk()
|
||||
|
||||
return gui.refreshAndFocusLblPanel(state)
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) escapeLineByLinePanel() {
|
||||
gui.State.Panels.LineByLine = nil
|
||||
}
|
||||
|
||||
func (gui *Gui) handleOpenFileAtLine() error {
|
||||
return gui.withLBLActiveCheck(func(state *LblPanelState) error {
|
||||
// again, would be good to use inheritance here (or maybe even composition)
|
||||
var filename string
|
||||
switch gui.State.MainContext {
|
||||
case gui.State.Contexts.PatchBuilding.GetKey():
|
||||
filename = gui.getSelectedCommitFileName()
|
||||
case gui.State.Contexts.Staging.GetKey():
|
||||
file := gui.getSelectedFile()
|
||||
if file == nil {
|
||||
return nil
|
||||
}
|
||||
filename = file.Name
|
||||
default:
|
||||
return errors.Errorf("unknown main context: %s", gui.State.MainContext)
|
||||
}
|
||||
|
||||
// need to look at current index, then work out what my hunk's header information is, and see how far my line is away from the hunk header
|
||||
lineNumber := state.CurrentLineNumber()
|
||||
if err := gui.os.OpenFileAtLine(filename, lineNumber); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleLineByLineNextPage() error {
|
||||
return gui.withLBLActiveCheck(func(state *LblPanelState) error {
|
||||
state.SetLineSelectMode()
|
||||
state.AdjustSelectedLineIdx(gui.pageDelta(gui.Views.Main))
|
||||
|
||||
return gui.refreshAndFocusLblPanel(state)
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleLineByLinePrevPage() error {
|
||||
return gui.withLBLActiveCheck(func(state *LblPanelState) error {
|
||||
state.SetLineSelectMode()
|
||||
state.AdjustSelectedLineIdx(-gui.pageDelta(gui.Views.Main))
|
||||
|
||||
return gui.refreshAndFocusLblPanel(state)
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleLineByLineGotoBottom() error {
|
||||
return gui.withLBLActiveCheck(func(state *LblPanelState) error {
|
||||
state.SelectBottom()
|
||||
|
||||
return gui.refreshAndFocusLblPanel(state)
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleLineByLineGotoTop() error {
|
||||
return gui.withLBLActiveCheck(func(state *LblPanelState) error {
|
||||
state.SelectTop()
|
||||
|
||||
return gui.refreshAndFocusLblPanel(state)
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) handlelineByLineNavigateTo(selectedLineIdx int) error {
|
||||
return gui.withLBLActiveCheck(func(state *LblPanelState) error {
|
||||
state.SetLineSelectMode()
|
||||
state.SelectLine(selectedLineIdx)
|
||||
|
||||
return gui.refreshAndFocusLblPanel(state)
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) withLBLActiveCheck(f func(*LblPanelState) error) error {
|
||||
gui.Mutexes.LineByLinePanelMutex.Lock()
|
||||
defer gui.Mutexes.LineByLinePanelMutex.Unlock()
|
||||
|
||||
state := gui.State.Panels.LineByLine
|
||||
if state == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return f(state)
|
||||
}
|
||||
|
||||
func (gui *Gui) handleLineByLineEdit() error {
|
||||
file := gui.getSelectedFile()
|
||||
if file == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
lineNumber := gui.State.Panels.LineByLine.CurrentLineNumber()
|
||||
return gui.helpers.Files.EditFileAtLine(file.Name, lineNumber)
|
||||
}
|
@ -34,7 +34,7 @@ func (gui *Gui) filesListContext() *context.WorkingTreeContext {
|
||||
})
|
||||
},
|
||||
OnFocusWrapper(gui.onFocusFile),
|
||||
OnFocusWrapper(gui.withDiffModeCheck(gui.filesRenderToMain)),
|
||||
gui.withDiffModeCheck(gui.filesRenderToMain),
|
||||
nil,
|
||||
gui.c,
|
||||
)
|
||||
@ -48,7 +48,7 @@ func (gui *Gui) branchesListContext() *context.BranchesContext {
|
||||
return presentation.GetBranchListDisplayStrings(gui.State.Model.Branches, gui.State.ScreenMode != SCREEN_NORMAL, gui.State.Modes.Diffing.Ref, gui.Tr)
|
||||
},
|
||||
nil,
|
||||
OnFocusWrapper(gui.withDiffModeCheck(gui.branchesRenderToMain)),
|
||||
gui.withDiffModeCheck(gui.branchesRenderToMain),
|
||||
nil,
|
||||
gui.c,
|
||||
)
|
||||
@ -57,12 +57,12 @@ func (gui *Gui) branchesListContext() *context.BranchesContext {
|
||||
func (gui *Gui) remotesListContext() *context.RemotesContext {
|
||||
return context.NewRemotesContext(
|
||||
func() []*models.Remote { return gui.State.Model.Remotes },
|
||||
gui.Views.Branches,
|
||||
gui.Views.Remotes,
|
||||
func(startIdx int, length int) [][]string {
|
||||
return presentation.GetRemoteListDisplayStrings(gui.State.Model.Remotes, gui.State.Modes.Diffing.Ref)
|
||||
},
|
||||
nil,
|
||||
OnFocusWrapper(gui.withDiffModeCheck(gui.remotesRenderToMain)),
|
||||
gui.withDiffModeCheck(gui.remotesRenderToMain),
|
||||
nil,
|
||||
gui.c,
|
||||
)
|
||||
@ -76,7 +76,7 @@ func (gui *Gui) remoteBranchesListContext() *context.RemoteBranchesContext {
|
||||
return presentation.GetRemoteBranchListDisplayStrings(gui.State.Model.RemoteBranches, gui.State.Modes.Diffing.Ref)
|
||||
},
|
||||
nil,
|
||||
OnFocusWrapper(gui.withDiffModeCheck(gui.remoteBranchesRenderToMain)),
|
||||
gui.withDiffModeCheck(gui.remoteBranchesRenderToMain),
|
||||
nil,
|
||||
gui.c,
|
||||
)
|
||||
@ -95,12 +95,12 @@ func (gui *Gui) withDiffModeCheck(f func() error) func() error {
|
||||
func (gui *Gui) tagsListContext() *context.TagsContext {
|
||||
return context.NewTagsContext(
|
||||
func() []*models.Tag { return gui.State.Model.Tags },
|
||||
gui.Views.Branches,
|
||||
gui.Views.Tags,
|
||||
func(startIdx int, length int) [][]string {
|
||||
return presentation.GetTagListDisplayStrings(gui.State.Model.Tags, gui.State.Modes.Diffing.Ref)
|
||||
},
|
||||
nil,
|
||||
OnFocusWrapper(gui.withDiffModeCheck(gui.tagsRenderToMain)),
|
||||
gui.withDiffModeCheck(gui.tagsRenderToMain),
|
||||
nil,
|
||||
gui.c,
|
||||
)
|
||||
@ -133,7 +133,7 @@ func (gui *Gui) branchCommitsListContext() *context.LocalCommitsContext {
|
||||
)
|
||||
},
|
||||
OnFocusWrapper(gui.onCommitFocus),
|
||||
OnFocusWrapper(gui.withDiffModeCheck(gui.branchCommitsRenderToMain)),
|
||||
gui.withDiffModeCheck(gui.branchCommitsRenderToMain),
|
||||
nil,
|
||||
gui.c,
|
||||
)
|
||||
@ -166,7 +166,7 @@ func (gui *Gui) subCommitsListContext() *context.SubCommitsContext {
|
||||
)
|
||||
},
|
||||
nil,
|
||||
OnFocusWrapper(gui.withDiffModeCheck(gui.subCommitsRenderToMain)),
|
||||
gui.withDiffModeCheck(gui.subCommitsRenderToMain),
|
||||
nil,
|
||||
gui.c,
|
||||
)
|
||||
@ -194,7 +194,7 @@ func (gui *Gui) shouldShowGraph() bool {
|
||||
func (gui *Gui) reflogCommitsListContext() *context.ReflogCommitsContext {
|
||||
return context.NewReflogCommitsContext(
|
||||
func() []*models.Commit { return gui.State.Model.FilteredReflogCommits },
|
||||
gui.Views.Commits,
|
||||
gui.Views.ReflogCommits,
|
||||
func(startIdx int, length int) [][]string {
|
||||
return presentation.GetReflogCommitListDisplayStrings(
|
||||
gui.State.Model.FilteredReflogCommits,
|
||||
@ -206,7 +206,7 @@ func (gui *Gui) reflogCommitsListContext() *context.ReflogCommitsContext {
|
||||
)
|
||||
},
|
||||
nil,
|
||||
OnFocusWrapper(gui.withDiffModeCheck(gui.reflogCommitsRenderToMain)),
|
||||
gui.withDiffModeCheck(gui.reflogCommitsRenderToMain),
|
||||
nil,
|
||||
gui.c,
|
||||
)
|
||||
@ -220,7 +220,7 @@ func (gui *Gui) stashListContext() *context.StashContext {
|
||||
return presentation.GetStashEntryListDisplayStrings(gui.State.Model.StashEntries, gui.State.Modes.Diffing.Ref)
|
||||
},
|
||||
nil,
|
||||
OnFocusWrapper(gui.withDiffModeCheck(gui.stashRenderToMain)),
|
||||
gui.withDiffModeCheck(gui.stashRenderToMain),
|
||||
nil,
|
||||
gui.c,
|
||||
)
|
||||
@ -240,8 +240,8 @@ func (gui *Gui) commitFilesListContext() *context.CommitFilesContext {
|
||||
return []string{line}
|
||||
})
|
||||
},
|
||||
OnFocusWrapper(gui.onCommitFileFocus),
|
||||
OnFocusWrapper(gui.withDiffModeCheck(gui.commitFilesRenderToMain)),
|
||||
nil,
|
||||
gui.withDiffModeCheck(gui.commitFilesRenderToMain),
|
||||
nil,
|
||||
gui.c,
|
||||
)
|
||||
@ -250,12 +250,12 @@ func (gui *Gui) commitFilesListContext() *context.CommitFilesContext {
|
||||
func (gui *Gui) submodulesListContext() *context.SubmodulesContext {
|
||||
return context.NewSubmodulesContext(
|
||||
func() []*models.SubmoduleConfig { return gui.State.Model.Submodules },
|
||||
gui.Views.Files,
|
||||
gui.Views.Submodules,
|
||||
func(startIdx int, length int) [][]string {
|
||||
return presentation.GetSubmoduleListDisplayStrings(gui.State.Model.Submodules)
|
||||
},
|
||||
nil,
|
||||
OnFocusWrapper(gui.withDiffModeCheck(gui.submodulesRenderToMain)),
|
||||
gui.withDiffModeCheck(gui.submodulesRenderToMain),
|
||||
nil,
|
||||
gui.c,
|
||||
)
|
||||
|
@ -10,18 +10,11 @@ import (
|
||||
type viewUpdateOpts struct {
|
||||
title string
|
||||
|
||||
// awkwardly calling this noWrap because of how hard Go makes it to have
|
||||
// a boolean option that defaults to true
|
||||
noWrap bool
|
||||
|
||||
highlight bool
|
||||
|
||||
task updateTask
|
||||
|
||||
context types.Context
|
||||
}
|
||||
|
||||
type refreshMainOpts struct {
|
||||
pair MainContextPair
|
||||
main *viewUpdateOpts
|
||||
secondary *viewUpdateOpts
|
||||
}
|
||||
@ -99,15 +92,21 @@ func (gui *Gui) runTaskForView(view *gocui.View, task updateTask) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) refreshMainView(opts *viewUpdateOpts, view *gocui.View) error {
|
||||
view.Title = opts.title
|
||||
view.Wrap = !opts.noWrap
|
||||
view.Highlight = opts.highlight
|
||||
context := opts.context
|
||||
if context == nil {
|
||||
context = gui.State.Contexts.Normal
|
||||
func (gui *Gui) moveMainContextPairToTop(pair MainContextPair) {
|
||||
gui.setWindowContext(pair.main)
|
||||
gui.moveToTopOfWindow(pair.main)
|
||||
if pair.secondary != nil {
|
||||
gui.setWindowContext(pair.secondary)
|
||||
gui.moveToTopOfWindow(pair.secondary)
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) refreshMainView(opts *viewUpdateOpts, context types.Context) error {
|
||||
view := context.GetView()
|
||||
|
||||
if opts.title != "" {
|
||||
view.Title = opts.title
|
||||
}
|
||||
gui.ViewContextMapSet(view.Name(), context)
|
||||
|
||||
if err := gui.runTaskForView(view, opts.task); err != nil {
|
||||
gui.c.Log.Error(err)
|
||||
@ -117,19 +116,75 @@ func (gui *Gui) refreshMainView(opts *viewUpdateOpts, view *gocui.View) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type MainContextPair struct {
|
||||
main types.Context
|
||||
secondary types.Context
|
||||
}
|
||||
|
||||
func (gui *Gui) normalMainContextPair() MainContextPair {
|
||||
return MainContextPair{
|
||||
main: gui.State.Contexts.Normal,
|
||||
secondary: gui.State.Contexts.NormalSecondary,
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) stagingMainContextPair() MainContextPair {
|
||||
return MainContextPair{
|
||||
main: gui.State.Contexts.Staging,
|
||||
secondary: gui.State.Contexts.StagingSecondary,
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) patchBuildingMainContextPair() MainContextPair {
|
||||
return MainContextPair{
|
||||
main: gui.State.Contexts.CustomPatchBuilder,
|
||||
secondary: gui.State.Contexts.CustomPatchBuilderSecondary,
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) mergingMainContextPair() MainContextPair {
|
||||
return MainContextPair{
|
||||
main: gui.State.Contexts.Merging,
|
||||
secondary: nil,
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) allMainContextPairs() []MainContextPair {
|
||||
return []MainContextPair{
|
||||
gui.normalMainContextPair(),
|
||||
gui.stagingMainContextPair(),
|
||||
gui.patchBuildingMainContextPair(),
|
||||
gui.mergingMainContextPair(),
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) refreshMainViews(opts refreshMainOpts) error {
|
||||
// need to reset scroll positions of all other main views
|
||||
for _, pair := range gui.allMainContextPairs() {
|
||||
if pair.main != opts.pair.main {
|
||||
_ = pair.main.GetView().SetOrigin(0, 0)
|
||||
}
|
||||
if pair.secondary != nil && pair.secondary != opts.pair.secondary {
|
||||
_ = pair.secondary.GetView().SetOrigin(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
if opts.main != nil {
|
||||
if err := gui.refreshMainView(opts.main, gui.Views.Main); err != nil {
|
||||
if err := gui.refreshMainView(opts.main, opts.pair.main); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if opts.secondary != nil {
|
||||
if err := gui.refreshMainView(opts.secondary, gui.Views.Secondary); err != nil {
|
||||
if err := gui.refreshMainView(opts.secondary, opts.pair.secondary); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if opts.pair.secondary != nil {
|
||||
opts.pair.secondary.GetView().Clear()
|
||||
}
|
||||
|
||||
gui.moveMainContextPairToTop(opts.pair)
|
||||
|
||||
gui.splitMainPanel(opts.secondary != nil)
|
||||
|
||||
return nil
|
||||
|
@ -146,17 +146,15 @@ func (gui *Gui) renderConflicts(hasFocus bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
gui.centerYPos(gui.Views.Main, state.GetConflictMiddle())
|
||||
gui.centerYPos(gui.Views.Merging, state.GetConflictMiddle())
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
return gui.refreshMainViews(refreshMainOpts{
|
||||
pair: gui.mergingMainContextPair(),
|
||||
main: &viewUpdateOpts{
|
||||
title: gui.c.Tr.MergeConflictsTitle,
|
||||
task: NewRenderStringWithoutScrollTask(content),
|
||||
context: gui.State.Contexts.Merging,
|
||||
noWrap: true,
|
||||
task: NewRenderStringWithoutScrollTask(content),
|
||||
},
|
||||
})
|
||||
}
|
||||
@ -239,14 +237,14 @@ func (gui *Gui) escapeMerge() error {
|
||||
|
||||
// doing this in separate UI thread so that we're not still holding the lock by the time refresh the file
|
||||
gui.OnUIThread(func() error {
|
||||
return gui.pushContext(gui.State.Contexts.Files)
|
||||
return gui.c.PushContext(gui.State.Contexts.Files)
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) renderingConflicts() bool {
|
||||
currentView := gui.g.CurrentView()
|
||||
if currentView != gui.Views.Main && currentView != gui.Views.Files {
|
||||
if currentView != gui.Views.Merging && currentView != gui.Views.Files {
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ func (gui *Gui) modeStatuses() []modeStatus {
|
||||
description: func() string {
|
||||
return gui.withResetButton(gui.c.Tr.LcBuildingPatch, style.FgYellow.SetBold())
|
||||
},
|
||||
reset: gui.handleResetPatch,
|
||||
reset: gui.helpers.PatchBuilding.Reset,
|
||||
},
|
||||
{
|
||||
isActive: gui.State.Modes.Filtering.Active,
|
||||
|
@ -21,11 +21,11 @@ func (gui *Gui) getBindings(context types.Context) []*types.Binding {
|
||||
|
||||
for _, binding := range bindings {
|
||||
if keybindings.GetKeyDisplay(binding.Key) != "" && binding.Description != "" {
|
||||
if len(binding.Contexts) == 0 && binding.ViewName == "" {
|
||||
if binding.ViewName == "" {
|
||||
bindingsGlobal = append(bindingsGlobal, binding)
|
||||
} else if binding.Tag == "navigation" {
|
||||
bindingsNavigation = append(bindingsNavigation, binding)
|
||||
} else if lo.Contains(binding.Contexts, string(context.GetKey())) {
|
||||
} else if binding.ViewName == context.GetViewName() {
|
||||
bindingsPanel = append(bindingsPanel, binding)
|
||||
}
|
||||
}
|
||||
|
@ -1,134 +0,0 @@
|
||||
package gui
|
||||
|
||||
import "github.com/samber/lo"
|
||||
|
||||
func (gui *Gui) refreshPatchBuildingPanel(selectedLineIdx int) error {
|
||||
if !gui.git.Patch.PatchManager.Active() {
|
||||
return gui.handleEscapePatchBuildingPanel()
|
||||
}
|
||||
|
||||
gui.Views.Main.Title = "Patch"
|
||||
gui.Views.Secondary.Title = "Custom Patch"
|
||||
|
||||
// get diff from commit file that's currently selected
|
||||
node := gui.State.Contexts.CommitFiles.GetSelected()
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
ref := gui.State.Contexts.CommitFiles.CommitFileTreeViewModel.GetRef()
|
||||
to := ref.RefName()
|
||||
from, reverse := gui.State.Modes.Diffing.GetFromAndReverseArgsForDiff(ref.ParentRefName())
|
||||
diff, err := gui.git.WorkingTree.ShowFileDiff(from, to, reverse, node.GetPath(), true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
secondaryDiff := gui.git.Patch.PatchManager.RenderPatchForFile(node.GetPath(), true, false, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
empty, err := gui.refreshLineByLinePanel(diff, secondaryDiff, false, selectedLineIdx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if empty {
|
||||
return gui.handleEscapePatchBuildingPanel()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) handleRefreshPatchBuildingPanel(selectedLineIdx int) error {
|
||||
gui.Mutexes.LineByLinePanelMutex.Lock()
|
||||
defer gui.Mutexes.LineByLinePanelMutex.Unlock()
|
||||
|
||||
return gui.refreshPatchBuildingPanel(selectedLineIdx)
|
||||
}
|
||||
|
||||
func (gui *Gui) onPatchBuildingFocus(selectedLineIdx int) error {
|
||||
gui.Mutexes.LineByLinePanelMutex.Lock()
|
||||
defer gui.Mutexes.LineByLinePanelMutex.Unlock()
|
||||
|
||||
if gui.State.Panels.LineByLine == nil || selectedLineIdx != -1 {
|
||||
return gui.refreshPatchBuildingPanel(selectedLineIdx)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) handleToggleSelectionForPatch() error {
|
||||
err := gui.withLBLActiveCheck(func(state *LblPanelState) error {
|
||||
toggleFunc := gui.git.Patch.PatchManager.AddFileLineRange
|
||||
filename := gui.getSelectedCommitFileName()
|
||||
includedLineIndices, err := gui.git.Patch.PatchManager.GetFileIncLineIndices(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
currentLineIsStaged := lo.Contains(includedLineIndices, state.GetSelectedLineIdx())
|
||||
if currentLineIsStaged {
|
||||
toggleFunc = gui.git.Patch.PatchManager.RemoveFileLineRange
|
||||
}
|
||||
|
||||
// add range of lines to those set for the file
|
||||
node := gui.State.Contexts.CommitFiles.GetSelected()
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
firstLineIdx, lastLineIdx := state.SelectedRange()
|
||||
|
||||
if err := toggleFunc(node.GetPath(), firstLineIdx, lastLineIdx); err != nil {
|
||||
// might actually want to return an error here
|
||||
gui.c.Log.Error(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := gui.handleRefreshPatchBuildingPanel(-1); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := gui.refreshCommitFilesContext(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) handleEscapePatchBuildingPanel() error {
|
||||
gui.escapeLineByLinePanel()
|
||||
|
||||
if gui.git.Patch.PatchManager.IsEmpty() {
|
||||
gui.git.Patch.PatchManager.Reset()
|
||||
}
|
||||
|
||||
if gui.currentContext().GetKey() == gui.State.Contexts.PatchBuilding.GetKey() {
|
||||
return gui.c.PushContext(gui.State.Contexts.CommitFiles)
|
||||
} else {
|
||||
// need to re-focus in case the secondary view should now be hidden
|
||||
return gui.currentContext().HandleFocus()
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) secondaryPatchPanelUpdateOpts() *viewUpdateOpts {
|
||||
if gui.git.Patch.PatchManager.Active() {
|
||||
patch := gui.git.Patch.PatchManager.RenderAggregatedPatchColored(false)
|
||||
|
||||
return &viewUpdateOpts{
|
||||
title: "Custom Patch",
|
||||
noWrap: true,
|
||||
highlight: true,
|
||||
context: gui.State.Contexts.PatchBuilding,
|
||||
task: NewRenderStringWithoutScrollTask(patch),
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package lbl
|
||||
package patch_exploring
|
||||
|
||||
import "github.com/jesseduffield/lazygit/pkg/utils"
|
||||
|
@ -1,4 +1,4 @@
|
||||
package lbl
|
||||
package patch_exploring
|
||||
|
||||
import (
|
||||
"testing"
|
@ -1,12 +1,12 @@
|
||||
package lbl
|
||||
package patch_exploring
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/patch"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// State represents the current state of the line-by-line context i.e. when
|
||||
// you're staging a file line-by-line or you're building a patch from an existing commit
|
||||
// State represents the current state of the patch explorer context i.e. when
|
||||
// you're staging a file or you're building a patch from an existing commit
|
||||
// this struct holds the info about the diff you're interacting with and what's currently selected.
|
||||
type State struct {
|
||||
selectedLineIdx int
|
||||
@ -26,6 +26,13 @@ const (
|
||||
)
|
||||
|
||||
func NewState(diff string, selectedLineIdx int, oldState *State, log *logrus.Entry) *State {
|
||||
if oldState != nil && diff == oldState.diff && selectedLineIdx == -1 {
|
||||
// if we're here then we can return the old state. If selectedLineIdx was not -1
|
||||
// then that would mean we were trying to click and potentiall drag a range, which
|
||||
// is why in that case we continue below
|
||||
return oldState
|
||||
}
|
||||
|
||||
patchParser := patch.NewPatchParser(log, diff)
|
||||
|
||||
if len(patchParser.StageableLines) == 0 {
|
||||
@ -178,14 +185,14 @@ func (s *State) AdjustSelectedLineIdx(change int) {
|
||||
s.SelectLine(s.selectedLineIdx + change)
|
||||
}
|
||||
|
||||
func (s *State) RenderForLineIndices(includedLineIndices []int) string {
|
||||
func (s *State) RenderForLineIndices(isFocused bool, includedLineIndices []int) string {
|
||||
firstLineIdx, lastLineIdx := s.SelectedRange()
|
||||
return s.patchParser.Render(firstLineIdx, lastLineIdx, includedLineIndices)
|
||||
return s.patchParser.Render(isFocused, firstLineIdx, lastLineIdx, includedLineIndices)
|
||||
}
|
||||
|
||||
func (s *State) PlainRenderSelected() string {
|
||||
firstLineIdx, lastLineIdx := s.SelectedRange()
|
||||
return s.patchParser.PlainRenderLines(firstLineIdx, lastLineIdx)
|
||||
return s.patchParser.RenderLinesPlain(firstLineIdx, lastLineIdx)
|
||||
}
|
||||
|
||||
func (s *State) SelectBottom() {
|
@ -5,6 +5,7 @@ package gui
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
@ -19,14 +20,20 @@ func (gui *Gui) desiredPtySize() *pty.Winsize {
|
||||
}
|
||||
|
||||
func (gui *Gui) onResize() error {
|
||||
gui.Mutexes.PtyMutex.Lock()
|
||||
defer gui.Mutexes.PtyMutex.Unlock()
|
||||
|
||||
if gui.State.Ptmx == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
gui.Log.Warn("resizing")
|
||||
|
||||
// TODO: handle resizing properly: we need to actually clear the main view
|
||||
// and re-read the output from our pty. Or we could just re-run the original
|
||||
// command from scratch
|
||||
if err := pty.Setsize(gui.State.Ptmx, gui.desiredPtySize()); err != nil {
|
||||
panic(err)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -57,20 +64,27 @@ func (gui *Gui) newPtyTask(view *gocui.View, cmd *exec.Cmd, prefix string) error
|
||||
|
||||
manager := gui.getManager(view)
|
||||
|
||||
var ptmx *os.File
|
||||
start := func() (*exec.Cmd, io.Reader) {
|
||||
ptmx, err := pty.StartWithSize(cmd, gui.desiredPtySize())
|
||||
var err error
|
||||
ptmx, err = pty.StartWithSize(cmd, gui.desiredPtySize())
|
||||
if err != nil {
|
||||
gui.c.Log.Error(err)
|
||||
}
|
||||
|
||||
gui.Mutexes.PtyMutex.Lock()
|
||||
gui.State.Ptmx = ptmx
|
||||
gui.Mutexes.PtyMutex.Unlock()
|
||||
|
||||
return cmd, ptmx
|
||||
}
|
||||
|
||||
onClose := func() {
|
||||
gui.State.Ptmx.Close()
|
||||
gui.Mutexes.PtyMutex.Lock()
|
||||
ptmx.Close()
|
||||
ptmx = nil
|
||||
gui.State.Ptmx = nil
|
||||
gui.Mutexes.PtyMutex.Unlock()
|
||||
}
|
||||
|
||||
if err := manager.NewTask(manager.NewCmdTask(start, prefix, height+oy+10, onClose), cmdStr); err != nil {
|
||||
|
@ -110,6 +110,7 @@ func (gui *Gui) handleShowAllBranchLogs() error {
|
||||
task := NewRunPtyTask(cmdObj.GetCmd())
|
||||
|
||||
return gui.refreshMainViews(refreshMainOpts{
|
||||
pair: gui.normalMainContextPair(),
|
||||
main: &viewUpdateOpts{
|
||||
title: gui.c.Tr.LogTitle,
|
||||
task: task,
|
||||
|
@ -12,6 +12,7 @@ func (gui *Gui) reflogCommitsRenderToMain() error {
|
||||
}
|
||||
|
||||
return gui.refreshMainViews(refreshMainOpts{
|
||||
pair: gui.normalMainContextPair(),
|
||||
main: &viewUpdateOpts{
|
||||
title: "Reflog Entry",
|
||||
task: task,
|
||||
|
@ -13,6 +13,7 @@ import (
|
||||
"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/patch_exploring"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
@ -31,6 +32,7 @@ func getScopeNames(scopes []types.RefreshableView) []string {
|
||||
types.REMOTES: "remotes",
|
||||
types.STATUS: "status",
|
||||
types.BISECT_INFO: "bisect",
|
||||
types.STAGING: "staging",
|
||||
}
|
||||
|
||||
return slices.Map(scopes, func(scope types.RefreshableView) string {
|
||||
@ -70,6 +72,8 @@ func (gui *Gui) Refresh(options types.RefreshOptions) error {
|
||||
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,
|
||||
@ -105,6 +109,11 @@ func (gui *Gui) Refresh(options types.RefreshOptions) error {
|
||||
refresh(func() { _ = gui.refreshRebaseCommits() })
|
||||
}
|
||||
|
||||
// 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() { _ = gui.refreshCommitFilesContext() })
|
||||
}
|
||||
|
||||
if scopeSet.Includes(types.FILES) || scopeSet.Includes(types.SUBMODULES) {
|
||||
refresh(func() { _ = gui.refreshFilesAndSubmodules() })
|
||||
}
|
||||
@ -121,6 +130,14 @@ func (gui *Gui) Refresh(options types.RefreshOptions) error {
|
||||
refresh(func() { _ = gui.refreshRemotes() })
|
||||
}
|
||||
|
||||
if scopeSet.Includes(types.STAGING) {
|
||||
refresh(func() { _ = gui.refreshStagingPanel(types.OnFocusOpts{}) })
|
||||
}
|
||||
|
||||
if scopeSet.Includes(types.PATCH_BUILDING) {
|
||||
refresh(func() { _ = gui.refreshPatchBuildingPanel(types.OnFocusOpts{}) })
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
gui.refreshStatus()
|
||||
@ -218,6 +235,21 @@ func (gui *Gui) refreshCommitsWithLimit() error {
|
||||
return gui.c.PostRefreshUpdate(gui.State.Contexts.LocalCommits)
|
||||
}
|
||||
|
||||
func (gui *Gui) refreshCommitFilesContext() error {
|
||||
ref := gui.State.Contexts.CommitFiles.GetRef()
|
||||
to := ref.RefName()
|
||||
from, reverse := gui.State.Modes.Diffing.GetFromAndReverseArgsForDiff(ref.ParentRefName())
|
||||
|
||||
files, err := gui.git.Loaders.CommitFiles.GetFilesInDiff(from, to, reverse)
|
||||
if err != nil {
|
||||
return gui.c.Error(err)
|
||||
}
|
||||
gui.State.Model.CommitFiles = files
|
||||
gui.State.Contexts.CommitFiles.CommitFileTreeViewModel.SetTree()
|
||||
|
||||
return gui.c.PostRefreshUpdate(gui.State.Contexts.CommitFiles)
|
||||
}
|
||||
|
||||
func (gui *Gui) refreshRebaseCommits() error {
|
||||
gui.Mutexes.LocalCommitsMutex.Lock()
|
||||
defer gui.Mutexes.LocalCommitsMutex.Unlock()
|
||||
@ -332,7 +364,7 @@ func (gui *Gui) refreshMergeState() error {
|
||||
gui.State.Panels.Merging.Lock()
|
||||
defer gui.State.Panels.Merging.Unlock()
|
||||
|
||||
if gui.currentContext().GetKey() != context.MAIN_MERGING_CONTEXT_KEY {
|
||||
if gui.currentContext().GetKey() != context.MERGING_MAIN_CONTEXT_KEY {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -535,3 +567,137 @@ func (gui *Gui) refreshStatus() {
|
||||
|
||||
gui.setViewContent(gui.Views.Status, status)
|
||||
}
|
||||
|
||||
func (gui *Gui) refreshStagingPanel(focusOpts types.OnFocusOpts) error {
|
||||
secondaryFocused := gui.secondaryStagingFocused()
|
||||
|
||||
mainSelectedLineIdx := -1
|
||||
secondarySelectedLineIdx := -1
|
||||
if focusOpts.ClickedViewLineIdx > 0 {
|
||||
if secondaryFocused {
|
||||
secondarySelectedLineIdx = focusOpts.ClickedViewLineIdx
|
||||
} else {
|
||||
mainSelectedLineIdx = focusOpts.ClickedViewLineIdx
|
||||
}
|
||||
}
|
||||
|
||||
mainContext := gui.State.Contexts.Staging
|
||||
secondaryContext := gui.State.Contexts.StagingSecondary
|
||||
|
||||
file := gui.getSelectedFile()
|
||||
if file == nil || (!file.HasUnstagedChanges && !file.HasStagedChanges) {
|
||||
return gui.handleStagingEscape()
|
||||
}
|
||||
|
||||
mainDiff := gui.git.WorkingTree.WorktreeFileDiff(file, true, false, false)
|
||||
secondaryDiff := gui.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(), gui.Log),
|
||||
)
|
||||
|
||||
secondaryContext.SetState(
|
||||
patch_exploring.NewState(secondaryDiff, secondarySelectedLineIdx, secondaryContext.GetState(), gui.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 gui.handleStagingEscape()
|
||||
}
|
||||
|
||||
if mainState == nil && !secondaryFocused {
|
||||
return gui.c.PushContext(secondaryContext, focusOpts)
|
||||
}
|
||||
|
||||
if secondaryState == nil && secondaryFocused {
|
||||
return gui.c.PushContext(mainContext, focusOpts)
|
||||
}
|
||||
|
||||
return gui.refreshMainViews(refreshMainOpts{
|
||||
pair: gui.stagingMainContextPair(),
|
||||
main: &viewUpdateOpts{
|
||||
task: NewRenderStringWithoutScrollTask(mainContent),
|
||||
title: gui.Tr.UnstagedChanges,
|
||||
},
|
||||
secondary: &viewUpdateOpts{
|
||||
task: NewRenderStringWithoutScrollTask(secondaryContent),
|
||||
title: gui.Tr.StagedChanges,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleStagingEscape() error {
|
||||
return gui.c.PushContext(gui.State.Contexts.Files)
|
||||
}
|
||||
|
||||
func (gui *Gui) secondaryStagingFocused() bool {
|
||||
return gui.currentStaticContext().GetKey() == gui.State.Contexts.StagingSecondary.GetKey()
|
||||
}
|
||||
|
||||
func (gui *Gui) refreshPatchBuildingPanel(opts types.OnFocusOpts) error {
|
||||
selectedLineIdx := -1
|
||||
if opts.ClickedWindowName == "main" {
|
||||
selectedLineIdx = opts.ClickedViewLineIdx
|
||||
}
|
||||
|
||||
if !gui.git.Patch.PatchManager.Active() {
|
||||
return gui.helpers.PatchBuilding.Escape()
|
||||
}
|
||||
|
||||
// get diff from commit file that's currently selected
|
||||
path := gui.State.Contexts.CommitFiles.GetSelectedPath()
|
||||
if path == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
ref := gui.State.Contexts.CommitFiles.CommitFileTreeViewModel.GetRef()
|
||||
to := ref.RefName()
|
||||
from, reverse := gui.State.Modes.Diffing.GetFromAndReverseArgsForDiff(ref.ParentRefName())
|
||||
diff, err := gui.git.WorkingTree.ShowFileDiff(from, to, reverse, path, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
secondaryDiff := gui.git.Patch.PatchManager.RenderPatchForFile(path, false, false, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
context := gui.State.Contexts.CustomPatchBuilder
|
||||
|
||||
oldState := context.GetState()
|
||||
|
||||
state := patch_exploring.NewState(diff, selectedLineIdx, oldState, gui.Log)
|
||||
context.SetState(state)
|
||||
if state == nil {
|
||||
return gui.helpers.PatchBuilding.Escape()
|
||||
}
|
||||
|
||||
mainContent := context.GetContentToRender(true)
|
||||
|
||||
return gui.refreshMainViews(refreshMainOpts{
|
||||
pair: gui.patchBuildingMainContextPair(),
|
||||
main: &viewUpdateOpts{
|
||||
task: NewRenderStringWithoutScrollTask(mainContent),
|
||||
title: gui.Tr.Patch,
|
||||
},
|
||||
secondary: &viewUpdateOpts{
|
||||
task: NewRenderStringWithoutScrollTask(secondaryDiff),
|
||||
title: gui.Tr.CustomPatch,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ func (gui *Gui) remoteBranchesRenderToMain() error {
|
||||
}
|
||||
|
||||
return gui.refreshMainViews(refreshMainOpts{
|
||||
pair: gui.normalMainContextPair(),
|
||||
main: &viewUpdateOpts{
|
||||
title: "Remote Branch",
|
||||
task: task,
|
||||
|
@ -19,6 +19,7 @@ func (gui *Gui) remotesRenderToMain() error {
|
||||
}
|
||||
|
||||
return gui.refreshMainViews(refreshMainOpts{
|
||||
pair: gui.normalMainContextPair(),
|
||||
main: &viewUpdateOpts{
|
||||
title: "Remote",
|
||||
task: task,
|
||||
|
@ -29,7 +29,7 @@ func (self *KeybindingCreator) call(customCommand config.CustomCommand, handler
|
||||
return nil, formatContextNotProvidedError(customCommand)
|
||||
}
|
||||
|
||||
viewName, contexts, err := self.getViewNameAndContexts(customCommand)
|
||||
viewName, err := self.getViewNameAndContexts(customCommand)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -41,7 +41,6 @@ func (self *KeybindingCreator) call(customCommand config.CustomCommand, handler
|
||||
|
||||
return &types.Binding{
|
||||
ViewName: viewName,
|
||||
Contexts: contexts,
|
||||
Key: self.getKey(customCommand.Key),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: handler,
|
||||
@ -49,22 +48,18 @@ func (self *KeybindingCreator) call(customCommand config.CustomCommand, handler
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (self *KeybindingCreator) getViewNameAndContexts(customCommand config.CustomCommand) (string, []string, error) {
|
||||
func (self *KeybindingCreator) getViewNameAndContexts(customCommand config.CustomCommand) (string, error) {
|
||||
if customCommand.Context == "global" {
|
||||
return "", nil, nil
|
||||
return "", nil
|
||||
}
|
||||
|
||||
ctx, ok := self.contextForContextKey(types.ContextKey(customCommand.Context))
|
||||
if !ok {
|
||||
return "", nil, formatUnknownContextError(customCommand)
|
||||
return "", formatUnknownContextError(customCommand)
|
||||
}
|
||||
|
||||
// here we assume that a given context will always belong to the same view.
|
||||
// Currently this is a safe bet but it's by no means guaranteed in the long term
|
||||
// and we might need to make some changes in the future to support it.
|
||||
viewName := ctx.GetViewName()
|
||||
contexts := []string{customCommand.Context}
|
||||
return viewName, contexts, nil
|
||||
return viewName, nil
|
||||
}
|
||||
|
||||
func (self *KeybindingCreator) contextForContextKey(contextKey types.ContextKey) (types.Context, bool) {
|
||||
|
@ -21,9 +21,9 @@ func (gui *Gui) nextSideWindow() error {
|
||||
return err
|
||||
}
|
||||
|
||||
viewName := gui.getViewNameForWindow(newWindow)
|
||||
context := gui.getContextForWindow(newWindow)
|
||||
|
||||
return gui.pushContextWithView(viewName)
|
||||
return gui.c.PushContext(context)
|
||||
}
|
||||
|
||||
func (gui *Gui) previousSideWindow() error {
|
||||
@ -47,13 +47,15 @@ func (gui *Gui) previousSideWindow() error {
|
||||
return err
|
||||
}
|
||||
|
||||
viewName := gui.getViewNameForWindow(newWindow)
|
||||
context := gui.getContextForWindow(newWindow)
|
||||
|
||||
return gui.pushContextWithView(viewName)
|
||||
return gui.c.PushContext(context)
|
||||
}
|
||||
|
||||
func (gui *Gui) goToSideWindow(sideViewName string) func() error {
|
||||
func (gui *Gui) goToSideWindow(window string) func() error {
|
||||
return func() error {
|
||||
return gui.pushContextWithView(sideViewName)
|
||||
context := gui.getContextForWindow(window)
|
||||
|
||||
return gui.c.PushContext(context)
|
||||
}
|
||||
}
|
||||
|
@ -1,227 +0,0 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/patch"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
func (gui *Gui) refreshStagingPanel(forceSecondaryFocused bool, selectedLineIdx int) error {
|
||||
gui.splitMainPanel(true)
|
||||
|
||||
file := gui.getSelectedFile()
|
||||
if file == nil || (!file.HasUnstagedChanges && !file.HasStagedChanges) {
|
||||
return gui.handleStagingEscape()
|
||||
}
|
||||
|
||||
secondaryFocused := false
|
||||
if forceSecondaryFocused {
|
||||
secondaryFocused = true
|
||||
} else if gui.State.Panels.LineByLine != nil {
|
||||
secondaryFocused = gui.State.Panels.LineByLine.SecondaryFocused
|
||||
}
|
||||
|
||||
if (secondaryFocused && !file.HasStagedChanges) || (!secondaryFocused && !file.HasUnstagedChanges) {
|
||||
secondaryFocused = !secondaryFocused
|
||||
}
|
||||
|
||||
if secondaryFocused {
|
||||
gui.Views.Main.Title = gui.c.Tr.StagedChanges
|
||||
gui.Views.Secondary.Title = gui.c.Tr.UnstagedChanges
|
||||
} else {
|
||||
gui.Views.Main.Title = gui.c.Tr.UnstagedChanges
|
||||
gui.Views.Secondary.Title = gui.c.Tr.StagedChanges
|
||||
}
|
||||
|
||||
// note for custom diffs, we'll need to send a flag here saying not to use the custom diff
|
||||
diff := gui.git.WorkingTree.WorktreeFileDiff(file, true, secondaryFocused, false)
|
||||
secondaryDiff := gui.git.WorkingTree.WorktreeFileDiff(file, true, !secondaryFocused, false)
|
||||
|
||||
// if we have e.g. a deleted file with nothing else to the diff will have only
|
||||
// 4-5 lines in which case we'll swap panels
|
||||
if len(strings.Split(diff, "\n")) < 5 {
|
||||
if len(strings.Split(secondaryDiff, "\n")) < 5 {
|
||||
return gui.handleStagingEscape()
|
||||
}
|
||||
secondaryFocused = !secondaryFocused
|
||||
diff, secondaryDiff = secondaryDiff, diff
|
||||
}
|
||||
|
||||
empty, err := gui.refreshLineByLinePanel(diff, secondaryDiff, secondaryFocused, selectedLineIdx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if empty {
|
||||
return gui.handleStagingEscape()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) handleTogglePanelClick() error {
|
||||
return gui.withLBLActiveCheck(func(state *LblPanelState) error {
|
||||
state.SecondaryFocused = !state.SecondaryFocused
|
||||
|
||||
return gui.refreshStagingPanel(false, gui.Views.Secondary.SelectedLineIdx())
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleRefreshStagingPanel(forceSecondaryFocused bool, selectedLineIdx int) error {
|
||||
gui.Mutexes.LineByLinePanelMutex.Lock()
|
||||
defer gui.Mutexes.LineByLinePanelMutex.Unlock()
|
||||
|
||||
return gui.refreshStagingPanel(forceSecondaryFocused, selectedLineIdx)
|
||||
}
|
||||
|
||||
func (gui *Gui) onStagingFocus(forceSecondaryFocused bool, selectedLineIdx int) error {
|
||||
gui.Mutexes.LineByLinePanelMutex.Lock()
|
||||
defer gui.Mutexes.LineByLinePanelMutex.Unlock()
|
||||
|
||||
if gui.State.Panels.LineByLine == nil || selectedLineIdx != -1 {
|
||||
return gui.refreshStagingPanel(forceSecondaryFocused, selectedLineIdx)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) handleTogglePanel() error {
|
||||
return gui.withLBLActiveCheck(func(state *LblPanelState) error {
|
||||
state.SecondaryFocused = !state.SecondaryFocused
|
||||
return gui.refreshStagingPanel(false, -1)
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleStagingEscape() error {
|
||||
gui.escapeLineByLinePanel()
|
||||
|
||||
return gui.c.PushContext(gui.State.Contexts.Files)
|
||||
}
|
||||
|
||||
func (gui *Gui) handleToggleStagedSelection() error {
|
||||
return gui.withLBLActiveCheck(func(state *LblPanelState) error {
|
||||
return gui.applySelection(state.SecondaryFocused, state)
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleResetSelection() error {
|
||||
return gui.withLBLActiveCheck(func(state *LblPanelState) error {
|
||||
if state.SecondaryFocused {
|
||||
// for backwards compatibility
|
||||
return gui.applySelection(true, state)
|
||||
}
|
||||
|
||||
if !gui.c.UserConfig.Gui.SkipUnstageLineWarning {
|
||||
return gui.c.Confirm(types.ConfirmOpts{
|
||||
Title: gui.c.Tr.UnstageLinesTitle,
|
||||
Prompt: gui.c.Tr.UnstageLinesPrompt,
|
||||
HandleConfirm: func() error {
|
||||
return gui.withLBLActiveCheck(func(state *LblPanelState) error {
|
||||
return gui.applySelection(true, state)
|
||||
})
|
||||
},
|
||||
})
|
||||
} else {
|
||||
return gui.applySelection(true, state)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) applySelection(reverse bool, state *LblPanelState) error {
|
||||
file := gui.getSelectedFile()
|
||||
if file == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
firstLineIdx, lastLineIdx := state.SelectedRange()
|
||||
patch := patch.ModifiedPatchForRange(gui.Log, file.Name, state.GetDiff(), firstLineIdx, lastLineIdx, reverse, false)
|
||||
|
||||
if patch == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// apply the patch then refresh this panel
|
||||
// create a new temp file with the patch, then call git apply with that patch
|
||||
applyFlags := []string{}
|
||||
if !reverse || state.SecondaryFocused {
|
||||
applyFlags = append(applyFlags, "cached")
|
||||
}
|
||||
gui.c.LogAction(gui.c.Tr.Actions.ApplyPatch)
|
||||
err := gui.git.WorkingTree.ApplyPatch(patch, applyFlags...)
|
||||
if err != nil {
|
||||
return gui.c.Error(err)
|
||||
}
|
||||
|
||||
if state.SelectingRange() {
|
||||
state.SetLineSelectMode()
|
||||
}
|
||||
|
||||
if err := gui.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.FILES}}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := gui.refreshStagingPanel(false, -1); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) HandleOpenFile() error {
|
||||
file := gui.getSelectedFile()
|
||||
if file == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return gui.helpers.Files.OpenFile(file.GetPath())
|
||||
}
|
||||
|
||||
func (gui *Gui) handleEditHunk() error {
|
||||
return gui.withLBLActiveCheck(func(state *LblPanelState) error {
|
||||
return gui.editHunk(state.SecondaryFocused, state)
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) editHunk(reverse bool, state *LblPanelState) error {
|
||||
file := gui.getSelectedFile()
|
||||
if file == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
hunk := state.CurrentHunk()
|
||||
patchText := patch.ModifiedPatchForRange(gui.Log, file.Name, state.GetDiff(), hunk.FirstLineIdx, hunk.LastLineIdx(), reverse, false)
|
||||
patchFilepath, err := gui.git.WorkingTree.SaveTemporaryPatch(patchText)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lineOffset := 3
|
||||
lineIdxInHunk := state.GetSelectedLineIdx() - hunk.FirstLineIdx
|
||||
if err := gui.helpers.Files.EditFileAtLine(patchFilepath, lineIdxInHunk+lineOffset); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
editedPatchText, err := gui.git.File.Cat(patchFilepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
applyFlags := []string{}
|
||||
if !reverse || state.SecondaryFocused {
|
||||
applyFlags = append(applyFlags, "cached")
|
||||
}
|
||||
gui.c.LogAction(gui.c.Tr.Actions.ApplyPatch)
|
||||
|
||||
lineCount := strings.Count(editedPatchText, "\n") + 1
|
||||
newPatchText := patch.ModifiedPatchForRange(gui.Log, file.Name, editedPatchText, 0, lineCount, false, false)
|
||||
if err := gui.git.WorkingTree.ApplyPatch(newPatchText, applyFlags...); err != nil {
|
||||
return gui.c.Error(err)
|
||||
}
|
||||
|
||||
if err := gui.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.FILES}}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := gui.refreshStagingPanel(false, -1); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
@ -10,6 +10,7 @@ func (gui *Gui) stashRenderToMain() error {
|
||||
}
|
||||
|
||||
return gui.refreshMainViews(refreshMainOpts{
|
||||
pair: gui.normalMainContextPair(),
|
||||
main: &viewUpdateOpts{
|
||||
title: "Stash",
|
||||
task: task,
|
||||
|
@ -88,8 +88,9 @@ func (gui *Gui) statusRenderToMain() error {
|
||||
}, "\n\n")
|
||||
|
||||
return gui.refreshMainViews(refreshMainOpts{
|
||||
pair: gui.normalMainContextPair(),
|
||||
main: &viewUpdateOpts{
|
||||
title: "",
|
||||
title: gui.c.Tr.StatusTitle,
|
||||
task: NewRenderStringTask(dashboardString),
|
||||
},
|
||||
})
|
||||
|
@ -14,6 +14,7 @@ func (gui *Gui) subCommitsRenderToMain() error {
|
||||
}
|
||||
|
||||
return gui.refreshMainViews(refreshMainOpts{
|
||||
pair: gui.normalMainContextPair(),
|
||||
main: &viewUpdateOpts{
|
||||
title: "Commit",
|
||||
task: task,
|
||||
|
@ -31,6 +31,7 @@ func (gui *Gui) submodulesRenderToMain() error {
|
||||
}
|
||||
|
||||
return gui.refreshMainViews(refreshMainOpts{
|
||||
pair: gui.normalMainContextPair(),
|
||||
main: &viewUpdateOpts{
|
||||
title: "Submodule",
|
||||
task: task,
|
||||
|
@ -1,16 +1,17 @@
|
||||
package gui
|
||||
|
||||
func (self *Gui) tagsRenderToMain() error {
|
||||
func (gui *Gui) tagsRenderToMain() error {
|
||||
var task updateTask
|
||||
tag := self.State.Contexts.Tags.GetSelected()
|
||||
tag := gui.State.Contexts.Tags.GetSelected()
|
||||
if tag == nil {
|
||||
task = NewRenderStringTask("No tags")
|
||||
} else {
|
||||
cmdObj := self.git.Branch.GetGraphCmdObj(tag.FullRefName())
|
||||
cmdObj := gui.git.Branch.GetGraphCmdObj(tag.FullRefName())
|
||||
task = NewRunCommandTask(cmdObj.GetCmd())
|
||||
}
|
||||
|
||||
return self.refreshMainViews(refreshMainOpts{
|
||||
return gui.refreshMainViews(refreshMainOpts{
|
||||
pair: gui.normalMainContextPair(),
|
||||
main: &viewUpdateOpts{
|
||||
title: "Tag",
|
||||
task: task,
|
||||
|
@ -24,19 +24,19 @@ func (gui *Gui) newCmdTask(view *gocui.View, cmd *exec.Cmd, prefix string) error
|
||||
start := func() (*exec.Cmd, io.Reader) {
|
||||
r, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
gui.c.Log.Warn(err)
|
||||
gui.c.Log.Error(err)
|
||||
}
|
||||
cmd.Stderr = cmd.Stdout
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
gui.c.Log.Warn(err)
|
||||
gui.c.Log.Error(err)
|
||||
}
|
||||
|
||||
return cmd, r
|
||||
}
|
||||
|
||||
if err := manager.NewTask(manager.NewCmdTask(start, prefix, height+oy+10, nil), cmdStr); err != nil {
|
||||
gui.c.Log.Warn(err)
|
||||
gui.c.Log.Error(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -37,6 +37,8 @@ type IGuiCommon interface {
|
||||
PushContext(context Context, opts ...OnFocusOpts) error
|
||||
PopContext() error
|
||||
CurrentContext() Context
|
||||
CurrentStaticContext() Context
|
||||
IsCurrentContext(Context) bool
|
||||
// enters search mode for the current view
|
||||
OpenSearch()
|
||||
|
||||
@ -162,7 +164,7 @@ type Mutexes struct {
|
||||
RefreshingStatusMutex *sync.Mutex
|
||||
SyncMutex *sync.Mutex
|
||||
LocalCommitsMutex *sync.Mutex
|
||||
LineByLinePanelMutex *sync.Mutex
|
||||
SubprocessMutex *sync.Mutex
|
||||
PopupMutex *sync.Mutex
|
||||
PtyMutex *sync.Mutex
|
||||
}
|
||||
|
@ -1,8 +1,11 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/patch_exploring"
|
||||
)
|
||||
|
||||
type ContextKind int
|
||||
@ -25,6 +28,9 @@ const (
|
||||
EXTRAS_CONTEXT
|
||||
// only used by the one global context, purely for the sake of defining keybindings globally
|
||||
GLOBAL_CONTEXT
|
||||
// a display context only renders a view. It has no keybindings associated and
|
||||
// it cannot receive focus.
|
||||
DISPLAY_CONTEXT
|
||||
)
|
||||
|
||||
type ParentContexter interface {
|
||||
@ -39,6 +45,8 @@ type IBaseContext interface {
|
||||
|
||||
GetKind() ContextKind
|
||||
GetViewName() string
|
||||
GetView() *gocui.View
|
||||
GetViewTrait() IViewTrait
|
||||
GetWindowName() string
|
||||
SetWindowName(string)
|
||||
GetKey() ContextKey
|
||||
@ -48,6 +56,9 @@ type IBaseContext interface {
|
||||
// of the same transient context can appear at once meaning one might be 'stolen'
|
||||
// from another window.
|
||||
IsTransient() bool
|
||||
// this tells us if the view's bounds are determined by its window or if they're
|
||||
// determined independently.
|
||||
HasControlledBounds() bool
|
||||
|
||||
// returns the desired title for the view upon activation. If there is no desired title (returns empty string), then
|
||||
// no title will be set
|
||||
@ -67,8 +78,8 @@ type IBaseContext interface {
|
||||
type Context interface {
|
||||
IBaseContext
|
||||
|
||||
HandleFocus(opts ...OnFocusOpts) error
|
||||
HandleFocusLost() error
|
||||
HandleFocus(opts OnFocusOpts) error
|
||||
HandleFocusLost(opts OnFocusLostOpts) error
|
||||
HandleRender() error
|
||||
HandleRenderToMain() error
|
||||
}
|
||||
@ -82,8 +93,20 @@ type IListContext interface {
|
||||
|
||||
OnSearchSelect(selectedLineIdx int) error
|
||||
FocusLine()
|
||||
}
|
||||
|
||||
GetViewTrait() IViewTrait
|
||||
type IPatchExplorerContext interface {
|
||||
Context
|
||||
|
||||
GetState() *patch_exploring.State
|
||||
SetState(*patch_exploring.State)
|
||||
GetIncludedLineIndices() []int
|
||||
RenderAndFocus(isFocused bool) error
|
||||
Render(isFocused bool) error
|
||||
Focus() error
|
||||
GetContentToRender(isFocused bool) string
|
||||
NavigateTo(isFocused bool, selectedLineIdx int) error
|
||||
GetMutex() *sync.Mutex
|
||||
}
|
||||
|
||||
type IViewTrait interface {
|
||||
@ -95,17 +118,22 @@ type IViewTrait interface {
|
||||
ViewPortYBounds() (int, int)
|
||||
ScrollLeft()
|
||||
ScrollRight()
|
||||
ScrollUp()
|
||||
ScrollDown()
|
||||
ScrollUp(value int)
|
||||
ScrollDown(value int)
|
||||
PageDelta() int
|
||||
SelectedLineIdx() int
|
||||
SetHighlight(bool)
|
||||
}
|
||||
|
||||
type OnFocusOpts struct {
|
||||
ClickedViewName string
|
||||
ClickedWindowName string
|
||||
ClickedViewLineIdx int
|
||||
}
|
||||
|
||||
type OnFocusLostOpts struct {
|
||||
NewContextKey ContextKey
|
||||
}
|
||||
|
||||
type ContextKey string
|
||||
|
||||
type KeybindingsOpts struct {
|
||||
|
@ -9,7 +9,6 @@ type Key interface{} // FIXME: find out how to get `gocui.Key | rune`
|
||||
// is ""
|
||||
type Binding struct {
|
||||
ViewName string
|
||||
Contexts []string
|
||||
Handler func() error
|
||||
Key Key
|
||||
Modifier gocui.Modifier
|
||||
|
@ -14,6 +14,9 @@ const (
|
||||
REMOTES
|
||||
STATUS
|
||||
SUBMODULES
|
||||
STAGING
|
||||
PATCH_BUILDING
|
||||
COMMIT_FILES
|
||||
// not actually a view. Will refactor this later
|
||||
BISECT_INFO
|
||||
)
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/spkg/bom"
|
||||
)
|
||||
@ -116,52 +117,71 @@ func (gui *Gui) popupPanelFocused() bool {
|
||||
return gui.isPopupPanel(gui.currentViewName())
|
||||
}
|
||||
|
||||
// secondaryViewFocused tells us whether it appears that the secondary view is focused. The view is actually never focused for real: we just swap the main and secondary views and then you're still focused on the main view so that we can give you access to all its keybindings for free. I will probably regret this design decision soon enough.
|
||||
func (gui *Gui) secondaryViewFocused() bool {
|
||||
state := gui.State.Panels.LineByLine
|
||||
return state != nil && state.SecondaryFocused
|
||||
}
|
||||
func (gui *Gui) onViewTabClick(windowName string, tabIndex int) error {
|
||||
tabs := gui.viewTabMap()[windowName]
|
||||
if len(tabs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) onViewTabClick(viewName string, tabIndex int) error {
|
||||
context := gui.State.ViewTabContextMap[viewName][tabIndex].Context
|
||||
viewName := tabs[tabIndex].ViewName
|
||||
|
||||
context, ok := gui.contextForView(viewName)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return gui.c.PushContext(context)
|
||||
}
|
||||
|
||||
func (gui *Gui) contextForView(viewName string) (types.Context, bool) {
|
||||
view, err := gui.g.View(viewName)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
for _, context := range gui.State.Contexts.Flatten() {
|
||||
if context.GetViewName() == view.Name() {
|
||||
return context, true
|
||||
}
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (gui *Gui) handleNextTab() error {
|
||||
v := getTabbedView(gui)
|
||||
if v == nil {
|
||||
view := getTabbedView(gui)
|
||||
if view == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return gui.onViewTabClick(
|
||||
v.Name(),
|
||||
utils.ModuloWithWrap(v.TabIndex+1, len(v.Tabs)),
|
||||
)
|
||||
for _, context := range gui.State.Contexts.Flatten() {
|
||||
if context.GetViewName() == view.Name() {
|
||||
return gui.onViewTabClick(
|
||||
context.GetWindowName(),
|
||||
utils.ModuloWithWrap(view.TabIndex+1, len(view.Tabs)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) handlePrevTab() error {
|
||||
v := getTabbedView(gui)
|
||||
if v == nil {
|
||||
view := getTabbedView(gui)
|
||||
if view == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return gui.onViewTabClick(
|
||||
v.Name(),
|
||||
utils.ModuloWithWrap(v.TabIndex-1, len(v.Tabs)),
|
||||
)
|
||||
}
|
||||
|
||||
// this is the distance we will move the cursor when paging up or down in a view
|
||||
func (gui *Gui) pageDelta(view *gocui.View) int {
|
||||
_, height := view.Size()
|
||||
|
||||
delta := height - 1
|
||||
if delta == 0 {
|
||||
return 1
|
||||
for _, context := range gui.State.Contexts.Flatten() {
|
||||
if context.GetViewName() == view.Name() {
|
||||
return gui.onViewTabClick(
|
||||
context.GetWindowName(),
|
||||
utils.ModuloWithWrap(view.TabIndex-1, len(view.Tabs)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return delta
|
||||
return nil
|
||||
}
|
||||
|
||||
func getTabbedView(gui *Gui) *gocui.View {
|
||||
|
160
pkg/gui/views.go
160
pkg/gui/views.go
@ -3,34 +3,43 @@ package gui
|
||||
import (
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||
)
|
||||
|
||||
type Views struct {
|
||||
Status *gocui.View
|
||||
Submodules *gocui.View
|
||||
Files *gocui.View
|
||||
Branches *gocui.View
|
||||
Remotes *gocui.View
|
||||
Tags *gocui.View
|
||||
RemoteBranches *gocui.View
|
||||
ReflogCommits *gocui.View
|
||||
Commits *gocui.View
|
||||
Stash *gocui.View
|
||||
Main *gocui.View
|
||||
Secondary *gocui.View
|
||||
Options *gocui.View
|
||||
Confirmation *gocui.View
|
||||
Menu *gocui.View
|
||||
CommitMessage *gocui.View
|
||||
CommitFiles *gocui.View
|
||||
SubCommits *gocui.View
|
||||
Information *gocui.View
|
||||
AppStatus *gocui.View
|
||||
Search *gocui.View
|
||||
SearchPrefix *gocui.View
|
||||
Limit *gocui.View
|
||||
Suggestions *gocui.View
|
||||
Tooltip *gocui.View
|
||||
Extras *gocui.View
|
||||
|
||||
Main *gocui.View
|
||||
Secondary *gocui.View
|
||||
Staging *gocui.View
|
||||
StagingSecondary *gocui.View
|
||||
PatchBuilding *gocui.View
|
||||
PatchBuildingSecondary *gocui.View
|
||||
Merging *gocui.View
|
||||
|
||||
Options *gocui.View
|
||||
Confirmation *gocui.View
|
||||
Menu *gocui.View
|
||||
CommitMessage *gocui.View
|
||||
CommitFiles *gocui.View
|
||||
SubCommits *gocui.View
|
||||
Information *gocui.View
|
||||
AppStatus *gocui.View
|
||||
Search *gocui.View
|
||||
SearchPrefix *gocui.View
|
||||
Limit *gocui.View
|
||||
Suggestions *gocui.View
|
||||
Tooltip *gocui.View
|
||||
Extras *gocui.View
|
||||
}
|
||||
|
||||
type viewNameMapping struct {
|
||||
@ -49,15 +58,26 @@ func (gui *Gui) orderedViewNameMappings() []viewNameMapping {
|
||||
// first layer. Ordering within this layer does not matter because there are
|
||||
// no overlapping views
|
||||
{viewPtr: &gui.Views.Status, name: "status"},
|
||||
{viewPtr: &gui.Views.Submodules, name: "submodules"},
|
||||
{viewPtr: &gui.Views.Files, name: "files"},
|
||||
{viewPtr: &gui.Views.Branches, name: "branches"},
|
||||
{viewPtr: &gui.Views.Tags, name: "tags"},
|
||||
{viewPtr: &gui.Views.Remotes, name: "remotes"},
|
||||
{viewPtr: &gui.Views.Branches, name: "localBranches"},
|
||||
{viewPtr: &gui.Views.RemoteBranches, name: "remoteBranches"},
|
||||
{viewPtr: &gui.Views.ReflogCommits, name: "reflogCommits"},
|
||||
{viewPtr: &gui.Views.Commits, name: "commits"},
|
||||
{viewPtr: &gui.Views.Stash, name: "stash"},
|
||||
{viewPtr: &gui.Views.SubCommits, name: "subCommits"},
|
||||
{viewPtr: &gui.Views.CommitFiles, name: "commitFiles"},
|
||||
{viewPtr: &gui.Views.Main, name: "main"},
|
||||
|
||||
{viewPtr: &gui.Views.Staging, name: "staging"},
|
||||
{viewPtr: &gui.Views.StagingSecondary, name: "stagingSecondary"},
|
||||
{viewPtr: &gui.Views.PatchBuilding, name: "patchBuilding"},
|
||||
{viewPtr: &gui.Views.PatchBuildingSecondary, name: "patchBuildingSecondary"},
|
||||
{viewPtr: &gui.Views.Merging, name: "merging"},
|
||||
{viewPtr: &gui.Views.Secondary, name: "secondary"},
|
||||
{viewPtr: &gui.Views.Main, name: "main"},
|
||||
|
||||
{viewPtr: &gui.Views.Extras, name: "extras"},
|
||||
|
||||
// bottom line
|
||||
@ -80,35 +100,13 @@ func (gui *Gui) orderedViewNameMappings() []viewNameMapping {
|
||||
}
|
||||
}
|
||||
|
||||
type controlledView struct {
|
||||
viewName string
|
||||
windowName string
|
||||
frame bool
|
||||
}
|
||||
|
||||
// controlled views have their size and position determined in arrangement.go.
|
||||
// Some views, like the confirmation panel, are currently sized at the time of
|
||||
// displaying the view, based on the view's contents.
|
||||
func (gui *Gui) controlledViews() []controlledView {
|
||||
return []controlledView{
|
||||
{viewName: "main", windowName: "main", frame: true},
|
||||
{viewName: "secondary", windowName: "secondary", frame: true},
|
||||
{viewName: "status", windowName: "status", frame: true},
|
||||
{viewName: "files", windowName: "files", frame: true},
|
||||
{viewName: "branches", windowName: "branches", frame: true},
|
||||
{viewName: "remoteBranches", windowName: "branches", frame: true},
|
||||
{viewName: "commitFiles", windowName: gui.State.Contexts.CommitFiles.GetWindowName(), frame: true},
|
||||
{viewName: "subCommits", windowName: gui.State.Contexts.SubCommits.GetWindowName(), frame: true},
|
||||
{viewName: "commits", windowName: "commits", frame: true},
|
||||
{viewName: "stash", windowName: "stash", frame: true},
|
||||
{viewName: "options", windowName: "options", frame: false},
|
||||
{viewName: "searchPrefix", windowName: "searchPrefix", frame: false},
|
||||
{viewName: "search", windowName: "search", frame: false},
|
||||
{viewName: "appStatus", windowName: "appStatus", frame: false},
|
||||
{viewName: "information", windowName: "information", frame: false},
|
||||
{viewName: "extras", windowName: "extras", frame: true},
|
||||
{viewName: "limit", windowName: "limit", frame: true},
|
||||
func (gui *Gui) windowForView(viewName string) string {
|
||||
context, ok := gui.contextForView(viewName)
|
||||
if !ok {
|
||||
panic("todo: deal with this")
|
||||
}
|
||||
|
||||
return context.GetWindowName()
|
||||
}
|
||||
|
||||
func (gui *Gui) createAllViews() error {
|
||||
@ -121,9 +119,11 @@ func (gui *Gui) createAllViews() error {
|
||||
}
|
||||
|
||||
gui.Views.Options.FgColor = theme.OptionsColor
|
||||
gui.Views.Options.Frame = false
|
||||
|
||||
gui.Views.SearchPrefix.BgColor = gocui.ColorDefault
|
||||
gui.Views.SearchPrefix.FgColor = gocui.ColorGreen
|
||||
gui.Views.SearchPrefix.Frame = false
|
||||
gui.setViewContent(gui.Views.SearchPrefix, SEARCH_PREFIX)
|
||||
|
||||
gui.Views.Stash.Title = gui.c.Tr.StashTitle
|
||||
@ -140,22 +140,44 @@ func (gui *Gui) createAllViews() error {
|
||||
gui.Views.Branches.Title = gui.c.Tr.BranchesTitle
|
||||
gui.Views.Branches.FgColor = theme.GocuiDefaultTextColor
|
||||
|
||||
gui.Views.Remotes.Title = gui.c.Tr.RemotesTitle
|
||||
gui.Views.Remotes.FgColor = theme.GocuiDefaultTextColor
|
||||
|
||||
gui.Views.Tags.Title = gui.c.Tr.TagsTitle
|
||||
gui.Views.Tags.FgColor = theme.GocuiDefaultTextColor
|
||||
|
||||
gui.Views.RemoteBranches.FgColor = theme.GocuiDefaultTextColor
|
||||
|
||||
gui.Views.Files.Title = gui.c.Tr.FilesTitle
|
||||
gui.Views.Files.FgColor = theme.GocuiDefaultTextColor
|
||||
|
||||
gui.Views.Secondary.Title = gui.c.Tr.DiffTitle
|
||||
gui.Views.Secondary.Wrap = true
|
||||
gui.Views.Secondary.FgColor = theme.GocuiDefaultTextColor
|
||||
gui.Views.Secondary.IgnoreCarriageReturns = true
|
||||
gui.Views.Secondary.CanScrollPastBottom = gui.c.UserConfig.Gui.ScrollPastBottom
|
||||
for _, view := range []*gocui.View{gui.Views.Main, gui.Views.Secondary, gui.Views.Staging, gui.Views.StagingSecondary, gui.Views.PatchBuilding, gui.Views.PatchBuildingSecondary, gui.Views.Merging} {
|
||||
view.Title = gui.c.Tr.DiffTitle
|
||||
view.Wrap = true
|
||||
view.FgColor = theme.GocuiDefaultTextColor
|
||||
view.IgnoreCarriageReturns = true
|
||||
view.CanScrollPastBottom = gui.c.UserConfig.Gui.ScrollPastBottom
|
||||
}
|
||||
|
||||
gui.Views.Main.Title = gui.c.Tr.DiffTitle
|
||||
gui.Views.Main.Wrap = true
|
||||
gui.Views.Main.FgColor = theme.GocuiDefaultTextColor
|
||||
gui.Views.Main.IgnoreCarriageReturns = true
|
||||
gui.Views.Main.CanScrollPastBottom = gui.c.UserConfig.Gui.ScrollPastBottom
|
||||
gui.Views.Staging.Title = gui.c.Tr.UnstagedChanges
|
||||
gui.Views.Staging.Highlight = true
|
||||
gui.Views.Staging.Wrap = true
|
||||
|
||||
gui.Views.StagingSecondary.Title = gui.c.Tr.StagedChanges
|
||||
gui.Views.StagingSecondary.Highlight = true
|
||||
gui.Views.StagingSecondary.Wrap = true
|
||||
|
||||
gui.Views.PatchBuilding.Title = gui.Tr.Patch
|
||||
gui.Views.PatchBuilding.Highlight = true
|
||||
gui.Views.PatchBuilding.Wrap = true
|
||||
|
||||
gui.Views.PatchBuildingSecondary.Title = gui.Tr.CustomPatch
|
||||
gui.Views.PatchBuildingSecondary.Highlight = true
|
||||
gui.Views.PatchBuildingSecondary.Wrap = true
|
||||
|
||||
gui.Views.Merging.Title = gui.c.Tr.MergeConflictsTitle
|
||||
gui.Views.Merging.Highlight = true
|
||||
gui.Views.Merging.Wrap = true
|
||||
|
||||
gui.Views.Limit.Title = gui.c.Tr.NotEnoughSpace
|
||||
gui.Views.Limit.Wrap = true
|
||||
@ -166,10 +188,12 @@ func (gui *Gui) createAllViews() error {
|
||||
gui.Views.Search.BgColor = gocui.ColorDefault
|
||||
gui.Views.Search.FgColor = gocui.ColorGreen
|
||||
gui.Views.Search.Editable = true
|
||||
gui.Views.Search.Frame = false
|
||||
|
||||
gui.Views.AppStatus.BgColor = gocui.ColorDefault
|
||||
gui.Views.AppStatus.FgColor = gocui.ColorCyan
|
||||
gui.Views.AppStatus.Visible = false
|
||||
gui.Views.AppStatus.Frame = false
|
||||
|
||||
gui.Views.CommitMessage.Visible = false
|
||||
gui.Views.CommitMessage.Title = gui.c.Tr.CommitMessage
|
||||
@ -189,6 +213,7 @@ func (gui *Gui) createAllViews() error {
|
||||
|
||||
gui.Views.Information.BgColor = gocui.ColorDefault
|
||||
gui.Views.Information.FgColor = gocui.ColorGreen
|
||||
gui.Views.Information.Frame = false
|
||||
|
||||
gui.Views.Extras.Title = gui.c.Tr.CommandLog
|
||||
gui.Views.Extras.FgColor = theme.GocuiDefaultTextColor
|
||||
@ -197,22 +222,3 @@ func (gui *Gui) createAllViews() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func initialViewContextMapping(contextTree *context.ContextTree) map[string]types.Context {
|
||||
return map[string]types.Context{
|
||||
"status": contextTree.Status,
|
||||
"files": contextTree.Files,
|
||||
"branches": contextTree.Branches,
|
||||
"remoteBranches": contextTree.RemoteBranches,
|
||||
"commits": contextTree.LocalCommits,
|
||||
"commitFiles": contextTree.CommitFiles,
|
||||
"subCommits": contextTree.SubCommits,
|
||||
"stash": contextTree.Stash,
|
||||
"menu": contextTree.Menu,
|
||||
"confirmation": contextTree.Confirmation,
|
||||
"commitMessage": contextTree.CommitMessage,
|
||||
"main": contextTree.Normal,
|
||||
"secondary": contextTree.Normal,
|
||||
"extras": contextTree.CommandLog,
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,11 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
// A window refers to a place on the screen which can hold one or more views.
|
||||
@ -10,21 +14,38 @@ import (
|
||||
// space. Right now most windows are 1:1 with views, except for commitFiles which
|
||||
// is a view that moves between windows
|
||||
|
||||
func (gui *Gui) initialWindowViewNameMap(contextTree *context.ContextTree) map[string]string {
|
||||
result := map[string]string{}
|
||||
|
||||
for _, context := range contextTree.Flatten() {
|
||||
result[context.GetWindowName()] = context.GetViewName()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (gui *Gui) getViewNameForWindow(window string) string {
|
||||
viewName, ok := gui.State.WindowViewNameMap[window]
|
||||
if !ok {
|
||||
return window
|
||||
panic(fmt.Sprintf("Viewname not found for window: %s", window))
|
||||
}
|
||||
|
||||
return viewName
|
||||
}
|
||||
|
||||
// for now all we actually care about is the context's view so we're storing that
|
||||
func (gui *Gui) setWindowContext(c types.Context) {
|
||||
if gui.State.WindowViewNameMap == nil {
|
||||
gui.State.WindowViewNameMap = map[string]string{}
|
||||
func (gui *Gui) getContextForWindow(window string) types.Context {
|
||||
viewName := gui.getViewNameForWindow(window)
|
||||
|
||||
context, ok := gui.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 (gui *Gui) setWindowContext(c types.Context) {
|
||||
if c.IsTransient() {
|
||||
gui.resetWindowContext(c)
|
||||
}
|
||||
@ -40,8 +61,46 @@ func (gui *Gui) currentWindow() string {
|
||||
func (gui *Gui) resetWindowContext(c types.Context) {
|
||||
for windowName, viewName := range gui.State.WindowViewNameMap {
|
||||
if viewName == c.GetViewName() && windowName != c.GetWindowName() {
|
||||
// we assume here that the window contains as its default view a view with the same name as the window
|
||||
gui.State.WindowViewNameMap[windowName] = windowName
|
||||
for _, context := range gui.State.Contexts.Flatten() {
|
||||
if context.GetKey() != c.GetKey() && context.GetWindowName() == windowName {
|
||||
gui.State.WindowViewNameMap[windowName] = context.GetViewName()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) moveToTopOfWindow(context types.Context) {
|
||||
view := context.GetView()
|
||||
if view == nil {
|
||||
return
|
||||
}
|
||||
|
||||
window := context.GetWindowName()
|
||||
|
||||
// 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 := gui.viewNamesInWindow(window)
|
||||
|
||||
// The views list is ordered highest-last, so we're grabbing the last view of the window
|
||||
topView := view
|
||||
for _, currentView := range gui.g.Views() {
|
||||
if lo.Contains(viewNamesInWindow, currentView.Name()) {
|
||||
topView = currentView
|
||||
}
|
||||
}
|
||||
|
||||
if err := gui.g.SetViewOnTopOf(view.Name(), topView.Name()); err != nil {
|
||||
gui.Log.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) viewNamesInWindow(windowName string) []string {
|
||||
result := []string{}
|
||||
for _, context := range gui.State.Contexts.Flatten() {
|
||||
if context.GetWindowName() == windowName {
|
||||
result = append(result, context.GetViewName())
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
@ -176,7 +176,7 @@ func chineseTranslationSet() TranslationSet {
|
||||
ToggleDragSelect: `切换拖动选择`,
|
||||
ToggleSelectHunk: `切换选择区块`,
|
||||
ToggleSelectionForPatch: `添加/移除 行到补丁`,
|
||||
TogglePanel: `切换到其他面板`,
|
||||
ToggleStagingPanel: `切换到其他面板`,
|
||||
ReturnToFilesPanel: `返回文件面板`,
|
||||
FastForward: `从上游快进此分支`,
|
||||
Fetching: "抓取并快进 {{.from}} -> {{.to}} ...",
|
||||
@ -298,7 +298,7 @@ func chineseTranslationSet() TranslationSet {
|
||||
PatchOptionsTitle: "补丁选项",
|
||||
NoPatchError: "尚未创建补丁。你可以在提交中的文件上按下“空格”或使用“回车”添加其中的特定行以开始构建补丁",
|
||||
LcEnterFile: "输入文件以将所选行添加到补丁中(或切换目录折叠)",
|
||||
ExitLineByLineMode: `退出逐行模式`,
|
||||
ExitCustomPatchBuilder: `退出逐行模式`,
|
||||
EnterUpstream: `以这种格式输入上游:'<远程仓库> <分支名称>'`,
|
||||
InvalidUpstream: "上游格式无效,格式应当为:'<remote> <branchname>'",
|
||||
ReturnToRemotesList: `返回远程仓库列表`,
|
||||
|
@ -141,7 +141,7 @@ func dutchTranslationSet() TranslationSet {
|
||||
ToggleDragSelect: `toggle drag selecteer`,
|
||||
ToggleSelectHunk: `toggle selecteer hunk`,
|
||||
ToggleSelectionForPatch: `voeg toe/verwijder lijn(en) in patch`,
|
||||
TogglePanel: `ga naar een ander paneel`,
|
||||
ToggleStagingPanel: `ga naar een ander paneel`,
|
||||
ReturnToFilesPanel: `ga terug naar het bestanden paneel`,
|
||||
FastForward: `fast-forward deze branch vanaf zijn upstream`,
|
||||
Fetching: "fetching en fast-forwarding {{.from}} -> {{.to}} ...",
|
||||
@ -258,7 +258,7 @@ func dutchTranslationSet() TranslationSet {
|
||||
PatchOptionsTitle: "Patch Opties",
|
||||
NoPatchError: "Nog geen patch gecreëerd. Om een patch te bouwen gebruik 'space' op een commit bestand of 'enter' om een spesiefieke lijnen toe te voegen",
|
||||
LcEnterFile: "enter bestand om geselecteerde regels toe te voegen aan de patch",
|
||||
ExitLineByLineMode: `sluit lijn-bij-lijn modus`,
|
||||
ExitCustomPatchBuilder: `sluit lijn-bij-lijn modus`,
|
||||
EnterUpstream: `Enter upstream als '<remote> <branchnaam>'`,
|
||||
ReturnToRemotesList: `ga terug naar remotes lijst`,
|
||||
LcAddNewRemote: `voeg een nieuwe remote toe`,
|
||||
|
@ -176,7 +176,7 @@ type TranslationSet struct {
|
||||
ToggleSelectHunk string
|
||||
ToggleSelectionForPatch string
|
||||
EditHunk string
|
||||
TogglePanel string
|
||||
ToggleStagingPanel string
|
||||
ReturnToFilesPanel string
|
||||
FastForward string
|
||||
Fetching string
|
||||
@ -307,7 +307,7 @@ type TranslationSet struct {
|
||||
PatchOptionsTitle string
|
||||
NoPatchError string
|
||||
LcEnterFile string
|
||||
ExitLineByLineMode string
|
||||
ExitCustomPatchBuilder string
|
||||
EnterUpstream string
|
||||
InvalidUpstream string
|
||||
ReturnToRemotesList string
|
||||
@ -503,6 +503,8 @@ type TranslationSet struct {
|
||||
NukeDescription string
|
||||
DiscardStagedChangesDescription string
|
||||
EmptyOutput string
|
||||
Patch string
|
||||
CustomPatch string
|
||||
Actions Actions
|
||||
Bisect Bisect
|
||||
}
|
||||
@ -812,7 +814,7 @@ func EnglishTranslationSet() TranslationSet {
|
||||
ToggleSelectHunk: `toggle select hunk`,
|
||||
ToggleSelectionForPatch: `add/remove line(s) to patch`,
|
||||
EditHunk: `edit hunk`,
|
||||
TogglePanel: `switch to other panel`,
|
||||
ToggleStagingPanel: `switch to other panel (staged/unstaged changes)`,
|
||||
ReturnToFilesPanel: `return to files panel`,
|
||||
FastForward: `fast-forward this branch from its upstream`,
|
||||
Fetching: "fetching and fast-forwarding {{.from}} -> {{.to}} ...",
|
||||
@ -944,7 +946,7 @@ func EnglishTranslationSet() TranslationSet {
|
||||
PatchOptionsTitle: "Patch Options",
|
||||
NoPatchError: "No patch created yet. To start building a patch, use 'space' on a commit file or enter to add specific lines",
|
||||
LcEnterFile: "enter file to add selected lines to the patch (or toggle directory collapsed)",
|
||||
ExitLineByLineMode: `exit line-by-line mode`,
|
||||
ExitCustomPatchBuilder: `exit custom patch builder`,
|
||||
EnterUpstream: `Enter upstream as '<remote> <branchname>'`,
|
||||
InvalidUpstream: "Invalid upstream. Must be in the format '<remote> <branchname>'",
|
||||
ReturnToRemotesList: `Return to remotes list`,
|
||||
@ -1140,6 +1142,8 @@ func EnglishTranslationSet() TranslationSet {
|
||||
NukeDescription: "If you want to make all the changes in the worktree go away, this is the way to do it. If there are dirty submodule changes this will stash those changes in the submodule(s).",
|
||||
DiscardStagedChangesDescription: "This will create a new stash entry containing only staged files and then drop it, so that the working tree is left with only unstaged changes",
|
||||
EmptyOutput: "<empty output>",
|
||||
Patch: "Patch",
|
||||
CustomPatch: "Custom patch",
|
||||
Actions: Actions{
|
||||
// TODO: combine this with the original keybinding descriptions (those are all in lowercase atm)
|
||||
CheckoutCommit: "Checkout commit",
|
||||
|
@ -176,7 +176,7 @@ func japaneseTranslationSet() TranslationSet {
|
||||
ToggleDragSelect: `範囲選択を切り替え`,
|
||||
ToggleSelectHunk: `hunk選択を切り替え`,
|
||||
ToggleSelectionForPatch: `行をパッチに追加/削除`,
|
||||
TogglePanel: `パネルを切り替え`,
|
||||
ToggleStagingPanel: `パネルを切り替え`,
|
||||
ReturnToFilesPanel: `ファイル一覧に戻る`,
|
||||
// FastForward: `fast-forward this branch from its upstream`,
|
||||
// Fetching: "fetching and fast-forwarding {{.from}} -> {{.to}} ...",
|
||||
@ -303,7 +303,7 @@ func japaneseTranslationSet() TranslationSet {
|
||||
// PatchOptionsTitle: "Patch Options",
|
||||
// NoPatchError: "No patch created yet. To start building a patch, use 'space' on a commit file or enter to add specific lines",
|
||||
// LcEnterFile: "enter file to add selected lines to the patch (or toggle directory collapsed)",
|
||||
ExitLineByLineMode: `line-by-lineモードを終了`,
|
||||
// ExitCustomPatchBuilder: ``,
|
||||
EnterUpstream: `'<remote> <branchname>' の形式でupstreamを入力`,
|
||||
InvalidUpstream: "upstreamの形式が正しくありません。'<remote> <branchname>' の形式で入力してください。",
|
||||
ReturnToRemotesList: `リモート一覧に戻る`,
|
||||
|
@ -9,7 +9,7 @@ lazygit!를 이용해주셔서 감사합니다. Seriously you rock. Three things
|
||||
2) 다음 사이트에서 최신 릴리스 노트를 읽어보세요.:
|
||||
https://github.com/jesseduffield/lazygit/releases
|
||||
|
||||
3) 만약 당신이 Git을 사용한다면, 그것은 당신을 프로그래머로 만들 것입니다!
|
||||
3) 만약 당신이 Git을 사용한다면, 그것은 당신을 프로그래머로 만들 것입니다!
|
||||
당신의 도움으로 우리는 lazygit을 더 좋게 만들 수 있습니다, 그러니 기여자가 되는 것을 고려해보세요. 그리고 재미에 참여하세요:
|
||||
https://github.com/jesseduffield/lazygit
|
||||
또한 오른쪽 하단의 기부 버튼을 클릭하여 저를 후원하고 작업할 내용을 알려주실 수 있습니다.
|
||||
@ -177,7 +177,7 @@ func koreanTranslationSet() TranslationSet {
|
||||
ToggleDragSelect: `드래그 선택 전환`,
|
||||
ToggleSelectHunk: `toggle select hunk`,
|
||||
ToggleSelectionForPatch: `line(s)을 패치에 추가/삭제`,
|
||||
TogglePanel: `패널 전환`,
|
||||
ToggleStagingPanel: `패널 전환`,
|
||||
ReturnToFilesPanel: `파일 목록으로 돌아가기`,
|
||||
FastForward: `fast-forward this branch from its upstream`,
|
||||
Fetching: "fetching and fast-forwarding {{.from}} -> {{.to}} ...",
|
||||
@ -304,84 +304,84 @@ func koreanTranslationSet() TranslationSet {
|
||||
PatchOptionsTitle: "Patch 옵션",
|
||||
NoPatchError: "No patch created yet. To start building a patch, use 'space' on a commit file or enter to add specific lines",
|
||||
LcEnterFile: "enter file to add selected lines to the patch (or toggle directory collapsed)",
|
||||
ExitLineByLineMode: `line-by-line 모드 종료`,
|
||||
EnterUpstream: `'<remote> <branchname>'와 같은 형식으로 입력하세요.`,
|
||||
InvalidUpstream: "upstream의 형식이 잘못되었습니다.'<remote> <branchname>' 와 같은 형식으로 입력하세요.",
|
||||
ReturnToRemotesList: `원격목록으로 돌아가기`,
|
||||
LcAddNewRemote: `새로운 Remote 추가`,
|
||||
LcNewRemoteName: `새로운 Remote 이름:`,
|
||||
LcNewRemoteUrl: `새로운 Remote URL:`,
|
||||
LcEditRemoteName: `{{.remoteName}} 의 새로운 Remote 이름 입력:`,
|
||||
LcEditRemoteUrl: `{{.remoteName}} 의 새로운 Remote URL 입력:`,
|
||||
LcRemoveRemote: `Remote를 삭제`,
|
||||
LcRemoveRemotePrompt: "정말로 Remote를 삭제하시겠습니까?",
|
||||
DeleteRemoteBranch: "원격 브랜치를 삭제",
|
||||
DeleteRemoteBranchMessage: "정말로 원격 브랜치를 삭제하시겠습니까?",
|
||||
LcSetUpstream: "set as upstream of checked-out branch",
|
||||
SetUpstreamTitle: "Set upstream branch",
|
||||
SetUpstreamMessage: "Are you sure you want to set the upstream branch of '{{.checkedOut}}' to '{{.selected}}'",
|
||||
LcEditRemote: "Remote를 수정",
|
||||
LcTagCommit: "tag commit",
|
||||
TagMenuTitle: "태그 작성",
|
||||
TagNameTitle: "태그 이름:",
|
||||
TagMessageTitle: "태그 메시지: ",
|
||||
LcAnnotatedTag: "annotated tag",
|
||||
LcLightweightTag: "lightweight tag",
|
||||
LcDeleteTag: "태그 삭제",
|
||||
DeleteTagTitle: "태그 삭제",
|
||||
DeleteTagPrompt: "정말로 태그 '{{.tagName}}' 를 삭제하시겠습니까?",
|
||||
PushTagTitle: "원격에 태그 '{{.tagName}}' 를 푸시",
|
||||
LcPushTag: "태그를 push",
|
||||
LcCreateTag: "태그를 생성",
|
||||
CreateTagTitle: "태그 이름:",
|
||||
LcFetchRemote: "원격을 업데이트",
|
||||
FetchingRemoteStatus: "원격을 업데이트 중",
|
||||
LcCheckoutCommit: "커밋을 체크아웃",
|
||||
SureCheckoutThisCommit: "정말로 선택한 커밋을 체크아웃 하시겠습니까?",
|
||||
LcGitFlowOptions: "git-flow 옵션 보기",
|
||||
NotAGitFlowBranch: "This does not seem to be a git flow branch",
|
||||
NewGitFlowBranchPrompt: "new {{.branchType}} name:",
|
||||
IgnoreTracked: "Ignore tracked file",
|
||||
IgnoreTrackedPrompt: "Are you sure you want to ignore a tracked file?",
|
||||
LcViewResetToUpstreamOptions: "view upstream reset options",
|
||||
LcNextScreenMode: "다음 스크린 모드 (normal/half/fullscreen)",
|
||||
LcPrevScreenMode: "이전 스크린 모드",
|
||||
LcStartSearch: "검색 시작",
|
||||
Panel: "패널",
|
||||
Keybindings: "키 바인딩",
|
||||
LcRenameBranch: "브랜치 이름 변경",
|
||||
NewBranchNamePrompt: "새로운 브랜치 이름 입력",
|
||||
RenameBranchWarning: "This branch is tracking a remote. This action will only rename the local branch name, not the name of the remote branch. Continue?",
|
||||
LcOpenMenu: "매뉴 열기",
|
||||
LcResetCherryPick: "reset cherry-picked (copied) commits selection",
|
||||
LcNextTab: "이전 탭",
|
||||
LcPrevTab: "다음 탭",
|
||||
LcCantUndoWhileRebasing: "리베이스중에는 되돌릴 수 없습니다.",
|
||||
LcCantRedoWhileRebasing: "리베이스중에는 다시 실행할 수 없습니다.",
|
||||
MustStashWarning: "Pulling a patch out into the index requires stashing and unstashing your changes. If something goes wrong, you'll be able to access your files from the stash. Continue?",
|
||||
MustStashTitle: "Must stash",
|
||||
ConfirmationTitle: "확인 패널",
|
||||
LcPrevPage: "이전 페이지",
|
||||
LcNextPage: "다음 페이지",
|
||||
LcGotoTop: "맨 위로 스크롤 ",
|
||||
LcGotoBottom: "맨 아래로 스크롤 ",
|
||||
LcFilteringBy: "filtering by",
|
||||
ResetInParentheses: "(reset)",
|
||||
LcOpenFilteringMenu: "view filter-by-path options",
|
||||
LcFilterBy: "filter by",
|
||||
LcExitFilterMode: "stop filtering by path",
|
||||
LcFilterPathOption: "enter path to filter by",
|
||||
EnterFileName: "Enter path:",
|
||||
FilteringMenuTitle: "Filtering",
|
||||
MustExitFilterModeTitle: "Command not available",
|
||||
MustExitFilterModePrompt: "Command not available in filtered mode. Exit filtered mode?",
|
||||
LcDiff: "Diff",
|
||||
LcEnterRefToDiff: "enter ref to diff",
|
||||
LcEnteRefName: "ref 입력:",
|
||||
LcExitDiffMode: "Diff 모드 종료",
|
||||
DiffingMenuTitle: "Diff",
|
||||
LcSwapDiff: "reverse diff direction",
|
||||
LcOpenDiffingMenu: "Diff 메뉴 열기",
|
||||
// ExitCustomPatchBuilder: ``,
|
||||
EnterUpstream: `'<remote> <branchname>'와 같은 형식으로 입력하세요.`,
|
||||
InvalidUpstream: "upstream의 형식이 잘못되었습니다.'<remote> <branchname>' 와 같은 형식으로 입력하세요.",
|
||||
ReturnToRemotesList: `원격목록으로 돌아가기`,
|
||||
LcAddNewRemote: `새로운 Remote 추가`,
|
||||
LcNewRemoteName: `새로운 Remote 이름:`,
|
||||
LcNewRemoteUrl: `새로운 Remote URL:`,
|
||||
LcEditRemoteName: `{{.remoteName}} 의 새로운 Remote 이름 입력:`,
|
||||
LcEditRemoteUrl: `{{.remoteName}} 의 새로운 Remote URL 입력:`,
|
||||
LcRemoveRemote: `Remote를 삭제`,
|
||||
LcRemoveRemotePrompt: "정말로 Remote를 삭제하시겠습니까?",
|
||||
DeleteRemoteBranch: "원격 브랜치를 삭제",
|
||||
DeleteRemoteBranchMessage: "정말로 원격 브랜치를 삭제하시겠습니까?",
|
||||
LcSetUpstream: "set as upstream of checked-out branch",
|
||||
SetUpstreamTitle: "Set upstream branch",
|
||||
SetUpstreamMessage: "Are you sure you want to set the upstream branch of '{{.checkedOut}}' to '{{.selected}}'",
|
||||
LcEditRemote: "Remote를 수정",
|
||||
LcTagCommit: "tag commit",
|
||||
TagMenuTitle: "태그 작성",
|
||||
TagNameTitle: "태그 이름:",
|
||||
TagMessageTitle: "태그 메시지: ",
|
||||
LcAnnotatedTag: "annotated tag",
|
||||
LcLightweightTag: "lightweight tag",
|
||||
LcDeleteTag: "태그 삭제",
|
||||
DeleteTagTitle: "태그 삭제",
|
||||
DeleteTagPrompt: "정말로 태그 '{{.tagName}}' 를 삭제하시겠습니까?",
|
||||
PushTagTitle: "원격에 태그 '{{.tagName}}' 를 푸시",
|
||||
LcPushTag: "태그를 push",
|
||||
LcCreateTag: "태그를 생성",
|
||||
CreateTagTitle: "태그 이름:",
|
||||
LcFetchRemote: "원격을 업데이트",
|
||||
FetchingRemoteStatus: "원격을 업데이트 중",
|
||||
LcCheckoutCommit: "커밋을 체크아웃",
|
||||
SureCheckoutThisCommit: "정말로 선택한 커밋을 체크아웃 하시겠습니까?",
|
||||
LcGitFlowOptions: "git-flow 옵션 보기",
|
||||
NotAGitFlowBranch: "This does not seem to be a git flow branch",
|
||||
NewGitFlowBranchPrompt: "new {{.branchType}} name:",
|
||||
IgnoreTracked: "Ignore tracked file",
|
||||
IgnoreTrackedPrompt: "Are you sure you want to ignore a tracked file?",
|
||||
LcViewResetToUpstreamOptions: "view upstream reset options",
|
||||
LcNextScreenMode: "다음 스크린 모드 (normal/half/fullscreen)",
|
||||
LcPrevScreenMode: "이전 스크린 모드",
|
||||
LcStartSearch: "검색 시작",
|
||||
Panel: "패널",
|
||||
Keybindings: "키 바인딩",
|
||||
LcRenameBranch: "브랜치 이름 변경",
|
||||
NewBranchNamePrompt: "새로운 브랜치 이름 입력",
|
||||
RenameBranchWarning: "This branch is tracking a remote. This action will only rename the local branch name, not the name of the remote branch. Continue?",
|
||||
LcOpenMenu: "매뉴 열기",
|
||||
LcResetCherryPick: "reset cherry-picked (copied) commits selection",
|
||||
LcNextTab: "이전 탭",
|
||||
LcPrevTab: "다음 탭",
|
||||
LcCantUndoWhileRebasing: "리베이스중에는 되돌릴 수 없습니다.",
|
||||
LcCantRedoWhileRebasing: "리베이스중에는 다시 실행할 수 없습니다.",
|
||||
MustStashWarning: "Pulling a patch out into the index requires stashing and unstashing your changes. If something goes wrong, you'll be able to access your files from the stash. Continue?",
|
||||
MustStashTitle: "Must stash",
|
||||
ConfirmationTitle: "확인 패널",
|
||||
LcPrevPage: "이전 페이지",
|
||||
LcNextPage: "다음 페이지",
|
||||
LcGotoTop: "맨 위로 스크롤 ",
|
||||
LcGotoBottom: "맨 아래로 스크롤 ",
|
||||
LcFilteringBy: "filtering by",
|
||||
ResetInParentheses: "(reset)",
|
||||
LcOpenFilteringMenu: "view filter-by-path options",
|
||||
LcFilterBy: "filter by",
|
||||
LcExitFilterMode: "stop filtering by path",
|
||||
LcFilterPathOption: "enter path to filter by",
|
||||
EnterFileName: "Enter path:",
|
||||
FilteringMenuTitle: "Filtering",
|
||||
MustExitFilterModeTitle: "Command not available",
|
||||
MustExitFilterModePrompt: "Command not available in filtered mode. Exit filtered mode?",
|
||||
LcDiff: "Diff",
|
||||
LcEnterRefToDiff: "enter ref to diff",
|
||||
LcEnteRefName: "ref 입력:",
|
||||
LcExitDiffMode: "Diff 모드 종료",
|
||||
DiffingMenuTitle: "Diff",
|
||||
LcSwapDiff: "reverse diff direction",
|
||||
LcOpenDiffingMenu: "Diff 메뉴 열기",
|
||||
// the actual view is the extras view which I intend to give more tabs in future but for now we'll only mention the command log part
|
||||
LcOpenExtrasMenu: "명령어 로그 메뉴 열기",
|
||||
LcShowingGitDiff: "showing output for:",
|
||||
|
@ -212,7 +212,7 @@ func polishTranslationSet() TranslationSet {
|
||||
LcStashOptions: "Opcje schowka",
|
||||
NotARepository: "Błąd: nie jesteś w repozytorium",
|
||||
LcJump: "przeskocz do panelu",
|
||||
ExitLineByLineMode: `wyście z trybu "linia po linii"`,
|
||||
ExitCustomPatchBuilder: `wyście z trybu "linia po linii"`,
|
||||
EnterUpstream: `Podaj gałąź nadrzędną jako '<remote> <branchname>'`,
|
||||
ReturnToRemotesList: `wróć do listy repozytoriów zdalnych`,
|
||||
IgnoreTracked: "Ignoruj plik śledzony",
|
||||
|
@ -197,7 +197,7 @@ func (self *ViewBufferManager) NewCmdTask(start func() (*exec.Cmd, io.Reader), p
|
||||
if err := cmd.Wait(); err != nil {
|
||||
// it's fine if we've killed this program ourselves
|
||||
if !strings.Contains(err.Error(), "signal: killed") {
|
||||
self.Log.Error(err)
|
||||
self.Log.Errorf("Unexpected error when running cmd task: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user