diff --git a/docs/Config.md b/docs/Config.md index 86681264c..08e82b5ab 100644 --- a/docs/Config.md +++ b/docs/Config.md @@ -36,6 +36,9 @@ gui: - bold inactiveBorderColor: - white + searchingActiveBorderColor: + - cyan + - bold optionsTextColor: - blue selectedLineBgColor: diff --git a/docs/README.md b/docs/README.md index 4f58a2a56..c0c8191d2 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,7 +1,8 @@ -# Documentation Overview +# Documentation Overview * [Configuration](./Config.md). * [Custom Commands](./Custom_Command_Keybindings.md) * [Custom Pagers](./Custom_Pagers.md) * [Keybindings](./keybindings) * [Undo/Redo](./Undoing.md) +* [Searching/Filtering](./Searching.md) diff --git a/docs/Searching.md b/docs/Searching.md new file mode 100644 index 000000000..589831c55 --- /dev/null +++ b/docs/Searching.md @@ -0,0 +1,21 @@ +# Searching/Filtering + +## View searching/filtering + +Depending on the currently focused view, hitting '/' will bring up a filter or search prompt. When filtering, the contents of the view will be filtered down to only those lines which match the query string. When searching, the contents of the view are not filtered, but matching lines are highlighted and you can iterate through matches with `n`/`N`. + +We intend to support filtering for the files view soon, but at the moment it uses searching. We intend to continue using search for the commits view because you typically care about the commits that come before/after a matching commit. + +If you would like both filtering and searching to be enabled on a given view, please raise an issue for this. + +## Filtering files by status + +You can filter the files view to only show staged/unstaged files by pressing `` in the files view. + +## Filtering commits by file path + +You can filter the commits view to only show commits which contain changes to a given file path. + +You can do this in a couple of ways: +1) Start lazygit with the -f flag e.g. `lazygit -f my/path` +2) From within lazygit, press `` and then enter the path of the file you want to filter by diff --git a/docs/keybindings/Keybindings_en.md b/docs/keybindings/Keybindings_en.md index 4dee9cbad..5e682be7e 100644 --- a/docs/keybindings/Keybindings_en.md +++ b/docs/keybindings/Keybindings_en.md @@ -36,8 +36,8 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ ,: Previous page .: Next page <: Scroll to top - /: Start search >: Scroll to bottom + /: Search the current view by text H: Scroll left L: Scroll right ]: Next tab @@ -56,6 +56,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ a: Toggle all files included in patch <enter>: Enter file to add selected lines to the patch (or toggle directory collapsed) `: Toggle file tree view + /: Search the current view by text ## Commit summary @@ -96,6 +97,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ c: Copy commit (cherry-pick) C: Copy commit range (cherry-pick) <enter>: View selected item's files + /: Search the current view by text ## Confirmation panel @@ -111,7 +113,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ <c-o>: Copy the file name to the clipboard d: View 'discard changes' options <space>: Toggle staged - <c-b>: Filter files (staged/unstaged) + <c-b>: Filter files by status c: Commit changes w: Commit changes without pre-commit hook A: Amend last commit @@ -129,6 +131,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ `: Toggle file tree view M: Open external merge tool (git mergetool) f: Fetch + /: Search the current view by text ## Local branches @@ -152,6 +155,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ R: Rename branch u: Set/Unset upstream <enter>: View commits + /: Filter the current view by text ## Main panel (merging) @@ -190,6 +194,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ e: Edit file <space>: Add/Remove line(s) to patch <esc>: Exit custom patch builder + /: Search the current view by text ## Main panel (staging) @@ -211,6 +216,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ c: Commit changes w: Commit changes without pre-commit hook C: Commit changes using git editor + /: Search the current view by text ## Menu @@ -218,6 +224,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_
   <enter>: Execute
   <esc>: Close
+  /: Filter the current view by text
 
## Reflog @@ -233,6 +240,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ C: Copy commit range (cherry-pick) <c-r>: Reset cherry-picked (copied) commits selection <enter>: View commits + /: Filter the current view by text ## Remote branches @@ -245,9 +253,9 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ r: Rebase checked-out branch onto this branch d: Delete branch u: Set as upstream of checked-out branch - <esc>: Return to remotes list g: View reset options <enter>: View commits + /: Filter the current view by text ## Remotes @@ -257,6 +265,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ n: Add new remote d: Remove remote e: Edit remote + /: Filter the current view by text ## Stash @@ -268,6 +277,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ n: New branch r: Rename stash <enter>: View selected item's files + /: Filter the current view by text ## Status @@ -293,6 +303,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ C: Copy commit range (cherry-pick) <c-r>: Reset cherry-picked (copied) commits selection <enter>: View selected item's files + /: Search the current view by text ## Submodules @@ -306,6 +317,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ e: Update submodule URL i: Initialize submodule b: View bulk submodule options + /: Filter the current view by text ## Tags @@ -317,4 +329,5 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ n: Create tag g: View reset options <enter>: View commits + /: Filter the current view by text diff --git a/docs/keybindings/Keybindings_ja.md b/docs/keybindings/Keybindings_ja.md index 1fbef475a..787796679 100644 --- a/docs/keybindings/Keybindings_ja.md +++ b/docs/keybindings/Keybindings_ja.md @@ -36,8 +36,8 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ ,: 前のページ .: 次のページ <: 最上部までスクロール - /: 検索を開始 >: 最下部までスクロール + /: 検索を開始 H: 左スクロール L: 右スクロール ]: 次のタブ @@ -53,6 +53,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ n: 新しいブランチを作成 r: Stashを変更 <enter>: View selected item's files + /: Filter the current view by text ## Sub-commits @@ -68,6 +69,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ C: コミットを範囲コピー (cherry-pick) <c-r>: Reset cherry-picked (copied) commits selection <enter>: View selected item's files + /: 検索を開始 ## コミット @@ -101,6 +103,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ c: コミットをコピー (cherry-pick) C: コミットを範囲コピー (cherry-pick) <enter>: View selected item's files + /: 検索を開始 ## コミットファイル @@ -115,6 +118,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ a: Toggle all files included in patch <enter>: Enter file to add selected lines to the patch (or toggle directory collapsed) `: ファイルツリーの表示を切り替え + /: 検索を開始 ## コミットメッセージ @@ -135,6 +139,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ e: サブモジュールのURLを更新 i: サブモジュールを初期化 b: View bulk submodule options + /: Filter the current view by text ## ステータス @@ -156,6 +161,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ n: タグを作成 g: View reset options <enter>: コミットを閲覧 + /: Filter the current view by text ## ファイル @@ -182,6 +188,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ `: ファイルツリーの表示を切り替え M: Git mergetoolを開く f: Fetch + /: 検索を開始 ## ブランチ @@ -205,6 +212,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ R: ブランチ名を変更 u: Set/Unset upstream <enter>: コミットを閲覧 + /: Filter the current view by text ## メインパネル (Merging) @@ -243,6 +251,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ e: ファイルを編集 <space>: 行をパッチに追加/削除 <esc>: Exit custom patch builder + /: 検索を開始 ## メインパネル (Staging) @@ -264,6 +273,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ c: 変更をコミット w: pre-commitフックを実行せずに変更をコミット C: gitエディタを使用して変更をコミット + /: 検索を開始 ## メニュー @@ -271,6 +281,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_
   <enter>: 実行
   <esc>: 閉じる
+  /: Filter the current view by text
 
## リモート @@ -280,6 +291,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ n: リモートを新規追加 d: リモートを削除 e: リモートを編集 + /: Filter the current view by text ## リモートブランチ @@ -292,9 +304,9 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ r: Rebase checked-out branch onto this branch d: ブランチを削除 u: Set as upstream of checked-out branch - <esc>: リモート一覧に戻る g: View reset options <enter>: コミットを閲覧 + /: Filter the current view by text ## 参照ログ @@ -310,6 +322,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ C: コミットを範囲コピー (cherry-pick) <c-r>: Reset cherry-picked (copied) commits selection <enter>: コミットを閲覧 + /: Filter the current view by text ## 確認パネル diff --git a/docs/keybindings/Keybindings_ko.md b/docs/keybindings/Keybindings_ko.md index 54c09e7e8..3ebe1f3ad 100644 --- a/docs/keybindings/Keybindings_ko.md +++ b/docs/keybindings/Keybindings_ko.md @@ -36,8 +36,8 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ ,: 이전 페이지 .: 다음 페이지 <: 맨 위로 스크롤 - /: 검색 시작 >: 맨 아래로 스크롤 + /: 검색 시작 H: 우 스크롤 L: 좌 스크롤 ]: 이전 탭 @@ -57,6 +57,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ C: 커밋을 범위로 복사 (cherry-pick) <c-r>: Reset cherry-picked (copied) commits selection <enter>: 커밋 보기 + /: Filter the current view by text ## Stash @@ -68,6 +69,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ n: 새 브랜치 생성 r: Rename stash <enter>: View selected item's files + /: Filter the current view by text ## Sub-commits @@ -83,6 +85,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ C: 커밋을 범위로 복사 (cherry-pick) <c-r>: Reset cherry-picked (copied) commits selection <enter>: View selected item's files + /: 검색 시작 ## 메뉴 @@ -90,6 +93,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_
   <enter>: 실행
   <esc>: 닫기
+  /: Filter the current view by text
 
## 메인 패널 (Merging) @@ -128,6 +132,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ e: 파일 편집 <space>: Line(s)을 패치에 추가/삭제 <esc>: Exit custom patch builder + /: 검색 시작 ## 메인 패널 (Staging) @@ -149,6 +154,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ c: 커밋 변경내용 w: Commit changes without pre-commit hook C: Git 편집기를 사용하여 변경 내용을 커밋합니다. + /: 검색 시작 ## 브랜치 @@ -172,6 +178,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ R: 브랜치 이름 변경 u: Set/Unset upstream <enter>: 커밋 보기 + /: Filter the current view by text ## 상태 @@ -195,6 +202,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ e: 서브모듈의 URL을 수정 i: 서브모듈 초기화 b: View bulk submodule options + /: Filter the current view by text ## 원격 @@ -204,6 +212,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ n: 새로운 Remote 추가 d: Remote를 삭제 e: Remote를 수정 + /: Filter the current view by text ## 원격 브랜치 @@ -216,9 +225,9 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ r: 체크아웃된 브랜치를 이 브랜치에 리베이스 d: 브랜치 삭제 u: Set as upstream of checked-out branch - <esc>: 원격목록으로 돌아가기 g: View reset options <enter>: 커밋 보기 + /: Filter the current view by text ## 커밋 @@ -252,6 +261,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ c: 커밋을 복사 (cherry-pick) C: 커밋을 범위로 복사 (cherry-pick) <enter>: View selected item's files + /: 검색 시작 ## 커밋 파일 @@ -266,6 +276,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ a: Toggle all files included in patch <enter>: Enter file to add selected lines to the patch (or toggle directory collapsed) `: 파일 트리뷰로 전환 + /: 검색 시작 ## 커밋메시지 @@ -284,6 +295,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ n: 태그를 생성 g: View reset options <enter>: 커밋 보기 + /: Filter the current view by text ## 파일 @@ -310,6 +322,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ `: 파일 트리뷰로 전환 M: Git mergetool를 열기 f: Fetch + /: 검색 시작 ## 확인 패널 diff --git a/docs/keybindings/Keybindings_nl.md b/docs/keybindings/Keybindings_nl.md index 223317778..feae84761 100644 --- a/docs/keybindings/Keybindings_nl.md +++ b/docs/keybindings/Keybindings_nl.md @@ -36,8 +36,8 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ ,: Vorige pagina .: Volgende pagina <: Scroll naar boven - /: Start met zoeken >: Scroll naar beneden + /: Start met zoeken H: Scroll left L: Scroll right ]: Volgende tabblad @@ -50,7 +50,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ <c-o>: Kopieer de bestandsnaam naar het klembord d: Bekijk 'veranderingen ongedaan maken' opties <space>: Toggle staged - <c-b>: Filter files (staged/unstaged) + <c-b>: Filter files by status c: Commit veranderingen w: Commit veranderingen zonder pre-commit hook A: Wijzig laatste commit @@ -68,6 +68,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ `: Toggle bestandsboom weergave M: Open external merge tool (git mergetool) f: Fetch + /: Start met zoeken ## Bevestigingspaneel @@ -98,6 +99,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ R: Hernoem branch u: Set/Unset upstream <enter>: Bekijk commits + /: Filter the current view by text ## Commit bericht @@ -119,6 +121,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ a: Toggle all files included in patch <enter>: Enter bestand om geselecteerde regels toe te voegen aan de patch `: Toggle bestandsboom weergave + /: Start met zoeken ## Commits @@ -152,6 +155,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ c: Kopieer commit (cherry-pick) C: Kopieer commit reeks (cherry-pick) <enter>: Bekijk gecommite bestanden + /: Start met zoeken ## Menu @@ -159,6 +163,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_
   <enter>: Uitvoeren
   <esc>: Sluiten
+  /: Filter the current view by text
 
## Mergen @@ -197,6 +202,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ e: Verander bestand <space>: Voeg toe/verwijder lijn(en) in patch <esc>: Sluit lijn-bij-lijn modus + /: Start met zoeken ## Reflog @@ -212,6 +218,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ C: Kopieer commit reeks (cherry-pick) <c-r>: Reset cherry-picked (gekopieerde) commits selectie <enter>: Bekijk commits + /: Filter the current view by text ## Remote branches @@ -224,9 +231,9 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ r: Rebase branch d: Verwijder branch u: Stel in als upstream van uitgecheckte branch - <esc>: Ga terug naar remotes lijst g: Bekijk reset opties <enter>: Bekijk commits + /: Filter the current view by text ## Remotes @@ -236,6 +243,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ n: Voeg een nieuwe remote toe d: Verwijder remote e: Wijzig remote + /: Filter the current view by text ## Staging @@ -257,6 +265,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ c: Commit veranderingen w: Commit veranderingen zonder pre-commit hook C: Commit veranderingen met de git editor + /: Start met zoeken ## Stash @@ -268,6 +277,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ n: Nieuwe branch r: Rename stash <enter>: Bekijk gecommite bestanden + /: Filter the current view by text ## Status @@ -293,6 +303,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ C: Kopieer commit reeks (cherry-pick) <c-r>: Reset cherry-picked (gekopieerde) commits selectie <enter>: Bekijk gecommite bestanden + /: Start met zoeken ## Submodules @@ -306,6 +317,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ e: Update submodule URL i: Initialiseer submodule b: Bekijk bulk submodule opties + /: Filter the current view by text ## Tags @@ -317,4 +329,5 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ n: Creëer tag g: Bekijk reset opties <enter>: Bekijk commits + /: Filter the current view by text diff --git a/docs/keybindings/Keybindings_pl.md b/docs/keybindings/Keybindings_pl.md index 7d2fef98b..c619bfff8 100644 --- a/docs/keybindings/Keybindings_pl.md +++ b/docs/keybindings/Keybindings_pl.md @@ -36,8 +36,8 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ ,: Previous page .: Next page <: Scroll to top - /: Start search >: Scroll to bottom + /: Search the current view by text H: Scroll left L: Scroll right ]: Next tab @@ -82,6 +82,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ c: Kopiuj commit (przebieranie) C: Kopiuj zakres commitów (przebieranie) <enter>: Przeglądaj pliki commita + /: Search the current view by text ## Confirmation panel @@ -112,6 +113,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ R: Rename branch u: Set/Unset upstream <enter>: View commits + /: Filter the current view by text ## Main panel (patch building) @@ -127,6 +129,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ e: Edytuj plik <space>: Add/Remove line(s) to patch <esc>: Wyście z trybu "linia po linii" + /: Search the current view by text ## Menu @@ -134,6 +137,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_
   <enter>: Wykonaj
   <esc>: Zamknij
+  /: Filter the current view by text
 
## Pliki @@ -142,7 +146,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ <c-o>: Copy the file name to the clipboard d: Pokaż opcje porzucania zmian <space>: Przełącz stan poczekalni - <c-b>: Filter files (staged/unstaged) + <c-b>: Filter files by status c: Zatwierdź zmiany w: Zatwierdź zmiany bez skryptu pre-commit A: Zmień ostatni commit @@ -160,6 +164,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ `: Toggle file tree view M: Open external merge tool (git mergetool) f: Pobierz + /: Search the current view by text ## Pliki commita @@ -174,6 +179,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ a: Toggle all files included in patch <enter>: Enter file to add selected lines to the patch (or toggle directory collapsed) `: Toggle file tree view + /: Search the current view by text ## Poczekalnia @@ -195,6 +201,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ c: Zatwierdź zmiany w: Zatwierdź zmiany bez skryptu pre-commit C: Zatwierdź zmiany używając edytora + /: Search the current view by text ## Reflog @@ -210,6 +217,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ C: Kopiuj zakres commitów (przebieranie) <c-r>: Reset cherry-picked (copied) commits selection <enter>: View commits + /: Filter the current view by text ## Remote branches @@ -222,9 +230,9 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ r: Zmiana bazy gałęzi d: Usuń gałąź u: Set as upstream of checked-out branch - <esc>: Wróć do listy repozytoriów zdalnych g: Wyświetl opcje resetu <enter>: View commits + /: Filter the current view by text ## Remotes @@ -234,6 +242,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ n: Add new remote d: Remove remote e: Edit remote + /: Filter the current view by text ## Scalanie @@ -261,6 +270,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ n: Nowa gałąź r: Rename stash <enter>: Przeglądaj pliki commita + /: Filter the current view by text ## Status @@ -286,6 +296,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ C: Kopiuj zakres commitów (przebieranie) <c-r>: Reset cherry-picked (copied) commits selection <enter>: Przeglądaj pliki commita + /: Search the current view by text ## Submodules @@ -299,6 +310,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ e: Update submodule URL i: Initialize submodule b: View bulk submodule options + /: Filter the current view by text ## Tags @@ -310,6 +322,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ n: Create tag g: Wyświetl opcje resetu <enter>: View commits + /: Filter the current view by text ## Zwykłe diff --git a/docs/keybindings/Keybindings_ru.md b/docs/keybindings/Keybindings_ru.md index 85a823ea8..cfd05fec8 100644 --- a/docs/keybindings/Keybindings_ru.md +++ b/docs/keybindings/Keybindings_ru.md @@ -36,8 +36,8 @@ _Связки клавиш_ ,: Предыдущая страница .: Следующая страница <: Пролистать наверх - /: Найти >: Прокрутить вниз + /: Найти H: Прокрутить влево L: Прокрутить вправо ]: Следующая вкладка @@ -63,6 +63,7 @@ _Связки клавиш_ c: Сохранить изменения w: Закоммитить изменения без предварительного хука коммита C: Сохранить изменения с помощью редактора git + /: Найти ## Главная панель (Обычный) @@ -101,6 +102,7 @@ _Связки клавиш_ e: Редактировать файл <space>: Добавить/удалить строку(и) для патча <esc>: Выйти из сборщика пользовательских патчей + /: Найти ## Журнал ссылок (Reflog) @@ -116,6 +118,7 @@ _Связки клавиш_ C: Скопировать несколько отобранных коммитов (cherry-pick) <c-r>: Сбросить отобранную (скопированную | cherry-picked) выборку коммитов <enter>: Просмотреть коммиты + /: Filter the current view by text ## Коммиты @@ -149,6 +152,7 @@ _Связки клавиш_ c: Скопировать отобранные коммит (cherry-pick) C: Скопировать несколько отобранных коммитов (cherry-pick) <enter>: Просмотреть файлы выбранного элемента + /: Найти ## Локальные Ветки @@ -172,6 +176,7 @@ _Связки клавиш_ R: Переименовать ветку u: Установить/убрать upstream-ветку <enter>: Просмотреть коммиты + /: Filter the current view by text ## Меню @@ -179,6 +184,7 @@ _Связки клавиш_
   <enter>: Выполнить
   <esc>: Закрыть
+  /: Filter the current view by text
 
## Панель Подтверждения @@ -201,6 +207,7 @@ _Связки клавиш_ C: Скопировать несколько отобранных коммитов (cherry-pick) <c-r>: Сбросить отобранную (скопированную | cherry-picked) выборку коммитов <enter>: Просмотреть файлы выбранного элемента + /: Найти ## Подмодули @@ -214,6 +221,7 @@ _Связки клавиш_ e: Обновить URL подмодуля i: Инициализировать подмодуль b: Просмотреть параметры массового подмодуля + /: Filter the current view by text ## Сводка коммита @@ -235,6 +243,7 @@ _Связки клавиш_ a: Переключить все файлы, включённые в патч <enter>: Введите файл, чтобы добавить выбранные строки в патч (или свернуть каталог переключения) `: Переключить вид дерева файлов + /: Найти ## Статус @@ -256,6 +265,7 @@ _Связки клавиш_ n: Создать тег g: Просмотреть параметры сброса <enter>: Просмотреть коммиты + /: Filter the current view by text ## Удалённые ветки @@ -268,9 +278,9 @@ _Связки клавиш_ r: Перебазировать переключённую ветку на эту ветку d: Удалить ветку u: Установить как upstream-ветку переключённую ветку - <esc>: Вернуться к списку удалённых репозитории g: Просмотреть параметры сброса <enter>: Просмотреть коммиты + /: Filter the current view by text ## Удалённые репозитории @@ -280,6 +290,7 @@ _Связки клавиш_ n: Добавить новую удалённую ветку d: Удалить удалённую ветку e: Редактировать удалённый репозитории + /: Filter the current view by text ## Файлы @@ -306,6 +317,7 @@ _Связки клавиш_ `: Переключить вид дерева файлов M: Открыть внешний инструмент слияния (git mergetool) f: Получить изменения + /: Найти ## Хранилище @@ -317,4 +329,5 @@ _Связки клавиш_ n: Новая ветка r: Переименовать хранилище <enter>: Просмотреть файлы выбранного элемента + /: Filter the current view by text diff --git a/docs/keybindings/Keybindings_zh-CN.md b/docs/keybindings/Keybindings_zh-CN.md index 6275f572d..3274b1a0c 100644 --- a/docs/keybindings/Keybindings_zh-CN.md +++ b/docs/keybindings/Keybindings_zh-CN.md @@ -36,8 +36,8 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ ,: 上一页 .: 下一页 <: 滚动到顶部 - /: 开始搜索 >: 滚动到底部 + /: 开始搜索 H: 向左滚动 L: 向右滚动 ]: 下一个标签 @@ -57,6 +57,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ C: 复制提交范围(拣选) <c-r>: 重置已拣选(复制)的提交 <enter>: 查看提交 + /: Filter the current view by text ## 分支页面 @@ -80,6 +81,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ R: 重命名分支 u: Set/Unset upstream <enter>: 查看提交 + /: Filter the current view by text ## 子提交 @@ -95,6 +97,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ C: 复制提交范围(拣选) <c-r>: 重置已拣选(复制)的提交 <enter>: 查看提交的文件 + /: 开始搜索 ## 子模块 @@ -108,6 +111,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ e: 更新子模块 URL i: 初始化子模块 b: 查看批量子模块选项 + /: Filter the current view by text ## 提交 @@ -141,6 +145,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ c: 复制提交(拣选) C: 复制提交范围(拣选) <enter>: 查看提交的文件 + /: 开始搜索 ## 提交文件 @@ -155,6 +160,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ a: Toggle all files included in patch <enter>: 输入文件以将所选行添加到补丁中(或切换目录折叠) `: 切换文件树视图 + /: 开始搜索 ## 提交讯息 @@ -170,7 +176,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ <c-o>: 将文件名复制到剪贴板 d: 查看'放弃更改'选项 <space>: 切换暂存状态 - <c-b>: Filter files (staged/unstaged) + <c-b>: Filter files by status c: 提交更改 w: 提交更改而无需预先提交钩子 A: 修补最后一次提交 @@ -188,6 +194,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ `: 切换文件树视图 M: 打开外部合并工具 (git mergetool) f: 抓取 + /: 开始搜索 ## 构建补丁中 @@ -203,6 +210,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ e: 编辑文件 <space>: 添加/移除 行到补丁 <esc>: 退出逐行模式 + /: 开始搜索 ## 标签页面 @@ -214,6 +222,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ n: 创建标签 g: 查看重置选项 <enter>: 查看提交 + /: Filter the current view by text ## 正在合并 @@ -251,6 +260,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ c: 提交更改 w: 提交更改而无需预先提交钩子 C: 提交更改(使用编辑器编辑提交信息) + /: 开始搜索 ## 正常 @@ -282,6 +292,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_
   <enter>: 执行
   <esc>: 关闭
