mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-05-10 20:05:50 +02:00
Standardise display of range selection across views
We're not fully standardising here: different contexts can store their range state however they like. What we are standardising on is that now the view is always responsible for highlighting the selected lines, meaning the context/controller needs to tell the view where the range start is. Two convenient benefits from this change: 1) we no longer need bespoke code in integration tests for asserting on selected lines because we can just ask the view 2) line selection in staging/patch-building/merge-conflicts views now look the same as in list views i.e. the highlight applies to the whole line (including trailing space) I also noticed a bug with merge conflicts not rendering the selection on focus though I suspect it wasn't a bug with any real consequences when the view wasn't displaying the selection. I'm going to scrap the selectedRangeBgColor config and just let it use the single line background color. Hopefully nobody cares, but there's really no need for an extra config.
This commit is contained in:
parent
c0c3aac02e
commit
f3eb180f75
28 changed files with 255 additions and 287 deletions
|
@ -57,8 +57,6 @@ gui:
|
|||
- blue
|
||||
selectedLineBgColor:
|
||||
- blue # set to `default` to have no background colour
|
||||
selectedRangeBgColor:
|
||||
- blue
|
||||
cherryPickedCommitBgColor:
|
||||
- cyan
|
||||
cherryPickedCommitFgColor:
|
||||
|
@ -390,15 +388,13 @@ The available attributes are:
|
|||
|
||||
## Highlighting the selected line
|
||||
|
||||
If you don't like the default behaviour of highlighting the selected line with a blue background, you can use the `selectedLineBgColor` and `selectedRangeBgColor` keys to customise the behaviour. If you just want to embolden the selected line (this was the original default), you can do the following:
|
||||
If you don't like the default behaviour of highlighting the selected line with a blue background, you can use the `selectedLineBgColor` key to customise the behaviour. If you just want to embolden the selected line (this was the original default), you can do the following:
|
||||
|
||||
```yaml
|
||||
gui:
|
||||
theme:
|
||||
selectedLineBgColor:
|
||||
- default
|
||||
selectedRangeBgColor:
|
||||
- default
|
||||
```
|
||||
|
||||
You can also use the reverse attribute like so:
|
||||
|
@ -408,8 +404,6 @@ gui:
|
|||
theme:
|
||||
selectedLineBgColor:
|
||||
- reverse
|
||||
selectedRangeBgColor:
|
||||
- reverse
|
||||
```
|
||||
|
||||
## Custom Author Color
|
||||
|
|
|
@ -37,6 +37,9 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||
<kbd>.</kbd>: Next page
|
||||
<kbd><</kbd>: Scroll to top
|
||||
<kbd>></kbd>: Scroll to bottom
|
||||
<kbd>v</kbd>: Toggle range select
|
||||
<kbd><s-down></kbd>: Range select down
|
||||
<kbd><s-up></kbd>: Range select up
|
||||
<kbd>/</kbd>: Search the current view by text
|
||||
<kbd>H</kbd>: Scroll left
|
||||
<kbd>L</kbd>: Scroll right
|
||||
|
@ -196,8 +199,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||
<pre>
|
||||
<kbd><left></kbd>: Select previous hunk
|
||||
<kbd><right></kbd>: Select next hunk
|
||||
<kbd>v</kbd>: Toggle drag select
|
||||
<kbd>V</kbd>: Toggle drag select
|
||||
<kbd>v</kbd>: Toggle range select
|
||||
<kbd>a</kbd>: Toggle select hunk
|
||||
<kbd><c-o></kbd>: Copy the selected text to the clipboard
|
||||
<kbd>o</kbd>: Open file
|
||||
|
@ -212,8 +214,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||
<pre>
|
||||
<kbd><left></kbd>: Select previous hunk
|
||||
<kbd><right></kbd>: Select next hunk
|
||||
<kbd>v</kbd>: Toggle drag select
|
||||
<kbd>V</kbd>: Toggle drag select
|
||||
<kbd>v</kbd>: Toggle range select
|
||||
<kbd>a</kbd>: Toggle select hunk
|
||||
<kbd><c-o></kbd>: Copy the selected text to the clipboard
|
||||
<kbd>o</kbd>: Open file
|
||||
|
|
|
@ -37,6 +37,9 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||
<kbd>.</kbd>: 次のページ
|
||||
<kbd><</kbd>: 最上部までスクロール
|
||||
<kbd>></kbd>: 最下部までスクロール
|
||||
<kbd>v</kbd>: 範囲選択を切り替え
|
||||
<kbd><s-down></kbd>: Range select down
|
||||
<kbd><s-up></kbd>: Range select up
|
||||
<kbd>/</kbd>: 検索を開始
|
||||
<kbd>H</kbd>: 左スクロール
|
||||
<kbd>L</kbd>: 右スクロール
|
||||
|
@ -270,7 +273,6 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||
<kbd><left></kbd>: 前のhunkを選択
|
||||
<kbd><right></kbd>: 次のhunkを選択
|
||||
<kbd>v</kbd>: 範囲選択を切り替え
|
||||
<kbd>V</kbd>: 範囲選択を切り替え
|
||||
<kbd>a</kbd>: Hunk選択を切り替え
|
||||
<kbd><c-o></kbd>: 選択されたテキストをクリップボードにコピー
|
||||
<kbd>o</kbd>: ファイルを開く
|
||||
|
@ -286,7 +288,6 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||
<kbd><left></kbd>: 前のhunkを選択
|
||||
<kbd><right></kbd>: 次のhunkを選択
|
||||
<kbd>v</kbd>: 範囲選択を切り替え
|
||||
<kbd>V</kbd>: 範囲選択を切り替え
|
||||
<kbd>a</kbd>: Hunk選択を切り替え
|
||||
<kbd><c-o></kbd>: 選択されたテキストをクリップボードにコピー
|
||||
<kbd>o</kbd>: ファイルを開く
|
||||
|
|
|
@ -37,6 +37,9 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||
<kbd>.</kbd>: 다음 페이지
|
||||
<kbd><</kbd>: 맨 위로 스크롤
|
||||
<kbd>></kbd>: 맨 아래로 스크롤
|
||||
<kbd>v</kbd>: 드래그 선택 전환
|
||||
<kbd><s-down></kbd>: Range select down
|
||||
<kbd><s-up></kbd>: Range select up
|
||||
<kbd>/</kbd>: 검색 시작
|
||||
<kbd>H</kbd>: 우 스크롤
|
||||
<kbd>L</kbd>: 좌 스크롤
|
||||
|
@ -141,7 +144,6 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||
<kbd><left></kbd>: 이전 hunk를 선택
|
||||
<kbd><right></kbd>: 다음 hunk를 선택
|
||||
<kbd>v</kbd>: 드래그 선택 전환
|
||||
<kbd>V</kbd>: 드래그 선택 전환
|
||||
<kbd>a</kbd>: Toggle select hunk
|
||||
<kbd><c-o></kbd>: 선택한 텍스트를 클립보드에 복사
|
||||
<kbd>o</kbd>: 파일 닫기
|
||||
|
@ -157,7 +159,6 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||
<kbd><left></kbd>: 이전 hunk를 선택
|
||||
<kbd><right></kbd>: 다음 hunk를 선택
|
||||
<kbd>v</kbd>: 드래그 선택 전환
|
||||
<kbd>V</kbd>: 드래그 선택 전환
|
||||
<kbd>a</kbd>: Toggle select hunk
|
||||
<kbd><c-o></kbd>: 선택한 텍스트를 클립보드에 복사
|
||||
<kbd>o</kbd>: 파일 닫기
|
||||
|
|
|
@ -37,6 +37,9 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||
<kbd>.</kbd>: Volgende pagina
|
||||
<kbd><</kbd>: Scroll naar boven
|
||||
<kbd>></kbd>: Scroll naar beneden
|
||||
<kbd>v</kbd>: Toggle drag selecteer
|
||||
<kbd><s-down></kbd>: Range select down
|
||||
<kbd><s-up></kbd>: Range select up
|
||||
<kbd>/</kbd>: Start met zoeken
|
||||
<kbd>H</kbd>: Scroll left
|
||||
<kbd>L</kbd>: Scroll right
|
||||
|
@ -205,7 +208,6 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||
<kbd><left></kbd>: Selecteer de vorige hunk
|
||||
<kbd><right></kbd>: Selecteer de volgende hunk
|
||||
<kbd>v</kbd>: Toggle drag selecteer
|
||||
<kbd>V</kbd>: Toggle drag selecteer
|
||||
<kbd>a</kbd>: Toggle selecteer hunk
|
||||
<kbd><c-o></kbd>: Copy the selected text to the clipboard
|
||||
<kbd>o</kbd>: Open bestand
|
||||
|
@ -266,7 +268,6 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||
<kbd><left></kbd>: Selecteer de vorige hunk
|
||||
<kbd><right></kbd>: Selecteer de volgende hunk
|
||||
<kbd>v</kbd>: Toggle drag selecteer
|
||||
<kbd>V</kbd>: Toggle drag selecteer
|
||||
<kbd>a</kbd>: Toggle selecteer hunk
|
||||
<kbd><c-o></kbd>: Copy the selected text to the clipboard
|
||||
<kbd>o</kbd>: Open bestand
|
||||
|
|
|
@ -37,6 +37,9 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||
<kbd>.</kbd>: Next page
|
||||
<kbd><</kbd>: Scroll to top
|
||||
<kbd>></kbd>: Scroll to bottom
|
||||
<kbd>v</kbd>: Toggle range select
|
||||
<kbd><s-down></kbd>: Range select down
|
||||
<kbd><s-up></kbd>: Range select up
|
||||
<kbd>/</kbd>: Search the current view by text
|
||||
<kbd>H</kbd>: Scroll left
|
||||
<kbd>L</kbd>: Scroll right
|
||||
|
@ -127,8 +130,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||
<pre>
|
||||
<kbd><left></kbd>: Poprzedni kawałek
|
||||
<kbd><right></kbd>: Następny kawałek
|
||||
<kbd>v</kbd>: Toggle drag select
|
||||
<kbd>V</kbd>: Toggle drag select
|
||||
<kbd>v</kbd>: Toggle range select
|
||||
<kbd>a</kbd>: Toggle select hunk
|
||||
<kbd><c-o></kbd>: Copy the selected text to the clipboard
|
||||
<kbd>o</kbd>: Otwórz plik
|
||||
|
@ -197,8 +199,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||
<pre>
|
||||
<kbd><left></kbd>: Poprzedni kawałek
|
||||
<kbd><right></kbd>: Następny kawałek
|
||||
<kbd>v</kbd>: Toggle drag select
|
||||
<kbd>V</kbd>: Toggle drag select
|
||||
<kbd>v</kbd>: Toggle range select
|
||||
<kbd>a</kbd>: Toggle select hunk
|
||||
<kbd><c-o></kbd>: Copy the selected text to the clipboard
|
||||
<kbd>o</kbd>: Otwórz plik
|
||||
|
|
|
@ -37,6 +37,9 @@ _Связки клавиш_
|
|||
<kbd>.</kbd>: Следующая страница
|
||||
<kbd><</kbd>: Пролистать наверх
|
||||
<kbd>></kbd>: Прокрутить вниз
|
||||
<kbd>v</kbd>: Переключить выборку перетаскивания
|
||||
<kbd><s-down></kbd>: Range select down
|
||||
<kbd><s-up></kbd>: Range select up
|
||||
<kbd>/</kbd>: Найти
|
||||
<kbd>H</kbd>: Прокрутить влево
|
||||
<kbd>L</kbd>: Прокрутить вправо
|
||||
|
@ -61,7 +64,6 @@ _Связки клавиш_
|
|||
<kbd><left></kbd>: Выбрать предыдущую часть
|
||||
<kbd><right></kbd>: Выбрать следующую часть
|
||||
<kbd>v</kbd>: Переключить выборку перетаскивания
|
||||
<kbd>V</kbd>: Переключить выборку перетаскивания
|
||||
<kbd>a</kbd>: Переключить выборку частей
|
||||
<kbd><c-o></kbd>: Скопировать выделенный текст в буфер обмена
|
||||
<kbd>o</kbd>: Открыть файл
|
||||
|
@ -106,7 +108,6 @@ _Связки клавиш_
|
|||
<kbd><left></kbd>: Выбрать предыдущую часть
|
||||
<kbd><right></kbd>: Выбрать следующую часть
|
||||
<kbd>v</kbd>: Переключить выборку перетаскивания
|
||||
<kbd>V</kbd>: Переключить выборку перетаскивания
|
||||
<kbd>a</kbd>: Переключить выборку частей
|
||||
<kbd><c-o></kbd>: Скопировать выделенный текст в буфер обмена
|
||||
<kbd>o</kbd>: Открыть файл
|
||||
|
|
|
@ -37,6 +37,9 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||
<kbd>.</kbd>: 下一页
|
||||
<kbd><</kbd>: 滚动到顶部
|
||||
<kbd>></kbd>: 滚动到底部
|
||||
<kbd>v</kbd>: 切换拖动选择
|
||||
<kbd><s-down></kbd>: Range select down
|
||||
<kbd><s-up></kbd>: Range select up
|
||||
<kbd>/</kbd>: 开始搜索
|
||||
<kbd>H</kbd>: 向左滚动
|
||||
<kbd>L</kbd>: 向右滚动
|
||||
|
@ -229,7 +232,6 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||
<kbd><left></kbd>: 选择上一个区块
|
||||
<kbd><right></kbd>: 选择下一个区块
|
||||
<kbd>v</kbd>: 切换拖动选择
|
||||
<kbd>V</kbd>: 切换拖动选择
|
||||
<kbd>a</kbd>: 切换选择区块
|
||||
<kbd><c-o></kbd>: 将选中文本复制到剪贴板
|
||||
<kbd>o</kbd>: 打开文件
|
||||
|
@ -274,7 +276,6 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||
<kbd><left></kbd>: 选择上一个区块
|
||||
<kbd><right></kbd>: 选择下一个区块
|
||||
<kbd>v</kbd>: 切换拖动选择
|
||||
<kbd>V</kbd>: 切换拖动选择
|
||||
<kbd>a</kbd>: 切换选择区块
|
||||
<kbd><c-o></kbd>: 将选中文本复制到剪贴板
|
||||
<kbd>o</kbd>: 打开文件
|
||||
|
|
|
@ -37,6 +37,9 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
|
|||
<kbd>.</kbd>: 下一頁
|
||||
<kbd><</kbd>: 捲動到頂部
|
||||
<kbd>></kbd>: 捲動到底部
|
||||
<kbd>v</kbd>: 切換拖曳選擇
|
||||
<kbd><s-down></kbd>: Range select down
|
||||
<kbd><s-up></kbd>: Range select up
|
||||
<kbd>/</kbd>: 開始搜尋
|
||||
<kbd>H</kbd>: 向左捲動
|
||||
<kbd>L</kbd>: 向右捲動
|
||||
|
@ -102,7 +105,6 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
|
|||
<kbd><left></kbd>: 選擇上一段
|
||||
<kbd><right></kbd>: 選擇下一段
|
||||
<kbd>v</kbd>: 切換拖曳選擇
|
||||
<kbd>V</kbd>: 切換拖曳選擇
|
||||
<kbd>a</kbd>: 切換選擇程式碼塊
|
||||
<kbd><c-o></kbd>: 複製所選文本至剪貼簿
|
||||
<kbd>o</kbd>: 開啟檔案
|
||||
|
@ -124,7 +126,6 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
|
|||
<kbd><left></kbd>: 選擇上一段
|
||||
<kbd><right></kbd>: 選擇下一段
|
||||
<kbd>v</kbd>: 切換拖曳選擇
|
||||
<kbd>V</kbd>: 切換拖曳選擇
|
||||
<kbd>a</kbd>: 切換選擇程式碼塊
|
||||
<kbd><c-o></kbd>: 複製所選文本至剪貼簿
|
||||
<kbd>o</kbd>: 開啟檔案
|
||||
|
|
|
@ -14,11 +14,6 @@ type patchPresenter struct {
|
|||
// if true, all following fields are ignored
|
||||
plain bool
|
||||
|
||||
isFocused bool
|
||||
// first line index for selected cursor range
|
||||
firstLineIndex int
|
||||
// last line index for selected cursor range
|
||||
lastLineIndex int
|
||||
// line indices for tagged lines (e.g. lines added to a custom patch)
|
||||
incLineIndices *set.Set[int]
|
||||
}
|
||||
|
@ -44,11 +39,6 @@ func formatRangePlain(patch *Patch, startIdx int, endIdx int) string {
|
|||
}
|
||||
|
||||
type FormatViewOpts struct {
|
||||
IsFocused bool
|
||||
// first line index for selected cursor range
|
||||
FirstLineIndex int
|
||||
// last line index for selected cursor range
|
||||
LastLineIndex int
|
||||
// line indices for tagged lines (e.g. lines added to a custom patch)
|
||||
IncLineIndices *set.Set[int]
|
||||
}
|
||||
|
@ -63,9 +53,6 @@ func formatView(patch *Patch, opts FormatViewOpts) string {
|
|||
presenter := &patchPresenter{
|
||||
patch: patch,
|
||||
plain: false,
|
||||
isFocused: opts.IsFocused,
|
||||
firstLineIndex: opts.FirstLineIndex,
|
||||
lastLineIndex: opts.LastLineIndex,
|
||||
incLineIndices: includedLineIndices,
|
||||
}
|
||||
return presenter.format()
|
||||
|
@ -112,7 +99,6 @@ func (self *patchPresenter) format() string {
|
|||
self.formatLineAux(
|
||||
hunk.headerContext,
|
||||
theme.DefaultTextColor,
|
||||
lineIdx,
|
||||
false,
|
||||
),
|
||||
)
|
||||
|
@ -139,23 +125,17 @@ func (self *patchPresenter) patchLineStyle(patchLine *PatchLine) style.TextStyle
|
|||
func (self *patchPresenter) formatLine(str string, textStyle style.TextStyle, index int) string {
|
||||
included := self.incLineIndices.Includes(index)
|
||||
|
||||
return self.formatLineAux(str, textStyle, index, included)
|
||||
return self.formatLineAux(str, textStyle, included)
|
||||
}
|
||||
|
||||
// 'selected' means you've got it highlighted with your cursor
|
||||
// 'included' means the line has been included in the patch (only applicable when
|
||||
// building a patch)
|
||||
func (self *patchPresenter) formatLineAux(str string, textStyle style.TextStyle, index int, included bool) string {
|
||||
func (self *patchPresenter) formatLineAux(str string, textStyle style.TextStyle, included bool) string {
|
||||
if self.plain {
|
||||
return str
|
||||
}
|
||||
|
||||
selected := self.isFocused && index >= self.firstLineIndex && index <= self.lastLineIndex
|
||||
|
||||
if selected {
|
||||
textStyle = textStyle.MergeStyle(theme.SelectedRangeBgColor)
|
||||
}
|
||||
|
||||
firstCharStyle := textStyle
|
||||
if included {
|
||||
firstCharStyle = firstCharStyle.MergeStyle(style.BgGreen)
|
||||
|
|
|
@ -197,9 +197,7 @@ func (p *PatchBuilder) RenderPatchForFile(filename string, plain bool, reverse b
|
|||
if plain {
|
||||
return patch.FormatPlain()
|
||||
} else {
|
||||
return patch.FormatView(FormatViewOpts{
|
||||
IsFocused: false,
|
||||
})
|
||||
return patch.FormatView(FormatViewOpts{})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -154,9 +154,6 @@ type ThemeConfig struct {
|
|||
// Background color of selected line.
|
||||
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#highlighting-the-selected-line
|
||||
SelectedLineBgColor []string `yaml:"selectedLineBgColor" jsonschema:"minItems=1,uniqueItems=true"`
|
||||
// Background color of selected range
|
||||
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#highlighting-the-selected-line
|
||||
SelectedRangeBgColor []string `yaml:"selectedRangeBgColor" jsonschema:"minItems=1,uniqueItems=true"`
|
||||
// Foreground color of copied commit
|
||||
CherryPickedCommitFgColor []string `yaml:"cherryPickedCommitFgColor" jsonschema:"minItems=1,uniqueItems=true"`
|
||||
// Background color of copied commit
|
||||
|
@ -622,7 +619,6 @@ func GetDefaultConfig() *UserConfig {
|
|||
InactiveBorderColor: []string{"default"},
|
||||
OptionsTextColor: []string{"blue"},
|
||||
SelectedLineBgColor: []string{"blue"},
|
||||
SelectedRangeBgColor: []string{"blue"},
|
||||
CherryPickedCommitBgColor: []string{"cyan"},
|
||||
CherryPickedCommitFgColor: []string{"blue"},
|
||||
MarkedBaseCommitBgColor: []string{"yellow"},
|
||||
|
|
|
@ -68,8 +68,8 @@ func (self *MergeConflictsContext) IsUserScrolling() bool {
|
|||
return self.viewModel.userVerticalScrolling
|
||||
}
|
||||
|
||||
func (self *MergeConflictsContext) RenderAndFocus(isFocused bool) error {
|
||||
self.setContent(isFocused)
|
||||
func (self *MergeConflictsContext) RenderAndFocus() error {
|
||||
self.setContent()
|
||||
self.FocusSelection()
|
||||
|
||||
self.c.Render()
|
||||
|
@ -77,30 +77,41 @@ func (self *MergeConflictsContext) RenderAndFocus(isFocused bool) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (self *MergeConflictsContext) Render(isFocused bool) error {
|
||||
self.setContent(isFocused)
|
||||
func (self *MergeConflictsContext) Render() error {
|
||||
self.setContent()
|
||||
|
||||
self.c.Render()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *MergeConflictsContext) GetContentToRender(isFocused bool) string {
|
||||
func (self *MergeConflictsContext) GetContentToRender() string {
|
||||
if self.GetState() == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return mergeconflicts.ColoredConflictFile(self.GetState(), isFocused)
|
||||
return mergeconflicts.ColoredConflictFile(self.GetState())
|
||||
}
|
||||
|
||||
func (self *MergeConflictsContext) setContent(isFocused bool) {
|
||||
self.GetView().SetContent(self.GetContentToRender(isFocused))
|
||||
func (self *MergeConflictsContext) setContent() {
|
||||
self.GetView().SetContent(self.GetContentToRender())
|
||||
}
|
||||
|
||||
func (self *MergeConflictsContext) FocusSelection() {
|
||||
if !self.IsUserScrolling() {
|
||||
_ = self.GetView().SetOriginY(self.GetOriginY())
|
||||
}
|
||||
|
||||
self.SetSelectedLineRange()
|
||||
}
|
||||
|
||||
func (self *MergeConflictsContext) SetSelectedLineRange() {
|
||||
startIdx, endIdx := self.GetState().GetSelectedRange()
|
||||
view := self.GetView()
|
||||
originY := view.OriginY()
|
||||
// As far as the view is concerned, we are always selecting a range
|
||||
view.SetRangeSelectStart(startIdx)
|
||||
view.SetCursorY(endIdx - originY)
|
||||
}
|
||||
|
||||
func (self *MergeConflictsContext) GetOriginY() int {
|
||||
|
|
|
@ -115,17 +115,10 @@ func (self *PatchExplorerContext) FocusSelection() {
|
|||
|
||||
_ = view.SetOriginY(newOriginY)
|
||||
|
||||
view.SetCursorY(state.GetSelectedLineIdx() - newOriginY)
|
||||
|
||||
// At present this is just bookkeeping: the reason for setting this would be
|
||||
// so that gocui knows which lines to highlight, but we're currently handling
|
||||
// highlighting ourselves.
|
||||
rangeStartLineIdx, isSelectingRange := state.RangeStartLineIdx()
|
||||
if isSelectingRange {
|
||||
view.SetRangeSelectStart(rangeStartLineIdx)
|
||||
} else {
|
||||
view.CancelRangeSelect()
|
||||
}
|
||||
startIdx, endIdx := state.SelectedRange()
|
||||
// As far as the view is concerned, we are always selecting a range
|
||||
view.SetRangeSelectStart(startIdx)
|
||||
view.SetCursorY(endIdx - newOriginY)
|
||||
}
|
||||
|
||||
func (self *PatchExplorerContext) GetContentToRender(isFocused bool) string {
|
||||
|
|
|
@ -205,7 +205,7 @@ func (self *FilesController) GetOnRenderToMain() func() error {
|
|||
}
|
||||
|
||||
if hasConflicts {
|
||||
return self.c.Helpers().MergeConflicts.Render(false)
|
||||
return self.c.Helpers().MergeConflicts.Render()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -69,14 +69,14 @@ func (self *MergeConflictsHelper) EscapeMerge() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (self *MergeConflictsHelper) SetConflictsAndRender(path string, isFocused bool) (bool, error) {
|
||||
func (self *MergeConflictsHelper) SetConflictsAndRender(path string) (bool, error) {
|
||||
hasConflicts, err := self.setMergeStateWithoutLock(path)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if hasConflicts {
|
||||
return true, self.context().Render(isFocused)
|
||||
return true, self.context().Render()
|
||||
}
|
||||
|
||||
return false, nil
|
||||
|
@ -100,8 +100,8 @@ func (self *MergeConflictsHelper) context() *context.MergeConflictsContext {
|
|||
return self.c.Contexts().MergeConflicts
|
||||
}
|
||||
|
||||
func (self *MergeConflictsHelper) Render(isFocused bool) error {
|
||||
content := self.context().GetContentToRender(isFocused)
|
||||
func (self *MergeConflictsHelper) Render() error {
|
||||
content := self.context().GetContentToRender()
|
||||
|
||||
var task types.UpdateTask
|
||||
if self.context().IsUserScrolling() {
|
||||
|
@ -127,7 +127,7 @@ func (self *MergeConflictsHelper) RefreshMergeState() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
hasConflicts, err := self.SetConflictsAndRender(self.c.Contexts().MergeConflicts.GetState().GetPath(), true)
|
||||
hasConflicts, err := self.SetConflictsAndRender(self.c.Contexts().MergeConflicts.GetState().GetPath())
|
||||
if err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
|
|
@ -145,7 +145,13 @@ func (self *MergeConflictsController) GetOnFocus() func(types.OnFocusOpts) error
|
|||
return func(types.OnFocusOpts) error {
|
||||
self.c.Views().MergeConflicts.Wrap = false
|
||||
|
||||
return self.c.Helpers().MergeConflicts.Render(true)
|
||||
if err := self.c.Helpers().MergeConflicts.Render(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.context().SetSelectedLineRange()
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -313,17 +319,13 @@ func (self *MergeConflictsController) onLastConflictResolved() error {
|
|||
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}})
|
||||
}
|
||||
|
||||
func (self *MergeConflictsController) isFocused() bool {
|
||||
return self.c.CurrentContext().GetKey() == self.context().GetKey()
|
||||
}
|
||||
|
||||
func (self *MergeConflictsController) withRenderAndFocus(f func() error) func() error {
|
||||
return self.withLock(func() error {
|
||||
if err := f(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return self.context().RenderAndFocus(self.isFocused())
|
||||
return self.context().RenderAndFocus()
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||
"github.com/samber/lo"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
@ -143,15 +142,6 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
|||
gui.State.ViewsSetup = true
|
||||
}
|
||||
|
||||
for _, listContext := range gui.c.Context().AllList() {
|
||||
view, err := gui.g.View(listContext.GetViewName())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
view.SelBgColor = theme.GocuiSelectedLineBgColor
|
||||
}
|
||||
|
||||
mainViewWidth, mainViewHeight := gui.Views.Main.Size()
|
||||
if mainViewWidth != gui.PrevLayout.MainWidth || mainViewHeight != gui.PrevLayout.MainHeight {
|
||||
gui.PrevLayout.MainWidth = mainViewWidth
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
func ColoredConflictFile(state *State, hasFocus bool) string {
|
||||
func ColoredConflictFile(state *State) string {
|
||||
content := state.GetContent()
|
||||
if len(state.conflicts) == 0 {
|
||||
return content
|
||||
|
@ -21,9 +21,6 @@ func ColoredConflictFile(state *State, hasFocus bool) string {
|
|||
textStyle = style.FgRed
|
||||
}
|
||||
|
||||
if hasFocus && state.conflictIndex < len(state.conflicts) && *state.conflicts[state.conflictIndex] == *conflict && shouldHighlightLine(i, conflict, state.Selection()) {
|
||||
textStyle = textStyle.MergeStyle(theme.SelectedRangeBgColor).SetBold()
|
||||
}
|
||||
if i == conflict.end && len(remainingConflicts) > 0 {
|
||||
conflict, remainingConflicts = shiftConflict(remainingConflicts)
|
||||
}
|
||||
|
@ -35,8 +32,3 @@ func ColoredConflictFile(state *State, hasFocus bool) string {
|
|||
func shiftConflict(conflicts []*mergeConflict) (*mergeConflict, []*mergeConflict) {
|
||||
return conflicts[0], conflicts[1:]
|
||||
}
|
||||
|
||||
func shouldHighlightLine(index int, conflict *mergeConflict, selection Selection) bool {
|
||||
selectionStart, selectionEnd := selection.bounds(conflict)
|
||||
return index >= selectionStart && index <= selectionEnd
|
||||
}
|
||||
|
|
|
@ -240,12 +240,8 @@ func (s *State) AdjustSelectedLineIdx(change int) {
|
|||
}
|
||||
|
||||
func (s *State) RenderForLineIndices(isFocused bool, includedLineIndices []int) string {
|
||||
firstLineIdx, lastLineIdx := s.SelectedRange()
|
||||
includedLineIndicesSet := set.NewFromSlice(includedLineIndices)
|
||||
return s.patch.FormatView(patch.FormatViewOpts{
|
||||
IsFocused: isFocused,
|
||||
FirstLineIndex: firstLineIdx,
|
||||
LastLineIndex: lastLineIdx,
|
||||
IncLineIndices: includedLineIndicesSet,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -91,6 +91,7 @@ func (gui *Gui) createAllViews() error {
|
|||
}
|
||||
(*mapping.viewPtr).FrameRunes = frameRunes
|
||||
(*mapping.viewPtr).FgColor = theme.GocuiDefaultTextColor
|
||||
(*mapping.viewPtr).SelBgColor = theme.GocuiSelectedLineBgColor
|
||||
}
|
||||
|
||||
gui.Views.Options.FgColor = theme.OptionsColor
|
||||
|
@ -134,23 +135,18 @@ func (gui *Gui) createAllViews() error {
|
|||
}
|
||||
|
||||
gui.Views.Staging.Title = gui.c.Tr.UnstagedChanges
|
||||
gui.Views.Staging.Highlight = false
|
||||
gui.Views.Staging.Wrap = true
|
||||
|
||||
gui.Views.StagingSecondary.Title = gui.c.Tr.StagedChanges
|
||||
gui.Views.StagingSecondary.Highlight = false
|
||||
gui.Views.StagingSecondary.Wrap = true
|
||||
|
||||
gui.Views.PatchBuilding.Title = gui.Tr.Patch
|
||||
gui.Views.PatchBuilding.Highlight = false
|
||||
gui.Views.PatchBuilding.Wrap = true
|
||||
|
||||
gui.Views.PatchBuildingSecondary.Title = gui.Tr.CustomPatch
|
||||
gui.Views.PatchBuildingSecondary.Highlight = false
|
||||
gui.Views.PatchBuildingSecondary.Wrap = true
|
||||
|
||||
gui.Views.MergeConflicts.Title = gui.c.Tr.MergeConflictsTitle
|
||||
gui.Views.MergeConflicts.Highlight = false
|
||||
gui.Views.MergeConflicts.Wrap = false
|
||||
|
||||
gui.Views.Limit.Title = gui.c.Tr.NotEnoughSpace
|
||||
|
|
|
@ -10,45 +10,24 @@ import (
|
|||
|
||||
type ViewDriver struct {
|
||||
// context is prepended to any error messages e.g. 'context: "current view"'
|
||||
context string
|
||||
getView func() *gocui.View
|
||||
t *TestDriver
|
||||
getSelectedLinesFn func() ([]string, error)
|
||||
getSelectedRangeFn func() (int, int, error)
|
||||
getSelectedLineIdxFn func() (int, error)
|
||||
context string
|
||||
getView func() *gocui.View
|
||||
t *TestDriver
|
||||
}
|
||||
|
||||
func (self *ViewDriver) getSelectedLines() ([]string, error) {
|
||||
if self.getSelectedLinesFn == nil {
|
||||
view := self.t.gui.View(self.getView().Name())
|
||||
|
||||
return []string{view.SelectedLine()}, nil
|
||||
}
|
||||
|
||||
return self.getSelectedLinesFn()
|
||||
func (self *ViewDriver) getSelectedLines() []string {
|
||||
view := self.t.gui.View(self.getView().Name())
|
||||
return view.SelectedLines()
|
||||
}
|
||||
|
||||
func (self *ViewDriver) getSelectedRange() (int, int, error) {
|
||||
if self.getSelectedRangeFn == nil {
|
||||
view := self.t.gui.View(self.getView().Name())
|
||||
idx := view.SelectedLineIdx()
|
||||
|
||||
return idx, idx, nil
|
||||
}
|
||||
|
||||
return self.getSelectedRangeFn()
|
||||
func (self *ViewDriver) getSelectedRange() (int, int) {
|
||||
view := self.t.gui.View(self.getView().Name())
|
||||
return view.SelectedLineRange()
|
||||
}
|
||||
|
||||
// even if you have a selected range, there may still be a line within that range
|
||||
// which the cursor points at. This function returns that line index.
|
||||
func (self *ViewDriver) getSelectedLineIdx() (int, error) {
|
||||
if self.getSelectedLineIdxFn == nil {
|
||||
view := self.t.gui.View(self.getView().Name())
|
||||
|
||||
return view.SelectedLineIdx(), nil
|
||||
}
|
||||
|
||||
return self.getSelectedLineIdxFn()
|
||||
func (self *ViewDriver) getSelectedLineIdx() int {
|
||||
view := self.t.gui.View(self.getView().Name())
|
||||
return view.SelectedLineIdx()
|
||||
}
|
||||
|
||||
// asserts that the view has the expected title
|
||||
|
@ -105,7 +84,7 @@ func (self *ViewDriver) ContainsLines(matchers ...*TextMatcher) *ViewDriver {
|
|||
content := self.getView().Buffer()
|
||||
lines := strings.Split(content, "\n")
|
||||
|
||||
startIdx, endIdx, err := self.getSelectedRange()
|
||||
startIdx, endIdx := self.getSelectedRange()
|
||||
|
||||
for i := 0; i < len(lines)-len(matchers)+1; i++ {
|
||||
matches := true
|
||||
|
@ -118,10 +97,6 @@ func (self *ViewDriver) ContainsLines(matchers ...*TextMatcher) *ViewDriver {
|
|||
break
|
||||
}
|
||||
if checkIsSelected {
|
||||
if err != nil {
|
||||
matches = false
|
||||
break
|
||||
}
|
||||
if lineIdx < startIdx || lineIdx > endIdx {
|
||||
matches = false
|
||||
break
|
||||
|
@ -181,10 +156,7 @@ func (self *ViewDriver) SelectedLines(matchers ...*TextMatcher) *ViewDriver {
|
|||
self.validateEnoughLines(matchers)
|
||||
|
||||
self.t.assertWithRetries(func() (bool, string) {
|
||||
selectedLines, err := self.getSelectedLines()
|
||||
if err != nil {
|
||||
return false, err.Error()
|
||||
}
|
||||
selectedLines := self.getSelectedLines()
|
||||
|
||||
selectedContent := strings.Join(selectedLines, "\n")
|
||||
expectedContent := expectedContentFromMatchers(matchers)
|
||||
|
@ -251,19 +223,13 @@ func (self *ViewDriver) assertLines(offset int, matchers ...*TextMatcher) *ViewD
|
|||
|
||||
if checkIsSelected {
|
||||
self.t.assertWithRetries(func() (bool, string) {
|
||||
startIdx, endIdx, err := self.getSelectedRange()
|
||||
if err != nil {
|
||||
return false, err.Error()
|
||||
}
|
||||
startIdx, endIdx := self.getSelectedRange()
|
||||
|
||||
if lineIdx < startIdx || lineIdx > endIdx {
|
||||
if startIdx == endIdx {
|
||||
return false, fmt.Sprintf("Unexpected selected line index in view '%s'. Expected %d, got %d", view.Name(), lineIdx, startIdx)
|
||||
} else {
|
||||
lines, err := self.getSelectedLines()
|
||||
if err != nil {
|
||||
return false, err.Error()
|
||||
}
|
||||
lines := self.getSelectedLines()
|
||||
return false, fmt.Sprintf("Unexpected selected line index in view '%s'. Expected line %d to be in range %d to %d. Selected lines:\n---\n%s\n---\n\nExpected line: '%s'", view.Name(), lineIdx, startIdx, endIdx, strings.Join(lines, "\n"), matcher.name())
|
||||
}
|
||||
}
|
||||
|
@ -286,15 +252,11 @@ func (self *ViewDriver) Content(matcher *TextMatcher) *ViewDriver {
|
|||
return self
|
||||
}
|
||||
|
||||
// asserts on the selected line of the view. If your view has multiple lines selected,
|
||||
// but also has a concept of a cursor position, this will assert on the line that
|
||||
// the cursor is on. Otherwise it will assert on the first line of the selection.
|
||||
// asserts on the selected line of the view. If you are selecting a range,
|
||||
// you should use the SelectedLines method instead.
|
||||
func (self *ViewDriver) SelectedLine(matcher *TextMatcher) *ViewDriver {
|
||||
self.t.assertWithRetries(func() (bool, string) {
|
||||
selectedLineIdx, err := self.getSelectedLineIdx()
|
||||
if err != nil {
|
||||
return false, err.Error()
|
||||
}
|
||||
selectedLineIdx := self.getSelectedLineIdx()
|
||||
|
||||
viewLines := self.getView().BufferLines()
|
||||
|
||||
|
@ -480,11 +442,7 @@ func (self *ViewDriver) NavigateToLine(matcher *TextMatcher) *ViewDriver {
|
|||
}
|
||||
})
|
||||
|
||||
selectedLineIdx, err := self.getSelectedLineIdx()
|
||||
if err != nil {
|
||||
self.t.fail(err.Error())
|
||||
return self
|
||||
}
|
||||
selectedLineIdx := self.getSelectedLineIdx()
|
||||
if selectedLineIdx == matchIndex {
|
||||
return self.SelectedLine(matcher)
|
||||
}
|
||||
|
@ -507,11 +465,7 @@ func (self *ViewDriver) NavigateToLine(matcher *TextMatcher) *ViewDriver {
|
|||
|
||||
for i := 0; i < maxNumKeyPresses; i++ {
|
||||
keyPress()
|
||||
idx, err := self.getSelectedLineIdx()
|
||||
if err != nil {
|
||||
self.t.fail(err.Error())
|
||||
return self
|
||||
}
|
||||
idx := self.getSelectedLineIdx()
|
||||
if ok, _ := matcher.test(lines[idx]); ok {
|
||||
return self
|
||||
}
|
||||
|
|
|
@ -2,11 +2,8 @@ package components
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
)
|
||||
|
||||
type Views struct {
|
||||
|
@ -30,95 +27,19 @@ func (self *Views) Secondary() *ViewDriver {
|
|||
}
|
||||
|
||||
func (self *Views) regularView(viewName string) *ViewDriver {
|
||||
return self.newStaticViewDriver(viewName, nil, nil, nil)
|
||||
}
|
||||
|
||||
func (self *Views) patchExplorerViewByName(viewName string) *ViewDriver {
|
||||
return self.newStaticViewDriver(
|
||||
viewName,
|
||||
func() ([]string, error) {
|
||||
ctx := self.t.gui.ContextForView(viewName).(*context.PatchExplorerContext)
|
||||
state := ctx.GetState()
|
||||
if state == nil {
|
||||
return nil, errors.New("Expected patch explorer to be activated")
|
||||
}
|
||||
selectedContent := state.PlainRenderSelected()
|
||||
// the above method returns a string with a trailing newline so we need to remove that before splitting
|
||||
selectedLines := strings.Split(strings.TrimSuffix(selectedContent, "\n"), "\n")
|
||||
return selectedLines, nil
|
||||
},
|
||||
func() (int, int, error) {
|
||||
ctx := self.t.gui.ContextForView(viewName).(*context.PatchExplorerContext)
|
||||
state := ctx.GetState()
|
||||
if state == nil {
|
||||
return 0, 0, errors.New("Expected patch explorer to be activated")
|
||||
}
|
||||
startIdx, endIdx := state.SelectedRange()
|
||||
return startIdx, endIdx, nil
|
||||
},
|
||||
func() (int, error) {
|
||||
ctx := self.t.gui.ContextForView(viewName).(*context.PatchExplorerContext)
|
||||
state := ctx.GetState()
|
||||
if state == nil {
|
||||
return 0, errors.New("Expected patch explorer to be activated")
|
||||
}
|
||||
return state.GetSelectedLineIdx(), nil
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// 'static' because it'll always refer to the same view, as opposed to the 'main' view which could actually be
|
||||
// one of several views, or the 'current' view which depends on focus.
|
||||
func (self *Views) newStaticViewDriver(
|
||||
viewName string,
|
||||
getSelectedLinesFn func() ([]string, error),
|
||||
getSelectedLineRangeFn func() (int, int, error),
|
||||
getSelectedLineIdxFn func() (int, error),
|
||||
) *ViewDriver {
|
||||
return &ViewDriver{
|
||||
context: fmt.Sprintf("%s view", viewName),
|
||||
getView: func() *gocui.View { return self.t.gui.View(viewName) },
|
||||
getSelectedLinesFn: getSelectedLinesFn,
|
||||
getSelectedRangeFn: getSelectedLineRangeFn,
|
||||
getSelectedLineIdxFn: getSelectedLineIdxFn,
|
||||
t: self.t,
|
||||
context: fmt.Sprintf("%s view", viewName),
|
||||
getView: func() *gocui.View { return self.t.gui.View(viewName) },
|
||||
t: self.t,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Views) MergeConflicts() *ViewDriver {
|
||||
viewName := "mergeConflicts"
|
||||
return self.newStaticViewDriver(
|
||||
viewName,
|
||||
func() ([]string, error) {
|
||||
ctx := self.t.gui.ContextForView(viewName).(*context.MergeConflictsContext)
|
||||
state := ctx.GetState()
|
||||
if state == nil {
|
||||
return nil, errors.New("Expected patch explorer to be activated")
|
||||
}
|
||||
selectedContent := strings.Split(state.PlainRenderSelected(), "\n")
|
||||
func (self *Views) patchExplorerViewByName(viewName string) *ViewDriver {
|
||||
return self.regularView(viewName)
|
||||
}
|
||||
|
||||
return selectedContent, nil
|
||||
},
|
||||
func() (int, int, error) {
|
||||
ctx := self.t.gui.ContextForView(viewName).(*context.MergeConflictsContext)
|
||||
state := ctx.GetState()
|
||||
if state == nil {
|
||||
return 0, 0, errors.New("Expected patch explorer to be activated")
|
||||
}
|
||||
startIdx, endIdx := state.GetSelectedRange()
|
||||
return startIdx, endIdx, nil
|
||||
},
|
||||
// there is no concept of a cursor in the merge conflicts panel so we just return the start of the selection
|
||||
func() (int, error) {
|
||||
ctx := self.t.gui.ContextForView(viewName).(*context.MergeConflictsContext)
|
||||
state := ctx.GetState()
|
||||
if state == nil {
|
||||
return 0, errors.New("Expected patch explorer to be activated")
|
||||
}
|
||||
startIdx, _ := state.GetSelectedRange()
|
||||
return startIdx, nil
|
||||
},
|
||||
)
|
||||
func (self *Views) MergeConflicts() *ViewDriver {
|
||||
return self.regularView("mergeConflicts")
|
||||
}
|
||||
|
||||
func (self *Views) Commits() *ViewDriver {
|
||||
|
|
|
@ -260,6 +260,7 @@ var tests = []*components.IntegrationTest{
|
|||
ui.DoublePopup,
|
||||
ui.EmptyMenu,
|
||||
ui.OpenLinkFailure,
|
||||
ui.RangeSelect,
|
||||
ui.SwitchTabFromMenu,
|
||||
undo.UndoCheckoutAndDrop,
|
||||
undo.UndoDrop,
|
||||
|
|
142
pkg/integration/tests/ui/range_select.go
Normal file
142
pkg/integration/tests/ui/range_select.go
Normal file
|
@ -0,0 +1,142 @@
|
|||
package ui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
// Here's the state machine we need to verify:
|
||||
// (no range, press 'v') -> sticky range
|
||||
// (no range, press arrow) -> no range
|
||||
// (no range, press shift+arrow) -> nonsticky range
|
||||
// (sticky range, press 'v') -> no range
|
||||
// (sticky range, press arrow) -> sticky range
|
||||
// (sticky range, press shift+arrow) -> nonsticky range
|
||||
// (nonsticky range, press 'v') -> no range
|
||||
// (nonsticky range, press arrow) -> no range
|
||||
// (nonsticky range, press shift+arrow) -> nonsticky range
|
||||
|
||||
// Importantly, if you press 'v' when in a nonsticky range, it clears the range,
|
||||
// so no matter which mode you're in, 'v' will cancel the range.
|
||||
// And, if you press shift+up/down when in a sticky range, it switches to a non-
|
||||
// sticky range, meaning if you then press up/down without shift, it clears
|
||||
// the range.
|
||||
|
||||
var RangeSelect = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Verify range select works as expected in list views and in patch explorer views",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
// We're testing the commits view as our representative list context,
|
||||
// as well as the staging view, and we're using the exact same code to test
|
||||
// both to ensure they have the exact same behaviour (they are currently implemented
|
||||
// separately)
|
||||
// In both views we're going to have 10 lines starting from 'line 1' going down to
|
||||
// 'line 10'.
|
||||
fileContent := ""
|
||||
total := 10
|
||||
for i := 1; i <= total; i++ {
|
||||
remaining := total - i + 1
|
||||
// Commits are displayed in reverse order so to we need to create them in reverse to have them appear as 'line 1', 'line 2' etc.
|
||||
shell.EmptyCommit(fmt.Sprintf("line %d", remaining))
|
||||
fileContent = fmt.Sprintf("%sline %d\n", fileContent, i)
|
||||
}
|
||||
shell.CreateFile("file1", fileContent)
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
assertRangeSelectBehaviour := func(v *ViewDriver) {
|
||||
v.
|
||||
SelectedLines(
|
||||
Contains("line 1"),
|
||||
).
|
||||
// (no range, press 'v') -> sticky range
|
||||
Press(keys.Universal.ToggleRangeSelect).
|
||||
SelectedLines(
|
||||
Contains("line 1"),
|
||||
).
|
||||
// (sticky range, press arrow) -> sticky range
|
||||
SelectNextItem().
|
||||
SelectedLines(
|
||||
Contains("line 1"),
|
||||
Contains("line 2"),
|
||||
).
|
||||
// (sticky range, press 'v') -> no range
|
||||
Press(keys.Universal.ToggleRangeSelect).
|
||||
SelectedLines(
|
||||
Contains("line 2"),
|
||||
).
|
||||
// (no range, press arrow) -> no range
|
||||
SelectPreviousItem().
|
||||
SelectedLines(
|
||||
Contains("line 1"),
|
||||
).
|
||||
// (no range, press shift+arrow) -> nonsticky range
|
||||
Press(keys.Universal.RangeSelectDown).
|
||||
SelectedLines(
|
||||
Contains("line 1"),
|
||||
Contains("line 2"),
|
||||
).
|
||||
// (nonsticky range, press shift+arrow) -> nonsticky range
|
||||
Press(keys.Universal.RangeSelectDown).
|
||||
SelectedLines(
|
||||
Contains("line 1"),
|
||||
Contains("line 2"),
|
||||
Contains("line 3"),
|
||||
).
|
||||
Press(keys.Universal.RangeSelectUp).
|
||||
SelectedLines(
|
||||
Contains("line 1"),
|
||||
Contains("line 2"),
|
||||
).
|
||||
// (nonsticky range, press arrow) -> no range
|
||||
SelectNextItem().
|
||||
SelectedLines(
|
||||
Contains("line 3"),
|
||||
).
|
||||
Press(keys.Universal.ToggleRangeSelect).
|
||||
SelectedLines(
|
||||
Contains("line 3"),
|
||||
).
|
||||
SelectNextItem().
|
||||
SelectedLines(
|
||||
Contains("line 3"),
|
||||
Contains("line 4"),
|
||||
).
|
||||
// (sticky range, press shift+arrow) -> nonsticky range
|
||||
Press(keys.Universal.RangeSelectDown).
|
||||
SelectedLines(
|
||||
Contains("line 3"),
|
||||
Contains("line 4"),
|
||||
Contains("line 5"),
|
||||
).
|
||||
SelectNextItem().
|
||||
SelectedLines(
|
||||
Contains("line 6"),
|
||||
).
|
||||
Press(keys.Universal.RangeSelectDown).
|
||||
SelectedLines(
|
||||
Contains("line 6"),
|
||||
Contains("line 7"),
|
||||
).
|
||||
// (nonsticky range, press 'v') -> no range
|
||||
Press(keys.Universal.ToggleRangeSelect).
|
||||
SelectedLines(
|
||||
Contains("line 7"),
|
||||
)
|
||||
}
|
||||
|
||||
assertRangeSelectBehaviour(t.Views().Commits().Focus())
|
||||
|
||||
t.Views().Files().
|
||||
Focus().
|
||||
SelectedLine(
|
||||
Contains("file1"),
|
||||
).
|
||||
PressEnter()
|
||||
|
||||
assertRangeSelectBehaviour(t.Views().Staging().IsFocused())
|
||||
},
|
||||
})
|
|
@ -30,9 +30,6 @@ var (
|
|||
// SelectedLineBgColor is the background color for the selected line
|
||||
SelectedLineBgColor = style.New()
|
||||
|
||||
// SelectedRangeBgColor is the background color of the selected range of lines
|
||||
SelectedRangeBgColor = style.New()
|
||||
|
||||
// CherryPickedCommitColor is the text style when cherry picking a commit
|
||||
CherryPickedCommitTextStyle = style.New()
|
||||
|
||||
|
@ -52,7 +49,6 @@ func UpdateTheme(themeConfig config.ThemeConfig) {
|
|||
InactiveBorderColor = GetGocuiStyle(themeConfig.InactiveBorderColor)
|
||||
SearchingActiveBorderColor = GetGocuiStyle(themeConfig.SearchingActiveBorderColor)
|
||||
SelectedLineBgColor = GetTextStyle(themeConfig.SelectedLineBgColor, true)
|
||||
SelectedRangeBgColor = GetTextStyle(themeConfig.SelectedRangeBgColor, true)
|
||||
|
||||
cherryPickedCommitBgTextStyle := GetTextStyle(themeConfig.CherryPickedCommitBgColor, true)
|
||||
cherryPickedCommitFgTextStyle := GetTextStyle(themeConfig.CherryPickedCommitFgColor, false)
|
||||
|
|
|
@ -176,18 +176,6 @@
|
|||
"blue"
|
||||
]
|
||||
},
|
||||
"selectedRangeBgColor": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"uniqueItems": true,
|
||||
"description": "Background color of selected range\nSee https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#highlighting-the-selected-line",
|
||||
"default": [
|
||||
"blue"
|
||||
]
|
||||
},
|
||||
"cherryPickedCommitFgColor": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
|
@ -667,6 +655,18 @@
|
|||
"type": "string",
|
||||
"default": "\u003e"
|
||||
},
|
||||
"toggleRangeSelect": {
|
||||
"type": "string",
|
||||
"default": "v"
|
||||
},
|
||||
"rangeSelectDown": {
|
||||
"type": "string",
|
||||
"default": "\u003cs-down\u003e"
|
||||
},
|
||||
"rangeSelectUp": {
|
||||
"type": "string",
|
||||
"default": "\u003cs-up\u003e"
|
||||
},
|
||||
"prevBlock": {
|
||||
"type": "string",
|
||||
"default": "\u003cleft\u003e"
|
||||
|
|
|
@ -10,8 +10,6 @@ gui:
|
|||
- bold
|
||||
inactiveBorderColor:
|
||||
- black
|
||||
SelectedRangeBgcolor:
|
||||
- reverse
|
||||
# Not important in tests but it creates clutter in demos
|
||||
showRandomTip: false
|
||||
animateExplosion: false # takes too long
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue