mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-07-30 03:23:08 +03:00
add integration test for staging view
This commit is contained in:
@ -0,0 +1 @@
|
||||
test
|
1
test/integration/staging/expected/.git_keep/HEAD
Normal file
1
test/integration/staging/expected/.git_keep/HEAD
Normal file
@ -0,0 +1 @@
|
||||
ref: refs/heads/master
|
10
test/integration/staging/expected/.git_keep/config
Normal file
10
test/integration/staging/expected/.git_keep/config
Normal file
@ -0,0 +1,10 @@
|
||||
[core]
|
||||
repositoryformatversion = 0
|
||||
filemode = true
|
||||
bare = false
|
||||
logallrefupdates = true
|
||||
ignorecase = true
|
||||
precomposeunicode = true
|
||||
[user]
|
||||
email = CI@example.com
|
||||
name = CI
|
1
test/integration/staging/expected/.git_keep/description
Normal file
1
test/integration/staging/expected/.git_keep/description
Normal file
@ -0,0 +1 @@
|
||||
Unnamed repository; edit this file 'description' to name the repository.
|
BIN
test/integration/staging/expected/.git_keep/index
Normal file
BIN
test/integration/staging/expected/.git_keep/index
Normal file
Binary file not shown.
7
test/integration/staging/expected/.git_keep/info/exclude
Normal file
7
test/integration/staging/expected/.git_keep/info/exclude
Normal file
@ -0,0 +1,7 @@
|
||||
# git ls-files --others --exclude-from=.git/info/exclude
|
||||
# Lines that start with '#' are comments.
|
||||
# For a project mostly in C, the following would be a good set of
|
||||
# exclude patterns (uncomment them if you want to use them):
|
||||
# *.[oa]
|
||||
# *~
|
||||
.DS_Store
|
2
test/integration/staging/expected/.git_keep/logs/HEAD
Normal file
2
test/integration/staging/expected/.git_keep/logs/HEAD
Normal file
@ -0,0 +1,2 @@
|
||||
0000000000000000000000000000000000000000 6806c569c7c063ec7ed3d16da3faa32a1622bb4b CI <CI@example.com> 1642402468 +1100 commit (initial): file1
|
||||
6806c569c7c063ec7ed3d16da3faa32a1622bb4b 4253d2597aec2a480a8e9054250e6b4aa5b76d9e CI <CI@example.com> 1642402495 +1100 commit: test
|
@ -0,0 +1,2 @@
|
||||
0000000000000000000000000000000000000000 6806c569c7c063ec7ed3d16da3faa32a1622bb4b CI <CI@example.com> 1642402468 +1100 commit (initial): file1
|
||||
6806c569c7c063ec7ed3d16da3faa32a1622bb4b 4253d2597aec2a480a8e9054250e6b4aa5b76d9e CI <CI@example.com> 1642402495 +1100 commit: test
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,2 @@
|
||||
x<01><>A
|
||||
<EFBFBD>0@Q<>9E<39><45>d<EFBFBD><64>4<EFBFBD>"BW=<3D>L:A<><41>R#x|{<04><><EFBFBD><EFBFBD>em<65><6D>-d8<64>]<5D>:̘H"%IYjN<1A><x<><11>P<>#c5<1B><>ꖒ<EFBFBD><EA9692><EFBFBD><EFBFBD>P-<2D>.aZ8T<38><54><19>{<7B>(<28>?<3F><><EFBFBD>v<EFBFBD><76>8<EFBFBD>7<EFBFBD>r۞z)k<>Z<EFBFBD><5A><EFBFBD><EFBFBD>1<EFBFBD>=8g<38>zLu<4C><75><EFBFBD><EFBFBD><EFBFBD>n~!<21>9o
|
@ -0,0 +1,4 @@
|
||||
xM<><4D>n<EFBFBD>0<0C>;<3B>)<29>N<EFBFBD><4E><EFBFBD>t<EFBFBD><74>N:d<><05><>Y<EFBFBD>hK8Yt%<25><><EFBFBD><EFBFBD>y<><I()
|
||||
<18>E<EFBFBD>"?<3F><>x<>㗇O<E39787>"G<>FT:<3A>Մ+Y<>.^k<>m<><6D><10>2<>F<EFBFBD><46>ËX<>&Zh<5A>"<22><07><>s<><73>9h<39><68><EFBFBD>M<EFBFBD><1C><><EFBFBD><19><>!<21><>a<EFBFBD><61><EFBFBD><EFBFBD>c<EFBFBD><63><EFBFBD><EFBFBD><EFBFBD>T?
|
||||
<17><>;e%:dĬ<64>-<2D><><EFBFBD>l<04>m< j҆4
|
||||
Otj%H<><48><EFBFBD><1A>\B ɝ<>5<EFBFBD>^<5E><1C><><EFBFBD>%sD
|
@ -0,0 +1,2 @@
|
||||
x<01><>A
|
||||
<EFBFBD>0Fa<46>9<EFBFBD><39><05>I~B"BW=<3D>$<24>`<60>!R"x|{<04><>^<5E><>m<EFBFBD><04><>Ìj6N*<2A>BȀi䌘W<E48C98>a<04><>(SR8<52><38>W?h^<5E>>/O<>j{<7B>v+<2B>=H"<<3C>#Ntavg='<27><><EFBFBD><EFBFBD>n<EFBFBD><6E><EFBFBD><01><>+<2B>
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,2 @@
|
||||
xM<><4D>n<EFBFBD>0<0C>;<3B>)<29>N<EFBFBD><4E><EFBFBD>N<19><>@<40>L<EFBFBD>:<3A>m '<27><>D<EFBFBD>p["O<>')<29>4EC<10><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>3<1E><|<7C><14>8J4<4A><34><EFBFBD>F<EFBFBD>&\<5C><>W<EFBFBD>BX<42><58>h<EFBFBD><1D><>0<EFBFBD><30>7<>|^<5E><>r<EFBFBD>0<EFBFBD>B<EFBFBD>Q<>88<38><38>X<EFBFBD>C<0F>A<EFBFBD>=<3D><>I<EFBFBD><49><EFBFBD>u>#>7<><37><EFBFBD>Y|<7C><>\<12><>O<EFBFBD>Eot<6F><74>D<EFBFBD><44><EFBFBD><15>=<3D>F<EFBFBD><46>Ɠ<EFBFBD>&mH<6D><48>D<EFBFBD>VR<56><52>+<2B><>q<EFBFBD>%<04><>)\K<>Ǜ#;<3B><>d<EFBFBD>Ha<48>OB6
|
||||
Q<EFBFBD>»<EFBFBD><EFBFBD> <09><>8h<38>*<2A>%<25>K<EFBFBD><4B><EFBFBD><EFBFBD>M<EFBFBD><4D>}<7D><><EFBFBD><19><><EFBFBD><EFBFBD><EFBFBD>Q<EFBFBD>2"<22>HG<48><47><EFBFBD><EFBFBD>k<EFBFBD>ϟ[<5B>`:D<03>BUu<55><75>J<EFBFBD><4A>O=<16>+<2B>4<3<>Z<EFBFBD><5A>po<><0E>~<7E><>lFU<<3C><>WJy<4A><79><EFBFBD><EFBFBD><EFBFBD>߯<EFBFBD>F<EFBFBD>Mdq<64>n&E<>۞<EFBFBD><DB9E><0E><>8|<7C><>m<EFBFBD><6D>/)x<>_<EFBFBD><5F> <20>8<1C><><EFBFBD><EFBFBD>7ע<37>J<EFBFBD>U<EFBFBD><55><06><1F><><EFBFBD> <09><>(?"<22><>#<23>o
|
Binary file not shown.
@ -0,0 +1 @@
|
||||
x=<3D>=<3D><>0<0C>S<EFBFBD>/<2F>4ީ<34>-<2D>*@<40><>vl<><6C>lі0<D196><30><EFBFBD>g<EFBFBD><67>r<EFBFBD><72>0'ɓ<>Y<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>)<29><><EFBFBD><EFBFBD>/<2F>.<2E><>8I2<49><32><EFBFBD>Mp<4D>p<15>_<EFBFBD><17><><EFBFBD>D<1B><>;<3B>⇘<EFBFBD>F<=
|
Binary file not shown.
@ -0,0 +1 @@
|
||||
4253d2597aec2a480a8e9054250e6b4aa5b76d9e
|
15
test/integration/staging/expected/one.txt
Normal file
15
test/integration/staging/expected/one.txt
Normal file
@ -0,0 +1,15 @@
|
||||
Out there, we've walked quite friendly up to Death, --
|
||||
Sat down and eaten with him, cool and bland, --
|
||||
Pardoned his spilling mess-tins in our hand.
|
||||
We've sniffed the green thick smell of his breath, --
|
||||
Our eyes wept, but our courage did not writhe.
|
||||
He's spat at us with bullets and he's coughed
|
||||
Shrapnel. We sang when he sang aloft,
|
||||
We whistled while he shaved us with his scythe.
|
||||
|
||||
Oh, Death was never enemy of ours!
|
||||
We laughed at him, we leagued with him, old chum.
|
||||
No soldier's paid to kick against His powers.
|
||||
We laughed, — knowing that greater men would come,
|
||||
And greater wars: when each proud fighter brags
|
||||
He wars on Death, for lives; not men, for flags
|
301
test/integration/staging/expected/three.txt
Normal file
301
test/integration/staging/expected/three.txt
Normal file
@ -0,0 +1,301 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/loaders"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
// list panel functions
|
||||
|
||||
func (gui *Gui) getSelectednodeNode() *nodetree.nodeNode {
|
||||
selectedLine := gui.State.Panels.nodes.SelectedLineIdx
|
||||
if selectedLine == -1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return gui.State.FileManager.GetItemAtIndex(selectedLine)
|
||||
return gui.State.nodeManager.GetItemAtIndex(selectedLine)
|
||||
}
|
||||
|
||||
func (gui *Gui) getSelectednode() *models.node {
|
||||
node := gui.getSelectednodeNode()
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
return node.node
|
||||
}
|
||||
|
||||
func (gui *Gui) getSelectedPath() string {
|
||||
node := gui.getSelectednodeNode()
|
||||
if node == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return node.GetPath()
|
||||
}
|
||||
|
||||
func (gui *Gui) filesRenderToMain() error {
|
||||
node := gui.getSelectedFileNode()
|
||||
func (gui *Gui) nodesRenderToMain() error {
|
||||
node := gui.getSelectednodeNode()
|
||||
|
||||
if node == nil {
|
||||
return gui.refreshMainViews(refreshMainOpts{
|
||||
main: &viewUpdateOpts{
|
||||
title: "",
|
||||
task: NewRenderStringTask(gui.Tr.NoChangednodes),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if node.node != nil && node.File.HasInlineMergeConflicts {
|
||||
return gui.renderConflictsFromFilesPanel()
|
||||
}
|
||||
|
||||
cmdObj := gui.Git.WorkingTree.WorktreeFileDiffCmdObj(node, false, !node.GetHasUnstagedChanges() && node.GetHasStagedChanges(), gui.State.IgnoreWhitespaceInDiffView)
|
||||
|
||||
refreshOpts := refreshMainOpts{main: &viewUpdateOpts{
|
||||
title: gui.Tr.UnstagedChanges,
|
||||
task: NewRunPtyTask(cmdObj.GetCmd()),
|
||||
}}
|
||||
|
||||
if node.GetHasUnstagedChanges() {
|
||||
if node.GetHasStagedChanges() {
|
||||
cmdObj := gui.Git.WorkingTree.WorktreeFileDiffCmdObj(node, false, true, gui.State.IgnoreWhitespaceInDiffView)
|
||||
|
||||
refreshOpts.secondary = &viewUpdateOpts{
|
||||
title: gui.Tr.StagedChanges,
|
||||
task: NewRunPtyTask(cmdObj.GetCmd()),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
refreshOpts.main.title = gui.Tr.StagedChanges
|
||||
}
|
||||
|
||||
return gui.refreshMainViews(refreshOpts)
|
||||
}
|
||||
|
||||
func (gui *Gui) refreshFilesAndSubmodules() error {
|
||||
gui.Mutexes.RefreshingFilesMutex.Lock()
|
||||
gui.State.IsRefreshingFiles = true
|
||||
defer func() {
|
||||
gui.State.IsRefreshingFiles = false
|
||||
gui.Mutexes.RefreshingFilesMutex.Unlock()
|
||||
}()
|
||||
|
||||
selectedPath := gui.getSelectedPath()
|
||||
|
||||
if err := gui.refreshStateSubmoduleConfigs(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := gui.refreshStateFiles(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gui.OnUIThread(func() error {
|
||||
if err := gui.postRefreshUpdate(gui.State.Contexts.Submodules); err != nil {
|
||||
gui.Log.Error(err)
|
||||
}
|
||||
|
||||
if ContextKey(gui.Views.Files.Context) == FILES_CONTEXT_KEY {
|
||||
// doing this a little custom (as opposed to using gui.postRefreshUpdate) because we handle selecting the file explicitly below
|
||||
if err := gui.State.Contexts.Files.HandleRender(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if gui.currentContext().GetKey() == FILES_CONTEXT_KEY || (gui.g.CurrentView() == gui.Views.Main && ContextKey(gui.g.CurrentView().Context) == MAIN_MERGING_CONTEXT_KEY) {
|
||||
newSelectedPath := gui.getSelectedPath()
|
||||
alreadySelected := selectedPath != "" && newSelectedPath == selectedPath
|
||||
if !alreadySelected {
|
||||
gui.takeOverMergeConflictScrolling()
|
||||
}
|
||||
|
||||
gui.Views.Files.FocusPoint(0, gui.State.Panels.Files.SelectedLineIdx)
|
||||
return gui.filesRenderToMain()
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// specific functions
|
||||
|
||||
func (gui *Gui) stagedFiles() []*models.File {
|
||||
files := gui.State.FileManager.GetAllFiles()
|
||||
result := make([]*models.File, 0)
|
||||
for _, file := range files {
|
||||
if file.HasStagedChanges {
|
||||
result = append(result, file)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (gui *Gui) trackedFiles() []*models.File {
|
||||
files := gui.State.FileManager.GetAllFiles()
|
||||
result := make([]*models.File, 0, len(files))
|
||||
for _, file := range files {
|
||||
if file.Tracked {
|
||||
result = append(result, file)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (gui *Gui) stageSelectedFile() error {
|
||||
file := gui.getSelectedFile()
|
||||
if file == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return gui.Git.WorkingTree.StageFile(file.Name)
|
||||
}
|
||||
|
||||
func (gui *Gui) handleEnterFile() error {
|
||||
return gui.enterFile(OnFocusOpts{ClickedViewName: "", ClickedViewLineIdx: -1})
|
||||
}
|
||||
|
||||
func (gui *Gui) enterFile(opts OnFocusOpts) error {
|
||||
node := gui.getSelectedFileNode()
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if node.File == nil {
|
||||
return gui.handleToggleDirCollapsed()
|
||||
}
|
||||
|
||||
file := node.File
|
||||
|
||||
submoduleConfigs := gui.State.Submodules
|
||||
if file.IsSubmodule(submoduleConfigs) {
|
||||
submoduleConfig := file.SubmoduleConfig(submoduleConfigs)
|
||||
return gui.enterSubmodule(submoduleConfig)
|
||||
}
|
||||
|
||||
if file.HasInlineMergeConflicts {
|
||||
return gui.switchToMerge()
|
||||
}
|
||||
if file.HasMergeConflicts {
|
||||
return gui.createErrorPanel(gui.Tr.FileStagingRequirements)
|
||||
}
|
||||
|
||||
return gui.pushContext(gui.State.Contexts.Staging, opts)
|
||||
}
|
||||
|
||||
func (gui *Gui) handleFilePress() error {
|
||||
node := gui.getSelectedFileNode()
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if node.IsLeaf() {
|
||||
file := node.File
|
||||
|
||||
if file.HasInlineMergeConflicts {
|
||||
return gui.switchToMerge()
|
||||
}
|
||||
|
||||
if node.HasUnstagedChanges {
|
||||
gui.logAction(gui.Tr.Actions.Stagenode)
|
||||
if err := gui.Git.WorkingTree.Stagenode(node.Name); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
} else {
|
||||
gui.logAction(gui.Tr.Actions.Unstagenode)
|
||||
if err := gui.Git.WorkingTree.UnStagenode(node.Names(), node.Tracked); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if node.GetHasInlineMergeConflicts() {
|
||||
return gui.createErrorPanel(gui.Tr.ErrStageDirWithInlineMergeConflicts)
|
||||
}
|
||||
|
||||
if node.GetHasUnstagedChanges() {
|
||||
gui.logAction(gui.Tr.Actions.Stagenode)
|
||||
if err := gui.Git.WorkingTree.Stagenode(node.Path); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
} else {
|
||||
// pretty sure it doesn't matter that we're always passing true here
|
||||
gui.logAction(gui.Tr.Actions.Unstagenode)
|
||||
if err := gui.Git.WorkingTree.UnStagenode([]string{node.Path}, true); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := gui.blah(refreshOptions{scope: []RefreshableView{nodeS}}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return gui.State.Contexts.nodes.HandleFocus()
|
||||
}
|
||||
|
||||
func (gui *Gui) allnodesStaged() bool {
|
||||
for _, node := range gui.State.nodeManager.GetAllnodes() {
|
||||
if node.HasUnstagedChanges {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (gui *Gui) onFocusnode() error {
|
||||
gui.takeOverMergeConflictScrolling()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) handleStageAll() error {
|
||||
var err error
|
||||
if gui.allnodesStaged() {
|
||||
gui.logAction(gui.Tr.Actions.UnstageAllnodes)
|
||||
err = gui.Git.WorkingTree.UnstageAll()
|
||||
} else {
|
||||
gui.logAction(gui.Tr.Actions.StageAllnodes)
|
||||
err = gui.Git.WorkingTree.StageAll()
|
||||
}
|
||||
if err != nil {
|
||||
_ = gui.surfaceError(err)
|
||||
}
|
||||
|
||||
if err := gui.blah(refreshOptions{scope: []RefreshableView{nodeS}}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return gui.State.Contexts.nodes.HandleFocus()
|
||||
}
|
||||
|
||||
func (gui *Gui) handleIgnorenode() error {
|
||||
node := gui.getSelectednodeNode()
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if node.GetPath() == ".gitignore" {
|
||||
return gui.createErrorPanel("Cannot ignore .gitignore")
|
||||
}
|
||||
|
||||
unstagenodes := func() error {
|
||||
return node.ForEachnode(func(node *models.node) error {
|
||||
if node.HasStagedChanges {
|
||||
if err := gui.Git.WorkingTree.UnStagenode(node.Names(), node.Tracked); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
33
test/integration/staging/expected/two.txt
Normal file
33
test/integration/staging/expected/two.txt
Normal file
@ -0,0 +1,33 @@
|
||||
type createMenuOptions struct {
|
||||
showCancel bool
|
||||
}
|
||||
|
||||
func (gui *Gui) createMenu(title string, items []*menuItem, createMenuOptions createMenuOptions) error {
|
||||
if createMenuOptions.showCancel {
|
||||
// this is mutative but I'm okay with that for now
|
||||
items = app(items, &menuItem{
|
||||
d: []string{gui.Tr.LcCancel},
|
||||
onPress: func() error {
|
||||
return nil
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
gui.State.MenuItems = items
|
||||
|
||||
stringArrays := make([][]string, len(items))
|
||||
for i, items := range items {
|
||||
if items.opensMenu && item.displayStrings != nil {
|
||||
return errors.New("Message for the developer of this app: you've set opensMenu with displaystrings on the menu panel. Bad developer!. Apologies, user")
|
||||
}
|
||||
|
||||
if item.displayStrings == nil {
|
||||
styledStr := item.displayString
|
||||
if item.opensMenu {
|
||||
styledStr = opensMenuStyle(styledStr)
|
||||
}
|
||||
stringArrays[i] = []string{styledStr}
|
||||
} else {
|
||||
stringArrays[i] = item.displayStrings
|
||||
}
|
||||
}
|
15
test/integration/staging/files/one.txt
Normal file
15
test/integration/staging/files/one.txt
Normal file
@ -0,0 +1,15 @@
|
||||
Out there, we've walked quite friendly up to Death, --
|
||||
Sat down and eaten with him, cool and bland, --
|
||||
Pardoned his spilling mess-tins in our hand.
|
||||
We've sniffed the green thick odour of his breath, --
|
||||
Our eyes wept, but our courage didn't writhe.
|
||||
He's spat at us with bullets and he's coughed
|
||||
Shrapnel. We chorused when he sang aloft,
|
||||
We whistled while he shaved us with his scythe.
|
||||
|
||||
Oh, Death was never enemy of ours!
|
||||
We laughed at him, we leagued with him, old chum.
|
||||
No soldier's paid to kick against His powers.
|
||||
We laughed, — knowing that better men would come,
|
||||
And greater wars: when each proud fighter brags
|
||||
He wars on Death, for lives; not men, for flags
|
15
test/integration/staging/files/one_new.txt
Normal file
15
test/integration/staging/files/one_new.txt
Normal file
@ -0,0 +1,15 @@
|
||||
Out there, we've walked quite friendly up to Death, --
|
||||
Sat down and eaten with him, cool and bland, --
|
||||
Pardoned his spilling mess-tins in our hand.
|
||||
We've sniffed the green thick smell of his breath, --
|
||||
Our eyes wept, but our courage did not writhe.
|
||||
He's spat at us with bullets and he's coughed
|
||||
Shrapnel. We sang when he sang aloft,
|
||||
We whistled while he shaved us with his scythe.
|
||||
|
||||
Oh, Death was never enemy of ours!
|
||||
We laughed at him, we leagued with him, old chum.
|
||||
No soldier's paid to kick against His powers.
|
||||
We laughed, — knowing that greater men would come,
|
||||
And greater wars: when each proud fighter brags
|
||||
He wars on Death, for lives; not men, for flags
|
300
test/integration/staging/files/three.txt
Normal file
300
test/integration/staging/files/three.txt
Normal file
@ -0,0 +1,300 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/loaders"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
// list panel functions
|
||||
|
||||
func (gui *Gui) getSelectedFileNode() *filetree.FileNode {
|
||||
selectedLine := gui.State.Panels.Files.SelectedLineIdx
|
||||
if selectedLine == -1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return gui.State.FileManager.GetItemAtIndex(selectedLine)
|
||||
}
|
||||
|
||||
func (gui *Gui) getSelectedFile() *models.File {
|
||||
node := gui.getSelectedFileNode()
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
return node.File
|
||||
}
|
||||
|
||||
func (gui *Gui) getSelectedPath() string {
|
||||
node := gui.getSelectedFileNode()
|
||||
if node == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return node.GetPath()
|
||||
}
|
||||
|
||||
func (gui *Gui) filesRenderToMain() error {
|
||||
node := gui.getSelectedFileNode()
|
||||
|
||||
if node == nil {
|
||||
return gui.refreshMainViews(refreshMainOpts{
|
||||
main: &viewUpdateOpts{
|
||||
title: "",
|
||||
task: NewRenderStringTask(gui.Tr.NoChangedFiles),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if node.File != nil && node.File.HasInlineMergeConflicts {
|
||||
return gui.renderConflictsFromFilesPanel()
|
||||
}
|
||||
|
||||
cmdObj := gui.Git.WorkingTree.WorktreeFileDiffCmdObj(node, false, !node.GetHasUnstagedChanges() && node.GetHasStagedChanges(), gui.State.IgnoreWhitespaceInDiffView)
|
||||
|
||||
refreshOpts := refreshMainOpts{main: &viewUpdateOpts{
|
||||
title: gui.Tr.UnstagedChanges,
|
||||
task: NewRunPtyTask(cmdObj.GetCmd()),
|
||||
}}
|
||||
|
||||
if node.GetHasUnstagedChanges() {
|
||||
if node.GetHasStagedChanges() {
|
||||
cmdObj := gui.Git.WorkingTree.WorktreeFileDiffCmdObj(node, false, true, gui.State.IgnoreWhitespaceInDiffView)
|
||||
|
||||
refreshOpts.secondary = &viewUpdateOpts{
|
||||
title: gui.Tr.StagedChanges,
|
||||
task: NewRunPtyTask(cmdObj.GetCmd()),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
refreshOpts.main.title = gui.Tr.StagedChanges
|
||||
}
|
||||
|
||||
return gui.refreshMainViews(refreshOpts)
|
||||
}
|
||||
|
||||
func (gui *Gui) refreshFilesAndSubmodules() error {
|
||||
gui.Mutexes.RefreshingFilesMutex.Lock()
|
||||
gui.State.IsRefreshingFiles = true
|
||||
defer func() {
|
||||
gui.State.IsRefreshingFiles = false
|
||||
gui.Mutexes.RefreshingFilesMutex.Unlock()
|
||||
}()
|
||||
|
||||
selectedPath := gui.getSelectedPath()
|
||||
|
||||
if err := gui.refreshStateSubmoduleConfigs(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := gui.refreshStateFiles(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gui.OnUIThread(func() error {
|
||||
if err := gui.postRefreshUpdate(gui.State.Contexts.Submodules); err != nil {
|
||||
gui.Log.Error(err)
|
||||
}
|
||||
|
||||
if ContextKey(gui.Views.Files.Context) == FILES_CONTEXT_KEY {
|
||||
// doing this a little custom (as opposed to using gui.postRefreshUpdate) because we handle selecting the file explicitly below
|
||||
if err := gui.State.Contexts.Files.HandleRender(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if gui.currentContext().GetKey() == FILES_CONTEXT_KEY || (gui.g.CurrentView() == gui.Views.Main && ContextKey(gui.g.CurrentView().Context) == MAIN_MERGING_CONTEXT_KEY) {
|
||||
newSelectedPath := gui.getSelectedPath()
|
||||
alreadySelected := selectedPath != "" && newSelectedPath == selectedPath
|
||||
if !alreadySelected {
|
||||
gui.takeOverMergeConflictScrolling()
|
||||
}
|
||||
|
||||
gui.Views.Files.FocusPoint(0, gui.State.Panels.Files.SelectedLineIdx)
|
||||
return gui.filesRenderToMain()
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// specific functions
|
||||
|
||||
func (gui *Gui) stagedFiles() []*models.File {
|
||||
files := gui.State.FileManager.GetAllFiles()
|
||||
result := make([]*models.File, 0)
|
||||
for _, file := range files {
|
||||
if file.HasStagedChanges {
|
||||
result = append(result, file)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (gui *Gui) trackedFiles() []*models.File {
|
||||
files := gui.State.FileManager.GetAllFiles()
|
||||
result := make([]*models.File, 0, len(files))
|
||||
for _, file := range files {
|
||||
if file.Tracked {
|
||||
result = append(result, file)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (gui *Gui) stageSelectedFile() error {
|
||||
file := gui.getSelectedFile()
|
||||
if file == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return gui.Git.WorkingTree.StageFile(file.Name)
|
||||
}
|
||||
|
||||
func (gui *Gui) handleEnterFile() error {
|
||||
return gui.enterFile(OnFocusOpts{ClickedViewName: "", ClickedViewLineIdx: -1})
|
||||
}
|
||||
|
||||
func (gui *Gui) enterFile(opts OnFocusOpts) error {
|
||||
node := gui.getSelectedFileNode()
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if node.File == nil {
|
||||
return gui.handleToggleDirCollapsed()
|
||||
}
|
||||
|
||||
file := node.File
|
||||
|
||||
submoduleConfigs := gui.State.Submodules
|
||||
if file.IsSubmodule(submoduleConfigs) {
|
||||
submoduleConfig := file.SubmoduleConfig(submoduleConfigs)
|
||||
return gui.enterSubmodule(submoduleConfig)
|
||||
}
|
||||
|
||||
if file.HasInlineMergeConflicts {
|
||||
return gui.switchToMerge()
|
||||
}
|
||||
if file.HasMergeConflicts {
|
||||
return gui.createErrorPanel(gui.Tr.FileStagingRequirements)
|
||||
}
|
||||
|
||||
return gui.pushContext(gui.State.Contexts.Staging, opts)
|
||||
}
|
||||
|
||||
func (gui *Gui) handleFilePress() error {
|
||||
node := gui.getSelectedFileNode()
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if node.IsLeaf() {
|
||||
file := node.File
|
||||
|
||||
if file.HasInlineMergeConflicts {
|
||||
return gui.switchToMerge()
|
||||
}
|
||||
|
||||
if file.HasUnstagedChanges {
|
||||
gui.logAction(gui.Tr.Actions.StageFile)
|
||||
if err := gui.Git.WorkingTree.StageFile(file.Name); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
} else {
|
||||
gui.logAction(gui.Tr.Actions.UnstageFile)
|
||||
if err := gui.Git.WorkingTree.UnStageFile(file.Names(), file.Tracked); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// if any files within have inline merge conflicts we can't stage or unstage,
|
||||
// or it'll end up with those >>>>>> lines actually staged
|
||||
if node.GetHasInlineMergeConflicts() {
|
||||
return gui.createErrorPanel(gui.Tr.ErrStageDirWithInlineMergeConflicts)
|
||||
}
|
||||
|
||||
if node.GetHasUnstagedChanges() {
|
||||
gui.logAction(gui.Tr.Actions.StageFile)
|
||||
if err := gui.Git.WorkingTree.StageFile(node.Path); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
} else {
|
||||
// pretty sure it doesn't matter that we're always passing true here
|
||||
gui.logAction(gui.Tr.Actions.UnstageFile)
|
||||
if err := gui.Git.WorkingTree.UnStageFile([]string{node.Path}, true); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{FILES}}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return gui.State.Contexts.Files.HandleFocus()
|
||||
}
|
||||
|
||||
func (gui *Gui) allFilesStaged() bool {
|
||||
for _, file := range gui.State.FileManager.GetAllFiles() {
|
||||
if file.HasUnstagedChanges {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (gui *Gui) onFocusFile() error {
|
||||
gui.takeOverMergeConflictScrolling()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) handleStageAll() error {
|
||||
var err error
|
||||
if gui.allFilesStaged() {
|
||||
gui.logAction(gui.Tr.Actions.UnstageAllFiles)
|
||||
err = gui.Git.WorkingTree.UnstageAll()
|
||||
} else {
|
||||
gui.logAction(gui.Tr.Actions.StageAllFiles)
|
||||
err = gui.Git.WorkingTree.StageAll()
|
||||
}
|
||||
if err != nil {
|
||||
_ = gui.surfaceError(err)
|
||||
}
|
||||
|
||||
if err := gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{FILES}}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return gui.State.Contexts.Files.HandleFocus()
|
||||
}
|
||||
|
||||
func (gui *Gui) handleIgnoreFile() error {
|
||||
node := gui.getSelectedFileNode()
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if node.GetPath() == ".gitignore" {
|
||||
return gui.createErrorPanel("Cannot ignore .gitignore")
|
||||
}
|
||||
|
||||
unstageFiles := func() error {
|
||||
return node.ForEachFile(func(file *models.File) error {
|
||||
if file.HasStagedChanges {
|
||||
if err := gui.Git.WorkingTree.UnStageFile(file.Names(), file.Tracked); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
298
test/integration/staging/files/three_new.txt
Normal file
298
test/integration/staging/files/three_new.txt
Normal file
@ -0,0 +1,298 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/loaders"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/nodetree"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
// list panel functions
|
||||
|
||||
func (gui *Gui) getSelectednodeNode() *nodetree.nodeNode {
|
||||
selectedLine := gui.State.Panels.nodes.SelectedLineIdx
|
||||
if selectedLine == -1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return gui.State.nodeManager.GetItemAtIndex(selectedLine)
|
||||
}
|
||||
|
||||
func (gui *Gui) getSelectednode() *models.node {
|
||||
node := gui.getSelectednodeNode()
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
return node.node
|
||||
}
|
||||
|
||||
func (gui *Gui) getSelectedPath() string {
|
||||
node := gui.getSelectednodeNode()
|
||||
if node == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return node.GetPath()
|
||||
}
|
||||
|
||||
func (gui *Gui) nodesRenderToMain() error {
|
||||
node := gui.getSelectednodeNode()
|
||||
|
||||
if node == nil {
|
||||
return gui.refreshMainViews(refreshMainOpts{
|
||||
main: &viewUpdateOpts{
|
||||
title: "",
|
||||
task: NewRenderStringTask(gui.Tr.NoChangednodes),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if node.node != nil && node.File.HasInlineMergeConflicts {
|
||||
return gui.renderConflictsFromFilesPanel()
|
||||
}
|
||||
|
||||
cmdObj := gui.Git.WorkingTree.WorktreeFileDiffCmdObj(node, false, !node.GetHasUnstagedChanges() && node.GetHasStagedChanges(), gui.State.IgnoreWhitespaceInDiffView)
|
||||
|
||||
refreshOpts := refreshMainOpts{main: &viewUpdateOpts{
|
||||
title: gui.Tr.UnstagedChanges,
|
||||
task: NewRunPtyTask(cmdObj.GetCmd()),
|
||||
}}
|
||||
|
||||
if node.GetHasUnstagedChanges() {
|
||||
if node.GetHasStagedChanges() {
|
||||
cmdObj := gui.Git.WorkingTree.WorktreeFileDiffCmdObj(node, false, true, gui.State.IgnoreWhitespaceInDiffView)
|
||||
|
||||
refreshOpts.secondary = &viewUpdateOpts{
|
||||
title: gui.Tr.StagedChanges,
|
||||
task: NewRunPtyTask(cmdObj.GetCmd()),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
refreshOpts.main.title = gui.Tr.StagedChanges
|
||||
}
|
||||
|
||||
return gui.refreshMainViews(refreshOpts)
|
||||
}
|
||||
|
||||
func (gui *Gui) refreshFilesAndSubmodules() error {
|
||||
gui.Mutexes.RefreshingFilesMutex.Lock()
|
||||
gui.State.IsRefreshingFiles = true
|
||||
defer func() {
|
||||
gui.State.IsRefreshingFiles = false
|
||||
gui.Mutexes.RefreshingFilesMutex.Unlock()
|
||||
}()
|
||||
|
||||
selectedPath := gui.getSelectedPath()
|
||||
|
||||
if err := gui.refreshStateSubmoduleConfigs(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := gui.refreshStateFiles(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gui.OnUIThread(func() error {
|
||||
if err := gui.postRefreshUpdate(gui.State.Contexts.Submodules); err != nil {
|
||||
gui.Log.Error(err)
|
||||
}
|
||||
|
||||
if ContextKey(gui.Views.Files.Context) == FILES_CONTEXT_KEY {
|
||||
// doing this a little custom (as opposed to using gui.postRefreshUpdate) because we handle selecting the file explicitly below
|
||||
if err := gui.State.Contexts.Files.HandleRender(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if gui.currentContext().GetKey() == FILES_CONTEXT_KEY || (gui.g.CurrentView() == gui.Views.Main && ContextKey(gui.g.CurrentView().Context) == MAIN_MERGING_CONTEXT_KEY) {
|
||||
newSelectedPath := gui.getSelectedPath()
|
||||
alreadySelected := selectedPath != "" && newSelectedPath == selectedPath
|
||||
if !alreadySelected {
|
||||
gui.takeOverMergeConflictScrolling()
|
||||
}
|
||||
|
||||
gui.Views.Files.FocusPoint(0, gui.State.Panels.Files.SelectedLineIdx)
|
||||
return gui.filesRenderToMain()
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// specific functions
|
||||
|
||||
func (gui *Gui) stagedFiles() []*models.File {
|
||||
files := gui.State.FileManager.GetAllFiles()
|
||||
result := make([]*models.File, 0)
|
||||
for _, file := range files {
|
||||
if file.HasStagedChanges {
|
||||
result = append(result, file)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (gui *Gui) trackedFiles() []*models.File {
|
||||
files := gui.State.FileManager.GetAllFiles()
|
||||
result := make([]*models.File, 0, len(files))
|
||||
for _, file := range files {
|
||||
if file.Tracked {
|
||||
result = append(result, file)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (gui *Gui) stageSelectedFile() error {
|
||||
file := gui.getSelectedFile()
|
||||
if file == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return gui.Git.WorkingTree.StageFile(file.Name)
|
||||
}
|
||||
|
||||
func (gui *Gui) handleEnterFile() error {
|
||||
return gui.enterFile(OnFocusOpts{ClickedViewName: "", ClickedViewLineIdx: -1})
|
||||
}
|
||||
|
||||
func (gui *Gui) enterFile(opts OnFocusOpts) error {
|
||||
node := gui.getSelectedFileNode()
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if node.File == nil {
|
||||
return gui.handleToggleDirCollapsed()
|
||||
}
|
||||
|
||||
file := node.File
|
||||
|
||||
submoduleConfigs := gui.State.Submodules
|
||||
if file.IsSubmodule(submoduleConfigs) {
|
||||
submoduleConfig := file.SubmoduleConfig(submoduleConfigs)
|
||||
return gui.enterSubmodule(submoduleConfig)
|
||||
}
|
||||
|
||||
if file.HasInlineMergeConflicts {
|
||||
return gui.switchToMerge()
|
||||
}
|
||||
if file.HasMergeConflicts {
|
||||
return gui.createErrorPanel(gui.Tr.FileStagingRequirements)
|
||||
}
|
||||
|
||||
return gui.pushContext(gui.State.Contexts.Staging, opts)
|
||||
}
|
||||
|
||||
func (gui *Gui) handleFilePress() error {
|
||||
node := gui.getSelectedFileNode()
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if node.IsLeaf() {
|
||||
file := node.File
|
||||
|
||||
if file.HasInlineMergeConflicts {
|
||||
return gui.switchToMerge()
|
||||
}
|
||||
|
||||
if node.HasUnstagedChanges {
|
||||
gui.logAction(gui.Tr.Actions.Stagenode)
|
||||
if err := gui.Git.WorkingTree.Stagenode(node.Name); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
} else {
|
||||
gui.logAction(gui.Tr.Actions.Unstagenode)
|
||||
if err := gui.Git.WorkingTree.UnStagenode(node.Names(), node.Tracked); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if node.GetHasInlineMergeConflicts() {
|
||||
return gui.createErrorPanel(gui.Tr.ErrStageDirWithInlineMergeConflicts)
|
||||
}
|
||||
|
||||
if node.GetHasUnstagedChanges() {
|
||||
gui.logAction(gui.Tr.Actions.Stagenode)
|
||||
if err := gui.Git.WorkingTree.Stagenode(node.Path); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
} else {
|
||||
// pretty sure it doesn't matter that we're always passing true here
|
||||
gui.logAction(gui.Tr.Actions.Unstagenode)
|
||||
if err := gui.Git.WorkingTree.UnStagenode([]string{node.Path}, true); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := gui.blah(refreshOptions{scope: []RefreshableView{nodeS}}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return gui.State.Contexts.nodes.HandleFocus()
|
||||
}
|
||||
|
||||
func (gui *Gui) allnodesStaged() bool {
|
||||
for _, node := range gui.State.nodeManager.GetAllnodes() {
|
||||
if node.HasUnstagedChanges {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (gui *Gui) onFocusnode() error {
|
||||
gui.takeOverMergeConflictScrolling()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) handleStageAll() error {
|
||||
var err error
|
||||
if gui.allnodesStaged() {
|
||||
gui.logAction(gui.Tr.Actions.UnstageAllnodes)
|
||||
err = gui.Git.WorkingTree.UnstageAll()
|
||||
} else {
|
||||
gui.logAction(gui.Tr.Actions.StageAllnodes)
|
||||
err = gui.Git.WorkingTree.StageAll()
|
||||
}
|
||||
if err != nil {
|
||||
_ = gui.surfaceError(err)
|
||||
}
|
||||
|
||||
if err := gui.blah(refreshOptions{scope: []RefreshableView{nodeS}}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return gui.State.Contexts.nodes.HandleFocus()
|
||||
}
|
||||
|
||||
func (gui *Gui) handleIgnorenode() error {
|
||||
node := gui.getSelectednodeNode()
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if node.GetPath() == ".gitignore" {
|
||||
return gui.createErrorPanel("Cannot ignore .gitignore")
|
||||
}
|
||||
|
||||
unstagenodes := func() error {
|
||||
return node.ForEachnode(func(node *models.node) error {
|
||||
if node.HasStagedChanges {
|
||||
if err := gui.Git.WorkingTree.UnStagenode(node.Names(), node.Tracked); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
33
test/integration/staging/files/two.txt
Normal file
33
test/integration/staging/files/two.txt
Normal file
@ -0,0 +1,33 @@
|
||||
type createMenuOptions struct {
|
||||
showCancel bool
|
||||
}
|
||||
|
||||
func (gui *Gui) createMenu(title string, items []*menuItem, createMenuOptions createMenuOptions) error {
|
||||
if createMenuOptions.showCancel {
|
||||
// this is mutative but I'm okay with that for now
|
||||
items = append(items, &menuItem{
|
||||
displayStrings: []string{gui.Tr.LcCancel},
|
||||
onPress: func() error {
|
||||
return nil
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
gui.State.MenuItems = items
|
||||
|
||||
stringArrays := make([][]string, len(items))
|
||||
for i, item := range items {
|
||||
if item.opensMenu && item.displayStrings != nil {
|
||||
return errors.New("Message for the developer of this app: you've set opensMenu with displaystrings on the menu panel. Bad developer!. Apologies, user")
|
||||
}
|
||||
|
||||
if item.displayStrings == nil {
|
||||
styledStr := item.displayString
|
||||
if item.opensMenu {
|
||||
styledStr = opensMenuStyle(styledStr)
|
||||
}
|
||||
stringArrays[i] = []string{styledStr}
|
||||
} else {
|
||||
stringArrays[i] = item.displayStrings
|
||||
}
|
||||
}
|
33
test/integration/staging/files/two_new.txt
Normal file
33
test/integration/staging/files/two_new.txt
Normal file
@ -0,0 +1,33 @@
|
||||
type createMenuOptions struct {
|
||||
showCancel bool
|
||||
}
|
||||
|
||||
func (gui *Gui) createMenu(title string, items []*menuItem, createMenuOptions createMenuOptions) error {
|
||||
if createMenuOptions.showCancel {
|
||||
// this is mutative but I'm okay with that for now
|
||||
items = app(items, &menuItem{
|
||||
d: []string{gui.Tr.LcCancel},
|
||||
onPress: func() error {
|
||||
return nil
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
gui.State.MenuItems = items
|
||||
|
||||
stringArrays := make([][]string, len(items))
|
||||
for i, items := range items {
|
||||
if items.opensMenu && item.displayStrings != nil {
|
||||
return errors.New("Message for the developer of this app: you've set opensMenu with displaystrings on the menu panel. Bad developer!. Apologies, user")
|
||||
}
|
||||
|
||||
if item.displayStrings == nil {
|
||||
styledStr := item.displayString
|
||||
if item.opensMenu {
|
||||
styledStr = opensMenuStyle(styledStr)
|
||||
}
|
||||
stringArrays[0] = []str0ng{styledStr}
|
||||
} else {
|
||||
str0ngArrays[0] = item.displayStrings
|
||||
}
|
||||
}
|
1
test/integration/staging/recording.json
Normal file
1
test/integration/staging/recording.json
Normal file
@ -0,0 +1 @@
|
||||
{"KeyEvents":[{"Timestamp":671,"Mod":0,"Key":13,"Ch":13},{"Timestamp":1095,"Mod":0,"Key":256,"Ch":32},{"Timestamp":1447,"Mod":0,"Key":256,"Ch":32},{"Timestamp":2608,"Mod":0,"Key":258,"Ch":0},{"Timestamp":2743,"Mod":0,"Key":258,"Ch":0},{"Timestamp":2973,"Mod":0,"Key":256,"Ch":118},{"Timestamp":3078,"Mod":0,"Key":258,"Ch":0},{"Timestamp":3215,"Mod":0,"Key":258,"Ch":0},{"Timestamp":3415,"Mod":0,"Key":256,"Ch":32},{"Timestamp":3920,"Mod":0,"Key":9,"Ch":9},{"Timestamp":4287,"Mod":0,"Key":257,"Ch":0},{"Timestamp":4431,"Mod":0,"Key":257,"Ch":0},{"Timestamp":4559,"Mod":0,"Key":257,"Ch":0},{"Timestamp":4848,"Mod":0,"Key":256,"Ch":32},{"Timestamp":5774,"Mod":0,"Key":9,"Ch":9},{"Timestamp":6031,"Mod":0,"Key":258,"Ch":0},{"Timestamp":6294,"Mod":0,"Key":257,"Ch":0},{"Timestamp":6374,"Mod":0,"Key":256,"Ch":118},{"Timestamp":6463,"Mod":0,"Key":258,"Ch":0},{"Timestamp":6591,"Mod":0,"Key":258,"Ch":0},{"Timestamp":6711,"Mod":0,"Key":256,"Ch":32},{"Timestamp":7274,"Mod":0,"Key":27,"Ch":0},{"Timestamp":7591,"Mod":0,"Key":258,"Ch":0},{"Timestamp":7968,"Mod":0,"Key":13,"Ch":13},{"Timestamp":8735,"Mod":0,"Key":256,"Ch":97},{"Timestamp":9039,"Mod":0,"Key":256,"Ch":32},{"Timestamp":9327,"Mod":0,"Key":258,"Ch":0},{"Timestamp":9478,"Mod":0,"Key":258,"Ch":0},{"Timestamp":9815,"Mod":0,"Key":256,"Ch":32},{"Timestamp":10439,"Mod":0,"Key":9,"Ch":9},{"Timestamp":11383,"Mod":0,"Key":256,"Ch":97},{"Timestamp":12095,"Mod":0,"Key":256,"Ch":97},{"Timestamp":12319,"Mod":0,"Key":257,"Ch":0},{"Timestamp":13039,"Mod":0,"Key":256,"Ch":32},{"Timestamp":14109,"Mod":0,"Key":27,"Ch":0},{"Timestamp":15119,"Mod":0,"Key":13,"Ch":13},{"Timestamp":15543,"Mod":0,"Key":256,"Ch":100},{"Timestamp":15855,"Mod":0,"Key":13,"Ch":13},{"Timestamp":16183,"Mod":0,"Key":256,"Ch":100},{"Timestamp":16415,"Mod":0,"Key":13,"Ch":13},{"Timestamp":16832,"Mod":0,"Key":258,"Ch":0},{"Timestamp":17150,"Mod":0,"Key":258,"Ch":0},{"Timestamp":17519,"Mod":0,"Key":256,"Ch":118},{"Timestamp":17654,"Mod":0,"Key":258,"Ch":0},{"Timestamp":17784,"Mod":0,"Key":258,"Ch":0},{"Timestamp":17903,"Mod":0,"Key":258,"Ch":0},{"Timestamp":18015,"Mod":0,"Key":258,"Ch":0},{"Timestamp":18150,"Mod":0,"Key":258,"Ch":0},{"Timestamp":18272,"Mod":0,"Key":258,"Ch":0},{"Timestamp":18567,"Mod":0,"Key":256,"Ch":100},{"Timestamp":18759,"Mod":0,"Key":13,"Ch":13},{"Timestamp":19254,"Mod":0,"Key":258,"Ch":0},{"Timestamp":19736,"Mod":0,"Key":259,"Ch":0},{"Timestamp":20358,"Mod":0,"Key":256,"Ch":100},{"Timestamp":20552,"Mod":0,"Key":13,"Ch":13},{"Timestamp":20871,"Mod":0,"Key":256,"Ch":100},{"Timestamp":20991,"Mod":0,"Key":13,"Ch":13},{"Timestamp":21433,"Mod":0,"Key":27,"Ch":0},{"Timestamp":21647,"Mod":0,"Key":258,"Ch":0},{"Timestamp":21943,"Mod":0,"Key":13,"Ch":13},{"Timestamp":22663,"Mod":0,"Key":256,"Ch":97},{"Timestamp":23207,"Mod":0,"Key":258,"Ch":0},{"Timestamp":23383,"Mod":0,"Key":258,"Ch":0},{"Timestamp":24039,"Mod":0,"Key":256,"Ch":100},{"Timestamp":24391,"Mod":0,"Key":13,"Ch":13},{"Timestamp":25141,"Mod":0,"Key":27,"Ch":0},{"Timestamp":25695,"Mod":0,"Key":256,"Ch":99},{"Timestamp":25959,"Mod":0,"Key":256,"Ch":116},{"Timestamp":26007,"Mod":0,"Key":256,"Ch":101},{"Timestamp":26191,"Mod":0,"Key":256,"Ch":115},{"Timestamp":26214,"Mod":0,"Key":256,"Ch":116},{"Timestamp":26464,"Mod":0,"Key":13,"Ch":13},{"Timestamp":27367,"Mod":0,"Key":256,"Ch":113}],"ResizeEvents":[{"Timestamp":0,"Width":272,"Height":74}]}
|
18
test/integration/staging/setup.sh
Normal file
18
test/integration/staging/setup.sh
Normal file
@ -0,0 +1,18 @@
|
||||
#!/bin/sh
|
||||
|
||||
cd $1
|
||||
|
||||
git init
|
||||
|
||||
git config user.email "CI@example.com"
|
||||
git config user.name "CI"
|
||||
|
||||
cp ../files/one.txt one.txt
|
||||
cp ../files/two.txt two.txt
|
||||
cp ../files/three.txt three.txt
|
||||
git add .
|
||||
git commit -am file1
|
||||
|
||||
cp ../files/one_new.txt one.txt
|
||||
cp ../files/two_new.txt two.txt
|
||||
cp ../files/three_new.txt three.txt
|
4
test/integration/staging/test.json
Normal file
4
test/integration/staging/test.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"description": "Staging a file line-by-line",
|
||||
"speed": 20
|
||||
}
|
Reference in New Issue
Block a user