+  /: Filter the current view by text
 
## 贮藏 @@ -293,6 +304,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ n: 新分支 r: Rename stash <enter>: 查看提交的文件 + /: Filter the current view by text ## 远程分支 @@ -305,9 +317,9 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ r: 将已检出的分支变基到该分支 d: 删除分支 u: 设置为检出分支的上游 - <esc>: 返回远程仓库列表 g: 查看重置选项 <enter>: 查看提交 + /: Filter the current view by text ## 远程页面 @@ -317,4 +329,5 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ n: 添加新的远程仓库 d: 删除远程 e: 编辑远程仓库 + /: Filter the current view by text diff --git a/docs/keybindings/Keybindings_zh-TW.md b/docs/keybindings/Keybindings_zh-TW.md index cc30d6acd..68859a23d 100644 --- a/docs/keybindings/Keybindings_zh-TW.md +++ b/docs/keybindings/Keybindings_zh-TW.md @@ -36,8 +36,8 @@ _說明:`` 表示 Ctrl+B、`` 表示 Alt+B,`B`表示 Shift+B_ ,: 上一頁 .: 下一頁 <: 捲動到頂部 - /: 開始搜尋 >: 捲動到底部 + /: 開始搜尋 H: 向左捲動 L: 向右捲動 ]: 下一個索引標籤 @@ -57,6 +57,7 @@ _說明:`` 表示 Ctrl+B、`` 表示 Alt+B,`B`表示 Shift+B_ C: 複製提交範圍 (揀選) <c-r>: 重設選定的揀選 (複製) 提交 <enter>: 檢視提交 + /: Filter the current view by text ## 主視窗 (一般) @@ -101,6 +102,7 @@ _說明:`` 表示 Ctrl+B、`` 表示 Alt+B,`B`表示 Shift+B_ c: 提交變更 w: 沒有預提交 hook 就提交更改 C: 使用 git 編輯器提交變更 + /: 開始搜尋 ## 主面板 (補丁生成) @@ -116,6 +118,7 @@ _說明:`` 表示 Ctrl+B、`` 表示 Alt+B,`B`表示 Shift+B_ e: 編輯檔案 <space>: 向 (或從) 補丁中添加/刪除行 <esc>: 退出自訂補丁建立器 + /: 開始搜尋 ## 功能表 @@ -123,6 +126,7 @@ _說明:`` 表示 Ctrl+B、`` 表示 Alt+B,`B`表示 Shift+B_
   <enter>: 執行
   <esc>: 關閉
+  /: Filter the current view by text
 
## 子提交 @@ -138,6 +142,7 @@ _說明:`` 表示 Ctrl+B、`` 表示 Alt+B,`B`表示 Shift+B_ C: 複製提交範圍 (揀選) <c-r>: 重設選定的揀選 (複製) 提交 <enter>: 檢視所選項目的檔案 + /: 開始搜尋 ## 子模組 @@ -151,6 +156,7 @@ _說明:`` 表示 Ctrl+B、`` 表示 Alt+B,`B`表示 Shift+B_ e: 更新子模組 URL i: 初始化子模組 b: 查看批量子模組選項 + /: Filter the current view by text ## 提交 @@ -184,6 +190,7 @@ _說明:`` 表示 Ctrl+B、`` 表示 Alt+B,`B`表示 Shift+B_ c: 複製提交 (揀選) C: 複製提交範圍 (揀選) <enter>: 檢視所選項目的檔案 + /: 開始搜尋 ## 提交摘要 @@ -205,6 +212,7 @@ _說明:`` 表示 Ctrl+B、`` 表示 Alt+B,`B`表示 Shift+B_ a: 切換所有檔案是否包含在補丁中 <enter>: 輸入檔案以將選定的行添加至補丁(或切換目錄折疊) `: 切換檔案樹狀視圖 + /: 開始搜尋 ## 收藏 (Stash) @@ -216,6 +224,7 @@ _說明:`` 表示 Ctrl+B、`` 表示 Alt+B,`B`表示 Shift+B_ n: 新分支 r: 重新命名收藏 <enter>: 檢視所選項目的檔案 + /: Filter the current view by text ## 本地分支 @@ -239,6 +248,7 @@ _說明:`` 表示 Ctrl+B、`` 表示 Alt+B,`B`表示 Shift+B_ R: 重新命名分支 u: 設定/取消設定上游 <enter>: 檢視提交 + /: Filter the current view by text ## 標籤 @@ -250,6 +260,7 @@ _說明:`` 表示 Ctrl+B、`` 表示 Alt+B,`B`表示 Shift+B_ n: 建立標籤 g: 檢視重設選項 <enter>: 檢視提交 + /: Filter the current view by text ## 檔案 @@ -276,6 +287,7 @@ _說明:`` 表示 Ctrl+B、`` 表示 Alt+B,`B`表示 Shift+B_ `: 切換檔案樹狀視圖 M: 開啟外部合併工具 (git mergetool) f: 擷取 + /: 開始搜尋 ## 狀態 @@ -302,6 +314,7 @@ _說明:`` 表示 Ctrl+B、`` 表示 Alt+B,`B`表示 Shift+B_ n: 新增遠端 d: 移除遠端 e: 編輯遠端 + /: Filter the current view by text ## 遠端分支 @@ -314,7 +327,7 @@ _說明:`` 表示 Ctrl+B、`` 表示 Alt+B,`B`表示 Shift+B_ r: 將已檢出的分支變基至此分支 d: 刪除分支 u: 將此分支設為當前分支之上游 - <esc>: 返回遠端列表 g: 檢視重設選項 <enter>: 檢視提交 + /: Filter the current view by text diff --git a/go.mod b/go.mod index c7c2e09fb..07ec61e96 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,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.20221018185014-fdd53fef665d - github.com/jesseduffield/gocui v0.3.1-0.20230601121845-cb89273fdd4e + github.com/jesseduffield/gocui v0.3.1-0.20230702054502-d6c452fc12ce github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10 github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5 github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e @@ -67,8 +67,8 @@ require ( golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8 // indirect golang.org/x/net v0.7.0 // indirect - golang.org/x/sys v0.8.0 // indirect - golang.org/x/term v0.8.0 // indirect - golang.org/x/text v0.9.0 // indirect + golang.org/x/sys v0.9.0 // indirect + golang.org/x/term v0.9.0 // indirect + golang.org/x/text v0.10.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect ) diff --git a/go.sum b/go.sum index cf3f7aab8..19d73cdd7 100644 --- a/go.sum +++ b/go.sum @@ -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.20221018185014-fdd53fef665d h1:bO+OmbreIv91rCe8NmscRwhFSqkDJtzWCPV4Y+SQuXE= github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d/go.mod h1:nGNEErzf+NRznT+N2SWqmHnDnF9aLgANB1CUNEan09o= -github.com/jesseduffield/gocui v0.3.1-0.20230601121845-cb89273fdd4e h1:NpsrRAbYUmMkxDgNAVSlu3LxtfwdDe140vWVo/VldgA= -github.com/jesseduffield/gocui v0.3.1-0.20230601121845-cb89273fdd4e/go.mod h1:dJ/BEUt3OWtaRg/PmuJWendRqREhre9JQ1SLvqrVJ8s= +github.com/jesseduffield/gocui v0.3.1-0.20230702054502-d6c452fc12ce h1:Xgm21B1an/outcRxnkDfMT6wKb6SKBR05jXOyfPA8WQ= +github.com/jesseduffield/gocui v0.3.1-0.20230702054502-d6c452fc12ce/go.mod h1:dJ/BEUt3OWtaRg/PmuJWendRqREhre9JQ1SLvqrVJ8s= 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/lazycore v0.0.0-20221012050358-03d2e40243c5 h1:CDuQmfOjAtb1Gms6a1p5L2P8RhbLUq5t8aL7PiQd2uY= @@ -206,21 +206,22 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28= +golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= 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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= +golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= diff --git a/pkg/commands/models/commit_file.go b/pkg/commands/models/commit_file.go index 45b56d2dd..d05b5b3fd 100644 --- a/pkg/commands/models/commit_file.go +++ b/pkg/commands/models/commit_file.go @@ -23,3 +23,7 @@ func (f *CommitFile) Added() bool { func (f *CommitFile) Deleted() bool { return f.ChangeStatus == "D" } + +func (f *CommitFile) GetPath() string { + return f.Name +} diff --git a/pkg/config/user_config.go b/pkg/config/user_config.go index c3e4fc07f..8faff4326 100644 --- a/pkg/config/user_config.go +++ b/pkg/config/user_config.go @@ -60,15 +60,16 @@ type GuiConfig struct { } type ThemeConfig struct { - ActiveBorderColor []string `yaml:"activeBorderColor"` - InactiveBorderColor []string `yaml:"inactiveBorderColor"` - OptionsTextColor []string `yaml:"optionsTextColor"` - SelectedLineBgColor []string `yaml:"selectedLineBgColor"` - SelectedRangeBgColor []string `yaml:"selectedRangeBgColor"` - CherryPickedCommitBgColor []string `yaml:"cherryPickedCommitBgColor"` - CherryPickedCommitFgColor []string `yaml:"cherryPickedCommitFgColor"` - UnstagedChangesColor []string `yaml:"unstagedChangesColor"` - DefaultFgColor []string `yaml:"defaultFgColor"` + ActiveBorderColor []string `yaml:"activeBorderColor"` + InactiveBorderColor []string `yaml:"inactiveBorderColor"` + SearchingActiveBorderColor []string `yaml:"searchingActiveBorderColor"` + OptionsTextColor []string `yaml:"optionsTextColor"` + SelectedLineBgColor []string `yaml:"selectedLineBgColor"` + SelectedRangeBgColor []string `yaml:"selectedRangeBgColor"` + CherryPickedCommitBgColor []string `yaml:"cherryPickedCommitBgColor"` + CherryPickedCommitFgColor []string `yaml:"cherryPickedCommitFgColor"` + UnstagedChangesColor []string `yaml:"unstagedChangesColor"` + DefaultFgColor []string `yaml:"defaultFgColor"` } type CommitLengthConfig struct { @@ -409,15 +410,16 @@ func GetDefaultConfig() *UserConfig { TimeFormat: "02 Jan 06", ShortTimeFormat: time.Kitchen, Theme: ThemeConfig{ - ActiveBorderColor: []string{"green", "bold"}, - InactiveBorderColor: []string{"default"}, - OptionsTextColor: []string{"blue"}, - SelectedLineBgColor: []string{"blue"}, - SelectedRangeBgColor: []string{"blue"}, - CherryPickedCommitBgColor: []string{"cyan"}, - CherryPickedCommitFgColor: []string{"blue"}, - UnstagedChangesColor: []string{"red"}, - DefaultFgColor: []string{"default"}, + ActiveBorderColor: []string{"green", "bold"}, + SearchingActiveBorderColor: []string{"cyan", "bold"}, + InactiveBorderColor: []string{"default"}, + OptionsTextColor: []string{"blue"}, + SelectedLineBgColor: []string{"blue"}, + SelectedRangeBgColor: []string{"blue"}, + CherryPickedCommitBgColor: []string{"cyan"}, + CherryPickedCommitFgColor: []string{"blue"}, + UnstagedChangesColor: []string{"red"}, + DefaultFgColor: []string{"default"}, }, CommitLength: CommitLengthConfig{Show: true}, SkipNoStagedFilesWarning: false, diff --git a/pkg/gui/command_log_panel.go b/pkg/gui/command_log_panel.go index 0a5ccfae3..2faee3572 100644 --- a/pkg/gui/command_log_panel.go +++ b/pkg/gui/command_log_panel.go @@ -135,10 +135,6 @@ func (gui *Gui) getRandomTip() string { "To escape a mode, for example cherry-picking, patch-building, diffing, or filtering mode, you can just spam the '%s' button. Unless of course you have `quitOnTopLevelReturn` enabled in your config", formattedKey(config.Universal.Return), ), - fmt.Sprintf( - "To search for a string in your panel, press '%s'", - formattedKey(config.Universal.StartSearch), - ), fmt.Sprintf( "You can page through the items of a panel using '%s' and '%s'", formattedKey(config.Universal.PrevPage), diff --git a/pkg/gui/context.go b/pkg/gui/context.go index b55713f27..38e1212c7 100644 --- a/pkg/gui/context.go +++ b/pkg/gui/context.go @@ -200,9 +200,9 @@ func (self *ContextMgr) RemoveContexts(contextsToRemove []types.Context) error { func (self *ContextMgr) deactivateContext(c types.Context, opts types.OnFocusLostOpts) error { view, _ := self.gui.c.GocuiGui().View(c.GetViewName()) - if view != nil && view.IsSearching() { - if err := self.gui.onSearchEscape(); err != nil { - return err + if opts.NewContextKey != context.SEARCH_CONTEXT_KEY { + if c.GetKind() == types.MAIN_CONTEXT || c.GetKind() == types.TEMPORARY_POPUP { + self.gui.helpers.Search.CancelSearchIfSearching(c) } } @@ -234,6 +234,8 @@ func (self *ContextMgr) ActivateContext(c types.Context, opts types.OnFocusOpts) return err } + self.gui.helpers.Search.RenderSearchStatus(c) + desiredTitle := c.Title() if desiredTitle != "" { v.Title = desiredTitle @@ -326,6 +328,30 @@ func (self *ContextMgr) IsCurrent(c types.Context) bool { return self.Current().GetKey() == c.GetKey() } +func (self *ContextMgr) AllFilterable() []types.IFilterableContext { + var result []types.IFilterableContext + + for _, context := range self.allContexts.Flatten() { + if ctx, ok := context.(types.IFilterableContext); ok { + result = append(result, ctx) + } + } + + return result +} + +func (self *ContextMgr) AllSearchable() []types.ISearchableContext { + var result []types.ISearchableContext + + for _, context := range self.allContexts.Flatten() { + if ctx, ok := context.(types.ISearchableContext); ok { + result = append(result, ctx) + } + } + + return result +} + // all list contexts func (self *ContextMgr) AllList() []types.IListContext { var listContexts []types.IListContext diff --git a/pkg/gui/context/branches_context.go b/pkg/gui/context/branches_context.go index c2463ad20..497b3a2c4 100644 --- a/pkg/gui/context/branches_context.go +++ b/pkg/gui/context/branches_context.go @@ -7,7 +7,7 @@ import ( ) type BranchesContext struct { - *BasicViewModel[*models.Branch] + *FilteredListViewModel[*models.Branch] *ListContextTrait } @@ -17,11 +17,16 @@ var ( ) func NewBranchesContext(c *ContextCommon) *BranchesContext { - viewModel := NewBasicViewModel(func() []*models.Branch { return c.Model().Branches }) + viewModel := NewFilteredListViewModel( + func() []*models.Branch { return c.Model().Branches }, + func(branch *models.Branch) []string { + return []string{branch.Name} + }, + ) getDisplayStrings := func(startIdx int, length int) [][]string { return presentation.GetBranchListDisplayStrings( - c.Model().Branches, + viewModel.GetItems(), c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL, c.Modes().Diffing.Ref, c.Tr, @@ -30,7 +35,7 @@ func NewBranchesContext(c *ContextCommon) *BranchesContext { } self := &BranchesContext{ - BasicViewModel: viewModel, + FilteredListViewModel: viewModel, ListContextTrait: &ListContextTrait{ Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{ View: c.Views().Branches, diff --git a/pkg/gui/context/commit_files_context.go b/pkg/gui/context/commit_files_context.go index 96b6f2fcf..5cb11d9cc 100644 --- a/pkg/gui/context/commit_files_context.go +++ b/pkg/gui/context/commit_files_context.go @@ -13,6 +13,7 @@ type CommitFilesContext struct { *filetree.CommitFileTreeViewModel *ListContextTrait *DynamicTitleBuilder + *SearchTrait } var ( @@ -38,9 +39,10 @@ func NewCommitFilesContext(c *ContextCommon) *CommitFilesContext { }) } - return &CommitFilesContext{ + ctx := &CommitFilesContext{ CommitFileTreeViewModel: viewModel, DynamicTitleBuilder: NewDynamicTitleBuilder(c.Tr.CommitFilesDynamicTitle), + SearchTrait: NewSearchTrait(c), ListContextTrait: &ListContextTrait{ Context: NewSimpleContext( NewBaseContext(NewBaseContextOpts{ @@ -57,6 +59,13 @@ func NewCommitFilesContext(c *ContextCommon) *CommitFilesContext { c: c, }, } + + ctx.GetView().SetOnSelectItem(ctx.SearchTrait.onSelectItemWrapper(func(selectedLineIdx int) error { + ctx.GetList().SetSelectedLineIdx(selectedLineIdx) + return ctx.HandleFocus(types.OnFocusOpts{}) + })) + + return ctx } func (self *CommitFilesContext) GetSelectedItemId() string { diff --git a/pkg/gui/context/filtered_list.go b/pkg/gui/context/filtered_list.go new file mode 100644 index 000000000..298d3d615 --- /dev/null +++ b/pkg/gui/context/filtered_list.go @@ -0,0 +1,93 @@ +package context + +import ( + "github.com/jesseduffield/lazygit/pkg/utils" + "github.com/sasha-s/go-deadlock" +) + +type FilteredList[T any] struct { + filteredIndices []int // if nil, we are not filtering + + getList func() []T + getFilterFields func(T) []string + filter string + + mutex *deadlock.Mutex +} + +func NewFilteredList[T any](getList func() []T, getFilterFields func(T) []string) *FilteredList[T] { + return &FilteredList[T]{ + getList: getList, + getFilterFields: getFilterFields, + mutex: &deadlock.Mutex{}, + } +} + +func (self *FilteredList[T]) GetFilter() string { + return self.filter +} + +func (self *FilteredList[T]) SetFilter(filter string) { + self.filter = filter + + self.applyFilter() +} + +func (self *FilteredList[T]) ClearFilter() { + self.SetFilter("") +} + +func (self *FilteredList[T]) IsFiltering() bool { + return self.filter != "" +} + +func (self *FilteredList[T]) GetFilteredList() []T { + if self.filteredIndices == nil { + return self.getList() + } + return utils.ValuesAtIndices(self.getList(), self.filteredIndices) +} + +// TODO: update to just 'Len' +func (self *FilteredList[T]) UnfilteredLen() int { + return len(self.getList()) +} + +func (self *FilteredList[T]) applyFilter() { + self.mutex.Lock() + defer self.mutex.Unlock() + + if self.filter == "" { + self.filteredIndices = nil + } else { + self.filteredIndices = []int{} + for i, item := range self.getList() { + for _, field := range self.getFilterFields(item) { + if self.match(field, self.filter) { + self.filteredIndices = append(self.filteredIndices, i) + break + } + } + } + } +} + +func (self *FilteredList[T]) match(haystack string, needle string) bool { + return utils.CaseAwareContains(haystack, needle) +} + +func (self *FilteredList[T]) UnfilteredIndex(index int) int { + self.mutex.Lock() + defer self.mutex.Unlock() + + if self.filteredIndices == nil { + return index + } + + // we use -1 when there are no items + if index == -1 { + return -1 + } + + return self.filteredIndices[index] +} diff --git a/pkg/gui/context/filtered_list_view_model.go b/pkg/gui/context/filtered_list_view_model.go new file mode 100644 index 000000000..77f6e1174 --- /dev/null +++ b/pkg/gui/context/filtered_list_view_model.go @@ -0,0 +1,33 @@ +package context + +type FilteredListViewModel[T any] struct { + *FilteredList[T] + *ListViewModel[T] +} + +func NewFilteredListViewModel[T any](getList func() []T, getFilterFields func(T) []string) *FilteredListViewModel[T] { + filteredList := NewFilteredList(getList, getFilterFields) + + self := &FilteredListViewModel[T]{ + FilteredList: filteredList, + } + + listViewModel := NewListViewModel(filteredList.GetFilteredList) + + self.ListViewModel = listViewModel + + return self +} + +// used for type switch +func (self *FilteredListViewModel[T]) IsFilterableContext() {} + +func (self *FilteredListViewModel[T]) ClearFilter() { + // Set the selected line index to the unfiltered index of the currently selected line, + // so that the current item is still selected after the filter is cleared. + unfilteredIndex := self.FilteredList.UnfilteredIndex(self.GetSelectedLineIdx()) + + self.FilteredList.ClearFilter() + + self.SetSelectedLineIdx(unfilteredIndex) +} diff --git a/pkg/gui/context/basic_view_model.go b/pkg/gui/context/list_view_model.go similarity index 56% rename from pkg/gui/context/basic_view_model.go rename to pkg/gui/context/list_view_model.go index a53be4d91..b70330d7d 100644 --- a/pkg/gui/context/basic_view_model.go +++ b/pkg/gui/context/list_view_model.go @@ -2,13 +2,13 @@ package context import "github.com/jesseduffield/lazygit/pkg/gui/context/traits" -type BasicViewModel[T any] struct { +type ListViewModel[T any] struct { *traits.ListCursor getModel func() []T } -func NewBasicViewModel[T any](getModel func() []T) *BasicViewModel[T] { - self := &BasicViewModel[T]{ +func NewListViewModel[T any](getModel func() []T) *ListViewModel[T] { + self := &ListViewModel[T]{ getModel: getModel, } @@ -17,11 +17,11 @@ func NewBasicViewModel[T any](getModel func() []T) *BasicViewModel[T] { return self } -func (self *BasicViewModel[T]) Len() int { +func (self *ListViewModel[T]) Len() int { return len(self.getModel()) } -func (self *BasicViewModel[T]) GetSelected() T { +func (self *ListViewModel[T]) GetSelected() T { if self.Len() == 0 { return Zero[T]() } @@ -29,6 +29,10 @@ func (self *BasicViewModel[T]) GetSelected() T { return self.getModel()[self.GetSelectedLineIdx()] } +func (self *ListViewModel[T]) GetItems() []T { + return self.getModel() +} + func Zero[T any]() T { return *new(T) } diff --git a/pkg/gui/context/local_commits_context.go b/pkg/gui/context/local_commits_context.go index 74363c52f..84204591c 100644 --- a/pkg/gui/context/local_commits_context.go +++ b/pkg/gui/context/local_commits_context.go @@ -13,6 +13,7 @@ import ( type LocalCommitsContext struct { *LocalCommitsViewModel *ListContextTrait + *SearchTrait } var ( @@ -57,8 +58,9 @@ func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext { ) } - return &LocalCommitsContext{ + ctx := &LocalCommitsContext{ LocalCommitsViewModel: viewModel, + SearchTrait: NewSearchTrait(c), ListContextTrait: &ListContextTrait{ Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{ View: c.Views().Commits, @@ -73,6 +75,13 @@ func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext { refreshViewportOnChange: true, }, } + + ctx.GetView().SetOnSelectItem(ctx.SearchTrait.onSelectItemWrapper(func(selectedLineIdx int) error { + ctx.GetList().SetSelectedLineIdx(selectedLineIdx) + return ctx.HandleFocus(types.OnFocusOpts{}) + })) + + return ctx } func (self *LocalCommitsContext) GetSelectedItemId() string { @@ -85,7 +94,7 @@ func (self *LocalCommitsContext) GetSelectedItemId() string { } type LocalCommitsViewModel struct { - *BasicViewModel[*models.Commit] + *ListViewModel[*models.Commit] // If this is true we limit the amount of commits we load, for the sake of keeping things fast. // If the user attempts to scroll past the end of the list, we will load more commits. @@ -97,7 +106,7 @@ type LocalCommitsViewModel struct { func NewLocalCommitsViewModel(getModel func() []*models.Commit, c *ContextCommon) *LocalCommitsViewModel { self := &LocalCommitsViewModel{ - BasicViewModel: NewBasicViewModel(getModel), + ListViewModel: NewListViewModel(getModel), limitCommits: true, showWholeGitGraph: c.UserConfig.Git.Log.ShowWholeGraph, } diff --git a/pkg/gui/context/menu_context.go b/pkg/gui/context/menu_context.go index 6f84a8274..088640ea0 100644 --- a/pkg/gui/context/menu_context.go +++ b/pkg/gui/context/menu_context.go @@ -56,7 +56,7 @@ func (self *MenuContext) GetSelectedItemId() string { type MenuViewModel struct { c *ContextCommon menuItems []*types.MenuItem - *BasicViewModel[*types.MenuItem] + *FilteredListViewModel[*types.MenuItem] } func NewMenuViewModel(c *ContextCommon) *MenuViewModel { @@ -65,7 +65,10 @@ func NewMenuViewModel(c *ContextCommon) *MenuViewModel { c: c, } - self.BasicViewModel = NewBasicViewModel(func() []*types.MenuItem { return self.menuItems }) + self.FilteredListViewModel = NewFilteredListViewModel( + func() []*types.MenuItem { return self.menuItems }, + func(item *types.MenuItem) []string { return item.LabelColumns }, + ) return self } @@ -76,11 +79,12 @@ func (self *MenuViewModel) SetMenuItems(items []*types.MenuItem) { // TODO: move into presentation package func (self *MenuViewModel) GetDisplayStrings(_startIdx int, _length int) [][]string { - showKeys := slices.Some(self.menuItems, func(item *types.MenuItem) bool { + menuItems := self.FilteredListViewModel.GetItems() + showKeys := slices.Some(menuItems, func(item *types.MenuItem) bool { return item.Key != nil }) - return slices.Map(self.menuItems, func(item *types.MenuItem) []string { + return slices.Map(menuItems, func(item *types.MenuItem) []string { displayStrings := item.LabelColumns if !showKeys { @@ -93,6 +97,7 @@ func (self *MenuViewModel) GetDisplayStrings(_startIdx int, _length int) [][]str self.c.UserConfig.Keybinding.Universal.Confirm, self.c.UserConfig.Keybinding.Universal.Select, self.c.UserConfig.Keybinding.Universal.Return, + self.c.UserConfig.Keybinding.Universal.StartSearch, } keyLabel := keybindings.LabelFromKey(item.Key) keyStyle := style.FgCyan diff --git a/pkg/gui/context/patch_explorer_context.go b/pkg/gui/context/patch_explorer_context.go index 1c986ee1d..17ecae4ae 100644 --- a/pkg/gui/context/patch_explorer_context.go +++ b/pkg/gui/context/patch_explorer_context.go @@ -9,6 +9,7 @@ import ( type PatchExplorerContext struct { *SimpleContext + *SearchTrait state *patch_exploring.State viewTrait *ViewTrait @@ -28,7 +29,7 @@ func NewPatchExplorerContext( c *ContextCommon, ) *PatchExplorerContext { - return &PatchExplorerContext{ + ctx := &PatchExplorerContext{ state: nil, viewTrait: NewViewTrait(view), c: c, @@ -42,7 +43,18 @@ func NewPatchExplorerContext( Focusable: true, HighlightOnFocus: true, })), + SearchTrait: NewSearchTrait(c), } + + ctx.GetView().SetOnSelectItem(ctx.SearchTrait.onSelectItemWrapper( + func(selectedLineIdx int) error { + ctx.GetMutex().Lock() + defer ctx.GetMutex().Unlock() + return ctx.NavigateTo(ctx.c.IsCurrentContext(ctx), selectedLineIdx) + }), + ) + + return ctx } func (self *PatchExplorerContext) IsPatchExplorerContext() {} diff --git a/pkg/gui/context/reflog_commits_context.go b/pkg/gui/context/reflog_commits_context.go index a92d605c8..421a7c8d5 100644 --- a/pkg/gui/context/reflog_commits_context.go +++ b/pkg/gui/context/reflog_commits_context.go @@ -9,7 +9,7 @@ import ( ) type ReflogCommitsContext struct { - *BasicViewModel[*models.Commit] + *FilteredListViewModel[*models.Commit] *ListContextTrait } @@ -19,11 +19,16 @@ var ( ) func NewReflogCommitsContext(c *ContextCommon) *ReflogCommitsContext { - viewModel := NewBasicViewModel(func() []*models.Commit { return c.Model().FilteredReflogCommits }) + viewModel := NewFilteredListViewModel( + func() []*models.Commit { return c.Model().FilteredReflogCommits }, + func(commit *models.Commit) []string { + return []string{commit.ShortSha(), commit.Name} + }, + ) getDisplayStrings := func(startIdx int, length int) [][]string { return presentation.GetReflogCommitListDisplayStrings( - c.Model().FilteredReflogCommits, + viewModel.GetItems(), c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL, c.Modes().CherryPicking.SelectedShaSet(), c.Modes().Diffing.Ref, @@ -35,7 +40,7 @@ func NewReflogCommitsContext(c *ContextCommon) *ReflogCommitsContext { } return &ReflogCommitsContext{ - BasicViewModel: viewModel, + FilteredListViewModel: viewModel, ListContextTrait: &ListContextTrait{ Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{ View: c.Views().ReflogCommits, diff --git a/pkg/gui/context/remote_branches_context.go b/pkg/gui/context/remote_branches_context.go index a085c18cc..602a19a65 100644 --- a/pkg/gui/context/remote_branches_context.go +++ b/pkg/gui/context/remote_branches_context.go @@ -7,7 +7,7 @@ import ( ) type RemoteBranchesContext struct { - *BasicViewModel[*models.RemoteBranch] + *FilteredListViewModel[*models.RemoteBranch] *ListContextTrait *DynamicTitleBuilder } @@ -20,15 +20,20 @@ var ( func NewRemoteBranchesContext( c *ContextCommon, ) *RemoteBranchesContext { - viewModel := NewBasicViewModel(func() []*models.RemoteBranch { return c.Model().RemoteBranches }) + viewModel := NewFilteredListViewModel( + func() []*models.RemoteBranch { return c.Model().RemoteBranches }, + func(remoteBranch *models.RemoteBranch) []string { + return []string{remoteBranch.Name} + }, + ) getDisplayStrings := func(startIdx int, length int) [][]string { - return presentation.GetRemoteBranchListDisplayStrings(c.Model().RemoteBranches, c.Modes().Diffing.Ref) + return presentation.GetRemoteBranchListDisplayStrings(viewModel.GetItems(), c.Modes().Diffing.Ref) } return &RemoteBranchesContext{ - BasicViewModel: viewModel, - DynamicTitleBuilder: NewDynamicTitleBuilder(c.Tr.RemoteBranchesDynamicTitle), + FilteredListViewModel: viewModel, + DynamicTitleBuilder: NewDynamicTitleBuilder(c.Tr.RemoteBranchesDynamicTitle), ListContextTrait: &ListContextTrait{ Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{ View: c.Views().RemoteBranches, diff --git a/pkg/gui/context/remotes_context.go b/pkg/gui/context/remotes_context.go index d1082ab52..f5e2a97ab 100644 --- a/pkg/gui/context/remotes_context.go +++ b/pkg/gui/context/remotes_context.go @@ -7,7 +7,7 @@ import ( ) type RemotesContext struct { - *BasicViewModel[*models.Remote] + *FilteredListViewModel[*models.Remote] *ListContextTrait } @@ -17,14 +17,19 @@ var ( ) func NewRemotesContext(c *ContextCommon) *RemotesContext { - viewModel := NewBasicViewModel(func() []*models.Remote { return c.Model().Remotes }) + viewModel := NewFilteredListViewModel( + func() []*models.Remote { return c.Model().Remotes }, + func(remote *models.Remote) []string { + return []string{remote.Name} + }, + ) getDisplayStrings := func(startIdx int, length int) [][]string { - return presentation.GetRemoteListDisplayStrings(c.Model().Remotes, c.Modes().Diffing.Ref) + return presentation.GetRemoteListDisplayStrings(viewModel.GetItems(), c.Modes().Diffing.Ref) } return &RemotesContext{ - BasicViewModel: viewModel, + FilteredListViewModel: viewModel, ListContextTrait: &ListContextTrait{ Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{ View: c.Views().Remotes, diff --git a/pkg/gui/context/search_trait.go b/pkg/gui/context/search_trait.go new file mode 100644 index 000000000..fad68d794 --- /dev/null +++ b/pkg/gui/context/search_trait.go @@ -0,0 +1,74 @@ +package context + +import ( + "fmt" + + "github.com/jesseduffield/lazygit/pkg/gui/keybindings" + "github.com/jesseduffield/lazygit/pkg/theme" +) + +type SearchTrait struct { + c *ContextCommon + + searchString string +} + +func NewSearchTrait(c *ContextCommon) *SearchTrait { + return &SearchTrait{c: c} +} + +func (self *SearchTrait) GetSearchString() string { + return self.searchString +} + +func (self *SearchTrait) SetSearchString(searchString string) { + self.searchString = searchString +} + +func (self *SearchTrait) ClearSearchString() { + self.SetSearchString("") +} + +// used for type switch +func (self *SearchTrait) IsSearchableContext() {} + +func (self *SearchTrait) onSelectItemWrapper(innerFunc func(int) error) func(int, int, int) error { + keybindingConfig := self.c.UserConfig.Keybinding + + return func(y int, index int, total int) error { + if total == 0 { + self.c.SetViewContent( + self.c.Views().Search, + fmt.Sprintf( + self.c.Tr.NoMatchesFor, + self.searchString, + theme.OptionsFgColor.Sprintf(self.c.Tr.ExitSearchMode, keybindings.Label(keybindingConfig.Universal.Return)), + ), + ) + return nil + } + self.c.SetViewContent( + self.c.Views().Search, + fmt.Sprintf( + self.c.Tr.MatchesFor, + self.searchString, + index+1, + total, + theme.OptionsFgColor.Sprintf( + self.c.Tr.SearchKeybindings, + keybindings.Label(keybindingConfig.Universal.NextMatch), + keybindings.Label(keybindingConfig.Universal.PrevMatch), + keybindings.Label(keybindingConfig.Universal.Return), + ), + ), + ) + if err := innerFunc(y); err != nil { + return err + } + return nil + } +} + +func (self *SearchTrait) IsSearching() bool { + return self.searchString != "" +} diff --git a/pkg/gui/context/stash_context.go b/pkg/gui/context/stash_context.go index 386292c00..7bd4740f8 100644 --- a/pkg/gui/context/stash_context.go +++ b/pkg/gui/context/stash_context.go @@ -7,7 +7,7 @@ import ( ) type StashContext struct { - *BasicViewModel[*models.StashEntry] + *FilteredListViewModel[*models.StashEntry] *ListContextTrait } @@ -19,14 +19,19 @@ var ( func NewStashContext( c *ContextCommon, ) *StashContext { - viewModel := NewBasicViewModel(func() []*models.StashEntry { return c.Model().StashEntries }) + viewModel := NewFilteredListViewModel( + func() []*models.StashEntry { return c.Model().StashEntries }, + func(stashEntry *models.StashEntry) []string { + return []string{stashEntry.Name} + }, + ) getDisplayStrings := func(startIdx int, length int) [][]string { - return presentation.GetStashEntryListDisplayStrings(c.Model().StashEntries, c.Modes().Diffing.Ref) + return presentation.GetStashEntryListDisplayStrings(viewModel.GetItems(), c.Modes().Diffing.Ref) } return &StashContext{ - BasicViewModel: viewModel, + FilteredListViewModel: viewModel, ListContextTrait: &ListContextTrait{ Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{ View: c.Views().Stash, diff --git a/pkg/gui/context/sub_commits_context.go b/pkg/gui/context/sub_commits_context.go index e2c89aa14..0cf884589 100644 --- a/pkg/gui/context/sub_commits_context.go +++ b/pkg/gui/context/sub_commits_context.go @@ -12,9 +12,12 @@ import ( ) type SubCommitsContext struct { + c *ContextCommon + *SubCommitsViewModel *ListContextTrait *DynamicTitleBuilder + *SearchTrait } var ( @@ -26,7 +29,7 @@ func NewSubCommitsContext( c *ContextCommon, ) *SubCommitsContext { viewModel := &SubCommitsViewModel{ - BasicViewModel: NewBasicViewModel( + ListViewModel: NewListViewModel( func() []*models.Commit { return c.Model().SubCommits }, ), ref: nil, @@ -60,8 +63,10 @@ func NewSubCommitsContext( ) } - return &SubCommitsContext{ + ctx := &SubCommitsContext{ + c: c, SubCommitsViewModel: viewModel, + SearchTrait: NewSearchTrait(c), DynamicTitleBuilder: NewDynamicTitleBuilder(c.Tr.SubCommitsDynamicTitle), ListContextTrait: &ListContextTrait{ Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{ @@ -78,12 +83,19 @@ func NewSubCommitsContext( refreshViewportOnChange: true, }, } + + ctx.GetView().SetOnSelectItem(ctx.SearchTrait.onSelectItemWrapper(func(selectedLineIdx int) error { + ctx.GetList().SetSelectedLineIdx(selectedLineIdx) + return ctx.HandleFocus(types.OnFocusOpts{}) + })) + + return ctx } type SubCommitsViewModel struct { // name of the ref that the sub-commits are shown for ref types.Ref - *BasicViewModel[*models.Commit] + *ListViewModel[*models.Commit] limitCommits bool } diff --git a/pkg/gui/context/submodules_context.go b/pkg/gui/context/submodules_context.go index 675e01cd1..e97fa4f5c 100644 --- a/pkg/gui/context/submodules_context.go +++ b/pkg/gui/context/submodules_context.go @@ -7,21 +7,26 @@ import ( ) type SubmodulesContext struct { - *BasicViewModel[*models.SubmoduleConfig] + *FilteredListViewModel[*models.SubmoduleConfig] *ListContextTrait } var _ types.IListContext = (*SubmodulesContext)(nil) func NewSubmodulesContext(c *ContextCommon) *SubmodulesContext { - viewModel := NewBasicViewModel(func() []*models.SubmoduleConfig { return c.Model().Submodules }) + viewModel := NewFilteredListViewModel( + func() []*models.SubmoduleConfig { return c.Model().Submodules }, + func(submodule *models.SubmoduleConfig) []string { + return []string{submodule.Name} + }, + ) getDisplayStrings := func(startIdx int, length int) [][]string { - return presentation.GetSubmoduleListDisplayStrings(c.Model().Submodules) + return presentation.GetSubmoduleListDisplayStrings(viewModel.GetItems()) } return &SubmodulesContext{ - BasicViewModel: viewModel, + FilteredListViewModel: viewModel, ListContextTrait: &ListContextTrait{ Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{ View: c.Views().Submodules, diff --git a/pkg/gui/context/suggestions_context.go b/pkg/gui/context/suggestions_context.go index 022e96daf..58b2205a4 100644 --- a/pkg/gui/context/suggestions_context.go +++ b/pkg/gui/context/suggestions_context.go @@ -7,7 +7,7 @@ import ( ) type SuggestionsContext struct { - *BasicViewModel[*types.Suggestion] + *ListViewModel[*types.Suggestion] *ListContextTrait State *SuggestionsContextState @@ -40,11 +40,11 @@ func NewSuggestionsContext( return presentation.GetSuggestionListDisplayStrings(state.Suggestions) } - viewModel := NewBasicViewModel(getModel) + viewModel := NewListViewModel(getModel) return &SuggestionsContext{ - State: state, - BasicViewModel: viewModel, + State: state, + ListViewModel: viewModel, ListContextTrait: &ListContextTrait{ Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{ View: c.Views().Suggestions, diff --git a/pkg/gui/context/tags_context.go b/pkg/gui/context/tags_context.go index e49cdad9b..71ea36981 100644 --- a/pkg/gui/context/tags_context.go +++ b/pkg/gui/context/tags_context.go @@ -7,7 +7,7 @@ import ( ) type TagsContext struct { - *BasicViewModel[*models.Tag] + *FilteredListViewModel[*models.Tag] *ListContextTrait } @@ -19,14 +19,19 @@ var ( func NewTagsContext( c *ContextCommon, ) *TagsContext { - viewModel := NewBasicViewModel(func() []*models.Tag { return c.Model().Tags }) + viewModel := NewFilteredListViewModel( + func() []*models.Tag { return c.Model().Tags }, + func(tag *models.Tag) []string { + return []string{tag.Name, tag.Message} + }, + ) getDisplayStrings := func(startIdx int, length int) [][]string { - return presentation.GetTagListDisplayStrings(c.Model().Tags, c.Modes().Diffing.Ref) + return presentation.GetTagListDisplayStrings(viewModel.GetItems(), c.Modes().Diffing.Ref) } return &TagsContext{ - BasicViewModel: viewModel, + FilteredListViewModel: viewModel, ListContextTrait: &ListContextTrait{ Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{ View: c.Views().Tags, diff --git a/pkg/gui/context/working_tree_context.go b/pkg/gui/context/working_tree_context.go index 45502eb60..107228ee8 100644 --- a/pkg/gui/context/working_tree_context.go +++ b/pkg/gui/context/working_tree_context.go @@ -11,6 +11,7 @@ import ( type WorkingTreeContext struct { *filetree.FileTreeViewModel *ListContextTrait + *SearchTrait } var _ types.IListContext = (*WorkingTreeContext)(nil) @@ -29,7 +30,8 @@ func NewWorkingTreeContext(c *ContextCommon) *WorkingTreeContext { }) } - return &WorkingTreeContext{ + ctx := &WorkingTreeContext{ + SearchTrait: NewSearchTrait(c), FileTreeViewModel: viewModel, ListContextTrait: &ListContextTrait{ Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{ @@ -44,6 +46,13 @@ func NewWorkingTreeContext(c *ContextCommon) *WorkingTreeContext { c: c, }, } + + ctx.GetView().SetOnSelectItem(ctx.SearchTrait.onSelectItemWrapper(func(selectedLineIdx int) error { + ctx.GetList().SetSelectedLineIdx(selectedLineIdx) + return ctx.HandleFocus(types.OnFocusOpts{}) + })) + + return ctx } func (self *WorkingTreeContext) GetSelectedItemId() string { diff --git a/pkg/gui/controllers.go b/pkg/gui/controllers.go index 78943e798..a17592513 100644 --- a/pkg/gui/controllers.go +++ b/pkg/gui/controllers.go @@ -99,6 +99,7 @@ func (gui *Gui) resetHelpersAndControllers() { modeHelper, appStatusHelper, ), + Search: helpers.NewSearchHelper(helperCommon), } gui.CustomCommandsClient = custom_commands.NewClient( @@ -162,6 +163,16 @@ func (gui *Gui) resetHelpersAndControllers() { sideWindowControllerFactory := controllers.NewSideWindowControllerFactory(common) + filterControllerFactory := controllers.NewFilterControllerFactory(common) + for _, context := range gui.c.Context().AllFilterable() { + controllers.AttachControllers(context, filterControllerFactory.Create(context)) + } + + searchControllerFactory := controllers.NewSearchControllerFactory(common) + for _, context := range gui.c.Context().AllSearchable() { + controllers.AttachControllers(context, searchControllerFactory.Create(context)) + } + // allow for navigating between side window contexts for _, context := range []types.Context{ gui.State.Contexts.Status, @@ -323,6 +334,10 @@ func (gui *Gui) resetHelpersAndControllers() { suggestionsController, ) + controllers.AttachControllers(gui.State.Contexts.Search, + controllers.NewSearchPromptController(common), + ) + controllers.AttachControllers(gui.State.Contexts.Global, syncController, undoController, diff --git a/pkg/gui/controllers/files_controller.go b/pkg/gui/controllers/files_controller.go index 11d39ee9f..61d91ad69 100644 --- a/pkg/gui/controllers/files_controller.go +++ b/pkg/gui/controllers/files_controller.go @@ -648,7 +648,7 @@ func (self *FilesController) handleStatusFilterPressed() error { }, }, { - Label: self.c.Tr.ResetCommitFilterState, + Label: self.c.Tr.ResetFilter, OnPress: func() error { return self.setStatusFiltering(filetree.DisplayAll) }, @@ -658,7 +658,7 @@ func (self *FilesController) handleStatusFilterPressed() error { } func (self *FilesController) setStatusFiltering(filter filetree.FileTreeDisplayFilter) error { - self.context().FileTreeViewModel.SetFilter(filter) + self.context().FileTreeViewModel.SetStatusFilter(filter) return self.c.PostRefreshUpdate(self.context()) } diff --git a/pkg/gui/controllers/filter_controller.go b/pkg/gui/controllers/filter_controller.go new file mode 100644 index 000000000..8b049b26c --- /dev/null +++ b/pkg/gui/controllers/filter_controller.go @@ -0,0 +1,48 @@ +package controllers + +import ( + "github.com/jesseduffield/lazygit/pkg/gui/types" +) + +type FilterControllerFactory struct { + c *ControllerCommon +} + +func NewFilterControllerFactory(c *ControllerCommon) *FilterControllerFactory { + return &FilterControllerFactory{ + c: c, + } +} + +func (self *FilterControllerFactory) Create(context types.IFilterableContext) *FilterController { + return &FilterController{ + baseController: baseController{}, + c: self.c, + context: context, + } +} + +type FilterController struct { + baseController + c *ControllerCommon + + context types.IFilterableContext +} + +func (self *FilterController) Context() types.Context { + return self.context +} + +func (self *FilterController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { + return []*types.Binding{ + { + Key: opts.GetKey(opts.Config.Universal.StartSearch), + Handler: self.OpenFilterPrompt, + Description: self.c.Tr.StartFilter, + }, + } +} + +func (self *FilterController) OpenFilterPrompt() error { + return self.c.Helpers().Search.OpenFilterPrompt(self.context) +} diff --git a/pkg/gui/controllers/helpers/confirmation_helper.go b/pkg/gui/controllers/helpers/confirmation_helper.go index 7968933fc..c721310b2 100644 --- a/pkg/gui/controllers/helpers/confirmation_helper.go +++ b/pkg/gui/controllers/helpers/confirmation_helper.go @@ -292,7 +292,9 @@ func (self *ConfirmationHelper) ResizePopupPanel(v *gocui.View, content string) } func (self *ConfirmationHelper) resizeMenu() { - itemCount := self.c.Contexts().Menu.GetList().Len() + // we want the unfiltered length here so that if we're filtering we don't + // resize the window + itemCount := self.c.Contexts().Menu.UnfilteredLen() offset := 3 panelWidth := self.getPopupPanelWidth() x0, y0, x1, y1 := self.getPopupPanelDimensionsForContentHeight(panelWidth, itemCount+offset) diff --git a/pkg/gui/controllers/helpers/helpers.go b/pkg/gui/controllers/helpers/helpers.go index faf342f0a..846638249 100644 --- a/pkg/gui/controllers/helpers/helpers.go +++ b/pkg/gui/controllers/helpers/helpers.go @@ -46,6 +46,7 @@ type Helpers struct { Mode *ModeHelper AppStatus *AppStatusHelper WindowArrangement *WindowArrangementHelper + Search *SearchHelper } func NewStubHelpers() *Helpers { @@ -78,5 +79,6 @@ func NewStubHelpers() *Helpers { Mode: &ModeHelper{}, AppStatus: &AppStatusHelper{}, WindowArrangement: &WindowArrangementHelper{}, + Search: &SearchHelper{}, } } diff --git a/pkg/gui/controllers/helpers/refresh_helper.go b/pkg/gui/controllers/helpers/refresh_helper.go index 7b37585a5..f0827dc41 100644 --- a/pkg/gui/controllers/helpers/refresh_helper.go +++ b/pkg/gui/controllers/helpers/refresh_helper.go @@ -464,10 +464,10 @@ func (self *RefreshHelper) refreshStateFiles() error { // I'd prefer to maintain as little state as possible. if conflictFileCount > 0 { if fileTreeViewModel.GetFilter() == filetree.DisplayAll { - fileTreeViewModel.SetFilter(filetree.DisplayConflicted) + fileTreeViewModel.SetStatusFilter(filetree.DisplayConflicted) } } else if fileTreeViewModel.GetFilter() == filetree.DisplayConflicted { - fileTreeViewModel.SetFilter(filetree.DisplayAll) + fileTreeViewModel.SetStatusFilter(filetree.DisplayAll) } self.c.Model().Files = files diff --git a/pkg/gui/controllers/helpers/search_helper.go b/pkg/gui/controllers/helpers/search_helper.go new file mode 100644 index 000000000..b244f20e4 --- /dev/null +++ b/pkg/gui/controllers/helpers/search_helper.go @@ -0,0 +1,260 @@ +package helpers + +import ( + "fmt" + + "github.com/jesseduffield/gocui" + "github.com/jesseduffield/lazygit/pkg/gui/context" + "github.com/jesseduffield/lazygit/pkg/gui/keybindings" + "github.com/jesseduffield/lazygit/pkg/gui/types" + "github.com/jesseduffield/lazygit/pkg/theme" +) + +// NOTE: this helper supports both filtering and searching. Filtering is when +// the contents of the list are filtered, whereas searching does not actually +// change the contents of the list but instead just highlights the search. +// The general term we use to capture both searching and filtering is... +// 'searching', which is unfortunate but I can't think of a better name. + +type SearchHelper struct { + c *HelperCommon +} + +func NewSearchHelper( + c *HelperCommon, +) *SearchHelper { + return &SearchHelper{ + c: c, + } +} + +func (self *SearchHelper) OpenFilterPrompt(context types.IFilterableContext) error { + state := self.searchState() + + state.Context = context + + self.searchPrefixView().SetContent(self.c.Tr.FilterPrefix) + promptView := self.promptView() + promptView.ClearTextArea() + promptView.TextArea.TypeString(context.GetFilter()) + promptView.RenderTextArea() + + if err := self.c.PushContext(self.c.Contexts().Search); err != nil { + return err + } + + return nil +} + +func (self *SearchHelper) OpenSearchPrompt(context types.ISearchableContext) error { + state := self.searchState() + + state.Context = context + searchString := context.GetSearchString() + + self.searchPrefixView().SetContent(self.c.Tr.SearchPrefix) + promptView := self.promptView() + promptView.ClearTextArea() + promptView.TextArea.TypeString(searchString) + promptView.RenderTextArea() + + if err := self.c.PushContext(self.c.Contexts().Search); err != nil { + return err + } + + return nil +} + +func (self *SearchHelper) DisplayFilterStatus(context types.IFilterableContext) { + state := self.searchState() + + state.Context = context + searchString := context.GetFilter() + + self.searchPrefixView().SetContent(self.c.Tr.FilterPrefix) + + promptView := self.promptView() + keybindingConfig := self.c.UserConfig.Keybinding + promptView.SetContent(fmt.Sprintf("matches for '%s' ", searchString) + theme.OptionsFgColor.Sprintf(self.c.Tr.ExitTextFilterMode, keybindings.Label(keybindingConfig.Universal.Return))) +} + +func (self *SearchHelper) DisplaySearchStatus(context types.ISearchableContext) { + state := self.searchState() + + state.Context = context + + self.searchPrefixView().SetContent(self.c.Tr.SearchPrefix) + _ = context.GetView().SelectCurrentSearchResult() +} + +func (self *SearchHelper) searchState() *types.SearchState { + return self.c.State().GetRepoState().GetSearchState() +} + +func (self *SearchHelper) searchPrefixView() *gocui.View { + return self.c.Views().SearchPrefix +} + +func (self *SearchHelper) promptView() *gocui.View { + return self.c.Contexts().Search.GetView() +} + +func (self *SearchHelper) promptContent() string { + return self.c.Contexts().Search.GetView().TextArea.GetContent() +} + +func (self *SearchHelper) Confirm() error { + state := self.searchState() + if self.promptContent() == "" { + return self.CancelPrompt() + } + + switch state.SearchType() { + case types.SearchTypeFilter: + return self.ConfirmFilter() + case types.SearchTypeSearch: + return self.ConfirmSearch() + case types.SearchTypeNone: + return self.c.PopContext() + } + + return nil +} + +func (self *SearchHelper) ConfirmFilter() error { + // We also do this on each keypress but we do it here again just in case + state := self.searchState() + + _, ok := state.Context.(types.IFilterableContext) + if !ok { + self.c.Log.Warnf("Context %s is not filterable", state.Context.GetKey()) + return nil + } + + self.OnPromptContentChanged(self.promptContent()) + + return self.c.PopContext() +} + +func (self *SearchHelper) ConfirmSearch() error { + state := self.searchState() + + context, ok := state.Context.(types.ISearchableContext) + if !ok { + self.c.Log.Warnf("Context %s is searchable", state.Context.GetKey()) + return nil + } + + searchString := self.promptContent() + context.SetSearchString(searchString) + + view := context.GetView() + + if err := self.c.PopContext(); err != nil { + return err + } + + if err := view.Search(searchString); err != nil { + return err + } + + return nil +} + +func (self *SearchHelper) CancelPrompt() error { + self.Cancel() + + return self.c.PopContext() +} + +func (self *SearchHelper) Cancel() { + state := self.searchState() + + switch context := state.Context.(type) { + case types.IFilterableContext: + context.ClearFilter() + _ = self.c.PostRefreshUpdate(context) + case types.ISearchableContext: + context.ClearSearchString() + context.GetView().ClearSearch() + default: + // do nothing + } + + self.HidePrompt() +} + +func (self *SearchHelper) OnPromptContentChanged(searchString string) { + state := self.searchState() + switch context := state.Context.(type) { + case types.IFilterableContext: + context.SetSelectedLineIdx(0) + _ = context.GetView().SetOriginY(0) + context.SetFilter(searchString) + _ = self.c.PostRefreshUpdate(context) + case types.ISearchableContext: + // do nothing + default: + // do nothing (shouldn't land here) + } +} + +func (self *SearchHelper) RenderSearchStatus(c types.Context) { + if c.GetKey() == context.SEARCH_CONTEXT_KEY { + return + } + + if searchableContext, ok := c.(types.ISearchableContext); ok { + if searchableContext.IsSearching() { + self.setSearchingFrameColor() + self.DisplaySearchStatus(searchableContext) + return + } + } + if filterableContext, ok := c.(types.IFilterableContext); ok { + if filterableContext.IsFiltering() { + self.setSearchingFrameColor() + self.DisplayFilterStatus(filterableContext) + return + } + } + + self.HidePrompt() +} + +func (self *SearchHelper) CancelSearchIfSearching(c types.Context) { + if searchableContext, ok := c.(types.ISearchableContext); ok { + view := searchableContext.GetView() + if view != nil && view.IsSearching() { + view.ClearSearch() + searchableContext.ClearSearchString() + self.Cancel() + } + return + } + + if filterableContext, ok := c.(types.IFilterableContext); ok { + if filterableContext.IsFiltering() { + filterableContext.ClearFilter() + self.Cancel() + } + return + } +} + +func (self *SearchHelper) HidePrompt() { + self.setNonSearchingFrameColor() + + state := self.searchState() + state.Context = nil +} + +func (self *SearchHelper) setSearchingFrameColor() { + self.c.GocuiGui().SelFgColor = theme.SearchingActiveBorderColor + self.c.GocuiGui().SelFrameColor = theme.SearchingActiveBorderColor +} + +func (self *SearchHelper) setNonSearchingFrameColor() { + self.c.GocuiGui().SelFgColor = theme.ActiveBorderColor + self.c.GocuiGui().SelFrameColor = theme.ActiveBorderColor +} diff --git a/pkg/gui/controllers/helpers/window_arrangement_helper.go b/pkg/gui/controllers/helpers/window_arrangement_helper.go index 20459993f..b45586764 100644 --- a/pkg/gui/controllers/helpers/window_arrangement_helper.go +++ b/pkg/gui/controllers/helpers/window_arrangement_helper.go @@ -55,7 +55,7 @@ func (self *WindowArrangementHelper) GetWindowDimensions(informationStr string, self.c.Modes().Filtering.Active() showInfoSection := self.c.UserConfig.Gui.ShowBottomLine || - self.c.State().GetRepoState().IsSearching() || + self.c.State().GetRepoState().InSearchPrompt() || self.modeHelper.IsAnyModeActive() || self.appStatusHelper.HasStatus() infoSectionSize := 0 @@ -174,11 +174,17 @@ func (self *WindowArrangementHelper) getMidSectionWeights() (int, int) { } func (self *WindowArrangementHelper) infoSectionChildren(informationStr string, appStatus string) []*boxlayout.Box { - if self.c.State().GetRepoState().IsSearching() { + if self.c.State().GetRepoState().InSearchPrompt() { + var prefix string + if self.c.State().GetRepoState().GetSearchState().SearchType() == types.SearchTypeSearch { + prefix = self.c.Tr.SearchPrefix + } else { + prefix = self.c.Tr.FilterPrefix + } return []*boxlayout.Box{ { Window: "searchPrefix", - Size: runewidth.StringWidth(self.c.Tr.SearchPrefix), + Size: runewidth.StringWidth(prefix), }, { Window: "search", diff --git a/pkg/gui/controllers/list_controller.go b/pkg/gui/controllers/list_controller.go index 2f995ebc8..fb6d8736a 100644 --- a/pkg/gui/controllers/list_controller.go +++ b/pkg/gui/controllers/list_controller.go @@ -150,18 +150,7 @@ func (self *ListController) GetKeybindings(opts types.KeybindingsOpts) []*types. {Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.GotoTop), Handler: self.HandleGotoTop, Description: self.c.Tr.GotoTop}, {Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.ScrollLeft), Handler: self.HandleScrollLeft}, {Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.ScrollRight), Handler: self.HandleScrollRight}, - { - Key: opts.GetKey(opts.Config.Universal.StartSearch), - Handler: func() error { self.c.OpenSearch(); return nil }, - Description: self.c.Tr.StartSearch, - Tag: "navigation", - }, - { - Key: opts.GetKey(opts.Config.Universal.GotoBottom), - Description: self.c.Tr.GotoBottom, - Handler: self.HandleGotoBottom, - Tag: "navigation", - }, + {Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.GotoBottom), Handler: self.HandleGotoBottom, Description: self.c.Tr.GotoBottom}, } } diff --git a/pkg/gui/controllers/local_commits_controller.go b/pkg/gui/controllers/local_commits_controller.go index 0ba80c768..49abe02ff 100644 --- a/pkg/gui/controllers/local_commits_controller.go +++ b/pkg/gui/controllers/local_commits_controller.go @@ -693,9 +693,7 @@ func (self *LocalCommitsController) openSearch() error { } } - self.c.OpenSearch() - - return nil + return self.c.Helpers().Search.OpenSearchPrompt(self.context()) } func (self *LocalCommitsController) gotoBottom() error { diff --git a/pkg/gui/controllers/patch_explorer_controller.go b/pkg/gui/controllers/patch_explorer_controller.go index 6de8fb8b9..dd19d08db 100644 --- a/pkg/gui/controllers/patch_explorer_controller.go +++ b/pkg/gui/controllers/patch_explorer_controller.go @@ -123,12 +123,6 @@ func (self *PatchExplorerController) GetKeybindings(opts types.KeybindingsOpts) 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.StartSearch, - }, { Key: opts.GetKey(opts.Config.Universal.CopyToClipboard), Handler: self.withLock(self.CopySelectedToClipboard), diff --git a/pkg/gui/controllers/quit_actions.go b/pkg/gui/controllers/quit_actions.go index 2487a62fe..a163f66c8 100644 --- a/pkg/gui/controllers/quit_actions.go +++ b/pkg/gui/controllers/quit_actions.go @@ -50,6 +50,19 @@ func (self *QuitActions) confirmQuitDuringUpdate() error { func (self *QuitActions) Escape() error { currentContext := self.c.CurrentContext() + switch ctx := currentContext.(type) { + case types.IFilterableContext: + if ctx.IsFiltering() { + self.c.Helpers().Search.Cancel() + return nil + } + case types.ISearchableContext: + if ctx.IsSearching() { + self.c.Helpers().Search.Cancel() + return nil + } + } + parentContext, hasParent := currentContext.GetParentContext() if hasParent && currentContext != nil && parentContext != nil { // TODO: think about whether this should be marked as a return rather than adding to the stack diff --git a/pkg/gui/controllers/remote_branches_controller.go b/pkg/gui/controllers/remote_branches_controller.go index c1cc9d46b..b26230d90 100644 --- a/pkg/gui/controllers/remote_branches_controller.go +++ b/pkg/gui/controllers/remote_branches_controller.go @@ -59,11 +59,6 @@ func (self *RemoteBranchesController) GetKeybindings(opts types.KeybindingsOpts) Handler: self.checkSelected(self.setAsUpstream), Description: self.c.Tr.SetAsUpstream, }, - { - Key: opts.GetKey(opts.Config.Universal.Return), - Handler: self.escape, - Description: self.c.Tr.ReturnToRemotesList, - }, { Key: opts.GetKey(opts.Config.Commits.ViewResetOptions), Handler: self.checkSelected(self.createResetMenu), @@ -115,10 +110,6 @@ func (self *RemoteBranchesController) checkSelected(callback func(*models.Remote } } -func (self *RemoteBranchesController) escape() error { - return self.c.PushContext(self.c.Contexts().Remotes) -} - func (self *RemoteBranchesController) delete(selectedBranch *models.RemoteBranch) error { message := fmt.Sprintf("%s '%s'?", self.c.Tr.DeleteRemoteBranchMessage, selectedBranch.FullName()) diff --git a/pkg/gui/controllers/remotes_controller.go b/pkg/gui/controllers/remotes_controller.go index 283119886..b6d9a963b 100644 --- a/pkg/gui/controllers/remotes_controller.go +++ b/pkg/gui/controllers/remotes_controller.go @@ -104,14 +104,16 @@ func (self *RemotesController) enter(remote *models.Remote) error { if len(remote.Branches) == 0 { newSelectedLine = -1 } - self.c.Contexts().RemoteBranches.SetSelectedLineIdx(newSelectedLine) - self.c.Contexts().RemoteBranches.SetTitleRef(remote.Name) + remoteBranchesContext := self.c.Contexts().RemoteBranches + remoteBranchesContext.SetSelectedLineIdx(newSelectedLine) + remoteBranchesContext.SetTitleRef(remote.Name) + remoteBranchesContext.SetParentContext(self.Context()) - if err := self.c.PostRefreshUpdate(self.c.Contexts().RemoteBranches); err != nil { + if err := self.c.PostRefreshUpdate(remoteBranchesContext); err != nil { return err } - return self.c.PushContext(self.c.Contexts().RemoteBranches) + return self.c.PushContext(remoteBranchesContext) } func (self *RemotesController) add() error { diff --git a/pkg/gui/controllers/search_controller.go b/pkg/gui/controllers/search_controller.go new file mode 100644 index 000000000..395784d10 --- /dev/null +++ b/pkg/gui/controllers/search_controller.go @@ -0,0 +1,48 @@ +package controllers + +import ( + "github.com/jesseduffield/lazygit/pkg/gui/types" +) + +type SearchControllerFactory struct { + c *ControllerCommon +} + +func NewSearchControllerFactory(c *ControllerCommon) *SearchControllerFactory { + return &SearchControllerFactory{ + c: c, + } +} + +func (self *SearchControllerFactory) Create(context types.ISearchableContext) *SearchController { + return &SearchController{ + baseController: baseController{}, + c: self.c, + context: context, + } +} + +type SearchController struct { + baseController + c *ControllerCommon + + context types.ISearchableContext +} + +func (self *SearchController) Context() types.Context { + return self.context +} + +func (self *SearchController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { + return []*types.Binding{ + { + Key: opts.GetKey(opts.Config.Universal.StartSearch), + Handler: self.OpenSearchPrompt, + Description: self.c.Tr.StartSearch, + }, + } +} + +func (self *SearchController) OpenSearchPrompt() error { + return self.c.Helpers().Search.OpenSearchPrompt(self.context) +} diff --git a/pkg/gui/controllers/search_prompt_controller.go b/pkg/gui/controllers/search_prompt_controller.go new file mode 100644 index 000000000..2326ed1c1 --- /dev/null +++ b/pkg/gui/controllers/search_prompt_controller.go @@ -0,0 +1,53 @@ +package controllers + +import ( + "github.com/jesseduffield/gocui" + "github.com/jesseduffield/lazygit/pkg/gui/types" +) + +type SearchPromptController struct { + baseController + c *ControllerCommon +} + +var _ types.IController = &SearchPromptController{} + +func NewSearchPromptController( + common *ControllerCommon, +) *SearchPromptController { + return &SearchPromptController{ + baseController: baseController{}, + c: common, + } +} + +func (self *SearchPromptController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { + return []*types.Binding{ + { + Key: opts.GetKey(opts.Config.Universal.Confirm), + Modifier: gocui.ModNone, + Handler: self.confirm, + }, + { + Key: opts.GetKey(opts.Config.Universal.Return), + Modifier: gocui.ModNone, + Handler: self.cancel, + }, + } +} + +func (self *SearchPromptController) Context() types.Context { + return self.context() +} + +func (self *SearchPromptController) context() types.Context { + return self.c.Contexts().Search +} + +func (self *SearchPromptController) confirm() error { + return self.c.Helpers().Search.Confirm() +} + +func (self *SearchPromptController) cancel() error { + return self.c.Helpers().Search.CancelPrompt() +} diff --git a/pkg/gui/controllers/switch_to_diff_files_controller.go b/pkg/gui/controllers/switch_to_diff_files_controller.go index ffec6936e..971efb7a1 100644 --- a/pkg/gui/controllers/switch_to_diff_files_controller.go +++ b/pkg/gui/controllers/switch_to_diff_files_controller.go @@ -83,6 +83,7 @@ func (self *SwitchToDiffFilesController) viewFiles(opts SwitchToCommitFilesConte diffFilesContext.SetCanRebase(opts.CanRebase) diffFilesContext.SetParentContext(opts.Context) diffFilesContext.SetWindowName(opts.Context.GetWindowName()) + diffFilesContext.ClearSearchString() if err := self.c.Refresh(types.RefreshOptions{ Scope: []types.RefreshableView{types.COMMIT_FILES}, diff --git a/pkg/gui/controllers/switch_to_sub_commits_controller.go b/pkg/gui/controllers/switch_to_sub_commits_controller.go index 5ae86f02b..8163181e5 100644 --- a/pkg/gui/controllers/switch_to_sub_commits_controller.go +++ b/pkg/gui/controllers/switch_to_sub_commits_controller.go @@ -71,12 +71,15 @@ func (self *SwitchToSubCommitsController) viewCommits() error { self.setSubCommits(commits) - self.c.Contexts().SubCommits.SetSelectedLineIdx(0) - self.c.Contexts().SubCommits.SetParentContext(self.context) - self.c.Contexts().SubCommits.SetWindowName(self.context.GetWindowName()) - self.c.Contexts().SubCommits.SetTitleRef(ref.Description()) - self.c.Contexts().SubCommits.SetRef(ref) - self.c.Contexts().SubCommits.SetLimitCommits(true) + subCommitsContext := self.c.Contexts().SubCommits + subCommitsContext.SetSelectedLineIdx(0) + subCommitsContext.SetParentContext(self.context) + subCommitsContext.SetWindowName(self.context.GetWindowName()) + subCommitsContext.SetTitleRef(ref.Description()) + subCommitsContext.SetRef(ref) + subCommitsContext.SetLimitCommits(true) + subCommitsContext.ClearSearchString() + subCommitsContext.GetView().ClearSearch() err = self.c.PostRefreshUpdate(self.c.Contexts().SubCommits) if err != nil { diff --git a/pkg/gui/editors.go b/pkg/gui/editors.go index 1fbba2aad..b095630ec 100644 --- a/pkg/gui/editors.go +++ b/pkg/gui/editors.go @@ -89,3 +89,14 @@ func (gui *Gui) promptEditor(v *gocui.View, key gocui.Key, ch rune, mod gocui.Mo return matched } + +func (gui *Gui) searchEditor(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) bool { + matched := gui.handleEditorKeypress(v.TextArea, key, ch, mod, false) + v.RenderTextArea() + + searchString := v.TextArea.GetContent() + + gui.helpers.Search.OnPromptContentChanged(searchString) + + return matched +} diff --git a/pkg/gui/filetree/file_tree.go b/pkg/gui/filetree/file_tree.go index 950bf24be..3f244edfe 100644 --- a/pkg/gui/filetree/file_tree.go +++ b/pkg/gui/filetree/file_tree.go @@ -34,7 +34,7 @@ type IFileTree interface { ITree[models.File] FilterFiles(test func(*models.File) bool) []*models.File - SetFilter(filter FileTreeDisplayFilter) + SetStatusFilter(filter FileTreeDisplayFilter) Get(index int) *FileNode GetFile(path string) *models.File GetAllItems() []*FileNode @@ -91,7 +91,7 @@ func (self *FileTree) FilterFiles(test func(*models.File) bool) []*models.File { return slices.Filter(self.getFiles(), test) } -func (self *FileTree) SetFilter(filter FileTreeDisplayFilter) { +func (self *FileTree) SetStatusFilter(filter FileTreeDisplayFilter) { self.filter = filter self.SetTree() } @@ -102,7 +102,7 @@ func (self *FileTree) ToggleShowTree() { } func (self *FileTree) Get(index int) *FileNode { - // need to traverse the three depth first until we get to the index. + // need to traverse the tree depth first until we get to the index. return NewFileNode(self.tree.GetNodeAtIndex(index+1, self.collapsedPaths)) // ignoring root } diff --git a/pkg/gui/filetree/file_tree_view_model.go b/pkg/gui/filetree/file_tree_view_model.go index 333be8da2..547b62b91 100644 --- a/pkg/gui/filetree/file_tree_view_model.go +++ b/pkg/gui/filetree/file_tree_view_model.go @@ -126,8 +126,8 @@ func (self *FileTreeViewModel) findNewSelectedIdx(prevNodes []*FileNode, currNod return -1 } -func (self *FileTreeViewModel) SetFilter(filter FileTreeDisplayFilter) { - self.IFileTree.SetFilter(filter) +func (self *FileTreeViewModel) SetStatusFilter(filter FileTreeDisplayFilter) { + self.IFileTree.SetStatusFilter(filter) self.IListCursor.SetSelectedLineIdx(0) } diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index b922c5f77..106aee7a9 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -201,7 +201,7 @@ type GuiRepoState struct { SplitMainPanel bool LimitCommits bool - Searching searchingState + SearchState *types.SearchState StartupStage types.StartupStage // Allows us to not load everything at once ContextMgr *ContextMgr @@ -256,8 +256,12 @@ func (self *GuiRepoState) SetScreenMode(value types.WindowMaximisation) { self.ScreenMode = value } -func (self *GuiRepoState) IsSearching() bool { - return self.Searching.isSearching +func (self *GuiRepoState) InSearchPrompt() bool { + return self.SearchState.SearchType() != types.SearchTypeNone +} + +func (self *GuiRepoState) GetSearchState() *types.SearchState { + return self.SearchState } func (self *GuiRepoState) SetSplitMainPanel(value bool) { @@ -268,12 +272,6 @@ func (self *GuiRepoState) GetSplitMainPanel() bool { return self.SplitMainPanel } -type searchingState struct { - view *gocui.View - isSearching bool - searchString string -} - func (gui *Gui) onNewRepo(startArgs appTypes.StartArgs, reuseState bool) error { var err error gui.git, err = commands.NewGitCommand( @@ -358,6 +356,7 @@ func (gui *Gui) resetState(startArgs appTypes.StartArgs, reuseState bool) types. ContextMgr: NewContextMgr(gui, contextTree), Contexts: contextTree, WindowViewNameMap: initialWindowViewNameMap(contextTree), + SearchState: types.NewSearchState(), } gui.RepoStateMap[Repo(currentDir)] = gui.State @@ -584,11 +583,12 @@ func (gui *Gui) Run(startArgs appTypes.StartArgs) error { }) deadlock.Opts.Disable = !gui.Debug - gui.g.OnSearchEscape = gui.onSearchEscape if err := gui.Config.ReloadUserConfig(); err != nil { return nil } userConfig := gui.UserConfig + + gui.g.OnSearchEscape = func() error { gui.helpers.Search.Cancel(); return nil } gui.g.SearchEscapeKey = keybindings.GetKey(userConfig.Keybinding.Universal.Return) gui.g.NextSearchMatchKey = keybindings.GetKey(userConfig.Keybinding.Universal.NextMatch) gui.g.PrevSearchMatchKey = keybindings.GetKey(userConfig.Keybinding.Universal.PrevMatch) diff --git a/pkg/gui/gui_common.go b/pkg/gui/gui_common.go index dfed29c44..8fc7732fc 100644 --- a/pkg/gui/gui_common.go +++ b/pkg/gui/gui_common.go @@ -128,10 +128,6 @@ func (self *guiCommon) Mutexes() types.Mutexes { return self.gui.Mutexes } -func (self *guiCommon) OpenSearch() { - _ = self.gui.handleOpenSearch(self.gui.currentViewName()) -} - func (self *guiCommon) GocuiGui() *gocui.Gui { return self.gui.g } diff --git a/pkg/gui/gui_driver.go b/pkg/gui/gui_driver.go index a90578b65..824c9ed33 100644 --- a/pkg/gui/gui_driver.go +++ b/pkg/gui/gui_driver.go @@ -2,6 +2,7 @@ package gui import ( "fmt" + "os" "strings" "time" @@ -70,7 +71,8 @@ func (self *GuiDriver) Fail(message string) { self.gui.g.Close() // need to give the gui time to close time.Sleep(time.Millisecond * 100) - panic(fullMessage) + fmt.Fprintln(os.Stderr, fullMessage) + panic("Test failed") } // logs to the normal place that you log to i.e. viewable with `lazygit --logs` diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go index 30ddb8951..cb14d0266 100644 --- a/pkg/gui/keybindings.go +++ b/pkg/gui/keybindings.go @@ -215,18 +215,6 @@ func (self *Gui) GetInitialKeybindings() ([]*types.Binding, []*gocui.ViewMouseBi Modifier: gocui.ModNone, Handler: self.scrollUpSecondary, }, - { - ViewName: "search", - Key: opts.GetKey(opts.Config.Universal.Confirm), - Modifier: gocui.ModNone, - Handler: self.handleSearch, - }, - { - ViewName: "search", - Key: opts.GetKey(opts.Config.Universal.Return), - Modifier: gocui.ModNone, - Handler: self.handleSearchEscape, - }, { ViewName: "confirmation", Key: opts.GetKey(opts.Config.Universal.PrevItem), diff --git a/pkg/gui/layout.go b/pkg/gui/layout.go index 88ddeca5f..ed10fda92 100644 --- a/pkg/gui/layout.go +++ b/pkg/gui/layout.go @@ -132,20 +132,6 @@ func (gui *Gui) layout(g *gocui.Gui) error { } view.SelBgColor = theme.GocuiSelectedLineBgColor - - // I doubt this is expensive though it's admittedly redundant after the first render - view.SetOnSelectItem(gui.onSelectItemWrapper(listContext.OnSearchSelect)) - } - - for _, context := range gui.c.Context().AllPatchExplorer() { - 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() diff --git a/pkg/gui/menu_panel.go b/pkg/gui/menu_panel.go index f830526db..19c58b145 100644 --- a/pkg/gui/menu_panel.go +++ b/pkg/gui/menu_panel.go @@ -46,9 +46,6 @@ func (gui *Gui) createMenu(opts types.CreateMenuOptions) error { gui.Views.Menu.Title = opts.Title gui.Views.Menu.FgColor = theme.GocuiDefaultTextColor - gui.Views.Menu.SetOnSelectItem(gui.onSelectItemWrapper(func(selectedLine int) error { - return nil - })) gui.Views.Tooltip.Wrap = true gui.Views.Tooltip.FgColor = theme.GocuiDefaultTextColor diff --git a/pkg/gui/searching.go b/pkg/gui/searching.go deleted file mode 100644 index 21a07c4c2..000000000 --- a/pkg/gui/searching.go +++ /dev/null @@ -1,103 +0,0 @@ -package gui - -import ( - "fmt" - - "github.com/jesseduffield/lazygit/pkg/gui/keybindings" - "github.com/jesseduffield/lazygit/pkg/theme" -) - -func (gui *Gui) handleOpenSearch(viewName string) error { - view, err := gui.g.View(viewName) - if err != nil { - return nil - } - - gui.State.Searching.isSearching = true - gui.State.Searching.view = view - - gui.Views.Search.ClearTextArea() - - if err := gui.c.PushContext(gui.State.Contexts.Search); err != nil { - return err - } - - return nil -} - -func (gui *Gui) handleSearch() error { - gui.State.Searching.searchString = gui.Views.Search.TextArea.GetContent() - if err := gui.c.PopContext(); err != nil { - return err - } - - view := gui.State.Searching.view - if view == nil { - return nil - } - - if err := view.Search(gui.State.Searching.searchString); err != nil { - return err - } - - return nil -} - -func (gui *Gui) onSelectItemWrapper(innerFunc func(int) error) func(int, int, int) error { - keybindingConfig := gui.c.UserConfig.Keybinding - - return func(y int, index int, total int) error { - if total == 0 { - gui.c.SetViewContent( - gui.Views.Search, - fmt.Sprintf( - gui.Tr.NoMatchesFor, - gui.State.Searching.searchString, - theme.OptionsFgColor.Sprintf(gui.Tr.ExitSearchMode, keybindings.Label(keybindingConfig.Universal.Return)), - ), - ) - return nil - } - gui.c.SetViewContent( - gui.Views.Search, - fmt.Sprintf( - gui.Tr.MatchesFor, - gui.State.Searching.searchString, - index+1, - total, - theme.OptionsFgColor.Sprintf( - gui.Tr.SearchKeybindings, - keybindings.Label(keybindingConfig.Universal.NextMatch), - keybindings.Label(keybindingConfig.Universal.PrevMatch), - keybindings.Label(keybindingConfig.Universal.Return), - ), - ), - ) - if err := innerFunc(y); err != nil { - return err - } - return nil - } -} - -func (gui *Gui) onSearchEscape() error { - gui.State.Searching.isSearching = false - if gui.State.Searching.view != nil { - gui.State.Searching.view.ClearSearch() - gui.State.Searching.view = nil - } - - return nil -} - -func (gui *Gui) handleSearchEscape() error { - if err := gui.onSearchEscape(); err != nil { - return err - } - - if err := gui.c.PopContext(); err != nil { - return err - } - - return nil -} diff --git a/pkg/gui/types/common.go b/pkg/gui/types/common.go index 3709d3c7b..09ab040f2 100644 --- a/pkg/gui/types/common.go +++ b/pkg/gui/types/common.go @@ -68,8 +68,6 @@ type IGuiCommon interface { Context() IContextMgr ActivateContext(context Context) error - // enters search mode for the current view - OpenSearch() GetConfig() config.AppConfigurer GetAppState() *config.AppState @@ -251,7 +249,8 @@ type IRepoStateAccessor interface { SetCurrentPopupOpts(*CreatePopupPanelOpts) GetScreenMode() WindowMaximisation SetScreenMode(WindowMaximisation) - IsSearching() bool + InSearchPrompt() bool + GetSearchState() *SearchState SetSplitMainPanel(bool) GetSplitMainPanel() bool } diff --git a/pkg/gui/types/context.go b/pkg/gui/types/context.go index bb8630bff..dca5b042c 100644 --- a/pkg/gui/types/context.go +++ b/pkg/gui/types/context.go @@ -87,6 +87,27 @@ type Context interface { HandleRenderToMain() error } +type IFilterableContext interface { + Context + IListPanelState + + SetFilter(string) + GetFilter() string + ClearFilter() + IsFiltering() bool + IsFilterableContext() +} + +type ISearchableContext interface { + Context + + SetSearchString(string) + GetSearchString() string + ClearSearchString() + IsSearching() bool + IsSearchableContext() +} + type DiffableContext interface { Context @@ -104,7 +125,6 @@ type IListContext interface { GetList() IList - OnSearchSelect(selectedLineIdx int) error FocusLine() IsListContext() // used for type switch } @@ -211,5 +231,7 @@ type IContextMgr interface { IsCurrent(c Context) bool ForEach(func(Context)) AllList() []IListContext + AllFilterable() []IFilterableContext + AllSearchable() []ISearchableContext AllPatchExplorer() []IPatchExplorerContext } diff --git a/pkg/gui/types/search_state.go b/pkg/gui/types/search_state.go new file mode 100644 index 000000000..9b24af095 --- /dev/null +++ b/pkg/gui/types/search_state.go @@ -0,0 +1,31 @@ +package types + +type SearchType int + +const ( + SearchTypeNone SearchType = iota + // searching is where matches are highlighted but the content is not filtered down + SearchTypeSearch + // filter is where the list is filtered down to only matches + SearchTypeFilter +) + +// TODO: could we remove this entirely? +type SearchState struct { + Context Context +} + +func NewSearchState() *SearchState { + return &SearchState{} +} + +func (self *SearchState) SearchType() SearchType { + switch self.Context.(type) { + case IFilterableContext: + return SearchTypeFilter + case ISearchableContext: + return SearchTypeSearch + default: + return SearchTypeNone + } +} diff --git a/pkg/gui/views.go b/pkg/gui/views.go index 6b97f17b7..15bd3c867 100644 --- a/pkg/gui/views.go +++ b/pkg/gui/views.go @@ -91,10 +91,16 @@ func (gui *Gui) createAllViews() error { gui.Views.Options.Frame = false gui.Views.SearchPrefix.BgColor = gocui.ColorDefault - gui.Views.SearchPrefix.FgColor = gocui.ColorGreen + gui.Views.SearchPrefix.FgColor = gocui.ColorCyan gui.Views.SearchPrefix.Frame = false gui.c.SetViewContent(gui.Views.SearchPrefix, gui.Tr.SearchPrefix) + gui.Views.Search.BgColor = gocui.ColorDefault + gui.Views.Search.FgColor = gocui.ColorCyan + gui.Views.Search.Editable = true + gui.Views.Search.Frame = false + gui.Views.Search.Editor = gocui.EditorFunc(gui.searchEditor) + gui.Views.Stash.Title = gui.c.Tr.StashTitle gui.Views.Commits.Title = gui.c.Tr.CommitsTitle @@ -141,11 +147,6 @@ func (gui *Gui) createAllViews() error { gui.Views.Status.Title = gui.c.Tr.StatusTitle - 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 diff --git a/pkg/i18n/dutch.go b/pkg/i18n/dutch.go index 6f17bb7c6..63059ee4d 100644 --- a/pkg/i18n/dutch.go +++ b/pkg/i18n/dutch.go @@ -35,7 +35,7 @@ func dutchTranslationSet() TranslationSet { Scroll: "Scroll", FilterStagedFiles: "Show only staged files", FilterUnstagedFiles: "Show only unstaged files", - ResetCommitFilterState: "Reset commit file state filter", + ResetFilter: "Reset commit file state filter", MergeConflictsTitle: "Merge conflicten", Checkout: "Uitchecken", PullWait: "Pullen...", diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index b803df360..f916c3c4d 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -54,7 +54,7 @@ type TranslationSet struct { FileFilter string FilterStagedFiles string FilterUnstagedFiles string - ResetCommitFilterState string + ResetFilter string MergeConflictsTitle string Checkout string NoChangedFiles string @@ -371,6 +371,7 @@ type TranslationSet struct { NextScreenMode string PrevScreenMode string StartSearch string + StartFilter string Panel string Keybindings string KeybindingsLegend string @@ -536,7 +537,9 @@ type TranslationSet struct { MatchesFor string SearchKeybindings string SearchPrefix string + FilterPrefix string ExitSearchMode string + ExitTextFilterMode string Actions Actions Bisect Bisect } @@ -741,10 +744,10 @@ func EnglishTranslationSet() TranslationSet { Scroll: "Scroll", MergeConflictsTitle: "Merge conflicts", Checkout: "Checkout", - FileFilter: "Filter files (staged/unstaged)", + FileFilter: "Filter files by status", FilterStagedFiles: "Show only staged files", FilterUnstagedFiles: "Show only unstaged files", - ResetCommitFilterState: "Reset filter", + ResetFilter: "Reset filter", NoChangedFiles: "No changed files", PullWait: "Pulling...", PushWait: "Pushing...", @@ -1054,50 +1057,52 @@ func EnglishTranslationSet() TranslationSet { GitFlowOptions: "Show git-flow options", 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?", - ExcludeTracked: "Exclude tracked file", - ExcludeTrackedPrompt: "Are you sure you want to exclude a tracked file?", - ViewResetToUpstreamOptions: "View upstream reset options", - NextScreenMode: "Next screen mode (normal/half/fullscreen)", - PrevScreenMode: "Prev screen mode", - StartSearch: "Start search", - Panel: "Panel", - KeybindingsLegend: "Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b", - RenameBranch: "Rename branch", - SetUnsetUpstream: "Set/Unset upstream", - NewBranchNamePrompt: "Enter new branch name for branch", - RenameBranchWarning: "This branch is tracking a remote. This action will only rename the local branch name, not the name of the remote branch. Continue?", - OpenMenu: "Open menu", - ResetCherryPick: "Reset cherry-picked (copied) commits selection", - NextTab: "Next tab", - PrevTab: "Previous tab", - CantUndoWhileRebasing: "Can't undo while rebasing", - CantRedoWhileRebasing: "Can't redo while rebasing", - 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: "Confirmation panel", - PrevPage: "Previous page", - NextPage: "Next page", - GotoTop: "Scroll to top", - GotoBottom: "Scroll to bottom", - FilteringBy: "Filtering by", - ResetInParentheses: "(Reset)", - OpenFilteringMenu: "View filter-by-path options", - FilterBy: "Filter by", - ExitFilterMode: "Stop filtering by path", - FilterPathOption: "Enter path to filter by", - EnterFileName: "Enter path:", - FilteringMenuTitle: "Filtering", - MustExitFilterModeTitle: "Command not available", - MustExitFilterModePrompt: "Command not available in filtered mode. Exit filtered mode?", - Diff: "Diff", - EnterRefToDiff: "Enter ref to diff", - EnterRefName: "Enter ref:", - ExitDiffMode: "Exit diff mode", - DiffingMenuTitle: "Diffing", - SwapDiff: "Reverse diff direction", - OpenDiffingMenu: "Open diff menu", + + IgnoreTracked: "Ignore tracked file", + IgnoreTrackedPrompt: "Are you sure you want to ignore a tracked file?", + ExcludeTracked: "Exclude tracked file", + ExcludeTrackedPrompt: "Are you sure you want to exclude a tracked file?", + ViewResetToUpstreamOptions: "View upstream reset options", + NextScreenMode: "Next screen mode (normal/half/fullscreen)", + PrevScreenMode: "Prev screen mode", + StartSearch: "Search the current view by text", + StartFilter: "Filter the current view by text", + Panel: "Panel", + KeybindingsLegend: "Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b", + RenameBranch: "Rename branch", + SetUnsetUpstream: "Set/Unset upstream", + NewBranchNamePrompt: "Enter new branch name for branch", + RenameBranchWarning: "This branch is tracking a remote. This action will only rename the local branch name, not the name of the remote branch. Continue?", + OpenMenu: "Open menu", + ResetCherryPick: "Reset cherry-picked (copied) commits selection", + NextTab: "Next tab", + PrevTab: "Previous tab", + CantUndoWhileRebasing: "Can't undo while rebasing", + CantRedoWhileRebasing: "Can't redo while rebasing", + 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: "Confirmation panel", + PrevPage: "Previous page", + NextPage: "Next page", + GotoTop: "Scroll to top", + GotoBottom: "Scroll to bottom", + FilteringBy: "Filtering by", + ResetInParentheses: "(Reset)", + OpenFilteringMenu: "View filter-by-path options", + FilterBy: "Filter by", + ExitFilterMode: "Stop filtering by path", + FilterPathOption: "Enter path to filter by", + EnterFileName: "Enter path:", + FilteringMenuTitle: "Filtering", + MustExitFilterModeTitle: "Command not available", + MustExitFilterModePrompt: "Command not available in filter-by-path mode. Exit filter-by-path mode?", + Diff: "Diff", + EnterRefToDiff: "Enter ref to diff", + EnterRefName: "Enter ref:", + ExitDiffMode: "Exit diff mode", + DiffingMenuTitle: "Diffing", + SwapDiff: "Reverse diff direction", + OpenDiffingMenu: "Open diff menu", // 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 OpenExtrasMenu: "Open command log menu", ShowingGitDiff: "Showing output for:", @@ -1223,9 +1228,11 @@ func EnglishTranslationSet() TranslationSet { CopyPatchToClipboard: "Copy patch to clipboard", NoMatchesFor: "No matches for '%s' %s", ExitSearchMode: "%s: Exit search mode", + ExitTextFilterMode: "%s: Exit filter mode", MatchesFor: "matches for '%s' (%d of %d) %s", // lowercase because it's after other text SearchKeybindings: "%s: Next match, %s: Previous match, %s: Exit search mode", SearchPrefix: "Search: ", + FilterPrefix: "Filter: ", Actions: Actions{ // TODO: combine this with the original keybinding descriptions (those are all in lowercase atm) CheckoutCommit: "Checkout commit", diff --git a/pkg/i18n/japanese.go b/pkg/i18n/japanese.go index 0c4e8aa13..a85187580 100644 --- a/pkg/i18n/japanese.go +++ b/pkg/i18n/japanese.go @@ -61,7 +61,7 @@ func japaneseTranslationSet() TranslationSet { FileFilter: "ファイルをフィルタ (ステージ/アンステージ)", FilterStagedFiles: "ステージされたファイルのみを表示", FilterUnstagedFiles: "ステージされていないファイルのみを表示", - ResetCommitFilterState: "フィルタをリセット", + ResetFilter: "フィルタをリセット", // NoChangedFiles: "No changed files", PullWait: "Pull中...", PushWait: "Push中...", diff --git a/pkg/i18n/korean.go b/pkg/i18n/korean.go index 4eba85cc8..7b2c9d20d 100644 --- a/pkg/i18n/korean.go +++ b/pkg/i18n/korean.go @@ -60,7 +60,7 @@ func koreanTranslationSet() TranslationSet { FileFilter: "파일을 필터하기 (Staged/unstaged)", FilterStagedFiles: "Staged된 파일만 표시", FilterUnstagedFiles: "Stage되지 않은 파일만 표시", - ResetCommitFilterState: "필터 리셋", + ResetFilter: "필터 리셋", NoChangedFiles: "변경된 파일이 없습니다.", PullWait: "업데이트 중...", PushWait: "푸시 중...", diff --git a/pkg/i18n/polish.go b/pkg/i18n/polish.go index 5ac6b8a3a..a2ff1bb12 100644 --- a/pkg/i18n/polish.go +++ b/pkg/i18n/polish.go @@ -31,7 +31,7 @@ func polishTranslationSet() TranslationSet { Scroll: "Przewiń", FilterStagedFiles: "Pokaż tylko pliki w poczekalni", FilterUnstagedFiles: "Pokaż tylko pliki poza poczekalnią", - ResetCommitFilterState: "Resetuj filtr commitów", + ResetFilter: "Resetuj filtr commitów", Checkout: "Przełącz", NoChangedFiles: "Brak zmienionych plików", PullWait: "Pobieranie zmian...", diff --git a/pkg/i18n/russian.go b/pkg/i18n/russian.go index 6d03bce94..d0881fb33 100644 --- a/pkg/i18n/russian.go +++ b/pkg/i18n/russian.go @@ -79,7 +79,7 @@ func RussianTranslationSet() TranslationSet { FileFilter: "Фильтровать файлы (проиндексированные/непроиндексированные)", FilterStagedFiles: "Показывать только проиндексированные файлы", FilterUnstagedFiles: "Показывать только непроиндексированные файлы", - ResetCommitFilterState: "Сбросить фильтр", + ResetFilter: "Сбросить фильтр", NoChangedFiles: "Нет изменённых файлов", PullWait: "Получение и слияние изменении...", PushWait: "Отправка изменении...", diff --git a/pkg/i18n/traditional_chinese.go b/pkg/i18n/traditional_chinese.go index b83f344f8..5f10fd10b 100644 --- a/pkg/i18n/traditional_chinese.go +++ b/pkg/i18n/traditional_chinese.go @@ -112,7 +112,7 @@ func traditionalChineseTranslationSet() TranslationSet { FileFilter: "篩選檔案 (預存/未預存)", FilterStagedFiles: "僅顯示預存的檔案", FilterUnstagedFiles: "僅顯示未預存的檔案", - ResetCommitFilterState: "重設篩選", + ResetFilter: "重設篩選", NoChangedFiles: "沒有變更的檔案", PullWait: "拉取...", PushWait: "推送...", diff --git a/pkg/integration/components/int_matcher.go b/pkg/integration/components/int_matcher.go index c80a60c85..4cfd0f958 100644 --- a/pkg/integration/components/int_matcher.go +++ b/pkg/integration/components/int_matcher.go @@ -10,9 +10,9 @@ type IntMatcher struct { func (self *IntMatcher) EqualsInt(target int) *IntMatcher { self.appendRule(matcherRule[int]{ - name: fmt.Sprintf("equals '%d'", target), + name: fmt.Sprintf("equals %d", target), testFn: func(value int) (bool, string) { - return value == target, fmt.Sprintf("Expected '%d' to equal '%d'", value, target) + return value == target, fmt.Sprintf("Expected %d to equal %d", value, target) }, }) @@ -21,9 +21,9 @@ func (self *IntMatcher) EqualsInt(target int) *IntMatcher { func (self *IntMatcher) GreaterThan(target int) *IntMatcher { self.appendRule(matcherRule[int]{ - name: fmt.Sprintf("greater than '%d'", target), + name: fmt.Sprintf("greater than %d", target), testFn: func(value int) (bool, string) { - return value > target, fmt.Sprintf("Expected '%d' to greater than '%d'", value, target) + return value > target, fmt.Sprintf("Expected %d to greater than %d", value, target) }, }) @@ -32,9 +32,9 @@ func (self *IntMatcher) GreaterThan(target int) *IntMatcher { func (self *IntMatcher) LessThan(target int) *IntMatcher { self.appendRule(matcherRule[int]{ - name: fmt.Sprintf("less than '%d'", target), + name: fmt.Sprintf("less than %d", target), testFn: func(value int) (bool, string) { - return value < target, fmt.Sprintf("Expected '%d' to less than '%d'", value, target) + return value < target, fmt.Sprintf("Expected %d to less than %d", value, target) }, }) diff --git a/pkg/integration/components/menu_driver.go b/pkg/integration/components/menu_driver.go index ac620f5a4..4e0b0f6da 100644 --- a/pkg/integration/components/menu_driver.go +++ b/pkg/integration/components/menu_driver.go @@ -48,6 +48,18 @@ func (self *MenuDriver) TopLines(matchers ...*TextMatcher) *MenuDriver { return self } +func (self *MenuDriver) Filter(text string) *MenuDriver { + self.getViewDriver().FilterOrSearch(text) + + return self +} + +func (self *MenuDriver) LineCount(matcher *IntMatcher) *MenuDriver { + self.getViewDriver().LineCount(matcher) + + return self +} + func (self *MenuDriver) checkNecessaryChecksCompleted() { if !self.hasCheckedTitle { self.t.Fail("You must check the title of a menu popup by calling Title() before calling Confirm()/Cancel().") diff --git a/pkg/integration/components/view_driver.go b/pkg/integration/components/view_driver.go index db7e76134..2c4a23572 100644 --- a/pkg/integration/components/view_driver.go +++ b/pkg/integration/components/view_driver.go @@ -66,7 +66,7 @@ func (self *ViewDriver) Title(expected *TextMatcher) *ViewDriver { // If you only care about a subset of lines, use the ContainsLines method instead. func (self *ViewDriver) Lines(matchers ...*TextMatcher) *ViewDriver { self.validateMatchersPassed(matchers) - self.LineCount(len(matchers)) + self.LineCount(EqualsInt(len(matchers))) return self.assertLines(0, matchers...) } @@ -470,33 +470,60 @@ func (self *ViewDriver) IsEmpty() *ViewDriver { return self } -func (self *ViewDriver) LineCount(expectedCount int) *ViewDriver { - if expectedCount == 0 { - return self.IsEmpty() - } - +func (self *ViewDriver) LineCount(matcher *IntMatcher) *ViewDriver { view := self.getView() self.t.assertWithRetries(func() (bool, string) { - lines := view.BufferLines() - return len(lines) == expectedCount, fmt.Sprintf("unexpected number of lines in view '%s'. Expected %d, got %d", view.Name(), expectedCount, len(lines)) + lineCount := self.getLineCount() + ok, _ := matcher.test(lineCount) + return ok, fmt.Sprintf("unexpected number of lines in view '%s'. Expected %s, got %d", view.Name(), matcher.name(), lineCount) }) + return self +} + +func (self *ViewDriver) getLineCount() int { + // can't rely entirely on view.BufferLines because it returns 1 even if there's nothing in the view + if strings.TrimSpace(self.getView().Buffer()) == "" { + return 0 + } + + view := self.getView() + return len(view.BufferLines()) +} + +func (self *ViewDriver) IsVisible() *ViewDriver { self.t.assertWithRetries(func() (bool, string) { - lines := view.BufferLines() - - // if the view has a single blank line (often the case) we want to treat that as having no lines - if len(lines) == 1 && expectedCount == 1 { - actual := strings.TrimSpace(view.Buffer()) - return actual != "", fmt.Sprintf("unexpected number of lines in view '%s'. Expected 1, got 0", view.Name()) - } - - return len(lines) == expectedCount, fmt.Sprintf("unexpected number of lines in view '%s'. Expected %d, got %d", view.Name(), expectedCount, len(lines)) + return self.getView().Visible, fmt.Sprintf("%s: Expected view to be visible, but it was not", self.context) }) return self } +func (self *ViewDriver) IsInvisible() *ViewDriver { + self.t.assertWithRetries(func() (bool, string) { + return !self.getView().Visible, fmt.Sprintf("%s: Expected view to be visible, but it was not", self.context) + }) + + return self +} + +// will filter or search depending on whether the view supports filtering/searching +func (self *ViewDriver) FilterOrSearch(text string) *ViewDriver { + self.IsFocused() + + self.Press(self.t.keys.Universal.StartSearch). + Tap(func() { + self.t.ExpectSearch(). + Type(text). + Confirm() + + self.t.Views().Search().IsVisible().Content(Contains(fmt.Sprintf("matches for '%s'", text))) + }) + + return self +} + // for when you want to make some assertion unrelated to the current view // without breaking the method chain func (self *ViewDriver) Tap(f func()) *ViewDriver { diff --git a/pkg/integration/tests/commit/search.go b/pkg/integration/tests/commit/search.go index 1d9390dc7..c0a9dfd0b 100644 --- a/pkg/integration/tests/commit/search.go +++ b/pkg/integration/tests/commit/search.go @@ -42,6 +42,7 @@ var Search = NewIntegrationTest(NewIntegrationTestArgs{ Press(keys.Universal.StartSearch). Tap(func() { t.ExpectSearch(). + Clear(). Type("o"). Confirm() diff --git a/pkg/integration/tests/file/discard_all_dir_changes.go b/pkg/integration/tests/file/discard_all_dir_changes.go new file mode 100644 index 000000000..1032a180a --- /dev/null +++ b/pkg/integration/tests/file/discard_all_dir_changes.go @@ -0,0 +1,117 @@ +package file + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var DiscardAllDirChanges = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Discarding all changes in a directory", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) { + }, + SetupRepo: func(shell *Shell) { + // typically we would use more bespoke shell methods here, but I struggled to find a way to do that, + // and this is copied over from a legacy integration test which did everything in a big shell script + // so I'm just copying it across. + + shell.CreateDir("dir") + + // common stuff + shell.RunShellCommand(`echo test > dir/both-deleted.txt`) + shell.RunShellCommand(`git checkout -b conflict && git add dir/both-deleted.txt`) + shell.RunShellCommand(`echo bothmodded > dir/both-modded.txt && git add dir/both-modded.txt`) + shell.RunShellCommand(`echo haha > dir/deleted-them.txt && git add dir/deleted-them.txt`) + shell.RunShellCommand(`echo haha2 > dir/deleted-us.txt && git add dir/deleted-us.txt`) + shell.RunShellCommand(`echo mod > dir/modded.txt && git add dir/modded.txt`) + shell.RunShellCommand(`echo mod > dir/modded-staged.txt && git add dir/modded-staged.txt`) + shell.RunShellCommand(`echo del > dir/deleted.txt && git add dir/deleted.txt`) + shell.RunShellCommand(`echo del > dir/deleted-staged.txt && git add dir/deleted-staged.txt`) + shell.RunShellCommand(`echo change-delete > dir/change-delete.txt && git add dir/change-delete.txt`) + shell.RunShellCommand(`echo delete-change > dir/delete-change.txt && git add dir/delete-change.txt`) + shell.RunShellCommand(`echo double-modded > dir/double-modded.txt && git add dir/double-modded.txt`) + shell.RunShellCommand(`echo "renamed\nhaha" > dir/renamed.txt && git add dir/renamed.txt`) + shell.RunShellCommand(`git commit -m one`) + + // stuff on other branch + shell.RunShellCommand(`git branch conflict_second && git mv dir/both-deleted.txt dir/added-them-changed-us.txt`) + shell.RunShellCommand(`git commit -m "dir/both-deleted.txt renamed in dir/added-them-changed-us.txt"`) + shell.RunShellCommand(`echo blah > dir/both-added.txt && git add dir/both-added.txt`) + shell.RunShellCommand(`echo mod1 > dir/both-modded.txt && git add dir/both-modded.txt`) + shell.RunShellCommand(`rm dir/deleted-them.txt && git add dir/deleted-them.txt`) + shell.RunShellCommand(`echo modded > dir/deleted-us.txt && git add dir/deleted-us.txt`) + shell.RunShellCommand(`git commit -m "two"`) + + // stuff on our branch + shell.RunShellCommand(`git checkout conflict_second`) + shell.RunShellCommand(`git mv dir/both-deleted.txt dir/changed-them-added-us.txt`) + shell.RunShellCommand(`git commit -m "both-deleted.txt renamed in dir/changed-them-added-us.txt"`) + shell.RunShellCommand(`echo mod2 > dir/both-modded.txt && git add dir/both-modded.txt`) + shell.RunShellCommand(`echo blah2 > dir/both-added.txt && git add dir/both-added.txt`) + shell.RunShellCommand(`echo modded > dir/deleted-them.txt && git add dir/deleted-them.txt`) + shell.RunShellCommand(`rm dir/deleted-us.txt && git add dir/deleted-us.txt`) + shell.RunShellCommand(`git commit -m "three"`) + shell.RunShellCommand(`git reset --hard conflict_second`) + shell.RunCommandExpectError([]string{"git", "merge", "conflict"}) + + shell.RunShellCommand(`echo "new" > dir/new.txt`) + shell.RunShellCommand(`echo "new staged" > dir/new-staged.txt && git add dir/new-staged.txt`) + shell.RunShellCommand(`echo mod2 > dir/modded.txt`) + shell.RunShellCommand(`echo mod2 > dir/modded-staged.txt && git add dir/modded-staged.txt`) + shell.RunShellCommand(`rm dir/deleted.txt`) + shell.RunShellCommand(`rm dir/deleted-staged.txt && git add dir/deleted-staged.txt`) + shell.RunShellCommand(`echo change-delete2 > dir/change-delete.txt && git add dir/change-delete.txt`) + shell.RunShellCommand(`rm dir/change-delete.txt`) + shell.RunShellCommand(`rm dir/delete-change.txt && git add dir/delete-change.txt`) + shell.RunShellCommand(`echo "changed" > dir/delete-change.txt`) + shell.RunShellCommand(`echo "change1" > dir/double-modded.txt && git add dir/double-modded.txt`) + shell.RunShellCommand(`echo "change2" > dir/double-modded.txt`) + shell.RunShellCommand(`echo before > dir/added-changed.txt && git add dir/added-changed.txt`) + shell.RunShellCommand(`echo after > dir/added-changed.txt`) + shell.RunShellCommand(`rm dir/renamed.txt && git add dir/renamed.txt`) + shell.RunShellCommand(`echo "renamed\nhaha" > dir/renamed2.txt && git add dir/renamed2.txt`) + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Files(). + IsFocused(). + Lines( + Contains("dir").IsSelected(), + Contains("UA").Contains("added-them-changed-us.txt"), + Contains("AA").Contains("both-added.txt"), + Contains("DD").Contains("both-deleted.txt"), + Contains("UU").Contains("both-modded.txt"), + Contains("AU").Contains("changed-them-added-us.txt"), + Contains("UD").Contains("deleted-them.txt"), + Contains("DU").Contains("deleted-us.txt"), + ). + Press(keys.Universal.Remove). + Tap(func() { + t.ExpectPopup().Menu(). + Title(Equals("dir")). + Select(Contains("Discard all changes")). + Confirm() + }). + Tap(func() { + t.Common().ContinueOnConflictsResolved() + }). + Lines( + Contains("dir").IsSelected(), + Contains(" M").Contains("added-changed.txt"), + Contains(" D").Contains("change-delete.txt"), + Contains("??").Contains("delete-change.txt"), + Contains(" D").Contains("deleted.txt"), + Contains(" M").Contains("double-modded.txt"), + Contains(" M").Contains("modded.txt"), + Contains("??").Contains("new.txt"), + ). + Press(keys.Universal.Remove). + Tap(func() { + t.ExpectPopup().Menu(). + Title(Equals("dir")). + Select(Contains("Discard all changes")). + Confirm() + }). + IsEmpty() + }, +}) diff --git a/pkg/integration/tests/file/discard_unstaged_dir_changes.go b/pkg/integration/tests/file/discard_unstaged_dir_changes.go new file mode 100644 index 000000000..89e53cab5 --- /dev/null +++ b/pkg/integration/tests/file/discard_unstaged_dir_changes.go @@ -0,0 +1,56 @@ +package file + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var DiscardUnstagedDirChanges = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Discarding unstaged changes in a directory", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) { + }, + SetupRepo: func(shell *Shell) { + shell.CreateDir("dir") + shell.CreateFileAndAdd("dir/file-one", "original content\n") + + shell.Commit("first commit") + + shell.UpdateFileAndAdd("dir/file-one", "original content\nnew content\n") + shell.UpdateFile("dir/file-one", "original content\nnew content\neven newer content\n") + + shell.CreateDir("dir/subdir") + shell.CreateFile("dir/subdir/unstaged-file-one", "unstaged file") + shell.CreateFile("dir/unstaged-file-two", "unstaged file") + + shell.CreateFile("unstaged-file-three", "unstaged file") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Files(). + IsFocused(). + Lines( + Contains("dir").IsSelected(), + Contains("subdir"), + Contains("??").Contains("unstaged-file-one"), + Contains("MM").Contains("file-one"), + Contains("??").Contains("unstaged-file-two"), + Contains("??").Contains("unstaged-file-three"), + ). + Press(keys.Universal.Remove). + Tap(func() { + t.ExpectPopup().Menu(). + Title(Equals("dir")). + Select(Contains("Discard unstaged changes")). + Confirm() + }). + Lines( + Contains("dir").IsSelected(), + Contains("M ").Contains("file-one"), + // this guy remains untouched because it wasn't inside the 'dir' directory + Contains("??").Contains("unstaged-file-three"), + ) + + t.FileSystem().FileContent("dir/file-one", Equals("original content\nnew content\n")) + }, +}) diff --git a/pkg/integration/tests/file/discard_unstaged_file_changes.go b/pkg/integration/tests/file/discard_unstaged_file_changes.go new file mode 100644 index 000000000..caa5ef4ab --- /dev/null +++ b/pkg/integration/tests/file/discard_unstaged_file_changes.go @@ -0,0 +1,41 @@ +package file + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var DiscardUnstagedFileChanges = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Discarding unstaged changes in a file", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) { + }, + SetupRepo: func(shell *Shell) { + shell.CreateFileAndAdd("file-one", "original content\n") + + shell.Commit("first commit") + + shell.UpdateFileAndAdd("file-one", "original content\nnew content\n") + shell.UpdateFile("file-one", "original content\nnew content\neven newer content\n") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Files(). + IsFocused(). + Lines( + Contains("MM").Contains("file-one").IsSelected(), + ). + Press(keys.Universal.Remove). + Tap(func() { + t.ExpectPopup().Menu(). + Title(Equals("file-one")). + Select(Contains("Discard unstaged changes")). + Confirm() + }). + Lines( + Contains("M ").Contains("file-one").IsSelected(), + ) + + t.FileSystem().FileContent("file-one", Equals("original content\nnew content\n")) + }, +}) diff --git a/pkg/integration/tests/filter_and_search/filter_commit_files.go b/pkg/integration/tests/filter_and_search/filter_commit_files.go new file mode 100644 index 000000000..953eaf34d --- /dev/null +++ b/pkg/integration/tests/filter_and_search/filter_commit_files.go @@ -0,0 +1,84 @@ +package filter_and_search + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var FilterCommitFiles = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Basic commit file filtering by text", + ExtraCmdArgs: []string{}, + Skip: true, // skipping until we have implemented file view filtering + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell.CreateDir("folder1") + shell.CreateFileAndAdd("folder1/apple-grape", "apple-grape") + shell.CreateFileAndAdd("folder1/apple-orange", "apple-orange") + shell.CreateFileAndAdd("folder1/grape-orange", "grape-orange") + shell.Commit("first commit") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Commits(). + Focus(). + Lines( + Contains(`first commit`).IsSelected(), + ). + Press(keys.Universal.Confirm) + + t.Views().CommitFiles(). + IsFocused(). + Lines( + Contains(`folder1`).IsSelected(), + Contains(`apple-grape`), + Contains(`apple-orange`), + Contains(`grape-orange`), + ). + Press(keys.Files.ToggleTreeView). + Lines( + Contains(`folder1/apple-grape`).IsSelected(), + Contains(`folder1/apple-orange`), + Contains(`folder1/grape-orange`), + ). + FilterOrSearch("apple"). + Lines( + Contains(`folder1/apple-grape`).IsSelected(), + Contains(`folder1/apple-orange`), + ). + Press(keys.Files.ToggleTreeView). + // filter still applies when we toggle tree view + Lines( + Contains(`folder1`), + Contains(`apple-grape`).IsSelected(), + Contains(`apple-orange`), + ). + Press(keys.Files.ToggleTreeView). + Lines( + Contains(`folder1/apple-grape`).IsSelected(), + Contains(`folder1/apple-orange`), + ). + NavigateToLine(Contains(`folder1/apple-orange`)). + Press(keys.Universal.Return). + Lines( + Contains(`folder1/apple-grape`), + // selection is retained after escaping filter mode + Contains(`folder1/apple-orange`).IsSelected(), + Contains(`folder1/grape-orange`), + ). + Tap(func() { + t.Views().Search().IsInvisible() + }). + Press(keys.Files.ToggleTreeView). + Lines( + Contains(`folder1`), + Contains(`apple-grape`), + Contains(`apple-orange`).IsSelected(), + Contains(`grape-orange`), + ). + FilterOrSearch("folder1/grape"). + Lines( + // first item is always selected after filtering + Contains(`folder1`).IsSelected(), + Contains(`grape-orange`), + ) + }, +}) diff --git a/pkg/integration/tests/filter_and_search/filter_files.go b/pkg/integration/tests/filter_and_search/filter_files.go new file mode 100644 index 000000000..6eae90c18 --- /dev/null +++ b/pkg/integration/tests/filter_and_search/filter_files.go @@ -0,0 +1,76 @@ +package filter_and_search + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var FilterFiles = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Basic file filtering by text", + ExtraCmdArgs: []string{}, + Skip: true, // Skipping until we have implemented file view filtering + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell.CreateDir("folder1") + shell.CreateFile("folder1/apple-grape", "apple-grape") + shell.CreateFile("folder1/apple-orange", "apple-orange") + shell.CreateFile("folder1/grape-orange", "grape-orange") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Files(). + Focus(). + Lines( + Contains(`folder1`).IsSelected(), + Contains(`apple-grape`), + Contains(`apple-orange`), + Contains(`grape-orange`), + ). + Press(keys.Files.ToggleTreeView). + Lines( + Contains(`folder1/apple-grape`).IsSelected(), + Contains(`folder1/apple-orange`), + Contains(`folder1/grape-orange`), + ). + FilterOrSearch("apple"). + Lines( + Contains(`folder1/apple-grape`).IsSelected(), + Contains(`folder1/apple-orange`), + ). + Press(keys.Files.ToggleTreeView). + // filter still applies when we toggle tree view + Lines( + Contains(`folder1`), + Contains(`apple-grape`).IsSelected(), + Contains(`apple-orange`), + ). + Press(keys.Files.ToggleTreeView). + Lines( + Contains(`folder1/apple-grape`).IsSelected(), + Contains(`folder1/apple-orange`), + ). + NavigateToLine(Contains(`folder1/apple-orange`)). + Press(keys.Universal.Return). + Lines( + Contains(`folder1/apple-grape`), + // selection is retained after escaping filter mode + Contains(`folder1/apple-orange`).IsSelected(), + Contains(`folder1/grape-orange`), + ). + Tap(func() { + t.Views().Search().IsInvisible() + }). + Press(keys.Files.ToggleTreeView). + Lines( + Contains(`folder1`), + Contains(`apple-grape`), + Contains(`apple-orange`).IsSelected(), + Contains(`grape-orange`), + ). + FilterOrSearch("folder1/grape"). + Lines( + // first item is always selected after filtering + Contains(`folder1`).IsSelected(), + Contains(`grape-orange`), + ) + }, +}) diff --git a/pkg/integration/tests/filter_and_search/filter_menu.go b/pkg/integration/tests/filter_and_search/filter_menu.go new file mode 100644 index 000000000..5dc15b663 --- /dev/null +++ b/pkg/integration/tests/filter_and_search/filter_menu.go @@ -0,0 +1,48 @@ +package filter_and_search + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var FilterMenu = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Filtering the keybindings menu", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell.CreateFile("myfile", "myfile") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Files(). + IsFocused(). + Lines( + Contains(`??`).Contains(`myfile`).IsSelected(), + ). + Press(keys.Universal.OptionMenu). + Tap(func() { + t.ExpectPopup().Menu(). + Title(Equals("Keybindings")). + Filter("Toggle staged"). + Lines( + // menu has filtered down to the one item that matches the filter + Contains(`Toggle staged`).IsSelected(), + ). + Confirm() + }) + + t.Views().Files(). + IsFocused(). + Lines( + // file has been staged + Contains(`A `).Contains(`myfile`).IsSelected(), + ). + // Upon opening the menu again, the filter should have been reset + Press(keys.Universal.OptionMenu). + Tap(func() { + t.ExpectPopup().Menu(). + Title(Equals("Keybindings")). + LineCount(GreaterThan(1)) + }) + }, +}) diff --git a/pkg/integration/tests/filter_and_search/filter_remote_branches.go b/pkg/integration/tests/filter_and_search/filter_remote_branches.go new file mode 100644 index 000000000..11cfea30b --- /dev/null +++ b/pkg/integration/tests/filter_and_search/filter_remote_branches.go @@ -0,0 +1,59 @@ +package filter_and_search + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var FilterRemoteBranches = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Filtering remote branches", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell.NewBranch("branch-apple") + shell.EmptyCommit("commit-one") + shell.NewBranch("branch-grape") + shell.NewBranch("branch-orange") + + shell.CloneIntoRemote("origin") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Remotes(). + Focus(). + Lines( + Contains(`origin`).IsSelected(), + ). + PressEnter() + + t.Views().RemoteBranches(). + IsFocused(). + Lines( + Contains(`branch-apple`).IsSelected(), + Contains(`branch-grape`), + Contains(`branch-orange`), + ). + FilterOrSearch("grape"). + Lines( + Contains(`branch-grape`).IsSelected(), + ). + // cancel the filter + PressEscape(). + Tap(func() { + t.Views().Search().IsInvisible() + }). + Lines( + Contains(`branch-apple`), + Contains(`branch-grape`).IsSelected(), + Contains(`branch-orange`), + ). + // return to remotes view + PressEscape() + + t.Views().Remotes(). + IsFocused(). + Lines( + Contains(`origin`).IsSelected(), + ) + }, +}) diff --git a/pkg/integration/tests/filter_and_search/nested_filter.go b/pkg/integration/tests/filter_and_search/nested_filter.go new file mode 100644 index 000000000..6444ad523 --- /dev/null +++ b/pkg/integration/tests/filter_and_search/nested_filter.go @@ -0,0 +1,151 @@ +package filter_and_search + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var NestedFilter = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Filter in the several nested panels and verify the filters are preserved as you escape back to the surface", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + // need to create some branches, each with their own commits + shell.NewBranch("branch-gold") + shell.CreateFileAndAdd("apple", "apple") + shell.CreateFileAndAdd("orange", "orange") + shell.CreateFileAndAdd("grape", "grape") + shell.Commit("commit-knife") + + shell.NewBranch("branch-silver") + shell.UpdateFileAndAdd("apple", "apple-2") + shell.UpdateFileAndAdd("orange", "orange-2") + shell.UpdateFileAndAdd("grape", "grape-2") + shell.Commit("commit-spoon") + + shell.NewBranch("branch-bronze") + shell.UpdateFileAndAdd("apple", "apple-3") + shell.UpdateFileAndAdd("orange", "orange-3") + shell.UpdateFileAndAdd("grape", "grape-3") + shell.Commit("commit-fork") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Branches(). + Focus(). + Lines( + Contains(`branch-bronze`).IsSelected(), + Contains(`branch-silver`), + Contains(`branch-gold`), + ). + FilterOrSearch("sil"). + Lines( + Contains(`branch-silver`).IsSelected(), + ). + PressEnter() + + t.Views().SubCommits(). + IsFocused(). + Lines( + Contains(`commit-spoon`).IsSelected(), + Contains(`commit-knife`), + ). + FilterOrSearch("knife"). + Lines( + // sub-commits view searches, it doesn't filter, so we haven't filtered down the list + Contains(`commit-spoon`), + Contains(`commit-knife`).IsSelected(), + ). + PressEnter() + + t.Views().CommitFiles(). + IsFocused(). + Lines( + Contains(`apple`).IsSelected(), + Contains(`grape`), + Contains(`orange`), + ). + FilterOrSearch("grape"). + Lines( + Contains(`apple`), + Contains(`grape`).IsSelected(), + Contains(`orange`), + ). + PressEnter() + + t.Views().PatchBuilding(). + IsFocused(). + FilterOrSearch("newline"). + SelectedLine(Contains("No newline at end of file")). + PressEscape(). // cancel search + Tap(func() { + t.Views().Search().IsInvisible() + }). + // escape to commit-files view + PressEscape() + + t.Views().CommitFiles(). + IsFocused(). + Lines( + Contains(`apple`), + Contains(`grape`).IsSelected(), + Contains(`orange`), + ). + Tap(func() { + t.Views().Search().IsVisible().Content(Contains("matches for 'grape'")) + }). + // cancel search + PressEscape(). + Tap(func() { + t.Views().Search().IsInvisible() + }). + Lines( + Contains(`apple`), + Contains(`grape`).IsSelected(), + Contains(`orange`), + ). + // escape to sub-commits view + PressEscape() + + t.Views().SubCommits(). + IsFocused(). + Lines( + Contains(`commit-spoon`), + Contains(`commit-knife`).IsSelected(), + ). + Tap(func() { + t.Views().Search().IsVisible().Content(Contains("matches for 'knife'")) + }). + // cancel search + PressEscape(). + Tap(func() { + t.Views().Search().IsInvisible() + }). + Lines( + Contains(`commit-spoon`), + // still selected + Contains(`commit-knife`).IsSelected(), + ). + // escape to branches view + PressEscape() + + t.Views().Branches(). + IsFocused(). + Lines( + Contains(`branch-silver`).IsSelected(), + ). + Tap(func() { + t.Views().Search().IsVisible().Content(Contains("matches for 'sil'")) + }). + // cancel search + PressEscape(). + Tap(func() { + t.Views().Search().IsInvisible() + }). + Lines( + Contains(`branch-bronze`), + Contains(`branch-silver`).IsSelected(), + Contains(`branch-gold`), + ) + }, +}) diff --git a/pkg/integration/tests/filter_and_search/nested_filter_transient.go b/pkg/integration/tests/filter_and_search/nested_filter_transient.go new file mode 100644 index 000000000..bf04406f5 --- /dev/null +++ b/pkg/integration/tests/filter_and_search/nested_filter_transient.go @@ -0,0 +1,106 @@ +package filter_and_search + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +// This one requires some explanation: the sub-commits and diff-file contexts are +// 'transient' in that they are spawned inside a window when you need them, but +// can be relocated elsewhere if you need them somewhere else. So for example if +// I hit enter on a branch I'll see the sub-commits view, but if I then navigate +// to the reflog context and hit enter on a reflog, the sub-commits view is moved +// to the reflog window. This is because we re-use the same view (it's a limitation +// that would be nice to remove in the future). +// Nonetheless, we need to ensure that upon moving the view, the filter is cancelled. + +var NestedFilterTransient = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Filter in a transient panel (sub-commits and diff-files) and ensure filter is cancelled when the panel is moved", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + // need to create some branches, each with their own commits + shell.NewBranch("mybranch") + shell.CreateFileAndAdd("file-one", "file-one") + shell.CreateFileAndAdd("file-two", "file-two") + shell.Commit("commit-one") + shell.EmptyCommit("commit-two") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Branches(). + Focus(). + Lines( + Contains(`mybranch`).IsSelected(), + ). + PressEnter() + + t.Views().SubCommits(). + IsFocused(). + Lines( + Contains(`commit-two`).IsSelected(), + Contains(`commit-one`), + ). + FilterOrSearch("one"). + Lines( + Contains(`commit-two`), + Contains(`commit-one`).IsSelected(), + ) + + t.Views().ReflogCommits(). + Focus(). + SelectedLine(Contains("commit: commit-two")). + PressEnter() + + t.Views().SubCommits(). + IsFocused(). + // the search on the sub-commits context has been cancelled + Lines( + Contains(`commit-two`).IsSelected(), + Contains(`commit-one`), + ). + Tap(func() { + t.Views().Search().IsInvisible() + }). + NavigateToLine(Contains("commit-one")). + PressEnter() + + // Now let's test the commit files context + t.Views().CommitFiles(). + IsFocused(). + Lines( + Contains(`file-one`).IsSelected(), + Contains(`file-two`), + ). + FilterOrSearch("two"). + Lines( + Contains(`file-one`), + Contains(`file-two`).IsSelected(), + ) + + t.Views().Branches(). + Focus(). + SelectedLine(Contains("mybranch")). + PressEnter() + + t.Views().SubCommits(). + IsFocused(). + Lines( + Contains(`commit-two`).IsSelected(), + Contains(`commit-one`), + ). + NavigateToLine(Contains("commit-one")). + PressEnter() + + t.Views().CommitFiles(). + IsFocused(). + // the search on the commit-files context has been cancelled + Lines( + Contains(`file-one`).IsSelected(), + Contains(`file-two`), + ). + Tap(func() { + t.Views().Search().IsInvisible() + }) + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index 99b7f2bdc..55085f989 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -13,6 +13,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/integration/tests/custom_commands" "github.com/jesseduffield/lazygit/pkg/integration/tests/diff" "github.com/jesseduffield/lazygit/pkg/integration/tests/file" + "github.com/jesseduffield/lazygit/pkg/integration/tests/filter_and_search" "github.com/jesseduffield/lazygit/pkg/integration/tests/filter_by_path" "github.com/jesseduffield/lazygit/pkg/integration/tests/interactive_rebase" "github.com/jesseduffield/lazygit/pkg/integration/tests/misc" @@ -87,10 +88,19 @@ var tests = []*components.IntegrationTest{ diff.DiffCommits, diff.IgnoreWhitespace, file.DirWithUntrackedFile, + file.DiscardAllDirChanges, file.DiscardChanges, file.DiscardStagedChanges, + file.DiscardUnstagedDirChanges, + file.DiscardUnstagedFileChanges, file.Gitignore, file.RememberCommitMessageAfterFail, + filter_and_search.FilterCommitFiles, + filter_and_search.FilterFiles, + filter_and_search.FilterMenu, + filter_and_search.FilterRemoteBranches, + filter_and_search.NestedFilter, + filter_and_search.NestedFilterTransient, filter_by_path.CliArg, filter_by_path.SelectFile, filter_by_path.TypeFile, diff --git a/pkg/theme/theme.go b/pkg/theme/theme.go index bb6ab43de..0a1624029 100644 --- a/pkg/theme/theme.go +++ b/pkg/theme/theme.go @@ -19,6 +19,9 @@ var ( // InactiveBorderColor is the border color of the inactive active frames InactiveBorderColor gocui.Attribute + // FilteredActiveBorderColor is the border color of the active frame, when it's being searched/filtered + SearchingActiveBorderColor gocui.Attribute + // GocuiSelectedLineBgColor is the background color for the selected line in gocui GocuiSelectedLineBgColor gocui.Attribute @@ -44,6 +47,7 @@ var ( func UpdateTheme(themeConfig config.ThemeConfig) { ActiveBorderColor = GetGocuiStyle(themeConfig.ActiveBorderColor) InactiveBorderColor = GetGocuiStyle(themeConfig.InactiveBorderColor) + SearchingActiveBorderColor = GetGocuiStyle(themeConfig.SearchingActiveBorderColor) SelectedLineBgColor = GetTextStyle(themeConfig.SelectedLineBgColor, true) SelectedRangeBgColor = GetTextStyle(themeConfig.SelectedRangeBgColor, true) diff --git a/pkg/utils/fuzzy_search.go b/pkg/utils/fuzzy_search.go deleted file mode 100644 index 5fce3dde9..000000000 --- a/pkg/utils/fuzzy_search.go +++ /dev/null @@ -1,21 +0,0 @@ -package utils - -import ( - "sort" - - "github.com/jesseduffield/generics/slices" - "github.com/sahilm/fuzzy" -) - -func FuzzySearch(needle string, haystack []string) []string { - if needle == "" { - return []string{} - } - - matches := fuzzy.Find(needle, haystack) - sort.Sort(matches) - - return slices.Map(matches, func(match fuzzy.Match) string { - return match.Str - }) -} diff --git a/pkg/utils/fuzzy_search_test.go b/pkg/utils/fuzzy_search_test.go deleted file mode 100644 index 808d83772..000000000 --- a/pkg/utils/fuzzy_search_test.go +++ /dev/null @@ -1,53 +0,0 @@ -package utils - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -// TestFuzzySearch is a function. -func TestFuzzySearch(t *testing.T) { - type scenario struct { - needle string - haystack []string - expected []string - } - - scenarios := []scenario{ - { - needle: "", - haystack: []string{"test"}, - expected: []string{}, - }, - { - needle: "test", - haystack: []string{"test"}, - expected: []string{"test"}, - }, - { - needle: "o", - haystack: []string{"a", "o", "e"}, - expected: []string{"o"}, - }, - { - needle: "mybranch", - haystack: []string{"my_branch", "mybranch", "branch", "this is my branch"}, - expected: []string{"mybranch", "my_branch", "this is my branch"}, - }, - { - needle: "test", - haystack: []string{"not a good match", "this 'test' is a good match", "test"}, - expected: []string{"test", "this 'test' is a good match"}, - }, - { - needle: "test", - haystack: []string{"Test"}, - expected: []string{"Test"}, - }, - } - - for _, s := range scenarios { - assert.EqualValues(t, s.expected, FuzzySearch(s.needle, s.haystack)) - } -} diff --git a/pkg/utils/search.go b/pkg/utils/search.go new file mode 100644 index 000000000..14b3d7b3e --- /dev/null +++ b/pkg/utils/search.go @@ -0,0 +1,48 @@ +package utils + +import ( + "sort" + "strings" + + "github.com/jesseduffield/generics/slices" + "github.com/sahilm/fuzzy" +) + +func FuzzySearch(needle string, haystack []string) []string { + if needle == "" { + return []string{} + } + + matches := fuzzy.Find(needle, haystack) + sort.Sort(matches) + + return slices.Map(matches, func(match fuzzy.Match) string { + return match.Str + }) +} + +func CaseAwareContains(haystack, needle string) bool { + // if needle contains an uppercase letter, we'll do a case sensitive search + if ContainsUppercase(needle) { + return strings.Contains(haystack, needle) + } + + return CaseInsensitiveContains(haystack, needle) +} + +func ContainsUppercase(s string) bool { + for _, r := range s { + if r >= 'A' && r <= 'Z' { + return true + } + } + + return false +} + +func CaseInsensitiveContains(haystack, needle string) bool { + return strings.Contains( + strings.ToLower(haystack), + strings.ToLower(needle), + ) +} diff --git a/pkg/utils/search_test.go b/pkg/utils/search_test.go new file mode 100644 index 000000000..79668c0f5 --- /dev/null +++ b/pkg/utils/search_test.go @@ -0,0 +1,80 @@ +package utils + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +// TestFuzzySearch is a function. +func TestFuzzySearch(t *testing.T) { + type scenario struct { + needle string + haystack []string + expected []string + } + + scenarios := []scenario{ + { + needle: "", + haystack: []string{"test"}, + expected: []string{}, + }, + { + needle: "test", + haystack: []string{"test"}, + expected: []string{"test"}, + }, + { + needle: "o", + haystack: []string{"a", "o", "e"}, + expected: []string{"o"}, + }, + { + needle: "mybranch", + haystack: []string{"my_branch", "mybranch", "branch", "this is my branch"}, + expected: []string{"mybranch", "my_branch", "this is my branch"}, + }, + { + needle: "test", + haystack: []string{"not a good match", "this 'test' is a good match", "test"}, + expected: []string{"test", "this 'test' is a good match"}, + }, + { + needle: "test", + haystack: []string{"Test"}, + expected: []string{"Test"}, + }, + } + + for _, s := range scenarios { + assert.EqualValues(t, s.expected, FuzzySearch(s.needle, s.haystack)) + } +} + +func TestCaseInsensitiveContains(t *testing.T) { + testCases := []struct { + haystack string + needle string + expected bool + }{ + {"Hello, World!", "world", true}, // Case-insensitive match + {"Hello, World!", "WORLD", true}, // Case-insensitive match + {"Hello, World!", "orl", true}, // Case-insensitive match + {"Hello, World!", "o, W", true}, // Case-insensitive match + {"Hello, World!", "hello", true}, // Case-insensitive match + {"Hello, World!", "Foo", false}, // No match + {"Hello, World!", "Hello, World!!", false}, // No match + {"Hello, World!", "", true}, // Empty needle matches + {"", "Hello", false}, // Empty haystack doesn't match + {"", "", true}, // Empty strings match + {"", " ", false}, // Empty haystack, non-empty needle + {" ", "", true}, // Non-empty haystack, empty needle + } + + for i, testCase := range testCases { + result := CaseInsensitiveContains(testCase.haystack, testCase.needle) + assert.Equal(t, testCase.expected, result, fmt.Sprintf("Test case %d failed. Expected '%v', got '%v' for '%s' in '%s'", i, testCase.expected, result, testCase.needle, testCase.haystack)) + } +} diff --git a/pkg/utils/slice.go b/pkg/utils/slice.go index aff6ae470..4a47f43b1 100644 --- a/pkg/utils/slice.go +++ b/pkg/utils/slice.go @@ -113,3 +113,14 @@ func MoveElement[T any](slice []T, from int, to int) []T { return newSlice } + +func ValuesAtIndices[T any](slice []T, indices []int) []T { + result := make([]T, len(indices)) + for i, index := range indices { + // gracefully handling the situation where the index is out of bounds + if index < len(slice) { + result[i] = slice[index] + } + } + return result +} diff --git a/vendor/github.com/jesseduffield/gocui/view.go b/vendor/github.com/jesseduffield/gocui/view.go index 6bc1c195c..f4b3ad528 100644 --- a/vendor/github.com/jesseduffield/gocui/view.go +++ b/vendor/github.com/jesseduffield/gocui/view.go @@ -207,6 +207,10 @@ func (v *View) gotoPreviousMatch() error { return v.SelectSearchResult(v.searcher.currentSearchIndex) } +func (v *View) SelectCurrentSearchResult() error { + return v.SelectSearchResult(v.searcher.currentSearchIndex) +} + func (v *View) SelectSearchResult(index int) error { itemCount := len(v.searcher.searchPositions) if itemCount == 0 { diff --git a/vendor/golang.org/x/sys/cpu/endian_little.go b/vendor/golang.org/x/sys/cpu/endian_little.go index fe545966b..55db853ef 100644 --- a/vendor/golang.org/x/sys/cpu/endian_little.go +++ b/vendor/golang.org/x/sys/cpu/endian_little.go @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build 386 || amd64 || amd64p32 || alpha || arm || arm64 || loong64 || mipsle || mips64le || mips64p32le || nios2 || ppc64le || riscv || riscv64 || sh -// +build 386 amd64 amd64p32 alpha arm arm64 loong64 mipsle mips64le mips64p32le nios2 ppc64le riscv riscv64 sh +//go:build 386 || amd64 || amd64p32 || alpha || arm || arm64 || loong64 || mipsle || mips64le || mips64p32le || nios2 || ppc64le || riscv || riscv64 || sh || wasm +// +build 386 amd64 amd64p32 alpha arm arm64 loong64 mipsle mips64le mips64p32le nios2 ppc64le riscv riscv64 sh wasm package cpu diff --git a/vendor/golang.org/x/sys/unix/mkall.sh b/vendor/golang.org/x/sys/unix/mkall.sh index 8e3947c36..e6f31d374 100644 --- a/vendor/golang.org/x/sys/unix/mkall.sh +++ b/vendor/golang.org/x/sys/unix/mkall.sh @@ -50,7 +50,7 @@ if [[ "$GOOS" = "linux" ]]; then # Use the Docker-based build system # Files generated through docker (use $cmd so you can Ctl-C the build or run) $cmd docker build --tag generate:$GOOS $GOOS - $cmd docker run --interactive --tty --volume $(cd -- "$(dirname -- "$0")/.." && /bin/pwd):/build generate:$GOOS + $cmd docker run --interactive --tty --volume $(cd -- "$(dirname -- "$0")/.." && pwd):/build generate:$GOOS exit fi diff --git a/vendor/golang.org/x/sys/unix/mkerrors.sh b/vendor/golang.org/x/sys/unix/mkerrors.sh index be0423e68..315646271 100644 --- a/vendor/golang.org/x/sys/unix/mkerrors.sh +++ b/vendor/golang.org/x/sys/unix/mkerrors.sh @@ -741,7 +741,8 @@ main(void) e = errors[i].num; if(i > 0 && errors[i-1].num == e) continue; - strcpy(buf, strerror(e)); + strncpy(buf, strerror(e), sizeof(buf) - 1); + buf[sizeof(buf) - 1] = '\0'; // lowercase first letter: Bad -> bad, but STREAM -> STREAM. if(A <= buf[0] && buf[0] <= Z && a <= buf[1] && buf[1] <= z) buf[0] += a - A; @@ -760,7 +761,8 @@ main(void) e = signals[i].num; if(i > 0 && signals[i-1].num == e) continue; - strcpy(buf, strsignal(e)); + strncpy(buf, strsignal(e), sizeof(buf) - 1); + buf[sizeof(buf) - 1] = '\0'; // lowercase first letter: Bad -> bad, but STREAM -> STREAM. if(A <= buf[0] && buf[0] <= Z && a <= buf[1] && buf[1] <= z) buf[0] += a - A; diff --git a/vendor/golang.org/x/sys/unix/syscall_linux.go b/vendor/golang.org/x/sys/unix/syscall_linux.go index fbaeb5fff..6de486bef 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux.go @@ -1699,12 +1699,23 @@ func PtracePokeUser(pid int, addr uintptr, data []byte) (count int, err error) { return ptracePoke(PTRACE_POKEUSR, PTRACE_PEEKUSR, pid, addr, data) } +// elfNT_PRSTATUS is a copy of the debug/elf.NT_PRSTATUS constant so +// x/sys/unix doesn't need to depend on debug/elf and thus +// compress/zlib, debug/dwarf, and other packages. +const elfNT_PRSTATUS = 1 + func PtraceGetRegs(pid int, regsout *PtraceRegs) (err error) { - return ptracePtr(PTRACE_GETREGS, pid, 0, unsafe.Pointer(regsout)) + var iov Iovec + iov.Base = (*byte)(unsafe.Pointer(regsout)) + iov.SetLen(int(unsafe.Sizeof(*regsout))) + return ptracePtr(PTRACE_GETREGSET, pid, uintptr(elfNT_PRSTATUS), unsafe.Pointer(&iov)) } func PtraceSetRegs(pid int, regs *PtraceRegs) (err error) { - return ptracePtr(PTRACE_SETREGS, pid, 0, unsafe.Pointer(regs)) + var iov Iovec + iov.Base = (*byte)(unsafe.Pointer(regs)) + iov.SetLen(int(unsafe.Sizeof(*regs))) + return ptracePtr(PTRACE_SETREGSET, pid, uintptr(elfNT_PRSTATUS), unsafe.Pointer(&iov)) } func PtraceSetOptions(pid int, options int) (err error) { @@ -2420,6 +2431,21 @@ func PthreadSigmask(how int, set, oldset *Sigset_t) error { return rtSigprocmask(how, set, oldset, _C__NSIG/8) } +//sysnb getresuid(ruid *_C_int, euid *_C_int, suid *_C_int) +//sysnb getresgid(rgid *_C_int, egid *_C_int, sgid *_C_int) + +func Getresuid() (ruid, euid, suid int) { + var r, e, s _C_int + getresuid(&r, &e, &s) + return int(r), int(e), int(s) +} + +func Getresgid() (rgid, egid, sgid int) { + var r, e, s _C_int + getresgid(&r, &e, &s) + return int(r), int(e), int(s) +} + /* * Unimplemented */ diff --git a/vendor/golang.org/x/sys/unix/syscall_openbsd.go b/vendor/golang.org/x/sys/unix/syscall_openbsd.go index f9c7a9663..c5f166a11 100644 --- a/vendor/golang.org/x/sys/unix/syscall_openbsd.go +++ b/vendor/golang.org/x/sys/unix/syscall_openbsd.go @@ -151,6 +151,21 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) { return } +//sysnb getresuid(ruid *_C_int, euid *_C_int, suid *_C_int) +//sysnb getresgid(rgid *_C_int, egid *_C_int, sgid *_C_int) + +func Getresuid() (ruid, euid, suid int) { + var r, e, s _C_int + getresuid(&r, &e, &s) + return int(r), int(e), int(s) +} + +func Getresgid() (rgid, egid, sgid int) { + var r, e, s _C_int + getresgid(&r, &e, &s) + return int(r), int(e), int(s) +} + //sys ioctl(fd int, req uint, arg uintptr) (err error) //sys ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) = SYS_IOCTL @@ -338,8 +353,6 @@ func Uname(uname *Utsname) error { // getgid // getitimer // getlogin -// getresgid -// getresuid // getthrid // ktrace // lfs_bmapv diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go index f61925269..48984202c 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go @@ -329,6 +329,54 @@ const ( SCM_WIFI_STATUS = 0x25 SFD_CLOEXEC = 0x400000 SFD_NONBLOCK = 0x4000 + SF_FP = 0x38 + SF_I0 = 0x20 + SF_I1 = 0x24 + SF_I2 = 0x28 + SF_I3 = 0x2c + SF_I4 = 0x30 + SF_I5 = 0x34 + SF_L0 = 0x0 + SF_L1 = 0x4 + SF_L2 = 0x8 + SF_L3 = 0xc + SF_L4 = 0x10 + SF_L5 = 0x14 + SF_L6 = 0x18 + SF_L7 = 0x1c + SF_PC = 0x3c + SF_RETP = 0x40 + SF_V9_FP = 0x70 + SF_V9_I0 = 0x40 + SF_V9_I1 = 0x48 + SF_V9_I2 = 0x50 + SF_V9_I3 = 0x58 + SF_V9_I4 = 0x60 + SF_V9_I5 = 0x68 + SF_V9_L0 = 0x0 + SF_V9_L1 = 0x8 + SF_V9_L2 = 0x10 + SF_V9_L3 = 0x18 + SF_V9_L4 = 0x20 + SF_V9_L5 = 0x28 + SF_V9_L6 = 0x30 + SF_V9_L7 = 0x38 + SF_V9_PC = 0x78 + SF_V9_RETP = 0x80 + SF_V9_XARG0 = 0x88 + SF_V9_XARG1 = 0x90 + SF_V9_XARG2 = 0x98 + SF_V9_XARG3 = 0xa0 + SF_V9_XARG4 = 0xa8 + SF_V9_XARG5 = 0xb0 + SF_V9_XXARG = 0xb8 + SF_XARG0 = 0x44 + SF_XARG1 = 0x48 + SF_XARG2 = 0x4c + SF_XARG3 = 0x50 + SF_XARG4 = 0x54 + SF_XARG5 = 0x58 + SF_XXARG = 0x5c SIOCATMARK = 0x8905 SIOCGPGRP = 0x8904 SIOCGSTAMPNS_NEW = 0x40108907 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux.go b/vendor/golang.org/x/sys/unix/zsyscall_linux.go index da63d9d78..722c29a00 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux.go @@ -2172,3 +2172,17 @@ func rtSigprocmask(how int, set *Sigset_t, oldset *Sigset_t, sigsetsize uintptr) } return } + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getresuid(ruid *_C_int, euid *_C_int, suid *_C_int) { + RawSyscallNoError(SYS_GETRESUID, uintptr(unsafe.Pointer(ruid)), uintptr(unsafe.Pointer(euid)), uintptr(unsafe.Pointer(suid))) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getresgid(rgid *_C_int, egid *_C_int, sgid *_C_int) { + RawSyscallNoError(SYS_GETRESGID, uintptr(unsafe.Pointer(rgid)), uintptr(unsafe.Pointer(egid)), uintptr(unsafe.Pointer(sgid))) + return +} diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go index 6699a783e..9ab9abf72 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go @@ -519,6 +519,28 @@ var libc_getcwd_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getresuid(ruid *_C_int, euid *_C_int, suid *_C_int) { + syscall_rawSyscall(libc_getresuid_trampoline_addr, uintptr(unsafe.Pointer(ruid)), uintptr(unsafe.Pointer(euid)), uintptr(unsafe.Pointer(suid))) + return +} + +var libc_getresuid_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getresuid getresuid "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getresgid(rgid *_C_int, egid *_C_int, sgid *_C_int) { + syscall_rawSyscall(libc_getresgid_trampoline_addr, uintptr(unsafe.Pointer(rgid)), uintptr(unsafe.Pointer(egid)), uintptr(unsafe.Pointer(sgid))) + return +} + +var libc_getresgid_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getresgid getresgid "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ioctl(fd int, req uint, arg uintptr) (err error) { _, _, e1 := syscall_syscall(libc_ioctl_trampoline_addr, uintptr(fd), uintptr(req), uintptr(arg)) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s index 04f0de34b..3dcacd30d 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s @@ -158,6 +158,16 @@ TEXT libc_getcwd_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_getcwd_trampoline_addr(SB), RODATA, $4 DATA ·libc_getcwd_trampoline_addr(SB)/4, $libc_getcwd_trampoline<>(SB) +TEXT libc_getresuid_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getresuid(SB) +GLOBL ·libc_getresuid_trampoline_addr(SB), RODATA, $4 +DATA ·libc_getresuid_trampoline_addr(SB)/4, $libc_getresuid_trampoline<>(SB) + +TEXT libc_getresgid_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getresgid(SB) +GLOBL ·libc_getresgid_trampoline_addr(SB), RODATA, $4 +DATA ·libc_getresgid_trampoline_addr(SB)/4, $libc_getresgid_trampoline<>(SB) + TEXT libc_ioctl_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ioctl(SB) GLOBL ·libc_ioctl_trampoline_addr(SB), RODATA, $4 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go index 1e775fe05..915761eab 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go @@ -519,15 +519,29 @@ var libc_getcwd_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func ioctl(fd int, req uint, arg uintptr) (err error) { - _, _, e1 := syscall_syscall(libc_ioctl_trampoline_addr, uintptr(fd), uintptr(req), uintptr(arg)) - if e1 != 0 { - err = errnoErr(e1) - } +func getresuid(ruid *_C_int, euid *_C_int, suid *_C_int) { + syscall_rawSyscall(libc_getresuid_trampoline_addr, uintptr(unsafe.Pointer(ruid)), uintptr(unsafe.Pointer(euid)), uintptr(unsafe.Pointer(suid))) return } -func ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) { +var libc_getresuid_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getresuid getresuid "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getresgid(rgid *_C_int, egid *_C_int, sgid *_C_int) { + syscall_rawSyscall(libc_getresgid_trampoline_addr, uintptr(unsafe.Pointer(rgid)), uintptr(unsafe.Pointer(egid)), uintptr(unsafe.Pointer(sgid))) + return +} + +var libc_getresgid_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getresgid getresgid "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func ioctl(fd int, req uint, arg uintptr) (err error) { _, _, e1 := syscall_syscall(libc_ioctl_trampoline_addr, uintptr(fd), uintptr(req), uintptr(arg)) if e1 != 0 { err = errnoErr(e1) @@ -541,6 +555,16 @@ var libc_ioctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) { + _, _, e1 := syscall_syscall(libc_ioctl_trampoline_addr, uintptr(fd), uintptr(req), uintptr(arg)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) { var _p0 unsafe.Pointer if len(mib) > 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s index 27b6f4df7..2763620b0 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s @@ -158,6 +158,16 @@ TEXT libc_getcwd_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_getcwd_trampoline_addr(SB), RODATA, $8 DATA ·libc_getcwd_trampoline_addr(SB)/8, $libc_getcwd_trampoline<>(SB) +TEXT libc_getresuid_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getresuid(SB) +GLOBL ·libc_getresuid_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getresuid_trampoline_addr(SB)/8, $libc_getresuid_trampoline<>(SB) + +TEXT libc_getresgid_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getresgid(SB) +GLOBL ·libc_getresgid_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getresgid_trampoline_addr(SB)/8, $libc_getresgid_trampoline<>(SB) + TEXT libc_ioctl_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ioctl(SB) GLOBL ·libc_ioctl_trampoline_addr(SB), RODATA, $8 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go index 7f6427899..8e87fdf15 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go @@ -519,6 +519,28 @@ var libc_getcwd_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getresuid(ruid *_C_int, euid *_C_int, suid *_C_int) { + syscall_rawSyscall(libc_getresuid_trampoline_addr, uintptr(unsafe.Pointer(ruid)), uintptr(unsafe.Pointer(euid)), uintptr(unsafe.Pointer(suid))) + return +} + +var libc_getresuid_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getresuid getresuid "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getresgid(rgid *_C_int, egid *_C_int, sgid *_C_int) { + syscall_rawSyscall(libc_getresgid_trampoline_addr, uintptr(unsafe.Pointer(rgid)), uintptr(unsafe.Pointer(egid)), uintptr(unsafe.Pointer(sgid))) + return +} + +var libc_getresgid_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getresgid getresgid "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ioctl(fd int, req uint, arg uintptr) (err error) { _, _, e1 := syscall_syscall(libc_ioctl_trampoline_addr, uintptr(fd), uintptr(req), uintptr(arg)) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s index b797045fd..c92231404 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s @@ -158,6 +158,16 @@ TEXT libc_getcwd_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_getcwd_trampoline_addr(SB), RODATA, $4 DATA ·libc_getcwd_trampoline_addr(SB)/4, $libc_getcwd_trampoline<>(SB) +TEXT libc_getresuid_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getresuid(SB) +GLOBL ·libc_getresuid_trampoline_addr(SB), RODATA, $4 +DATA ·libc_getresuid_trampoline_addr(SB)/4, $libc_getresuid_trampoline<>(SB) + +TEXT libc_getresgid_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getresgid(SB) +GLOBL ·libc_getresgid_trampoline_addr(SB), RODATA, $4 +DATA ·libc_getresgid_trampoline_addr(SB)/4, $libc_getresgid_trampoline<>(SB) + TEXT libc_ioctl_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ioctl(SB) GLOBL ·libc_ioctl_trampoline_addr(SB), RODATA, $4 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go index 756ef7b17..12a7a2160 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go @@ -519,6 +519,28 @@ var libc_getcwd_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getresuid(ruid *_C_int, euid *_C_int, suid *_C_int) { + syscall_rawSyscall(libc_getresuid_trampoline_addr, uintptr(unsafe.Pointer(ruid)), uintptr(unsafe.Pointer(euid)), uintptr(unsafe.Pointer(suid))) + return +} + +var libc_getresuid_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getresuid getresuid "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getresgid(rgid *_C_int, egid *_C_int, sgid *_C_int) { + syscall_rawSyscall(libc_getresgid_trampoline_addr, uintptr(unsafe.Pointer(rgid)), uintptr(unsafe.Pointer(egid)), uintptr(unsafe.Pointer(sgid))) + return +} + +var libc_getresgid_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getresgid getresgid "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ioctl(fd int, req uint, arg uintptr) (err error) { _, _, e1 := syscall_syscall(libc_ioctl_trampoline_addr, uintptr(fd), uintptr(req), uintptr(arg)) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s index a87126622..a6bc32c92 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s @@ -158,6 +158,16 @@ TEXT libc_getcwd_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_getcwd_trampoline_addr(SB), RODATA, $8 DATA ·libc_getcwd_trampoline_addr(SB)/8, $libc_getcwd_trampoline<>(SB) +TEXT libc_getresuid_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getresuid(SB) +GLOBL ·libc_getresuid_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getresuid_trampoline_addr(SB)/8, $libc_getresuid_trampoline<>(SB) + +TEXT libc_getresgid_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getresgid(SB) +GLOBL ·libc_getresgid_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getresgid_trampoline_addr(SB)/8, $libc_getresgid_trampoline<>(SB) + TEXT libc_ioctl_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ioctl(SB) GLOBL ·libc_ioctl_trampoline_addr(SB), RODATA, $8 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go index 7bc2e24eb..b19e8aa03 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go @@ -519,6 +519,28 @@ var libc_getcwd_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getresuid(ruid *_C_int, euid *_C_int, suid *_C_int) { + syscall_rawSyscall(libc_getresuid_trampoline_addr, uintptr(unsafe.Pointer(ruid)), uintptr(unsafe.Pointer(euid)), uintptr(unsafe.Pointer(suid))) + return +} + +var libc_getresuid_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getresuid getresuid "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getresgid(rgid *_C_int, egid *_C_int, sgid *_C_int) { + syscall_rawSyscall(libc_getresgid_trampoline_addr, uintptr(unsafe.Pointer(rgid)), uintptr(unsafe.Pointer(egid)), uintptr(unsafe.Pointer(sgid))) + return +} + +var libc_getresgid_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getresgid getresgid "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ioctl(fd int, req uint, arg uintptr) (err error) { _, _, e1 := syscall_syscall(libc_ioctl_trampoline_addr, uintptr(fd), uintptr(req), uintptr(arg)) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s index 05d4bffd7..b4e7bceab 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s @@ -158,6 +158,16 @@ TEXT libc_getcwd_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_getcwd_trampoline_addr(SB), RODATA, $8 DATA ·libc_getcwd_trampoline_addr(SB)/8, $libc_getcwd_trampoline<>(SB) +TEXT libc_getresuid_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getresuid(SB) +GLOBL ·libc_getresuid_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getresuid_trampoline_addr(SB)/8, $libc_getresuid_trampoline<>(SB) + +TEXT libc_getresgid_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getresgid(SB) +GLOBL ·libc_getresgid_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getresgid_trampoline_addr(SB)/8, $libc_getresgid_trampoline<>(SB) + TEXT libc_ioctl_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ioctl(SB) GLOBL ·libc_ioctl_trampoline_addr(SB), RODATA, $8 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go index 739be6217..fb99594c9 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go @@ -519,6 +519,28 @@ var libc_getcwd_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getresuid(ruid *_C_int, euid *_C_int, suid *_C_int) { + syscall_rawSyscall(libc_getresuid_trampoline_addr, uintptr(unsafe.Pointer(ruid)), uintptr(unsafe.Pointer(euid)), uintptr(unsafe.Pointer(suid))) + return +} + +var libc_getresuid_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getresuid getresuid "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getresgid(rgid *_C_int, egid *_C_int, sgid *_C_int) { + syscall_rawSyscall(libc_getresgid_trampoline_addr, uintptr(unsafe.Pointer(rgid)), uintptr(unsafe.Pointer(egid)), uintptr(unsafe.Pointer(sgid))) + return +} + +var libc_getresgid_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getresgid getresgid "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ioctl(fd int, req uint, arg uintptr) (err error) { _, _, e1 := syscall_syscall(libc_ioctl_trampoline_addr, uintptr(fd), uintptr(req), uintptr(arg)) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s index 74a25f8d6..ca3f76600 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s @@ -189,6 +189,18 @@ TEXT libc_getcwd_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_getcwd_trampoline_addr(SB), RODATA, $8 DATA ·libc_getcwd_trampoline_addr(SB)/8, $libc_getcwd_trampoline<>(SB) +TEXT libc_getresuid_trampoline<>(SB),NOSPLIT,$0-0 + CALL libc_getresuid(SB) + RET +GLOBL ·libc_getresuid_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getresuid_trampoline_addr(SB)/8, $libc_getresuid_trampoline<>(SB) + +TEXT libc_getresgid_trampoline<>(SB),NOSPLIT,$0-0 + CALL libc_getresgid(SB) + RET +GLOBL ·libc_getresgid_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getresgid_trampoline_addr(SB)/8, $libc_getresgid_trampoline<>(SB) + TEXT libc_ioctl_trampoline<>(SB),NOSPLIT,$0-0 CALL libc_ioctl(SB) RET diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go index 7d95a1978..32cbbbc52 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go @@ -519,6 +519,28 @@ var libc_getcwd_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getresuid(ruid *_C_int, euid *_C_int, suid *_C_int) { + syscall_rawSyscall(libc_getresuid_trampoline_addr, uintptr(unsafe.Pointer(ruid)), uintptr(unsafe.Pointer(euid)), uintptr(unsafe.Pointer(suid))) + return +} + +var libc_getresuid_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getresuid getresuid "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getresgid(rgid *_C_int, egid *_C_int, sgid *_C_int) { + syscall_rawSyscall(libc_getresgid_trampoline_addr, uintptr(unsafe.Pointer(rgid)), uintptr(unsafe.Pointer(egid)), uintptr(unsafe.Pointer(sgid))) + return +} + +var libc_getresgid_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getresgid getresgid "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ioctl(fd int, req uint, arg uintptr) (err error) { _, _, e1 := syscall_syscall(libc_ioctl_trampoline_addr, uintptr(fd), uintptr(req), uintptr(arg)) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s index 990be2457..477a7d5b2 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s @@ -158,6 +158,16 @@ TEXT libc_getcwd_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_getcwd_trampoline_addr(SB), RODATA, $8 DATA ·libc_getcwd_trampoline_addr(SB)/8, $libc_getcwd_trampoline<>(SB) +TEXT libc_getresuid_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getresuid(SB) +GLOBL ·libc_getresuid_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getresuid_trampoline_addr(SB)/8, $libc_getresuid_trampoline<>(SB) + +TEXT libc_getresgid_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getresgid(SB) +GLOBL ·libc_getresgid_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getresgid_trampoline_addr(SB)/8, $libc_getresgid_trampoline<>(SB) + TEXT libc_ioctl_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ioctl(SB) GLOBL ·libc_ioctl_trampoline_addr(SB), RODATA, $8 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux.go b/vendor/golang.org/x/sys/unix/ztypes_linux.go index ca84727cf..00c3b8c20 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux.go @@ -2555,6 +2555,11 @@ const ( BPF_REG_8 = 0x8 BPF_REG_9 = 0x9 BPF_REG_10 = 0xa + BPF_CGROUP_ITER_ORDER_UNSPEC = 0x0 + BPF_CGROUP_ITER_SELF_ONLY = 0x1 + BPF_CGROUP_ITER_DESCENDANTS_PRE = 0x2 + BPF_CGROUP_ITER_DESCENDANTS_POST = 0x3 + BPF_CGROUP_ITER_ANCESTORS_UP = 0x4 BPF_MAP_CREATE = 0x0 BPF_MAP_LOOKUP_ELEM = 0x1 BPF_MAP_UPDATE_ELEM = 0x2 @@ -2566,6 +2571,7 @@ const ( BPF_PROG_ATTACH = 0x8 BPF_PROG_DETACH = 0x9 BPF_PROG_TEST_RUN = 0xa + BPF_PROG_RUN = 0xa BPF_PROG_GET_NEXT_ID = 0xb BPF_MAP_GET_NEXT_ID = 0xc BPF_PROG_GET_FD_BY_ID = 0xd @@ -2610,6 +2616,7 @@ const ( BPF_MAP_TYPE_CPUMAP = 0x10 BPF_MAP_TYPE_XSKMAP = 0x11 BPF_MAP_TYPE_SOCKHASH = 0x12 + BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED = 0x13 BPF_MAP_TYPE_CGROUP_STORAGE = 0x13 BPF_MAP_TYPE_REUSEPORT_SOCKARRAY = 0x14 BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE = 0x15 @@ -2620,6 +2627,10 @@ const ( BPF_MAP_TYPE_STRUCT_OPS = 0x1a BPF_MAP_TYPE_RINGBUF = 0x1b BPF_MAP_TYPE_INODE_STORAGE = 0x1c + BPF_MAP_TYPE_TASK_STORAGE = 0x1d + BPF_MAP_TYPE_BLOOM_FILTER = 0x1e + BPF_MAP_TYPE_USER_RINGBUF = 0x1f + BPF_MAP_TYPE_CGRP_STORAGE = 0x20 BPF_PROG_TYPE_UNSPEC = 0x0 BPF_PROG_TYPE_SOCKET_FILTER = 0x1 BPF_PROG_TYPE_KPROBE = 0x2 @@ -2651,6 +2662,7 @@ const ( BPF_PROG_TYPE_EXT = 0x1c BPF_PROG_TYPE_LSM = 0x1d BPF_PROG_TYPE_SK_LOOKUP = 0x1e + BPF_PROG_TYPE_SYSCALL = 0x1f BPF_CGROUP_INET_INGRESS = 0x0 BPF_CGROUP_INET_EGRESS = 0x1 BPF_CGROUP_INET_SOCK_CREATE = 0x2 @@ -2689,6 +2701,12 @@ const ( BPF_XDP_CPUMAP = 0x23 BPF_SK_LOOKUP = 0x24 BPF_XDP = 0x25 + BPF_SK_SKB_VERDICT = 0x26 + BPF_SK_REUSEPORT_SELECT = 0x27 + BPF_SK_REUSEPORT_SELECT_OR_MIGRATE = 0x28 + BPF_PERF_EVENT = 0x29 + BPF_TRACE_KPROBE_MULTI = 0x2a + BPF_LSM_CGROUP = 0x2b BPF_LINK_TYPE_UNSPEC = 0x0 BPF_LINK_TYPE_RAW_TRACEPOINT = 0x1 BPF_LINK_TYPE_TRACING = 0x2 @@ -2696,6 +2714,9 @@ const ( BPF_LINK_TYPE_ITER = 0x4 BPF_LINK_TYPE_NETNS = 0x5 BPF_LINK_TYPE_XDP = 0x6 + BPF_LINK_TYPE_PERF_EVENT = 0x7 + BPF_LINK_TYPE_KPROBE_MULTI = 0x8 + BPF_LINK_TYPE_STRUCT_OPS = 0x9 BPF_ANY = 0x0 BPF_NOEXIST = 0x1 BPF_EXIST = 0x2 @@ -2733,6 +2754,7 @@ const ( BPF_F_ZERO_CSUM_TX = 0x2 BPF_F_DONT_FRAGMENT = 0x4 BPF_F_SEQ_NUMBER = 0x8 + BPF_F_TUNINFO_FLAGS = 0x10 BPF_F_INDEX_MASK = 0xffffffff BPF_F_CURRENT_CPU = 0xffffffff BPF_F_CTXLEN_MASK = 0xfffff00000000 @@ -2747,6 +2769,7 @@ const ( BPF_F_ADJ_ROOM_ENCAP_L4_GRE = 0x8 BPF_F_ADJ_ROOM_ENCAP_L4_UDP = 0x10 BPF_F_ADJ_ROOM_NO_CSUM_RESET = 0x20 + BPF_F_ADJ_ROOM_ENCAP_L2_ETH = 0x40 BPF_ADJ_ROOM_ENCAP_L2_MASK = 0xff BPF_ADJ_ROOM_ENCAP_L2_SHIFT = 0x38 BPF_F_SYSCTL_BASE_NAME = 0x1 @@ -2771,10 +2794,16 @@ const ( BPF_LWT_ENCAP_SEG6 = 0x0 BPF_LWT_ENCAP_SEG6_INLINE = 0x1 BPF_LWT_ENCAP_IP = 0x2 + BPF_F_BPRM_SECUREEXEC = 0x1 + BPF_F_BROADCAST = 0x8 + BPF_F_EXCLUDE_INGRESS = 0x10 + BPF_SKB_TSTAMP_UNSPEC = 0x0 + BPF_SKB_TSTAMP_DELIVERY_MONO = 0x1 BPF_OK = 0x0 BPF_DROP = 0x2 BPF_REDIRECT = 0x7 BPF_LWT_REROUTE = 0x80 + BPF_FLOW_DISSECTOR_CONTINUE = 0x81 BPF_SOCK_OPS_RTO_CB_FLAG = 0x1 BPF_SOCK_OPS_RETRANS_CB_FLAG = 0x2 BPF_SOCK_OPS_STATE_CB_FLAG = 0x4 @@ -2838,6 +2867,10 @@ const ( BPF_FIB_LKUP_RET_UNSUPP_LWT = 0x6 BPF_FIB_LKUP_RET_NO_NEIGH = 0x7 BPF_FIB_LKUP_RET_FRAG_NEEDED = 0x8 + BPF_MTU_CHK_SEGS = 0x1 + BPF_MTU_CHK_RET_SUCCESS = 0x0 + BPF_MTU_CHK_RET_FRAG_NEEDED = 0x1 + BPF_MTU_CHK_RET_SEGS_TOOBIG = 0x2 BPF_FD_TYPE_RAW_TRACEPOINT = 0x0 BPF_FD_TYPE_TRACEPOINT = 0x1 BPF_FD_TYPE_KPROBE = 0x2 @@ -2847,6 +2880,19 @@ const ( BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG = 0x1 BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL = 0x2 BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP = 0x4 + BPF_CORE_FIELD_BYTE_OFFSET = 0x0 + BPF_CORE_FIELD_BYTE_SIZE = 0x1 + BPF_CORE_FIELD_EXISTS = 0x2 + BPF_CORE_FIELD_SIGNED = 0x3 + BPF_CORE_FIELD_LSHIFT_U64 = 0x4 + BPF_CORE_FIELD_RSHIFT_U64 = 0x5 + BPF_CORE_TYPE_ID_LOCAL = 0x6 + BPF_CORE_TYPE_ID_TARGET = 0x7 + BPF_CORE_TYPE_EXISTS = 0x8 + BPF_CORE_TYPE_SIZE = 0x9 + BPF_CORE_ENUMVAL_EXISTS = 0xa + BPF_CORE_ENUMVAL_VALUE = 0xb + BPF_CORE_TYPE_MATCHES = 0xc ) const ( diff --git a/vendor/golang.org/x/sys/windows/syscall_windows.go b/vendor/golang.org/x/sys/windows/syscall_windows.go index 3723b2c22..964590075 100644 --- a/vendor/golang.org/x/sys/windows/syscall_windows.go +++ b/vendor/golang.org/x/sys/windows/syscall_windows.go @@ -405,7 +405,7 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys VerQueryValue(block unsafe.Pointer, subBlock string, pointerToBufferPointer unsafe.Pointer, bufSize *uint32) (err error) = version.VerQueryValueW // Process Status API (PSAPI) -//sys EnumProcesses(processIds []uint32, bytesReturned *uint32) (err error) = psapi.EnumProcesses +//sys enumProcesses(processIds *uint32, nSize uint32, bytesReturned *uint32) (err error) = psapi.EnumProcesses //sys EnumProcessModules(process Handle, module *Handle, cb uint32, cbNeeded *uint32) (err error) = psapi.EnumProcessModules //sys EnumProcessModulesEx(process Handle, module *Handle, cb uint32, cbNeeded *uint32, filterFlag uint32) (err error) = psapi.EnumProcessModulesEx //sys GetModuleInformation(process Handle, module Handle, modinfo *ModuleInfo, cb uint32) (err error) = psapi.GetModuleInformation @@ -1354,6 +1354,17 @@ func SetsockoptIPv6Mreq(fd Handle, level, opt int, mreq *IPv6Mreq) (err error) { return syscall.EWINDOWS } +func EnumProcesses(processIds []uint32, bytesReturned *uint32) error { + // EnumProcesses syscall expects the size parameter to be in bytes, but the code generated with mksyscall uses + // the length of the processIds slice instead. Hence, this wrapper function is added to fix the discrepancy. + var p *uint32 + if len(processIds) > 0 { + p = &processIds[0] + } + size := uint32(len(processIds) * 4) + return enumProcesses(p, size, bytesReturned) +} + func Getpid() (pid int) { return int(GetCurrentProcessId()) } func FindFirstFile(name *uint16, data *Win32finddata) (handle Handle, err error) { diff --git a/vendor/golang.org/x/sys/windows/zsyscall_windows.go b/vendor/golang.org/x/sys/windows/zsyscall_windows.go index a81ea2c70..566dd3e31 100644 --- a/vendor/golang.org/x/sys/windows/zsyscall_windows.go +++ b/vendor/golang.org/x/sys/windows/zsyscall_windows.go @@ -3516,12 +3516,8 @@ func EnumProcessModulesEx(process Handle, module *Handle, cb uint32, cbNeeded *u return } -func EnumProcesses(processIds []uint32, bytesReturned *uint32) (err error) { - var _p0 *uint32 - if len(processIds) > 0 { - _p0 = &processIds[0] - } - r1, _, e1 := syscall.Syscall(procEnumProcesses.Addr(), 3, uintptr(unsafe.Pointer(_p0)), uintptr(len(processIds)), uintptr(unsafe.Pointer(bytesReturned))) +func enumProcesses(processIds *uint32, nSize uint32, bytesReturned *uint32) (err error) { + r1, _, e1 := syscall.Syscall(procEnumProcesses.Addr(), 3, uintptr(unsafe.Pointer(processIds)), uintptr(nSize), uintptr(unsafe.Pointer(bytesReturned))) if r1 == 0 { err = errnoErr(e1) } diff --git a/vendor/modules.txt b/vendor/modules.txt index 04937f5b2..62261a087 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -172,7 +172,7 @@ github.com/jesseduffield/go-git/v5/utils/merkletrie/filesystem github.com/jesseduffield/go-git/v5/utils/merkletrie/index github.com/jesseduffield/go-git/v5/utils/merkletrie/internal/frame github.com/jesseduffield/go-git/v5/utils/merkletrie/noder -# github.com/jesseduffield/gocui v0.3.1-0.20230601121845-cb89273fdd4e +# github.com/jesseduffield/gocui v0.3.1-0.20230702054502-d6c452fc12ce ## explicit; go 1.12 github.com/jesseduffield/gocui # github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10 @@ -293,17 +293,17 @@ golang.org/x/exp/slices golang.org/x/net/context golang.org/x/net/internal/socks golang.org/x/net/proxy -# golang.org/x/sys v0.8.0 +# golang.org/x/sys v0.9.0 ## explicit; go 1.17 golang.org/x/sys/cpu golang.org/x/sys/internal/unsafeheader golang.org/x/sys/plan9 golang.org/x/sys/unix golang.org/x/sys/windows -# golang.org/x/term v0.8.0 +# golang.org/x/term v0.9.0 ## explicit; go 1.17 golang.org/x/term -# golang.org/x/text v0.9.0 +# golang.org/x/text v0.10.0 ## explicit; go 1.17 golang.org/x/text/encoding golang.org/x/text/encoding/internal/identifier